2022-04-05 11:04:08 +02:00
// Copyright 2022 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"
"time"
"github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/dendrite/roomserver/api"
2023-04-27 13:54:20 +02:00
"github.com/matrix-org/dendrite/roomserver/types"
2022-04-05 11:04:08 +02:00
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/gomatrixserverlib"
2023-04-19 16:50:33 +02:00
"github.com/matrix-org/gomatrixserverlib/spec"
2022-04-05 11:04:08 +02:00
"github.com/matrix-org/util"
"github.com/sirupsen/logrus"
)
type Upgrader struct {
Cfg * config . RoomServer
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
func ( r * Upgrader ) PerformRoomUpgrade (
ctx context . Context ,
2023-04-28 17:46:01 +02:00
roomID , userID string , roomVersion gomatrixserverlib . RoomVersion ,
) ( newRoomID string , err error ) {
return r . performRoomUpgrade ( ctx , roomID , userID , roomVersion )
2022-04-05 11:04:08 +02:00
}
func ( r * Upgrader ) performRoomUpgrade (
ctx context . Context ,
2023-04-28 17:46:01 +02:00
roomID , userID string , roomVersion gomatrixserverlib . RoomVersion ,
) ( string , error ) {
2022-11-15 16:05:23 +01:00
_ , userDomain , err := r . Cfg . Matrix . SplitLocalID ( '@' , userID )
2022-10-26 13:59:19 +02:00
if err != nil {
2023-04-28 17:46:01 +02:00
return "" , api . ErrNotAllowed { Err : fmt . Errorf ( "error validating the user ID" ) }
2022-10-26 13:59:19 +02:00
}
2022-04-05 11:04:08 +02:00
evTime := time . Now ( )
// Return an immediate error if the room does not exist
if err := r . validateRoomExists ( ctx , roomID ) ; err != nil {
2023-04-28 17:46:01 +02:00
return "" , err
2022-04-05 11:04:08 +02:00
}
// 1. Check if the user is authorized to actually perform the upgrade (can send m.room.tombstone)
if ! r . userIsAuthorized ( ctx , userID , roomID ) {
2023-04-28 17:46:01 +02:00
return "" , api . ErrNotAllowed { Err : fmt . Errorf ( "You don't have permission to upgrade the room, power level too low." ) }
2022-04-05 11:04:08 +02:00
}
// TODO (#267): Check room ID doesn't clash with an existing one, and we
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
2022-10-26 13:59:19 +02:00
newRoomID := fmt . Sprintf ( "!%s:%s" , util . RandomString ( 16 ) , userDomain )
2022-04-05 11:04:08 +02:00
// Get the existing room state for the old room.
oldRoomReq := & api . QueryLatestEventsAndStateRequest {
RoomID : roomID ,
}
oldRoomRes := & api . QueryLatestEventsAndStateResponse { }
if err := r . URSAPI . QueryLatestEventsAndState ( ctx , oldRoomReq , oldRoomRes ) ; err != nil {
2023-04-28 17:46:01 +02:00
return "" , fmt . Errorf ( "Failed to get latest state: %s" , err )
2022-04-05 11:04:08 +02:00
}
// Make the tombstone event
tombstoneEvent , pErr := r . makeTombstoneEvent ( ctx , evTime , userID , roomID , newRoomID )
if pErr != nil {
return "" , pErr
}
// Generate the initial events we need to send into the new room. This includes copied state events and bans
// as well as the power level events needed to set up the room
2023-04-28 17:46:01 +02:00
eventsToMake , pErr := r . generateInitialEvents ( ctx , oldRoomRes , userID , roomID , roomVersion , tombstoneEvent )
2022-04-05 11:04:08 +02:00
if pErr != nil {
return "" , pErr
}
2022-06-06 16:18:02 +02:00
// Send the setup events to the new room
2023-04-28 17:46:01 +02:00
if pErr = r . sendInitialEvents ( ctx , evTime , userID , userDomain , newRoomID , roomVersion , eventsToMake ) ; pErr != nil {
2022-04-05 11:04:08 +02:00
return "" , pErr
}
2022-06-06 16:18:02 +02:00
// 5. Send the tombstone event to the old room
2022-10-26 13:59:19 +02:00
if pErr = r . sendHeaderedEvent ( ctx , userDomain , tombstoneEvent , string ( userDomain ) ) ; pErr != nil {
2022-04-05 11:04:08 +02:00
return "" , pErr
}
// If the old room was public, make sure the new one is too
if pErr = r . publishIfOldRoomWasPublic ( ctx , roomID , newRoomID ) ; pErr != nil {
return "" , pErr
}
// If the old room had a canonical alias event, it should be deleted in the old room
2022-10-26 13:59:19 +02:00
if pErr = r . clearOldCanonicalAliasEvent ( ctx , oldRoomRes , evTime , userID , userDomain , roomID ) ; pErr != nil {
2022-04-05 11:04:08 +02:00
return "" , pErr
}
// 4. Move local aliases to the new room
if pErr = moveLocalAliases ( ctx , roomID , newRoomID , userID , r . URSAPI ) ; pErr != nil {
return "" , pErr
}
// 6. Restrict power levels in the old room
2022-10-26 13:59:19 +02:00
if pErr = r . restrictOldRoomPowerLevels ( ctx , evTime , userID , userDomain , roomID ) ; pErr != nil {
2022-04-05 11:04:08 +02:00
return "" , pErr
}
return newRoomID , nil
}
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) getRoomPowerLevels ( ctx context . Context , roomID string ) ( * gomatrixserverlib . PowerLevelContent , error ) {
2022-04-05 11:04:08 +02:00
oldPowerLevelsEvent := api . GetStateEvent ( ctx , r . URSAPI , roomID , gomatrixserverlib . StateKeyTuple {
2023-04-19 16:50:33 +02:00
EventType : spec . MRoomPowerLevels ,
2022-04-05 11:04:08 +02:00
StateKey : "" ,
} )
2023-04-28 17:46:01 +02:00
return oldPowerLevelsEvent . PowerLevels ( )
2022-04-05 11:04:08 +02:00
}
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) restrictOldRoomPowerLevels ( ctx context . Context , evTime time . Time , userID string , userDomain spec . ServerName , roomID string ) error {
2022-04-05 11:04:08 +02:00
restrictedPowerLevelContent , pErr := r . getRoomPowerLevels ( ctx , roomID )
if pErr != nil {
return pErr
}
// From: https://spec.matrix.org/v1.2/client-server-api/#server-behaviour-16
// If possible, the power levels in the old room should also be modified to
// prevent sending of events and inviting new users. For example, setting
// events_default and invite to the greater of 50 and users_default + 1.
restrictedDefaultPowerLevel := int64 ( 50 )
if restrictedPowerLevelContent . UsersDefault + 1 > restrictedDefaultPowerLevel {
restrictedDefaultPowerLevel = restrictedPowerLevelContent . UsersDefault + 1
}
restrictedPowerLevelContent . EventsDefault = restrictedDefaultPowerLevel
restrictedPowerLevelContent . Invite = restrictedDefaultPowerLevel
restrictedPowerLevelsHeadered , resErr := r . makeHeaderedEvent ( ctx , evTime , userID , roomID , fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomPowerLevels ,
2022-04-05 11:04:08 +02:00
StateKey : "" ,
Content : restrictedPowerLevelContent ,
} )
2023-04-28 17:46:01 +02:00
switch resErr . ( type ) {
case api . ErrNotAllowed :
util . GetLogger ( ctx ) . WithField ( logrus . ErrorKey , resErr ) . Warn ( "UpgradeRoom: Could not restrict power levels in old room" )
case nil :
return r . sendHeaderedEvent ( ctx , userDomain , restrictedPowerLevelsHeadered , api . DoNotSendToOtherServers )
default :
return resErr
2022-04-05 11:04:08 +02:00
}
return nil
}
func moveLocalAliases ( ctx context . Context ,
roomID , newRoomID , userID string ,
2023-04-28 17:46:01 +02:00
URSAPI api . RoomserverInternalAPI ,
) ( err error ) {
2022-04-05 11:04:08 +02:00
aliasReq := api . GetAliasesForRoomIDRequest { RoomID : roomID }
aliasRes := api . GetAliasesForRoomIDResponse { }
if err = URSAPI . GetAliasesForRoomID ( ctx , & aliasReq , & aliasRes ) ; err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "Failed to get old room aliases: %w" , err )
2022-04-05 11:04:08 +02:00
}
for _ , alias := range aliasRes . Aliases {
removeAliasReq := api . RemoveRoomAliasRequest { UserID : userID , Alias : alias }
removeAliasRes := api . RemoveRoomAliasResponse { }
if err = URSAPI . RemoveRoomAlias ( ctx , & removeAliasReq , & removeAliasRes ) ; err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "Failed to remove old room alias: %w" , err )
2022-04-05 11:04:08 +02:00
}
setAliasReq := api . SetRoomAliasRequest { UserID : userID , Alias : alias , RoomID : newRoomID }
setAliasRes := api . SetRoomAliasResponse { }
if err = URSAPI . SetRoomAlias ( ctx , & setAliasReq , & setAliasRes ) ; err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "Failed to set new room alias: %w" , err )
2022-04-05 11:04:08 +02:00
}
}
return nil
}
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) clearOldCanonicalAliasEvent ( ctx context . Context , oldRoom * api . QueryLatestEventsAndStateResponse , evTime time . Time , userID string , userDomain spec . ServerName , roomID string ) error {
2022-04-05 11:04:08 +02:00
for _ , event := range oldRoom . StateEvents {
2023-04-19 16:50:33 +02:00
if event . Type ( ) != spec . MRoomCanonicalAlias || ! event . StateKeyEquals ( "" ) {
2022-04-05 11:04:08 +02:00
continue
}
var aliasContent struct {
Alias string ` json:"alias" `
AltAliases [ ] string ` json:"alt_aliases" `
}
if err := json . Unmarshal ( event . Content ( ) , & aliasContent ) ; err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "failed to unmarshal canonical aliases: %w" , err )
2022-04-05 11:04:08 +02:00
}
if aliasContent . Alias == "" && len ( aliasContent . AltAliases ) == 0 {
// There are no canonical aliases to clear, therefore do nothing.
return nil
}
}
emptyCanonicalAliasEvent , resErr := r . makeHeaderedEvent ( ctx , evTime , userID , roomID , fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomCanonicalAlias ,
2022-04-05 11:04:08 +02:00
Content : map [ string ] interface { } { } ,
} )
2023-04-28 17:46:01 +02:00
switch resErr . ( type ) {
case api . ErrNotAllowed :
util . GetLogger ( ctx ) . WithField ( logrus . ErrorKey , resErr ) . Warn ( "UpgradeRoom: Could not set empty canonical alias event in old room" )
case nil :
return r . sendHeaderedEvent ( ctx , userDomain , emptyCanonicalAliasEvent , api . DoNotSendToOtherServers )
default :
return resErr
2022-04-05 11:04:08 +02:00
}
return nil
}
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) publishIfOldRoomWasPublic ( ctx context . Context , roomID , newRoomID string ) error {
2022-04-05 11:04:08 +02:00
// check if the old room was published
var pubQueryRes api . QueryPublishedRoomsResponse
err := r . URSAPI . QueryPublishedRooms ( ctx , & api . QueryPublishedRoomsRequest {
RoomID : roomID ,
} , & pubQueryRes )
if err != nil {
2023-04-28 17:46:01 +02:00
return err
2022-04-05 11:04:08 +02:00
}
// if the old room is published (was public), publish the new room
if len ( pubQueryRes . RoomIDs ) == 1 {
publishNewRoomAndUnpublishOldRoom ( ctx , r . URSAPI , roomID , newRoomID )
}
return nil
}
func publishNewRoomAndUnpublishOldRoom (
ctx context . Context ,
URSAPI api . RoomserverInternalAPI ,
oldRoomID , newRoomID string ,
) {
// expose this room in the published room list
2022-08-11 16:29:33 +02:00
if err := URSAPI . PerformPublish ( ctx , & api . PerformPublishRequest {
2022-04-05 11:04:08 +02:00
RoomID : newRoomID ,
2023-04-28 17:46:01 +02:00
Visibility : spec . Public ,
} ) ; err != nil {
2022-04-05 11:04:08 +02:00
// treat as non-fatal since the room is already made by this point
2023-04-28 17:46:01 +02:00
util . GetLogger ( ctx ) . WithError ( err ) . Error ( "failed to publish room" )
2022-04-05 11:04:08 +02:00
}
// remove the old room from the published room list
2022-08-11 16:29:33 +02:00
if err := URSAPI . PerformPublish ( ctx , & api . PerformPublishRequest {
2022-04-05 11:04:08 +02:00
RoomID : oldRoomID ,
Visibility : "private" ,
2023-04-28 17:46:01 +02:00
} ) ; err != nil {
2022-04-05 11:04:08 +02:00
// treat as non-fatal since the room is already made by this point
2023-04-28 17:46:01 +02:00
util . GetLogger ( ctx ) . WithError ( err ) . Error ( "failed to un-publish room" )
2022-04-05 11:04:08 +02:00
}
}
func ( r * Upgrader ) validateRoomExists ( ctx context . Context , roomID string ) error {
2023-04-27 08:07:13 +02:00
if _ , err := r . URSAPI . QueryRoomVersionForRoom ( ctx , roomID ) ; err != nil {
2023-05-17 02:33:27 +02:00
return eventutil . ErrRoomNoExists { }
2022-04-05 11:04:08 +02:00
}
return nil
}
func ( r * Upgrader ) userIsAuthorized ( ctx context . Context , userID , roomID string ,
) bool {
plEvent := api . GetStateEvent ( ctx , r . URSAPI , roomID , gomatrixserverlib . StateKeyTuple {
2023-04-19 16:50:33 +02:00
EventType : spec . MRoomPowerLevels ,
2022-04-05 11:04:08 +02:00
StateKey : "" ,
} )
if plEvent == nil {
return false
}
pl , err := plEvent . PowerLevels ( )
if err != nil {
return false
}
// Check for power level required to send tombstone event (marks the current room as obsolete),
// if not found, use the StateDefault power level
return pl . UserLevel ( userID ) >= pl . EventLevel ( "m.room.tombstone" , true )
}
// nolint:gocyclo
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) generateInitialEvents ( ctx context . Context , oldRoom * api . QueryLatestEventsAndStateResponse , userID , roomID string , newVersion gomatrixserverlib . RoomVersion , tombstoneEvent * types . HeaderedEvent ) ( [ ] fledglingEvent , error ) {
2023-04-27 13:54:20 +02:00
state := make ( map [ gomatrixserverlib . StateKeyTuple ] * types . HeaderedEvent , len ( oldRoom . StateEvents ) )
2022-04-05 11:04:08 +02:00
for _ , event := range oldRoom . StateEvents {
if event . StateKey ( ) == nil {
// This shouldn't ever happen, but better to be safe than sorry.
continue
}
2023-04-19 16:50:33 +02:00
if event . Type ( ) == spec . MRoomMember && ! event . StateKeyEquals ( userID ) {
2023-04-27 08:07:13 +02:00
// With the exception of bans which we do want to copy, we
2022-04-05 11:04:08 +02:00
// should ignore membership events that aren't our own, as event auth will
// prevent us from being able to create membership events on behalf of other
// users anyway unless they are invites or bans.
membership , err := event . Membership ( )
if err != nil {
continue
}
switch membership {
2023-04-19 16:50:33 +02:00
case spec . Ban :
2022-04-05 11:04:08 +02:00
default :
continue
}
}
2023-04-27 08:07:13 +02:00
// skip events that rely on a specific user being present
sKey := * event . StateKey ( )
if event . Type ( ) != spec . MRoomMember && len ( sKey ) > 0 && sKey [ : 1 ] == "@" {
continue
}
2022-04-05 11:04:08 +02:00
state [ gomatrixserverlib . StateKeyTuple { EventType : event . Type ( ) , StateKey : * event . StateKey ( ) } ] = event
}
// The following events are ones that we are going to override manually
// in the following section.
override := map [ gomatrixserverlib . StateKeyTuple ] struct { } {
2023-04-19 16:50:33 +02:00
{ EventType : spec . MRoomCreate , StateKey : "" } : { } ,
{ EventType : spec . MRoomMember , StateKey : userID } : { } ,
{ EventType : spec . MRoomPowerLevels , StateKey : "" } : { } ,
{ EventType : spec . MRoomJoinRules , StateKey : "" } : { } ,
2022-04-05 11:04:08 +02:00
}
// The overridden events are essential events that must be present in the
// old room state. Check that they are there.
for tuple := range override {
if _ , ok := state [ tuple ] ; ! ok {
2023-04-28 17:46:01 +02:00
return nil , fmt . Errorf ( "essential event of type %q state key %q is missing" , tuple . EventType , tuple . StateKey )
2022-04-05 11:04:08 +02:00
}
}
2023-04-19 16:50:33 +02:00
oldCreateEvent := state [ gomatrixserverlib . StateKeyTuple { EventType : spec . MRoomCreate , StateKey : "" } ]
oldMembershipEvent := state [ gomatrixserverlib . StateKeyTuple { EventType : spec . MRoomMember , StateKey : userID } ]
oldPowerLevelsEvent := state [ gomatrixserverlib . StateKeyTuple { EventType : spec . MRoomPowerLevels , StateKey : "" } ]
oldJoinRulesEvent := state [ gomatrixserverlib . StateKeyTuple { EventType : spec . MRoomJoinRules , StateKey : "" } ]
2022-04-05 11:04:08 +02:00
// Create the new room create event. Using a map here instead of CreateContent
// means that we preserve any other interesting fields that might be present
// in the create event (such as for the room types MSC).
newCreateContent := map [ string ] interface { } { }
_ = json . Unmarshal ( oldCreateEvent . Content ( ) , & newCreateContent )
newCreateContent [ "creator" ] = userID
newCreateContent [ "room_version" ] = newVersion
newCreateContent [ "predecessor" ] = gomatrixserverlib . PreviousRoom {
EventID : tombstoneEvent . EventID ( ) ,
RoomID : roomID ,
}
newCreateEvent := fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomCreate ,
2022-04-05 11:04:08 +02:00
StateKey : "" ,
Content : newCreateContent ,
}
// Now create the new membership event. Same rules apply as above, so
// that we preserve fields we don't otherwise know about. We'll always
// set the membership to join though, because that is necessary to auth
// the events after it.
newMembershipContent := map [ string ] interface { } { }
_ = json . Unmarshal ( oldMembershipEvent . Content ( ) , & newMembershipContent )
2023-04-19 16:50:33 +02:00
newMembershipContent [ "membership" ] = spec . Join
2022-04-05 11:04:08 +02:00
newMembershipEvent := fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomMember ,
2022-04-05 11:04:08 +02:00
StateKey : userID ,
Content : newMembershipContent ,
}
// We might need to temporarily give ourselves a higher power level
// than we had in the old room in order to be able to send all of
// the relevant state events. This function will return whether we
// had to override the power level events or not — if we did, we
// need to send the original power levels again later on.
powerLevelContent , err := oldPowerLevelsEvent . PowerLevels ( )
if err != nil {
util . GetLogger ( ctx ) . WithError ( err ) . Error ( )
2023-04-28 17:46:01 +02:00
return nil , fmt . Errorf ( "Power level event content was invalid" )
2022-04-05 11:04:08 +02:00
}
tempPowerLevelsEvent , powerLevelsOverridden := createTemporaryPowerLevels ( powerLevelContent , userID )
// Now do the join rules event, same as the create and membership
// events. We'll set a sane default of "invite" so that if the
// existing join rules contains garbage, the room can still be
// upgraded.
newJoinRulesContent := map [ string ] interface { } {
2023-04-19 16:50:33 +02:00
"join_rule" : spec . Invite , // sane default
2022-04-05 11:04:08 +02:00
}
_ = json . Unmarshal ( oldJoinRulesEvent . Content ( ) , & newJoinRulesContent )
newJoinRulesEvent := fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomJoinRules ,
2022-04-05 11:04:08 +02:00
StateKey : "" ,
Content : newJoinRulesContent ,
}
eventsToMake := make ( [ ] fledglingEvent , 0 , len ( state ) )
eventsToMake = append (
eventsToMake , newCreateEvent , newMembershipEvent ,
tempPowerLevelsEvent , newJoinRulesEvent ,
)
// For some reason Sytest expects there to be a guest access event.
// Create one if it doesn't exist.
2023-04-19 16:50:33 +02:00
if _ , ok := state [ gomatrixserverlib . StateKeyTuple { EventType : spec . MRoomGuestAccess , StateKey : "" } ] ; ! ok {
2022-04-05 11:04:08 +02:00
eventsToMake = append ( eventsToMake , fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomGuestAccess ,
2022-04-05 11:04:08 +02:00
Content : map [ string ] string {
"guest_access" : "forbidden" ,
} ,
} )
}
// Duplicate all of the old state events into the new room.
for tuple , event := range state {
if _ , ok := override [ tuple ] ; ok {
// Don't duplicate events we have overridden already. They
// are already in `eventsToMake`.
continue
}
newEvent := fledglingEvent {
Type : tuple . EventType ,
StateKey : tuple . StateKey ,
}
if err = json . Unmarshal ( event . Content ( ) , & newEvent . Content ) ; err != nil {
logrus . WithError ( err ) . Error ( "Failed to unmarshal old event" )
continue
}
eventsToMake = append ( eventsToMake , newEvent )
}
// If we sent a temporary power level event into the room before,
// override that now by restoring the original power levels.
if powerLevelsOverridden {
eventsToMake = append ( eventsToMake , fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomPowerLevels ,
2022-04-05 11:04:08 +02:00
Content : powerLevelContent ,
} )
}
return eventsToMake , nil
}
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) sendInitialEvents ( ctx context . Context , evTime time . Time , userID string , userDomain spec . ServerName , newRoomID string , newVersion gomatrixserverlib . RoomVersion , eventsToMake [ ] fledglingEvent ) error {
2022-04-05 11:04:08 +02:00
var err error
2023-04-27 13:54:20 +02:00
var builtEvents [ ] * types . HeaderedEvent
2022-04-05 11:04:08 +02:00
authEvents := gomatrixserverlib . NewAuthEvents ( nil )
for i , e := range eventsToMake {
depth := i + 1 // depth starts at 1
2023-05-04 12:17:42 +02:00
proto := gomatrixserverlib . ProtoEvent {
2022-04-05 11:04:08 +02:00
Sender : userID ,
RoomID : newRoomID ,
Type : e . Type ,
StateKey : & e . StateKey ,
Depth : int64 ( depth ) ,
}
2023-05-04 12:17:42 +02:00
err = proto . SetContent ( e . Content )
2022-04-05 11:04:08 +02:00
if err != nil {
2023-05-04 12:17:42 +02:00
return fmt . Errorf ( "failed to set content of new %q event: %w" , proto . Type , err )
2022-04-05 11:04:08 +02:00
}
if i > 0 {
2023-05-24 12:14:42 +02:00
proto . PrevEvents = [ ] string { builtEvents [ i - 1 ] . EventID ( ) }
2022-04-05 11:04:08 +02:00
}
2023-05-04 12:17:42 +02:00
var verImpl gomatrixserverlib . IRoomVersion
verImpl , err = gomatrixserverlib . GetRoomVersion ( newVersion )
if err != nil {
return err
}
builder := verImpl . NewEventBuilderFromProtoEvent ( & proto )
if err = builder . AddAuthEvents ( & authEvents ) ; err != nil {
return err
}
2023-05-03 11:21:27 +02:00
var event gomatrixserverlib . PDU
2023-05-04 12:17:42 +02:00
event , err = builder . Build ( evTime , userDomain , r . Cfg . Matrix . KeyID , r . Cfg . Matrix . PrivateKey )
2022-04-05 11:04:08 +02:00
if err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "failed to build new %q event: %w" , builder . Type , err )
2022-04-05 11:04:08 +02:00
}
if err = gomatrixserverlib . Allowed ( event , & authEvents ) ; err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "Failed to auth new %q event: %w" , builder . Type , err )
2022-04-05 11:04:08 +02:00
}
// Add the event to the list of auth events
2023-05-02 16:03:16 +02:00
builtEvents = append ( builtEvents , & types . HeaderedEvent { PDU : event } )
2022-04-05 11:04:08 +02:00
err = authEvents . AddEvent ( event )
if err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "failed to add new %q event to auth set: %w" , builder . Type , err )
2022-04-05 11:04:08 +02:00
}
}
inputs := make ( [ ] api . InputRoomEvent , 0 , len ( builtEvents ) )
for _ , event := range builtEvents {
inputs = append ( inputs , api . InputRoomEvent {
Kind : api . KindNew ,
Event : event ,
2022-10-26 13:59:19 +02:00
Origin : userDomain ,
2022-04-05 11:04:08 +02:00
SendAsServer : api . DoNotSendToOtherServers ,
} )
}
2022-11-15 16:05:23 +01:00
if err = api . SendInputRoomEvents ( ctx , r . URSAPI , userDomain , inputs , false ) ; err != nil {
2023-04-28 17:46:01 +02:00
return fmt . Errorf ( "failed to send new room %q to roomserver: %w" , newRoomID , err )
2022-04-05 11:04:08 +02:00
}
return nil
}
func ( r * Upgrader ) makeTombstoneEvent (
ctx context . Context ,
evTime time . Time ,
userID , roomID , newRoomID string ,
2023-04-28 17:46:01 +02:00
) ( * types . HeaderedEvent , error ) {
2022-04-05 11:04:08 +02:00
content := map [ string ] interface { } {
"body" : "This room has been replaced" ,
"replacement_room" : newRoomID ,
}
event := fledglingEvent {
Type : "m.room.tombstone" ,
Content : content ,
}
return r . makeHeaderedEvent ( ctx , evTime , userID , roomID , event )
}
2023-04-28 17:46:01 +02:00
func ( r * Upgrader ) makeHeaderedEvent ( ctx context . Context , evTime time . Time , userID , roomID string , event fledglingEvent ) ( * types . HeaderedEvent , error ) {
2023-05-04 12:17:42 +02:00
proto := gomatrixserverlib . ProtoEvent {
2022-04-05 11:04:08 +02:00
Sender : userID ,
RoomID : roomID ,
Type : event . Type ,
StateKey : & event . StateKey ,
}
2023-05-04 12:17:42 +02:00
err := proto . SetContent ( event . Content )
2022-04-05 11:04:08 +02:00
if err != nil {
2023-05-04 12:17:42 +02:00
return nil , fmt . Errorf ( "failed to set new %q event content: %w" , proto . Type , err )
2022-04-05 11:04:08 +02:00
}
2022-11-15 16:05:23 +01:00
// Get the sender domain.
2023-05-04 12:17:42 +02:00
_ , senderDomain , serr := r . Cfg . Matrix . SplitLocalID ( '@' , proto . Sender )
2022-11-15 16:05:23 +01:00
if serr != nil {
2023-05-04 12:17:42 +02:00
return nil , fmt . Errorf ( "Failed to split user ID %q: %w" , proto . Sender , err )
2022-11-15 16:05:23 +01:00
}
identity , err := r . Cfg . Matrix . SigningIdentityFor ( senderDomain )
if err != nil {
2023-04-28 17:46:01 +02:00
return nil , fmt . Errorf ( "failed to get signing identity for %q: %w" , senderDomain , err )
2022-11-15 16:05:23 +01:00
}
2022-04-05 11:04:08 +02:00
var queryRes api . QueryLatestEventsAndStateResponse
2023-05-04 12:17:42 +02:00
headeredEvent , err := eventutil . QueryAndBuildEvent ( ctx , & proto , r . Cfg . Matrix , identity , evTime , r . URSAPI , & queryRes )
2023-05-17 02:33:27 +02:00
switch e := err . ( type ) {
case nil :
case eventutil . ErrRoomNoExists :
return nil , e
case gomatrixserverlib . BadJSONError :
2023-04-28 17:46:01 +02:00
return nil , e
2023-05-17 02:33:27 +02:00
case gomatrixserverlib . EventValidationError :
2023-04-28 17:46:01 +02:00
return nil , e
2023-05-17 02:33:27 +02:00
default :
2023-05-04 12:17:42 +02:00
return nil , fmt . Errorf ( "failed to build new %q event: %w" , proto . Type , err )
2022-04-05 11:04:08 +02:00
}
2023-05-17 02:33:27 +02:00
2022-04-05 11:04:08 +02:00
// check to see if this user can perform this operation
2023-05-02 16:03:16 +02:00
stateEvents := make ( [ ] gomatrixserverlib . PDU , len ( queryRes . StateEvents ) )
2022-04-05 11:04:08 +02:00
for i := range queryRes . StateEvents {
2023-05-02 16:03:16 +02:00
stateEvents [ i ] = queryRes . StateEvents [ i ] . PDU
2022-04-05 11:04:08 +02:00
}
2023-05-02 16:03:16 +02:00
provider := gomatrixserverlib . NewAuthEvents ( stateEvents )
if err = gomatrixserverlib . Allowed ( headeredEvent . PDU , & provider ) ; err != nil {
2023-05-04 12:17:42 +02:00
return nil , api . ErrNotAllowed { Err : fmt . Errorf ( "failed to auth new %q event: %w" , proto . Type , err ) } // TODO: Is this error string comprehensible to the client?
2022-04-05 11:04:08 +02:00
}
return headeredEvent , nil
}
func createTemporaryPowerLevels ( powerLevelContent * gomatrixserverlib . PowerLevelContent , userID string ) ( fledglingEvent , bool ) {
// Work out what power level we need in order to be able to send events
// of all types into the room.
neededPowerLevel := powerLevelContent . StateDefault
for _ , powerLevel := range powerLevelContent . Events {
if powerLevel > neededPowerLevel {
neededPowerLevel = powerLevel
}
}
// Make a copy of the existing power level content.
tempPowerLevelContent := * powerLevelContent
powerLevelsOverridden := false
// At this point, the "Users", "Events" and "Notifications" keys are all
// pointing to the map of the original PL content, so we will specifically
// override the users map with a new one and duplicate the values deeply,
// so that we can modify them without modifying the original.
tempPowerLevelContent . Users = make ( map [ string ] int64 , len ( powerLevelContent . Users ) )
for key , value := range powerLevelContent . Users {
tempPowerLevelContent . Users [ key ] = value
}
// If the user who is upgrading the room doesn't already have sufficient
// power, then elevate their power levels.
if tempPowerLevelContent . UserLevel ( userID ) < neededPowerLevel {
tempPowerLevelContent . Users [ userID ] = neededPowerLevel
powerLevelsOverridden = true
}
// Then return the temporary power levels event.
return fledglingEvent {
2023-04-19 16:50:33 +02:00
Type : spec . MRoomPowerLevels ,
2022-04-05 11:04:08 +02:00
Content : tempPowerLevelContent ,
} , powerLevelsOverridden
}
func ( r * Upgrader ) sendHeaderedEvent (
ctx context . Context ,
2023-04-19 16:50:33 +02:00
serverName spec . ServerName ,
2023-04-27 13:54:20 +02:00
headeredEvent * types . HeaderedEvent ,
2022-06-06 18:27:50 +02:00
sendAsServer string ,
2023-04-28 17:46:01 +02:00
) error {
2022-04-05 11:04:08 +02:00
var inputs [ ] api . InputRoomEvent
inputs = append ( inputs , api . InputRoomEvent {
Kind : api . KindNew ,
Event : headeredEvent ,
2022-10-26 13:59:19 +02:00
Origin : serverName ,
2022-06-06 18:27:50 +02:00
SendAsServer : sendAsServer ,
2022-04-05 11:04:08 +02:00
} )
2023-04-28 17:46:01 +02:00
return api . SendInputRoomEvents ( ctx , r . URSAPI , serverName , inputs , false )
2022-04-05 11:04:08 +02:00
}