<s> from twisted . internet import defer <EOL> from synapse . api . errors import LimitExceededError , SynapseError , AuthError <EOL> from synapse . crypto . event_signing import add_hashes_and_signatures <EOL> from synapse . api . constants import Membership , EventTypes <EOL> from synapse . types import UserID , RoomAlias , Requester <EOL> from synapse . push . action_generator import ActionGenerator <EOL> from synapse . util . logcontext import PreserveLoggingContext <EOL> import logging <EOL> logger = logging . getLogger ( __name__ ) <EOL> VISIBILITY_PRIORITY = ( <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> ) <EOL> class BaseHandler ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , hs ) : <EOL> self . store = hs . get_datastore ( ) <EOL> self . auth = hs . get_auth ( ) <EOL> self . notifier = hs . get_notifier ( ) <EOL> self . state_handler = hs . get_state_handler ( ) <EOL> self . distributor = hs . get_distributor ( ) <EOL> self . ratelimiter = hs . get_ratelimiter ( ) <EOL> self . clock = hs . get_clock ( ) <EOL> self . hs = hs <EOL> self . signing_key = hs . config . signing_key [ <NUM_LIT:0> ] <EOL> self . server_name = hs . hostname <EOL> self . event_builder_factory = hs . get_event_builder_factory ( ) <EOL> @ defer . inlineCallbacks <EOL> def filter_events_for_clients ( self , user_tuples , events , event_id_to_state ) : <EOL> """<STR_LIT>""" <EOL> forgotten = yield defer . gatherResults ( [ <EOL> self . store . who_forgot_in_room ( <EOL> room_id , <EOL> ) <EOL> for room_id in frozenset ( e . room_id for e in events ) <EOL> ] , consumeErrors = True ) <EOL> event_id_forgotten = frozenset ( <EOL> row [ "<STR_LIT>" ] for rows in forgotten for row in rows <EOL> ) <EOL> def allowed ( event , user_id , is_peeking ) : <EOL> state = event_id_to_state [ event . event_id ] <EOL> visibility_event = state . get ( ( EventTypes . RoomHistoryVisibility , "<STR_LIT>" ) , None ) <EOL> if visibility_event : <EOL> visibility = visibility_event . content . get ( "<STR_LIT>" , "<STR_LIT>" ) <EOL> else : <EOL> visibility = "<STR_LIT>" <EOL> if visibility not in VISIBILITY_PRIORITY : <EOL> visibility = "<STR_LIT>" <EOL> if visibility == "<STR_LIT>" : <EOL> return True <EOL> if event . type == EventTypes . RoomHistoryVisibility : <EOL> prev_content = event . unsigned . get ( "<STR_LIT>" , { } ) <EOL> prev_visibility = prev_content . get ( "<STR_LIT>" , None ) <EOL> if prev_visibility not in VISIBILITY_PRIORITY : <EOL> prev_visibility = "<STR_LIT>" <EOL> new_priority = VISIBILITY_PRIORITY . index ( visibility ) <EOL> old_priority = VISIBILITY_PRIORITY . index ( prev_visibility ) <EOL> if old_priority < new_priority : <EOL> visibility = prev_visibility <EOL> membership_event = state . get ( ( EventTypes . Member , user_id ) , None ) <EOL> if membership_event : <EOL> if membership_event . event_id in event_id_forgotten : <EOL> membership = None <EOL> else : <EOL> membership = membership_event . membership <EOL> else : <EOL> membership = None <EOL> if membership == Membership . JOIN : <EOL> return True <EOL> if visibility == "<STR_LIT>" : <EOL> return False <EOL> elif visibility == "<STR_LIT>" : <EOL> return membership == Membership . INVITE <EOL> else : <EOL> return not is_peeking <EOL> defer . returnValue ( { <EOL> user_id : [ <EOL> event <EOL> for event in events <EOL> if allowed ( event , user_id , is_peeking ) <EOL> ] <EOL> for user_id , is_peeking in user_tuples <EOL> } ) <EOL> @ defer . inlineCallbacks <EOL> def _filter_events_for_client ( self , user_id , events , is_peeking = False ) : <EOL> """<STR_LIT>""" <EOL> types = ( <EOL> ( EventTypes . RoomHistoryVisibility , "<STR_LIT>" ) , <EOL> ( EventTypes . Member , user_id ) , <EOL> ) <EOL> event_id_to_state = yield self . store . get_state_for_events ( <EOL> frozenset ( e . event_id for e in events ) , <EOL> types = types <EOL> ) <EOL> res = yield self . filter_events_for_clients ( <EOL> [ ( user_id , is_peeking ) ] , events , event_id_to_state <EOL> ) <EOL> defer . returnValue ( res . get ( user_id , [ ] ) ) <EOL> def ratelimit ( self , requester ) : <EOL> time_now = self . clock . time ( ) <EOL> allowed , time_allowed = self . ratelimiter . send_message ( <EOL> requester . user . to_string ( ) , time_now , <EOL> msg_rate_hz = self . hs . config . rc_messages_per_second , <EOL> burst_count = self . hs . config . rc_message_burst_count , <EOL> ) <EOL> if not allowed : <EOL> raise LimitExceededError ( <EOL> retry_after_ms = int ( <NUM_LIT:1000> * ( time_allowed - time_now ) ) , <EOL> ) <EOL> @ defer . inlineCallbacks <EOL> def _create_new_client_event ( self , builder ) : <EOL> latest_ret = yield self . store . get_latest_event_ids_and_hashes_in_room ( <EOL> builder . room_id , <EOL> ) <EOL> if latest_ret : <EOL> depth = max ( [ d for _ , _ , d in latest_ret ] ) + <NUM_LIT:1> <EOL> else : <EOL> depth = <NUM_LIT:1> <EOL> prev_events = [ <EOL> ( event_id , prev_hashes ) <EOL> for event_id , prev_hashes , _ in latest_ret <EOL> ] <EOL> builder . prev_events = prev_events <EOL> builder . depth = depth <EOL> state_handler = self . state_handler <EOL> context = yield state_handler . compute_event_context ( builder ) <EOL> if ( <EOL> not self . is_host_in_room ( context . current_state ) and <EOL> builder . type == EventTypes . Member <EOL> ) : <EOL> prev_member_event = yield self . store . get_room_member ( <EOL> builder . sender , builder . room_id <EOL> ) <EOL> context_event_ids = ( <EOL> e . event_id for e in context . current_state . values ( ) <EOL> ) <EOL> if ( <EOL> prev_member_event and <EOL> prev_member_event . event_id not in context_event_ids <EOL> ) : <EOL> builder . prev_events = ( <EOL> prev_member_event . event_id , <EOL> prev_member_event . prev_events <EOL> ) <EOL> context = yield state_handler . compute_event_context ( <EOL> builder , <EOL> old_state = ( prev_member_event , ) , <EOL> outlier = True <EOL> ) <EOL> if builder . is_state ( ) : <EOL> builder . prev_state = yield self . store . add_event_hashes ( <EOL> context . prev_state_events <EOL> ) <EOL> yield self . auth . add_auth_events ( builder , context ) <EOL> add_hashes_and_signatures ( <EOL> builder , self . server_name , self . signing_key <EOL> ) <EOL> event = builder . build ( ) <EOL> logger . debug ( <EOL> "<STR_LIT>" , <EOL> event . event_id , context . current_state , <EOL> ) <EOL> defer . returnValue ( <EOL> ( event , context , ) <EOL> ) <EOL> def is_host_in_room ( self , current_state ) : <EOL> room_members = [ <EOL> ( state_key , event . membership ) <EOL> for ( ( event_type , state_key ) , event ) in current_state . items ( ) <EOL> if event_type == EventTypes . Member <EOL> ] <EOL> if len ( room_members ) == <NUM_LIT:0> : <EOL> create_event = current_state . get ( ( "<STR_LIT>" , "<STR_LIT>" ) ) <EOL> if create_event : <EOL> return True <EOL> for ( state_key , membership ) in room_members : <EOL> if ( <EOL> UserID . from_string ( state_key ) . domain == self . hs . hostname <EOL> and membership == Membership . JOIN <EOL> ) : <EOL> return True <EOL> return False <EOL> @ defer . inlineCallbacks <EOL> def handle_new_client_event ( <EOL> self , <EOL> requester , <EOL> event , <EOL> context , <EOL> ratelimit = True , <EOL> extra_users = [ ] <EOL> ) : <EOL> if ratelimit : <EOL> self . ratelimit ( requester ) <EOL> self . auth . check ( event , auth_events = context . current_state ) <EOL> yield self . maybe_kick_guest_users ( event , context . current_state . values ( ) ) <EOL> if event . type == EventTypes . CanonicalAlias : <EOL> room_alias_str = event . content . get ( "<STR_LIT>" , None ) <EOL> if room_alias_str : <EOL> room_alias = RoomAlias . from_string ( room_alias_str ) <EOL> directory_handler = self . hs . get_handlers ( ) . directory_handler <EOL> mapping = yield directory_handler . get_association ( room_alias ) <EOL> if mapping [ "<STR_LIT>" ] != event . room_id : <EOL> raise SynapseError ( <EOL> <NUM_LIT> , <EOL> "<STR_LIT>" % ( <EOL> room_alias_str , <EOL> ) <EOL> ) <EOL> federation_handler = self . hs . get_handlers ( ) . federation_handler <EOL> if event . type == EventTypes . Member : <EOL> if event . content [ "<STR_LIT>" ] == Membership . INVITE : <EOL> def is_inviter_member_event ( e ) : <EOL> return ( <EOL> e . type == EventTypes . Member and <EOL> e . sender == event . sender <EOL> ) <EOL> event . unsigned [ "<STR_LIT>" ] = [ <EOL> { <EOL> "<STR_LIT:type>" : e . type , <EOL> "<STR_LIT>" : e . state_key , <EOL> "<STR_LIT:content>" : e . content , <EOL> "<STR_LIT>" : e . sender , <EOL> } <EOL> for k , e in context . current_state . items ( ) <EOL> if e . type in self . hs . config . room_invite_state_types <EOL> or is_inviter_member_event ( e ) <EOL> ] <EOL> invitee = UserID . from_string ( event . state_key ) <EOL> if not self . hs . is_mine ( invitee ) : <EOL> returned_invite = yield federation_handler . send_invite ( <EOL> invitee . domain , <EOL> event , <EOL> ) <EOL> event . unsigned . pop ( "<STR_LIT>" , None ) <EOL> event . signatures . update ( <EOL> returned_invite . signatures <EOL> ) <EOL> if event . type == EventTypes . Redaction : <EOL> if self . auth . check_redaction ( event , auth_events = context . current_state ) : <EOL> original_event = yield self . store . get_event ( <EOL> event . redacts , <EOL> check_redacted = False , <EOL> get_prev_content = False , <EOL> allow_rejected = False , <EOL> allow_none = False <EOL> ) <EOL> if event . user_id != original_event . user_id : <EOL> raise AuthError ( <EOL> <NUM_LIT> , <EOL> "<STR_LIT>" <EOL> ) <EOL> if event . type == EventTypes . Create and context . current_state : <EOL> raise AuthError ( <EOL> <NUM_LIT> , <EOL> "<STR_LIT>" , <EOL> ) <EOL> action_generator = ActionGenerator ( self . hs ) <EOL> yield action_generator . handle_push_actions_for_event ( <EOL> event , context , self <EOL> ) <EOL> ( event_stream_id , max_stream_id ) = yield self . store . persist_event ( <EOL> event , context = context <EOL> ) <EOL> destinations = set ( ) <EOL> for k , s in context . current_state . items ( ) : <EOL> try : <EOL> if k [ <NUM_LIT:0> ] == EventTypes . Member : <EOL> if s . content [ "<STR_LIT>" ] == Membership . JOIN : <EOL> destinations . add ( <EOL> UserID . from_string ( s . state_key ) . domain <EOL> ) <EOL> except SynapseError : <EOL> logger . warn ( <EOL> "<STR_LIT>" , s . event_id <EOL> ) <EOL> with PreserveLoggingContext ( ) : <EOL> self . notifier . on_new_room_event ( <EOL> event , event_stream_id , max_stream_id , <EOL> extra_users = extra_users <EOL> ) <EOL> event . unsigned . pop ( "<STR_LIT>" , None ) <EOL> federation_handler . handle_new_event ( <EOL> event , destinations = destinations , <EOL> ) <EOL> @ defer . inlineCallbacks <EOL> def maybe_kick_guest_users ( self , event , current_state ) : <EOL> if event . type == EventTypes . GuestAccess : <EOL> guest_access = event . content . get ( "<STR_LIT>" , "<STR_LIT>" ) <EOL> if guest_access != "<STR_LIT>" : <EOL> yield self . kick_guest_users ( current_state ) <EOL> @ defer . inlineCallbacks <EOL> def kick_guest_users ( self , current_state ) : <EOL> for member_event in current_state : <EOL> try : <EOL> if member_event . type != EventTypes . Member : <EOL> continue <EOL> target_user = UserID . from_string ( member_event . state_key ) <EOL> if not self . hs . is_mine ( target_user ) : <EOL> continue <EOL> if member_event . content [ "<STR_LIT>" ] not in { <EOL> Membership . JOIN , <EOL> Membership . INVITE <EOL> } : <EOL> continue <EOL> if ( <EOL> "<STR_LIT>" not in member_event . content <EOL> or member_event . content [ "<STR_LIT>" ] != "<STR_LIT>" <EOL> ) : <EOL> continue <EOL> requester = Requester ( target_user , "<STR_LIT>" , True ) <EOL> handler = self . hs . get_handlers ( ) . room_member_handler <EOL> yield handler . update_membership ( <EOL> requester , <EOL> target_user , <EOL> member_event . room_id , <EOL> "<STR_LIT>" , <EOL> ratelimit = False , <EOL> ) <EOL> except Exception as e : <EOL> logger . warn ( "<STR_LIT>" % ( e , ) ) </s>
