2017-04-20 22:40:52 +00:00
// Copyright 2017 Vector Creations Ltd
2020-02-05 18:06:39 +00:00
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
2017-04-20 22:40:52 +00:00
//
// 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.
2020-05-01 09:48:17 +00:00
package internal
2017-02-07 17:20:05 +00:00
import (
2017-09-13 15:30:19 +00:00
"context"
2020-07-21 14:48:21 +00:00
"fmt"
2017-08-21 15:37:11 +00:00
2020-07-07 11:51:55 +00:00
"github.com/matrix-org/dendrite/internal/eventutil"
2017-02-07 17:20:05 +00:00
"github.com/matrix-org/dendrite/roomserver/api"
2020-09-02 12:47:31 +00:00
"github.com/matrix-org/dendrite/roomserver/internal/helpers"
2017-03-08 15:10:26 +00:00
"github.com/matrix-org/dendrite/roomserver/state"
2017-02-09 16:48:14 +00:00
"github.com/matrix-org/dendrite/roomserver/types"
2017-02-07 17:20:05 +00:00
"github.com/matrix-org/gomatrixserverlib"
2020-04-28 10:46:47 +00:00
"github.com/sirupsen/logrus"
2017-02-07 17:20:05 +00:00
)
2017-12-15 15:22:06 +00:00
// processRoomEvent can only be called once at a time
//
// TODO(#375): This should be rewritten to allow concurrent calls. The
// difficulty is in ensuring that we correctly annotate events with the correct
// state deltas when sending to kafka streams
2020-07-07 11:51:55 +00:00
// TODO: Break up function - we should probably do transaction ID checks before calling this.
// nolint:gocyclo
2020-05-20 17:03:06 +00:00
func ( r * RoomserverInternalAPI ) processRoomEvent (
2017-09-13 15:30:19 +00:00
ctx context . Context ,
input api . InputRoomEvent ,
2018-05-26 11:03:35 +00:00
) ( eventID string , err error ) {
2017-02-07 17:20:05 +00:00
// Parse and validate the event JSON
2020-03-27 16:28:22 +00:00
headered := input . Event
event := headered . Unwrap ( )
2017-02-07 17:20:05 +00:00
2020-05-18 16:49:24 +00:00
// Check that the event passes authentication checks and work out
// the numeric IDs for the auth events.
2020-09-02 12:47:31 +00:00
authEventNIDs , err := helpers . CheckAuthEvents ( ctx , r . DB , headered , input . AuthEventIDs )
2017-02-09 16:48:14 +00:00
if err != nil {
2020-05-12 15:24:28 +00:00
logrus . WithError ( err ) . WithField ( "event_id" , event . EventID ( ) ) . WithField ( "auth_event_ids" , input . AuthEventIDs ) . Error ( "processRoomEvent.checkAuthEvents failed for event" )
2018-05-26 11:03:35 +00:00
return
}
2020-05-18 16:49:24 +00:00
// If we don't have a transaction ID then get one.
2018-05-26 11:03:35 +00:00
if input . TransactionID != nil {
tdID := input . TransactionID
2020-05-20 17:03:06 +00:00
eventID , err = r . DB . GetTransactionEventID (
2020-03-27 16:28:22 +00:00
ctx , tdID . TransactionID , tdID . SessionID , event . Sender ( ) ,
2018-05-26 11:03:35 +00:00
)
// On error OR event with the transaction already processed/processesing
if err != nil || eventID != "" {
return
}
2017-02-07 17:20:05 +00:00
}
2020-05-18 16:49:24 +00:00
// Store the event.
2020-09-02 09:02:48 +00:00
_ , stateAtEvent , redactionEvent , redactedEventID , err := r . DB . StoreEvent ( ctx , event , input . TransactionID , authEventNIDs )
2017-02-15 11:05:45 +00:00
if err != nil {
2020-07-21 14:48:21 +00:00
return "" , fmt . Errorf ( "r.DB.StoreEvent: %w" , err )
2017-02-09 16:48:14 +00:00
}
2020-07-07 11:51:55 +00:00
// if storing this event results in it being redacted then do so.
if redactedEventID == event . EventID ( ) {
r , rerr := eventutil . RedactEvent ( redactionEvent , & event )
if rerr != nil {
2020-07-21 14:48:21 +00:00
return "" , fmt . Errorf ( "eventutil.RedactEvent: %w" , rerr )
2020-07-07 11:51:55 +00:00
}
event = * r
}
2017-02-07 17:20:05 +00:00
2020-05-18 16:49:24 +00:00
// For outliers we can stop after we've stored the event itself as it
// doesn't have any associated state to store and we don't need to
// notify anyone about it.
2017-02-07 17:20:05 +00:00
if input . Kind == api . KindOutlier {
2020-05-18 16:49:24 +00:00
logrus . WithFields ( logrus . Fields {
"event_id" : event . EventID ( ) ,
"type" : event . Type ( ) ,
"room" : event . RoomID ( ) ,
} ) . Info ( "Stored outlier" )
2018-05-26 11:03:35 +00:00
return event . EventID ( ) , nil
2017-02-07 17:20:05 +00:00
}
2020-09-02 09:02:48 +00:00
roomInfo , err := r . DB . RoomInfo ( ctx , event . RoomID ( ) )
if err != nil {
return "" , fmt . Errorf ( "r.DB.RoomInfo: %w" , err )
}
if roomInfo == nil {
return "" , fmt . Errorf ( "r.DB.RoomInfo missing for room %s" , event . RoomID ( ) )
}
2017-02-15 11:05:45 +00:00
if stateAtEvent . BeforeStateSnapshotNID == 0 {
// We haven't calculated a state for this event yet.
// Lets calculate one.
2020-09-02 09:02:48 +00:00
err = r . calculateAndSetState ( ctx , input , * roomInfo , & stateAtEvent , event )
2017-09-20 09:59:19 +00:00
if err != nil {
2020-07-21 14:48:21 +00:00
return "" , fmt . Errorf ( "r.calculateAndSetState: %w" , err )
2017-09-20 09:59:19 +00:00
}
2017-02-15 11:05:45 +00:00
}
2020-05-20 17:03:06 +00:00
if err = r . updateLatestEvents (
2020-05-18 16:49:24 +00:00
ctx , // context
2020-09-02 09:02:48 +00:00
roomInfo , // room info for the room being updated
2020-05-18 16:49:24 +00:00
stateAtEvent , // state at event (below)
event , // event
input . SendAsServer , // send as server
input . TransactionID , // transaction ID
) ; err != nil {
2020-07-21 14:48:21 +00:00
return "" , fmt . Errorf ( "r.updateLatestEvents: %w" , err )
2020-05-18 16:49:24 +00:00
}
2020-07-07 11:51:55 +00:00
// processing this event resulted in an event (which may not be the one we're processing)
// being redacted. We are guaranteed to have both sides (the redaction/redacted event),
// so notify downstream components to redact this event - they should have it if they've
// been tracking our output log.
if redactedEventID != "" {
err = r . WriteOutputEvents ( event . RoomID ( ) , [ ] api . OutputEvent {
{
Type : api . OutputTypeRedactedEvent ,
RedactedEvent : & api . OutputRedactedEvent {
RedactedEventID : redactedEventID ,
RedactedBecause : redactionEvent . Headered ( headered . RoomVersion ) ,
} ,
} ,
} )
if err != nil {
2020-07-21 14:48:21 +00:00
return "" , fmt . Errorf ( "r.WriteOutputEvents: %w" , err )
2020-07-07 11:51:55 +00:00
}
}
2017-02-21 14:50:30 +00:00
// Update the extremities of the event graph for the room
2020-05-18 16:49:24 +00:00
return event . EventID ( ) , nil
2018-05-26 11:03:35 +00:00
}
2020-05-20 17:03:06 +00:00
func ( r * RoomserverInternalAPI ) calculateAndSetState (
2018-05-26 11:03:35 +00:00
ctx context . Context ,
input api . InputRoomEvent ,
2020-09-02 09:02:48 +00:00
roomInfo types . RoomInfo ,
2018-05-26 11:03:35 +00:00
stateAtEvent * types . StateAtEvent ,
event gomatrixserverlib . Event ,
) error {
2020-03-19 18:33:04 +00:00
var err error
2020-09-02 09:02:48 +00:00
roomState := state . NewStateResolution ( r . DB , roomInfo )
2020-02-05 16:25:58 +00:00
2018-05-26 11:03:35 +00:00
if input . HasState {
2020-05-20 17:03:06 +00:00
// Check here if we think we're in the room already.
2020-05-18 16:49:24 +00:00
stateAtEvent . Overwrite = true
2020-05-20 17:03:06 +00:00
var joinEventNIDs [ ] types . EventNID
// Request join memberships only for local users only.
2020-09-02 09:02:48 +00:00
if joinEventNIDs , err = r . DB . GetMembershipEventNIDsForRoom ( ctx , roomInfo . RoomNID , true , true ) ; err == nil {
2020-05-20 17:03:06 +00:00
// If we have no local users that are joined to the room then any state about
// the room that we have is quite possibly out of date. Therefore in that case
// we should overwrite it rather than merge it.
stateAtEvent . Overwrite = len ( joinEventNIDs ) == 0
}
2020-05-18 16:49:24 +00:00
2018-05-26 11:03:35 +00:00
// We've been told what the state at the event is so we don't need to calculate it.
// Check that those state events are in the database and store the state.
var entries [ ] types . StateEntry
2020-05-20 17:03:06 +00:00
if entries , err = r . DB . StateEntriesForEventIDs ( ctx , input . StateEventIDs ) ; err != nil {
2018-05-26 11:03:35 +00:00
return err
}
2020-09-02 09:02:48 +00:00
if stateAtEvent . BeforeStateSnapshotNID , err = r . DB . AddState ( ctx , roomInfo . RoomNID , nil , entries ) ; err != nil {
2018-05-26 11:03:35 +00:00
return err
}
} else {
2020-05-18 16:49:24 +00:00
stateAtEvent . Overwrite = false
2018-05-26 11:03:35 +00:00
// We haven't been told what the state at the event is so we need to calculate it from the prev_events
2020-09-02 09:02:48 +00:00
if stateAtEvent . BeforeStateSnapshotNID , err = roomState . CalculateAndStoreStateBeforeEvent ( ctx , event ) ; err != nil {
2018-05-26 11:03:35 +00:00
return err
}
}
2020-05-20 17:03:06 +00:00
return r . DB . SetState ( ctx , stateAtEvent . EventNID , stateAtEvent . BeforeStateSnapshotNID )
2017-08-21 15:37:11 +00:00
}