room.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  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 itertools
  18. import logging
  19. import math
  20. import string
  21. from collections import OrderedDict
  22. from six import iteritems, string_types
  23. from twisted.internet import defer
  24. from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
  25. from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
  26. from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
  27. from synapse.storage.state import StateFilter
  28. from synapse.types import RoomAlias, RoomID, RoomStreamToken, StreamToken, UserID
  29. from synapse.util import stringutils
  30. from synapse.util.async_helpers import Linearizer
  31. from synapse.util.caches.response_cache import ResponseCache
  32. from synapse.visibility import filter_events_for_client
  33. from ._base import BaseHandler
  34. logger = logging.getLogger(__name__)
  35. id_server_scheme = "https://"
  36. FIVE_MINUTES_IN_MS = 5 * 60 * 1000
  37. class RoomCreationHandler(BaseHandler):
  38. PRESETS_DICT = {
  39. RoomCreationPreset.PRIVATE_CHAT: {
  40. "join_rules": JoinRules.INVITE,
  41. "history_visibility": "shared",
  42. "original_invitees_have_ops": False,
  43. "guest_can_join": True,
  44. },
  45. RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
  46. "join_rules": JoinRules.INVITE,
  47. "history_visibility": "shared",
  48. "original_invitees_have_ops": True,
  49. "guest_can_join": True,
  50. },
  51. RoomCreationPreset.PUBLIC_CHAT: {
  52. "join_rules": JoinRules.PUBLIC,
  53. "history_visibility": "shared",
  54. "original_invitees_have_ops": False,
  55. "guest_can_join": False,
  56. },
  57. }
  58. def __init__(self, hs):
  59. super(RoomCreationHandler, self).__init__(hs)
  60. self.spam_checker = hs.get_spam_checker()
  61. self.event_creation_handler = hs.get_event_creation_handler()
  62. self.room_member_handler = hs.get_room_member_handler()
  63. self.config = hs.config
  64. # linearizer to stop two upgrades happening at once
  65. self._upgrade_linearizer = Linearizer("room_upgrade_linearizer")
  66. # If a user tries to update the same room multiple times in quick
  67. # succession, only process the first attempt and return its result to
  68. # subsequent requests
  69. self._upgrade_response_cache = ResponseCache(
  70. hs, "room_upgrade", timeout_ms=FIVE_MINUTES_IN_MS
  71. )
  72. self._server_notices_mxid = hs.config.server_notices_mxid
  73. self.third_party_event_rules = hs.get_third_party_event_rules()
  74. @defer.inlineCallbacks
  75. def upgrade_room(self, requester, old_room_id, new_version):
  76. """Replace a room with a new room with a different version
  77. Args:
  78. requester (synapse.types.Requester): the user requesting the upgrade
  79. old_room_id (unicode): the id of the room to be replaced
  80. new_version (unicode): the new room version to use
  81. Returns:
  82. Deferred[unicode]: the new room id
  83. """
  84. yield self.ratelimit(requester)
  85. user_id = requester.user.to_string()
  86. # Check if this room is already being upgraded by another person
  87. for key in self._upgrade_response_cache.pending_result_cache:
  88. if key[0] == old_room_id and key[1] != user_id:
  89. # Two different people are trying to upgrade the same room.
  90. # Send the second an error.
  91. #
  92. # Note that this of course only gets caught if both users are
  93. # on the same homeserver.
  94. raise SynapseError(
  95. 400, "An upgrade for this room is currently in progress"
  96. )
  97. # Upgrade the room
  98. #
  99. # If this user has sent multiple upgrade requests for the same room
  100. # and one of them is not complete yet, cache the response and
  101. # return it to all subsequent requests
  102. ret = yield self._upgrade_response_cache.wrap(
  103. (old_room_id, user_id),
  104. self._upgrade_room,
  105. requester,
  106. old_room_id,
  107. new_version, # args for _upgrade_room
  108. )
  109. return ret
  110. @defer.inlineCallbacks
  111. def _upgrade_room(self, requester, old_room_id, new_version):
  112. user_id = requester.user.to_string()
  113. # start by allocating a new room id
  114. r = yield self.store.get_room(old_room_id)
  115. if r is None:
  116. raise NotFoundError("Unknown room id %s" % (old_room_id,))
  117. new_room_id = yield self._generate_room_id(
  118. creator_id=user_id, is_public=r["is_public"]
  119. )
  120. logger.info("Creating new room %s to replace %s", new_room_id, old_room_id)
  121. # we create and auth the tombstone event before properly creating the new
  122. # room, to check our user has perms in the old room.
  123. tombstone_event, tombstone_context = (
  124. yield self.event_creation_handler.create_event(
  125. requester,
  126. {
  127. "type": EventTypes.Tombstone,
  128. "state_key": "",
  129. "room_id": old_room_id,
  130. "sender": user_id,
  131. "content": {
  132. "body": "This room has been replaced",
  133. "replacement_room": new_room_id,
  134. },
  135. },
  136. token_id=requester.access_token_id,
  137. )
  138. )
  139. old_room_version = yield self.store.get_room_version(old_room_id)
  140. yield self.auth.check_from_context(
  141. old_room_version, tombstone_event, tombstone_context
  142. )
  143. yield self.clone_existing_room(
  144. requester,
  145. old_room_id=old_room_id,
  146. new_room_id=new_room_id,
  147. new_room_version=new_version,
  148. tombstone_event_id=tombstone_event.event_id,
  149. )
  150. # now send the tombstone
  151. yield self.event_creation_handler.send_nonmember_event(
  152. requester, tombstone_event, tombstone_context
  153. )
  154. old_room_state = yield tombstone_context.get_current_state_ids(self.store)
  155. # update any aliases
  156. yield self._move_aliases_to_new_room(
  157. requester, old_room_id, new_room_id, old_room_state
  158. )
  159. # and finally, shut down the PLs in the old room, and update them in the new
  160. # room.
  161. yield self._update_upgraded_room_pls(
  162. requester, old_room_id, new_room_id, old_room_state
  163. )
  164. return new_room_id
  165. @defer.inlineCallbacks
  166. def _update_upgraded_room_pls(
  167. self, requester, old_room_id, new_room_id, old_room_state
  168. ):
  169. """Send updated power levels in both rooms after an upgrade
  170. Args:
  171. requester (synapse.types.Requester): the user requesting the upgrade
  172. old_room_id (unicode): the id of the room to be replaced
  173. new_room_id (unicode): the id of the replacement room
  174. old_room_state (dict[tuple[str, str], str]): the state map for the old room
  175. Returns:
  176. Deferred
  177. """
  178. old_room_pl_event_id = old_room_state.get((EventTypes.PowerLevels, ""))
  179. if old_room_pl_event_id is None:
  180. logger.warning(
  181. "Not supported: upgrading a room with no PL event. Not setting PLs "
  182. "in old room."
  183. )
  184. return
  185. old_room_pl_state = yield self.store.get_event(old_room_pl_event_id)
  186. # we try to stop regular users from speaking by setting the PL required
  187. # to send regular events and invites to 'Moderator' level. That's normally
  188. # 50, but if the default PL in a room is 50 or more, then we set the
  189. # required PL above that.
  190. pl_content = dict(old_room_pl_state.content)
  191. users_default = int(pl_content.get("users_default", 0))
  192. restricted_level = max(users_default + 1, 50)
  193. updated = False
  194. for v in ("invite", "events_default"):
  195. current = int(pl_content.get(v, 0))
  196. if current < restricted_level:
  197. logger.info(
  198. "Setting level for %s in %s to %i (was %i)",
  199. v,
  200. old_room_id,
  201. restricted_level,
  202. current,
  203. )
  204. pl_content[v] = restricted_level
  205. updated = True
  206. else:
  207. logger.info("Not setting level for %s (already %i)", v, current)
  208. if updated:
  209. try:
  210. yield self.event_creation_handler.create_and_send_nonmember_event(
  211. requester,
  212. {
  213. "type": EventTypes.PowerLevels,
  214. "state_key": "",
  215. "room_id": old_room_id,
  216. "sender": requester.user.to_string(),
  217. "content": pl_content,
  218. },
  219. ratelimit=False,
  220. )
  221. except AuthError as e:
  222. logger.warning("Unable to update PLs in old room: %s", e)
  223. logger.info("Setting correct PLs in new room")
  224. yield self.event_creation_handler.create_and_send_nonmember_event(
  225. requester,
  226. {
  227. "type": EventTypes.PowerLevels,
  228. "state_key": "",
  229. "room_id": new_room_id,
  230. "sender": requester.user.to_string(),
  231. "content": old_room_pl_state.content,
  232. },
  233. ratelimit=False,
  234. )
  235. @defer.inlineCallbacks
  236. def clone_existing_room(
  237. self, requester, old_room_id, new_room_id, new_room_version, tombstone_event_id
  238. ):
  239. """Populate a new room based on an old room
  240. Args:
  241. requester (synapse.types.Requester): the user requesting the upgrade
  242. old_room_id (unicode): the id of the room to be replaced
  243. new_room_id (unicode): the id to give the new room (should already have been
  244. created with _gemerate_room_id())
  245. new_room_version (unicode): the new room version to use
  246. tombstone_event_id (unicode|str): the ID of the tombstone event in the old
  247. room.
  248. Returns:
  249. Deferred[None]
  250. """
  251. user_id = requester.user.to_string()
  252. if not self.spam_checker.user_may_create_room(user_id):
  253. raise SynapseError(403, "You are not permitted to create rooms")
  254. creation_content = {
  255. "room_version": new_room_version,
  256. "predecessor": {"room_id": old_room_id, "event_id": tombstone_event_id},
  257. }
  258. # Check if old room was non-federatable
  259. # Get old room's create event
  260. old_room_create_event = yield self.store.get_create_event_for_room(old_room_id)
  261. # Check if the create event specified a non-federatable room
  262. if not old_room_create_event.content.get("m.federate", True):
  263. # If so, mark the new room as non-federatable as well
  264. creation_content["m.federate"] = False
  265. initial_state = dict()
  266. # Replicate relevant room events
  267. types_to_copy = (
  268. (EventTypes.JoinRules, ""),
  269. (EventTypes.Name, ""),
  270. (EventTypes.Topic, ""),
  271. (EventTypes.RoomHistoryVisibility, ""),
  272. (EventTypes.GuestAccess, ""),
  273. (EventTypes.RoomAvatar, ""),
  274. (EventTypes.Encryption, ""),
  275. (EventTypes.ServerACL, ""),
  276. (EventTypes.RelatedGroups, ""),
  277. )
  278. old_room_state_ids = yield self.store.get_filtered_current_state_ids(
  279. old_room_id, StateFilter.from_types(types_to_copy)
  280. )
  281. # map from event_id to BaseEvent
  282. old_room_state_events = yield self.store.get_events(old_room_state_ids.values())
  283. for k, old_event_id in iteritems(old_room_state_ids):
  284. old_event = old_room_state_events.get(old_event_id)
  285. if old_event:
  286. initial_state[k] = old_event.content
  287. yield self._send_events_for_new_room(
  288. requester,
  289. new_room_id,
  290. # we expect to override all the presets with initial_state, so this is
  291. # somewhat arbitrary.
  292. preset_config=RoomCreationPreset.PRIVATE_CHAT,
  293. invite_list=[],
  294. initial_state=initial_state,
  295. creation_content=creation_content,
  296. )
  297. # Transfer membership events
  298. old_room_member_state_ids = yield self.store.get_filtered_current_state_ids(
  299. old_room_id, StateFilter.from_types([(EventTypes.Member, None)])
  300. )
  301. # map from event_id to BaseEvent
  302. old_room_member_state_events = yield self.store.get_events(
  303. old_room_member_state_ids.values()
  304. )
  305. for k, old_event in iteritems(old_room_member_state_events):
  306. # Only transfer ban events
  307. if (
  308. "membership" in old_event.content
  309. and old_event.content["membership"] == "ban"
  310. ):
  311. yield self.room_member_handler.update_membership(
  312. requester,
  313. UserID.from_string(old_event["state_key"]),
  314. new_room_id,
  315. "ban",
  316. ratelimit=False,
  317. content=old_event.content,
  318. )
  319. # XXX invites/joins
  320. # XXX 3pid invites
  321. @defer.inlineCallbacks
  322. def _move_aliases_to_new_room(
  323. self, requester, old_room_id, new_room_id, old_room_state
  324. ):
  325. directory_handler = self.hs.get_handlers().directory_handler
  326. aliases = yield self.store.get_aliases_for_room(old_room_id)
  327. # check to see if we have a canonical alias.
  328. canonical_alias = None
  329. canonical_alias_event_id = old_room_state.get((EventTypes.CanonicalAlias, ""))
  330. if canonical_alias_event_id:
  331. canonical_alias_event = yield self.store.get_event(canonical_alias_event_id)
  332. if canonical_alias_event:
  333. canonical_alias = canonical_alias_event.content.get("alias", "")
  334. # first we try to remove the aliases from the old room (we suppress sending
  335. # the room_aliases event until the end).
  336. #
  337. # Note that we'll only be able to remove aliases that (a) aren't owned by an AS,
  338. # and (b) unless the user is a server admin, which the user created.
  339. #
  340. # This is probably correct - given we don't allow such aliases to be deleted
  341. # normally, it would be odd to allow it in the case of doing a room upgrade -
  342. # but it makes the upgrade less effective, and you have to wonder why a room
  343. # admin can't remove aliases that point to that room anyway.
  344. # (cf https://github.com/matrix-org/synapse/issues/2360)
  345. #
  346. removed_aliases = []
  347. for alias_str in aliases:
  348. alias = RoomAlias.from_string(alias_str)
  349. try:
  350. yield directory_handler.delete_association(
  351. requester, alias, send_event=False
  352. )
  353. removed_aliases.append(alias_str)
  354. except SynapseError as e:
  355. logger.warning("Unable to remove alias %s from old room: %s", alias, e)
  356. # if we didn't find any aliases, or couldn't remove anyway, we can skip the rest
  357. # of this.
  358. if not removed_aliases:
  359. return
  360. try:
  361. # this can fail if, for some reason, our user doesn't have perms to send
  362. # m.room.aliases events in the old room (note that we've already checked that
  363. # they have perms to send a tombstone event, so that's not terribly likely).
  364. #
  365. # If that happens, it's regrettable, but we should carry on: it's the same
  366. # as when you remove an alias from the directory normally - it just means that
  367. # the aliases event gets out of sync with the directory
  368. # (cf https://github.com/vector-im/riot-web/issues/2369)
  369. yield directory_handler.send_room_alias_update_event(requester, old_room_id)
  370. except AuthError as e:
  371. logger.warning("Failed to send updated alias event on old room: %s", e)
  372. # we can now add any aliases we successfully removed to the new room.
  373. for alias in removed_aliases:
  374. try:
  375. yield directory_handler.create_association(
  376. requester,
  377. RoomAlias.from_string(alias),
  378. new_room_id,
  379. servers=(self.hs.hostname,),
  380. send_event=False,
  381. check_membership=False,
  382. )
  383. logger.info("Moved alias %s to new room", alias)
  384. except SynapseError as e:
  385. # I'm not really expecting this to happen, but it could if the spam
  386. # checking module decides it shouldn't, or similar.
  387. logger.error("Error adding alias %s to new room: %s", alias, e)
  388. try:
  389. if canonical_alias and (canonical_alias in removed_aliases):
  390. yield self.event_creation_handler.create_and_send_nonmember_event(
  391. requester,
  392. {
  393. "type": EventTypes.CanonicalAlias,
  394. "state_key": "",
  395. "room_id": new_room_id,
  396. "sender": requester.user.to_string(),
  397. "content": {"alias": canonical_alias},
  398. },
  399. ratelimit=False,
  400. )
  401. yield directory_handler.send_room_alias_update_event(requester, new_room_id)
  402. except SynapseError as e:
  403. # again I'm not really expecting this to fail, but if it does, I'd rather
  404. # we returned the new room to the client at this point.
  405. logger.error("Unable to send updated alias events in new room: %s", e)
  406. @defer.inlineCallbacks
  407. def create_room(self, requester, config, ratelimit=True, creator_join_profile=None):
  408. """ Creates a new room.
  409. Args:
  410. requester (synapse.types.Requester):
  411. The user who requested the room creation.
  412. config (dict) : A dict of configuration options.
  413. ratelimit (bool): set to False to disable the rate limiter
  414. creator_join_profile (dict|None):
  415. Set to override the displayname and avatar for the creating
  416. user in this room. If unset, displayname and avatar will be
  417. derived from the user's profile. If set, should contain the
  418. values to go in the body of the 'join' event (typically
  419. `avatar_url` and/or `displayname`.
  420. Returns:
  421. Deferred[dict]:
  422. a dict containing the keys `room_id` and, if an alias was
  423. requested, `room_alias`.
  424. Raises:
  425. SynapseError if the room ID couldn't be stored, or something went
  426. horribly wrong.
  427. ResourceLimitError if server is blocked to some resource being
  428. exceeded
  429. """
  430. user_id = requester.user.to_string()
  431. yield self.auth.check_auth_blocking(user_id)
  432. if (
  433. self._server_notices_mxid is not None
  434. and requester.user.to_string() == self._server_notices_mxid
  435. ):
  436. # allow the server notices mxid to create rooms
  437. is_requester_admin = True
  438. else:
  439. is_requester_admin = yield self.auth.is_server_admin(requester.user)
  440. # Check whether the third party rules allows/changes the room create
  441. # request.
  442. yield self.third_party_event_rules.on_create_room(
  443. requester, config, is_requester_admin=is_requester_admin
  444. )
  445. if not is_requester_admin and not self.spam_checker.user_may_create_room(
  446. user_id
  447. ):
  448. raise SynapseError(403, "You are not permitted to create rooms")
  449. if ratelimit:
  450. yield self.ratelimit(requester)
  451. room_version = config.get(
  452. "room_version", self.config.default_room_version.identifier
  453. )
  454. if not isinstance(room_version, string_types):
  455. raise SynapseError(400, "room_version must be a string", Codes.BAD_JSON)
  456. if room_version not in KNOWN_ROOM_VERSIONS:
  457. raise SynapseError(
  458. 400,
  459. "Your homeserver does not support this room version",
  460. Codes.UNSUPPORTED_ROOM_VERSION,
  461. )
  462. if "room_alias_name" in config:
  463. for wchar in string.whitespace:
  464. if wchar in config["room_alias_name"]:
  465. raise SynapseError(400, "Invalid characters in room alias")
  466. room_alias = RoomAlias(config["room_alias_name"], self.hs.hostname)
  467. mapping = yield self.store.get_association_from_room_alias(room_alias)
  468. if mapping:
  469. raise SynapseError(400, "Room alias already taken", Codes.ROOM_IN_USE)
  470. else:
  471. room_alias = None
  472. invite_list = config.get("invite", [])
  473. for i in invite_list:
  474. try:
  475. UserID.from_string(i)
  476. except Exception:
  477. raise SynapseError(400, "Invalid user_id: %s" % (i,))
  478. yield self.event_creation_handler.assert_accepted_privacy_policy(requester)
  479. invite_3pid_list = config.get("invite_3pid", [])
  480. visibility = config.get("visibility", None)
  481. is_public = visibility == "public"
  482. room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public)
  483. if room_alias:
  484. directory_handler = self.hs.get_handlers().directory_handler
  485. yield directory_handler.create_association(
  486. requester=requester,
  487. room_id=room_id,
  488. room_alias=room_alias,
  489. servers=[self.hs.hostname],
  490. send_event=False,
  491. check_membership=False,
  492. )
  493. preset_config = config.get(
  494. "preset",
  495. RoomCreationPreset.PRIVATE_CHAT
  496. if visibility == "private"
  497. else RoomCreationPreset.PUBLIC_CHAT,
  498. )
  499. raw_initial_state = config.get("initial_state", [])
  500. initial_state = OrderedDict()
  501. for val in raw_initial_state:
  502. initial_state[(val["type"], val.get("state_key", ""))] = val["content"]
  503. creation_content = config.get("creation_content", {})
  504. # override any attempt to set room versions via the creation_content
  505. creation_content["room_version"] = room_version
  506. yield self._send_events_for_new_room(
  507. requester,
  508. room_id,
  509. preset_config=preset_config,
  510. invite_list=invite_list,
  511. initial_state=initial_state,
  512. creation_content=creation_content,
  513. room_alias=room_alias,
  514. power_level_content_override=config.get("power_level_content_override"),
  515. creator_join_profile=creator_join_profile,
  516. )
  517. if "name" in config:
  518. name = config["name"]
  519. yield self.event_creation_handler.create_and_send_nonmember_event(
  520. requester,
  521. {
  522. "type": EventTypes.Name,
  523. "room_id": room_id,
  524. "sender": user_id,
  525. "state_key": "",
  526. "content": {"name": name},
  527. },
  528. ratelimit=False,
  529. )
  530. if "topic" in config:
  531. topic = config["topic"]
  532. yield self.event_creation_handler.create_and_send_nonmember_event(
  533. requester,
  534. {
  535. "type": EventTypes.Topic,
  536. "room_id": room_id,
  537. "sender": user_id,
  538. "state_key": "",
  539. "content": {"topic": topic},
  540. },
  541. ratelimit=False,
  542. )
  543. for invitee in invite_list:
  544. content = {}
  545. is_direct = config.get("is_direct", None)
  546. if is_direct:
  547. content["is_direct"] = is_direct
  548. yield self.room_member_handler.update_membership(
  549. requester,
  550. UserID.from_string(invitee),
  551. room_id,
  552. "invite",
  553. ratelimit=False,
  554. content=content,
  555. )
  556. for invite_3pid in invite_3pid_list:
  557. id_server = invite_3pid["id_server"]
  558. address = invite_3pid["address"]
  559. medium = invite_3pid["medium"]
  560. yield self.hs.get_room_member_handler().do_3pid_invite(
  561. room_id,
  562. requester.user,
  563. medium,
  564. address,
  565. id_server,
  566. requester,
  567. txn_id=None,
  568. )
  569. result = {"room_id": room_id}
  570. if room_alias:
  571. result["room_alias"] = room_alias.to_string()
  572. yield directory_handler.send_room_alias_update_event(requester, room_id)
  573. return result
  574. @defer.inlineCallbacks
  575. def _send_events_for_new_room(
  576. self,
  577. creator, # A Requester object.
  578. room_id,
  579. preset_config,
  580. invite_list,
  581. initial_state,
  582. creation_content,
  583. room_alias=None,
  584. power_level_content_override=None,
  585. creator_join_profile=None,
  586. ):
  587. def create(etype, content, **kwargs):
  588. e = {"type": etype, "content": content}
  589. e.update(event_keys)
  590. e.update(kwargs)
  591. return e
  592. @defer.inlineCallbacks
  593. def send(etype, content, **kwargs):
  594. event = create(etype, content, **kwargs)
  595. logger.info("Sending %s in new room", etype)
  596. yield self.event_creation_handler.create_and_send_nonmember_event(
  597. creator, event, ratelimit=False
  598. )
  599. config = RoomCreationHandler.PRESETS_DICT[preset_config]
  600. creator_id = creator.user.to_string()
  601. event_keys = {"room_id": room_id, "sender": creator_id, "state_key": ""}
  602. creation_content.update({"creator": creator_id})
  603. yield send(etype=EventTypes.Create, content=creation_content)
  604. logger.info("Sending %s in new room", EventTypes.Member)
  605. yield self.room_member_handler.update_membership(
  606. creator,
  607. creator.user,
  608. room_id,
  609. "join",
  610. ratelimit=False,
  611. content=creator_join_profile,
  612. )
  613. # We treat the power levels override specially as this needs to be one
  614. # of the first events that get sent into a room.
  615. pl_content = initial_state.pop((EventTypes.PowerLevels, ""), None)
  616. if pl_content is not None:
  617. yield send(etype=EventTypes.PowerLevels, content=pl_content)
  618. else:
  619. power_level_content = {
  620. "users": {creator_id: 100},
  621. "users_default": 0,
  622. "events": {
  623. EventTypes.Name: 50,
  624. EventTypes.PowerLevels: 100,
  625. EventTypes.RoomHistoryVisibility: 100,
  626. EventTypes.CanonicalAlias: 50,
  627. EventTypes.RoomAvatar: 50,
  628. },
  629. "events_default": 0,
  630. "state_default": 50,
  631. "ban": 50,
  632. "kick": 50,
  633. "redact": 50,
  634. "invite": 0,
  635. }
  636. if config["original_invitees_have_ops"]:
  637. for invitee in invite_list:
  638. power_level_content["users"][invitee] = 100
  639. if power_level_content_override:
  640. power_level_content.update(power_level_content_override)
  641. yield send(etype=EventTypes.PowerLevels, content=power_level_content)
  642. if room_alias and (EventTypes.CanonicalAlias, "") not in initial_state:
  643. yield send(
  644. etype=EventTypes.CanonicalAlias,
  645. content={"alias": room_alias.to_string()},
  646. )
  647. if (EventTypes.JoinRules, "") not in initial_state:
  648. yield send(
  649. etype=EventTypes.JoinRules, content={"join_rule": config["join_rules"]}
  650. )
  651. if (EventTypes.RoomHistoryVisibility, "") not in initial_state:
  652. yield send(
  653. etype=EventTypes.RoomHistoryVisibility,
  654. content={"history_visibility": config["history_visibility"]},
  655. )
  656. if config["guest_can_join"]:
  657. if (EventTypes.GuestAccess, "") not in initial_state:
  658. yield send(
  659. etype=EventTypes.GuestAccess, content={"guest_access": "can_join"}
  660. )
  661. for (etype, state_key), content in initial_state.items():
  662. yield send(etype=etype, state_key=state_key, content=content)
  663. @defer.inlineCallbacks
  664. def _generate_room_id(self, creator_id, is_public):
  665. # autogen room IDs and try to create it. We may clash, so just
  666. # try a few times till one goes through, giving up eventually.
  667. attempts = 0
  668. while attempts < 5:
  669. try:
  670. random_string = stringutils.random_string(18)
  671. gen_room_id = RoomID(random_string, self.hs.hostname).to_string()
  672. if isinstance(gen_room_id, bytes):
  673. gen_room_id = gen_room_id.decode("utf-8")
  674. yield self.store.store_room(
  675. room_id=gen_room_id,
  676. room_creator_user_id=creator_id,
  677. is_public=is_public,
  678. )
  679. return gen_room_id
  680. except StoreError:
  681. attempts += 1
  682. raise StoreError(500, "Couldn't generate a room ID.")
  683. class RoomContextHandler(object):
  684. def __init__(self, hs):
  685. self.hs = hs
  686. self.store = hs.get_datastore()
  687. @defer.inlineCallbacks
  688. def get_event_context(self, user, room_id, event_id, limit, event_filter):
  689. """Retrieves events, pagination tokens and state around a given event
  690. in a room.
  691. Args:
  692. user (UserID)
  693. room_id (str)
  694. event_id (str)
  695. limit (int): The maximum number of events to return in total
  696. (excluding state).
  697. event_filter (Filter|None): the filter to apply to the events returned
  698. (excluding the target event_id)
  699. Returns:
  700. dict, or None if the event isn't found
  701. """
  702. before_limit = math.floor(limit / 2.0)
  703. after_limit = limit - before_limit
  704. users = yield self.store.get_users_in_room(room_id)
  705. is_peeking = user.to_string() not in users
  706. def filter_evts(events):
  707. return filter_events_for_client(
  708. self.store, user.to_string(), events, is_peeking=is_peeking
  709. )
  710. event = yield self.store.get_event(
  711. event_id, get_prev_content=True, allow_none=True
  712. )
  713. if not event:
  714. return None
  715. return
  716. filtered = yield (filter_evts([event]))
  717. if not filtered:
  718. raise AuthError(403, "You don't have permission to access that event.")
  719. results = yield self.store.get_events_around(
  720. room_id, event_id, before_limit, after_limit, event_filter
  721. )
  722. results["events_before"] = yield filter_evts(results["events_before"])
  723. results["events_after"] = yield filter_evts(results["events_after"])
  724. results["event"] = event
  725. if results["events_after"]:
  726. last_event_id = results["events_after"][-1].event_id
  727. else:
  728. last_event_id = event_id
  729. if event_filter and event_filter.lazy_load_members():
  730. state_filter = StateFilter.from_lazy_load_member_list(
  731. ev.sender
  732. for ev in itertools.chain(
  733. results["events_before"],
  734. (results["event"],),
  735. results["events_after"],
  736. )
  737. )
  738. else:
  739. state_filter = StateFilter.all()
  740. # XXX: why do we return the state as of the last event rather than the
  741. # first? Shouldn't we be consistent with /sync?
  742. # https://github.com/matrix-org/matrix-doc/issues/687
  743. state = yield self.store.get_state_for_events(
  744. [last_event_id], state_filter=state_filter
  745. )
  746. results["state"] = list(state[last_event_id].values())
  747. # We use a dummy token here as we only care about the room portion of
  748. # the token, which we replace.
  749. token = StreamToken.START
  750. results["start"] = token.copy_and_replace(
  751. "room_key", results["start"]
  752. ).to_string()
  753. results["end"] = token.copy_and_replace("room_key", results["end"]).to_string()
  754. return results
  755. class RoomEventSource(object):
  756. def __init__(self, hs):
  757. self.store = hs.get_datastore()
  758. @defer.inlineCallbacks
  759. def get_new_events(
  760. self, user, from_key, limit, room_ids, is_guest, explicit_room_id=None
  761. ):
  762. # We just ignore the key for now.
  763. to_key = yield self.get_current_key()
  764. from_token = RoomStreamToken.parse(from_key)
  765. if from_token.topological:
  766. logger.warn("Stream has topological part!!!! %r", from_key)
  767. from_key = "s%s" % (from_token.stream,)
  768. app_service = self.store.get_app_service_by_user_id(user.to_string())
  769. if app_service:
  770. # We no longer support AS users using /sync directly.
  771. # See https://github.com/matrix-org/matrix-doc/issues/1144
  772. raise NotImplementedError()
  773. else:
  774. room_events = yield self.store.get_membership_changes_for_user(
  775. user.to_string(), from_key, to_key
  776. )
  777. room_to_events = yield self.store.get_room_events_stream_for_rooms(
  778. room_ids=room_ids,
  779. from_key=from_key,
  780. to_key=to_key,
  781. limit=limit or 10,
  782. order="ASC",
  783. )
  784. events = list(room_events)
  785. events.extend(e for evs, _ in room_to_events.values() for e in evs)
  786. events.sort(key=lambda e: e.internal_metadata.order)
  787. if limit:
  788. events[:] = events[:limit]
  789. if events:
  790. end_key = events[-1].internal_metadata.after
  791. else:
  792. end_key = to_key
  793. return (events, end_key)
  794. def get_current_key(self):
  795. return self.store.get_room_events_max_id()
  796. def get_current_key_for_room(self, room_id):
  797. return self.store.get_room_events_max_id(room_id)
  798. @defer.inlineCallbacks
  799. def get_pagination_rows(self, user, config, key):
  800. events, next_key = yield self.store.paginate_room_events(
  801. room_id=key,
  802. from_key=config.from_key,
  803. to_key=config.to_key,
  804. direction=config.direction,
  805. limit=config.limit,
  806. )
  807. return (events, next_key)