You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

192 lines
6.8 KiB

  1. # Copyright 2014-2016 OpenMarket Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import logging
  15. import random
  16. from typing import TYPE_CHECKING, Iterable, List, Optional
  17. from synapse.api.constants import EduTypes, EventTypes, Membership, PresenceState
  18. from synapse.api.errors import AuthError, SynapseError
  19. from synapse.events import EventBase
  20. from synapse.events.utils import SerializeEventConfig
  21. from synapse.handlers.presence import format_user_presence_state
  22. from synapse.storage.databases.main.events_worker import EventRedactBehaviour
  23. from synapse.streams.config import PaginationConfig
  24. from synapse.types import JsonDict, Requester, UserID
  25. from synapse.visibility import filter_events_for_client
  26. if TYPE_CHECKING:
  27. from synapse.server import HomeServer
  28. logger = logging.getLogger(__name__)
  29. class EventStreamHandler:
  30. def __init__(self, hs: "HomeServer"):
  31. self.store = hs.get_datastores().main
  32. self.clock = hs.get_clock()
  33. self.hs = hs
  34. self.notifier = hs.get_notifier()
  35. self.state = hs.get_state_handler()
  36. self._server_notices_sender = hs.get_server_notices_sender()
  37. self._event_serializer = hs.get_event_client_serializer()
  38. async def get_stream(
  39. self,
  40. requester: Requester,
  41. pagin_config: PaginationConfig,
  42. timeout: int = 0,
  43. as_client_event: bool = True,
  44. affect_presence: bool = True,
  45. room_id: Optional[str] = None,
  46. ) -> JsonDict:
  47. """Fetches the events stream for a given user."""
  48. if room_id:
  49. blocked = await self.store.is_room_blocked(room_id)
  50. if blocked:
  51. raise SynapseError(403, "This room has been blocked on this server")
  52. # send any outstanding server notices to the user.
  53. await self._server_notices_sender.on_user_syncing(requester.user.to_string())
  54. presence_handler = self.hs.get_presence_handler()
  55. context = await presence_handler.user_syncing(
  56. requester.user.to_string(),
  57. requester.device_id,
  58. affect_presence=affect_presence,
  59. presence_state=PresenceState.ONLINE,
  60. )
  61. with context:
  62. if timeout:
  63. # If they've set a timeout set a minimum limit.
  64. timeout = max(timeout, 500)
  65. # Add some randomness to this value to try and mitigate against
  66. # thundering herds on restart.
  67. timeout = random.randint(int(timeout * 0.9), int(timeout * 1.1))
  68. stream_result = await self.notifier.get_events_for(
  69. requester.user,
  70. pagin_config,
  71. timeout,
  72. is_guest=requester.is_guest,
  73. explicit_room_id=room_id,
  74. )
  75. events = stream_result.events
  76. time_now = self.clock.time_msec()
  77. # When the user joins a new room, or another user joins a currently
  78. # joined room, we need to send down presence for those users.
  79. to_add: List[JsonDict] = []
  80. for event in events:
  81. if not isinstance(event, EventBase):
  82. continue
  83. if event.type == EventTypes.Member:
  84. if event.membership != Membership.JOIN:
  85. continue
  86. # Send down presence.
  87. if event.state_key == requester.user.to_string():
  88. # Send down presence for everyone in the room.
  89. users: Iterable[str] = await self.store.get_users_in_room(
  90. event.room_id
  91. )
  92. else:
  93. users = [event.state_key]
  94. states = await presence_handler.get_states(users)
  95. to_add.extend(
  96. {
  97. "type": EduTypes.PRESENCE,
  98. "content": format_user_presence_state(state, time_now),
  99. }
  100. for state in states
  101. )
  102. events.extend(to_add)
  103. chunks = await self._event_serializer.serialize_events(
  104. events,
  105. time_now,
  106. config=SerializeEventConfig(
  107. as_client_event=as_client_event, requester=requester
  108. ),
  109. )
  110. chunk = {
  111. "chunk": chunks,
  112. "start": await stream_result.start_token.to_string(self.store),
  113. "end": await stream_result.end_token.to_string(self.store),
  114. }
  115. return chunk
  116. class EventHandler:
  117. def __init__(self, hs: "HomeServer"):
  118. self.store = hs.get_datastores().main
  119. self._storage_controllers = hs.get_storage_controllers()
  120. async def get_event(
  121. self,
  122. user: UserID,
  123. room_id: Optional[str],
  124. event_id: str,
  125. show_redacted: bool = False,
  126. ) -> Optional[EventBase]:
  127. """Retrieve a single specified event.
  128. Args:
  129. user: The local user requesting the event
  130. room_id: The expected room id. We'll return None if the
  131. event's room does not match.
  132. event_id: The event ID to obtain.
  133. show_redacted: Should the full content of redacted events be returned?
  134. Returns:
  135. An event, or None if there is no event matching this ID.
  136. Raises:
  137. AuthError: if the user does not have the rights to inspect this event.
  138. """
  139. redact_behaviour = (
  140. EventRedactBehaviour.as_is if show_redacted else EventRedactBehaviour.redact
  141. )
  142. event = await self.store.get_event(
  143. event_id,
  144. check_room_id=room_id,
  145. redact_behaviour=redact_behaviour,
  146. allow_none=True,
  147. )
  148. if not event:
  149. return None
  150. is_user_in_room = await self.store.check_local_user_in_room(
  151. user_id=user.to_string(), room_id=event.room_id
  152. )
  153. # The user is peeking if they aren't in the room already
  154. is_peeking = not is_user_in_room
  155. filtered = await filter_events_for_client(
  156. self._storage_controllers, user.to_string(), [event], is_peeking=is_peeking
  157. )
  158. if not filtered:
  159. raise AuthError(403, "You don't have permission to access that event.")
  160. return event