room.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014 - 2016 OpenMarket Ltd
  3. # Copyright 2018 New Vector Ltd
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """Contains functions for performing events on rooms."""
  17. import logging
  18. import math
  19. import string
  20. from collections import OrderedDict
  21. from twisted.internet import defer
  22. from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
  23. from synapse.api.errors import AuthError, Codes, StoreError, SynapseError
  24. from synapse.types import RoomAlias, RoomID, RoomStreamToken, UserID
  25. from synapse.util import stringutils
  26. from synapse.visibility import filter_events_for_client
  27. from ._base import BaseHandler
  28. logger = logging.getLogger(__name__)
  29. id_server_scheme = "https://"
  30. class RoomCreationHandler(BaseHandler):
  31. PRESETS_DICT = {
  32. RoomCreationPreset.PRIVATE_CHAT: {
  33. "join_rules": JoinRules.INVITE,
  34. "history_visibility": "shared",
  35. "original_invitees_have_ops": False,
  36. "guest_can_join": True,
  37. },
  38. RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
  39. "join_rules": JoinRules.INVITE,
  40. "history_visibility": "shared",
  41. "original_invitees_have_ops": True,
  42. "guest_can_join": True,
  43. },
  44. RoomCreationPreset.PUBLIC_CHAT: {
  45. "join_rules": JoinRules.PUBLIC,
  46. "history_visibility": "shared",
  47. "original_invitees_have_ops": False,
  48. "guest_can_join": False,
  49. },
  50. }
  51. def __init__(self, hs):
  52. super(RoomCreationHandler, self).__init__(hs)
  53. self.spam_checker = hs.get_spam_checker()
  54. self.event_creation_handler = hs.get_event_creation_handler()
  55. @defer.inlineCallbacks
  56. def create_room(self, requester, config, ratelimit=True,
  57. creator_join_profile=None):
  58. """ Creates a new room.
  59. Args:
  60. requester (synapse.types.Requester):
  61. The user who requested the room creation.
  62. config (dict) : A dict of configuration options.
  63. ratelimit (bool): set to False to disable the rate limiter
  64. creator_join_profile (dict|None):
  65. Set to override the displayname and avatar for the creating
  66. user in this room. If unset, displayname and avatar will be
  67. derived from the user's profile. If set, should contain the
  68. values to go in the body of the 'join' event (typically
  69. `avatar_url` and/or `displayname`.
  70. Returns:
  71. Deferred[dict]:
  72. a dict containing the keys `room_id` and, if an alias was
  73. requested, `room_alias`.
  74. Raises:
  75. SynapseError if the room ID couldn't be stored, or something went
  76. horribly wrong.
  77. """
  78. user_id = requester.user.to_string()
  79. if not self.spam_checker.user_may_create_room(user_id):
  80. raise SynapseError(403, "You are not permitted to create rooms")
  81. if ratelimit:
  82. yield self.ratelimit(requester)
  83. if "room_alias_name" in config:
  84. for wchar in string.whitespace:
  85. if wchar in config["room_alias_name"]:
  86. raise SynapseError(400, "Invalid characters in room alias")
  87. room_alias = RoomAlias(
  88. config["room_alias_name"],
  89. self.hs.hostname,
  90. )
  91. mapping = yield self.store.get_association_from_room_alias(
  92. room_alias
  93. )
  94. if mapping:
  95. raise SynapseError(
  96. 400,
  97. "Room alias already taken",
  98. Codes.ROOM_IN_USE
  99. )
  100. else:
  101. room_alias = None
  102. invite_list = config.get("invite", [])
  103. for i in invite_list:
  104. try:
  105. UserID.from_string(i)
  106. except Exception:
  107. raise SynapseError(400, "Invalid user_id: %s" % (i,))
  108. yield self.event_creation_handler.assert_accepted_privacy_policy(
  109. requester,
  110. )
  111. invite_3pid_list = config.get("invite_3pid", [])
  112. visibility = config.get("visibility", None)
  113. is_public = visibility == "public"
  114. # autogen room IDs and try to create it. We may clash, so just
  115. # try a few times till one goes through, giving up eventually.
  116. attempts = 0
  117. room_id = None
  118. while attempts < 5:
  119. try:
  120. random_string = stringutils.random_string(18)
  121. gen_room_id = RoomID(
  122. random_string,
  123. self.hs.hostname,
  124. )
  125. yield self.store.store_room(
  126. room_id=gen_room_id.to_string(),
  127. room_creator_user_id=user_id,
  128. is_public=is_public
  129. )
  130. room_id = gen_room_id.to_string()
  131. break
  132. except StoreError:
  133. attempts += 1
  134. if not room_id:
  135. raise StoreError(500, "Couldn't generate a room ID.")
  136. if room_alias:
  137. directory_handler = self.hs.get_handlers().directory_handler
  138. yield directory_handler.create_association(
  139. user_id=user_id,
  140. room_id=room_id,
  141. room_alias=room_alias,
  142. servers=[self.hs.hostname],
  143. )
  144. preset_config = config.get(
  145. "preset",
  146. RoomCreationPreset.PRIVATE_CHAT
  147. if visibility == "private"
  148. else RoomCreationPreset.PUBLIC_CHAT
  149. )
  150. raw_initial_state = config.get("initial_state", [])
  151. initial_state = OrderedDict()
  152. for val in raw_initial_state:
  153. initial_state[(val["type"], val.get("state_key", ""))] = val["content"]
  154. creation_content = config.get("creation_content", {})
  155. room_member_handler = self.hs.get_room_member_handler()
  156. yield self._send_events_for_new_room(
  157. requester,
  158. room_id,
  159. room_member_handler,
  160. preset_config=preset_config,
  161. invite_list=invite_list,
  162. initial_state=initial_state,
  163. creation_content=creation_content,
  164. room_alias=room_alias,
  165. power_level_content_override=config.get("power_level_content_override", {}),
  166. creator_join_profile=creator_join_profile,
  167. )
  168. if "name" in config:
  169. name = config["name"]
  170. yield self.event_creation_handler.create_and_send_nonmember_event(
  171. requester,
  172. {
  173. "type": EventTypes.Name,
  174. "room_id": room_id,
  175. "sender": user_id,
  176. "state_key": "",
  177. "content": {"name": name},
  178. },
  179. ratelimit=False)
  180. if "topic" in config:
  181. topic = config["topic"]
  182. yield self.event_creation_handler.create_and_send_nonmember_event(
  183. requester,
  184. {
  185. "type": EventTypes.Topic,
  186. "room_id": room_id,
  187. "sender": user_id,
  188. "state_key": "",
  189. "content": {"topic": topic},
  190. },
  191. ratelimit=False)
  192. for invitee in invite_list:
  193. content = {}
  194. is_direct = config.get("is_direct", None)
  195. if is_direct:
  196. content["is_direct"] = is_direct
  197. yield room_member_handler.update_membership(
  198. requester,
  199. UserID.from_string(invitee),
  200. room_id,
  201. "invite",
  202. ratelimit=False,
  203. content=content,
  204. )
  205. for invite_3pid in invite_3pid_list:
  206. id_server = invite_3pid["id_server"]
  207. address = invite_3pid["address"]
  208. medium = invite_3pid["medium"]
  209. yield self.hs.get_room_member_handler().do_3pid_invite(
  210. room_id,
  211. requester.user,
  212. medium,
  213. address,
  214. id_server,
  215. requester,
  216. txn_id=None,
  217. )
  218. result = {"room_id": room_id}
  219. if room_alias:
  220. result["room_alias"] = room_alias.to_string()
  221. yield directory_handler.send_room_alias_update_event(
  222. requester, user_id, room_id
  223. )
  224. defer.returnValue(result)
  225. @defer.inlineCallbacks
  226. def _send_events_for_new_room(
  227. self,
  228. creator, # A Requester object.
  229. room_id,
  230. room_member_handler,
  231. preset_config,
  232. invite_list,
  233. initial_state,
  234. creation_content,
  235. room_alias,
  236. power_level_content_override,
  237. creator_join_profile,
  238. ):
  239. def create(etype, content, **kwargs):
  240. e = {
  241. "type": etype,
  242. "content": content,
  243. }
  244. e.update(event_keys)
  245. e.update(kwargs)
  246. return e
  247. @defer.inlineCallbacks
  248. def send(etype, content, **kwargs):
  249. event = create(etype, content, **kwargs)
  250. yield self.event_creation_handler.create_and_send_nonmember_event(
  251. creator,
  252. event,
  253. ratelimit=False
  254. )
  255. config = RoomCreationHandler.PRESETS_DICT[preset_config]
  256. creator_id = creator.user.to_string()
  257. event_keys = {
  258. "room_id": room_id,
  259. "sender": creator_id,
  260. "state_key": "",
  261. }
  262. creation_content.update({"creator": creator_id})
  263. yield send(
  264. etype=EventTypes.Create,
  265. content=creation_content,
  266. )
  267. yield room_member_handler.update_membership(
  268. creator,
  269. creator.user,
  270. room_id,
  271. "join",
  272. ratelimit=False,
  273. content=creator_join_profile,
  274. )
  275. # We treat the power levels override specially as this needs to be one
  276. # of the first events that get sent into a room.
  277. pl_content = initial_state.pop((EventTypes.PowerLevels, ''), None)
  278. if pl_content is not None:
  279. yield send(
  280. etype=EventTypes.PowerLevels,
  281. content=pl_content,
  282. )
  283. else:
  284. power_level_content = {
  285. "users": {
  286. creator_id: 100,
  287. },
  288. "users_default": 0,
  289. "events": {
  290. EventTypes.Name: 50,
  291. EventTypes.PowerLevels: 100,
  292. EventTypes.RoomHistoryVisibility: 100,
  293. EventTypes.CanonicalAlias: 50,
  294. EventTypes.RoomAvatar: 50,
  295. },
  296. "events_default": 0,
  297. "state_default": 50,
  298. "ban": 50,
  299. "kick": 50,
  300. "redact": 50,
  301. "invite": 0,
  302. }
  303. if config["original_invitees_have_ops"]:
  304. for invitee in invite_list:
  305. power_level_content["users"][invitee] = 100
  306. power_level_content.update(power_level_content_override)
  307. yield send(
  308. etype=EventTypes.PowerLevels,
  309. content=power_level_content,
  310. )
  311. if room_alias and (EventTypes.CanonicalAlias, '') not in initial_state:
  312. yield send(
  313. etype=EventTypes.CanonicalAlias,
  314. content={"alias": room_alias.to_string()},
  315. )
  316. if (EventTypes.JoinRules, '') not in initial_state:
  317. yield send(
  318. etype=EventTypes.JoinRules,
  319. content={"join_rule": config["join_rules"]},
  320. )
  321. if (EventTypes.RoomHistoryVisibility, '') not in initial_state:
  322. yield send(
  323. etype=EventTypes.RoomHistoryVisibility,
  324. content={"history_visibility": config["history_visibility"]}
  325. )
  326. if config["guest_can_join"]:
  327. if (EventTypes.GuestAccess, '') not in initial_state:
  328. yield send(
  329. etype=EventTypes.GuestAccess,
  330. content={"guest_access": "can_join"}
  331. )
  332. for (etype, state_key), content in initial_state.items():
  333. yield send(
  334. etype=etype,
  335. state_key=state_key,
  336. content=content,
  337. )
  338. class RoomContextHandler(BaseHandler):
  339. @defer.inlineCallbacks
  340. def get_event_context(self, user, room_id, event_id, limit):
  341. """Retrieves events, pagination tokens and state around a given event
  342. in a room.
  343. Args:
  344. user (UserID)
  345. room_id (str)
  346. event_id (str)
  347. limit (int): The maximum number of events to return in total
  348. (excluding state).
  349. Returns:
  350. dict, or None if the event isn't found
  351. """
  352. before_limit = math.floor(limit / 2.)
  353. after_limit = limit - before_limit
  354. now_token = yield self.hs.get_event_sources().get_current_token()
  355. users = yield self.store.get_users_in_room(room_id)
  356. is_peeking = user.to_string() not in users
  357. def filter_evts(events):
  358. return filter_events_for_client(
  359. self.store,
  360. user.to_string(),
  361. events,
  362. is_peeking=is_peeking
  363. )
  364. event = yield self.store.get_event(event_id, get_prev_content=True,
  365. allow_none=True)
  366. if not event:
  367. defer.returnValue(None)
  368. return
  369. filtered = yield(filter_evts([event]))
  370. if not filtered:
  371. raise AuthError(
  372. 403,
  373. "You don't have permission to access that event."
  374. )
  375. results = yield self.store.get_events_around(
  376. room_id, event_id, before_limit, after_limit
  377. )
  378. results["events_before"] = yield filter_evts(results["events_before"])
  379. results["events_after"] = yield filter_evts(results["events_after"])
  380. results["event"] = event
  381. if results["events_after"]:
  382. last_event_id = results["events_after"][-1].event_id
  383. else:
  384. last_event_id = event_id
  385. state = yield self.store.get_state_for_events(
  386. [last_event_id], None
  387. )
  388. results["state"] = list(state[last_event_id].values())
  389. results["start"] = now_token.copy_and_replace(
  390. "room_key", results["start"]
  391. ).to_string()
  392. results["end"] = now_token.copy_and_replace(
  393. "room_key", results["end"]
  394. ).to_string()
  395. defer.returnValue(results)
  396. class RoomEventSource(object):
  397. def __init__(self, hs):
  398. self.store = hs.get_datastore()
  399. @defer.inlineCallbacks
  400. def get_new_events(
  401. self,
  402. user,
  403. from_key,
  404. limit,
  405. room_ids,
  406. is_guest,
  407. explicit_room_id=None,
  408. ):
  409. # We just ignore the key for now.
  410. to_key = yield self.get_current_key()
  411. from_token = RoomStreamToken.parse(from_key)
  412. if from_token.topological:
  413. logger.warn("Stream has topological part!!!! %r", from_key)
  414. from_key = "s%s" % (from_token.stream,)
  415. app_service = self.store.get_app_service_by_user_id(
  416. user.to_string()
  417. )
  418. if app_service:
  419. # We no longer support AS users using /sync directly.
  420. # See https://github.com/matrix-org/matrix-doc/issues/1144
  421. raise NotImplementedError()
  422. else:
  423. room_events = yield self.store.get_membership_changes_for_user(
  424. user.to_string(), from_key, to_key
  425. )
  426. room_to_events = yield self.store.get_room_events_stream_for_rooms(
  427. room_ids=room_ids,
  428. from_key=from_key,
  429. to_key=to_key,
  430. limit=limit or 10,
  431. order='ASC',
  432. )
  433. events = list(room_events)
  434. events.extend(e for evs, _ in room_to_events.values() for e in evs)
  435. events.sort(key=lambda e: e.internal_metadata.order)
  436. if limit:
  437. events[:] = events[:limit]
  438. if events:
  439. end_key = events[-1].internal_metadata.after
  440. else:
  441. end_key = to_key
  442. defer.returnValue((events, end_key))
  443. def get_current_key(self):
  444. return self.store.get_room_events_max_id()
  445. def get_current_key_for_room(self, room_id):
  446. return self.store.get_room_events_max_id(room_id)
  447. @defer.inlineCallbacks
  448. def get_pagination_rows(self, user, config, key):
  449. events, next_key = yield self.store.paginate_room_events(
  450. room_id=key,
  451. from_key=config.from_key,
  452. to_key=config.to_key,
  453. direction=config.direction,
  454. limit=config.limit,
  455. )
  456. defer.returnValue((events, next_key))