room.py 16 KB

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