def _delete_room_alias_txn( self, txn: LoggingTransaction, room_alias: RoomAlias ) -> Optional[str]: txn.execute( "SELECT room_id FROM room_aliases WHERE room_alias = ?", (room_alias.to_string(),), ) res = txn.fetchone() if res: room_id = res[0] else: return None txn.execute( "DELETE FROM room_aliases WHERE room_alias = ?", (room_alias.to_string(),) ) txn.execute( "DELETE FROM room_alias_servers WHERE room_alias = ?", (room_alias.to_string(),), ) self._invalidate_cache_and_stream(txn, self.get_aliases_for_room, (room_id,)) return room_id
async def get_association_from_room_alias( self, room_alias: RoomAlias) -> Optional[RoomAliasMapping]: """Gets the room_id and server list for a given room_alias Args: room_alias: The alias to translate to an ID. Returns: The room alias mapping or None if no association can be found. """ room_id = await self.db_pool.simple_select_one_onecol( "room_aliases", {"room_alias": room_alias.to_string()}, "room_id", allow_none=True, desc="get_association_from_room_alias", ) if not room_id: return None servers = await self.db_pool.simple_select_onecol( "room_alias_servers", {"room_alias": room_alias.to_string()}, "server", desc="get_association_from_room_alias", ) if not servers: return None return RoomAliasMapping(room_id, room_alias.to_string(), servers)
async def get_association(self, room_alias: RoomAlias) -> JsonDict: room_id = None if self.hs.is_mine(room_alias): result: Optional[ RoomAliasMapping] = await self.get_association_from_room_alias( room_alias) if result: room_id = result.room_id servers = result.servers else: try: fed_result: Optional[ JsonDict] = await self.federation.make_query( destination=room_alias.domain, query_type="directory", args={"room_alias": room_alias.to_string()}, retry_on_dns_fail=False, ignore_backoff=True, ) except RequestSendFailed: raise SynapseError(502, "Failed to fetch alias") except CodeMessageException as e: logging.warning("Error retrieving alias") if e.code == 404: fed_result = None else: raise SynapseError(502, "Failed to fetch alias") if fed_result and "room_id" in fed_result and "servers" in fed_result: room_id = fed_result["room_id"] servers = fed_result["servers"] if not room_id: raise SynapseError( 404, "Room alias %s not found" % (room_alias.to_string(), ), Codes.NOT_FOUND, ) users = await self.store.get_users_in_room(room_id) extra_servers = {get_domain_from_id(u) for u in users} servers = set(extra_servers) | set(servers) # If this server is in the list of servers, return it first. if self.server_name in servers: servers = [self.server_name ] + [s for s in servers if s != self.server_name] else: servers = list(servers) return {"room_id": room_id, "servers": servers}
async def query_room_alias_exists( self, room_alias: RoomAlias ) -> Optional[RoomAliasMapping]: """Check if an application service knows this room alias exists. Args: room_alias: The room alias to query. Returns: namedtuple: with keys "room_id" and "servers" or None if no association can be found. """ room_alias_str = room_alias.to_string() services = self.store.get_app_services() alias_query_services = [ s for s in services if (s.is_interested_in_alias(room_alias_str)) ] for alias_service in alias_query_services: is_known_alias = await self.appservice_api.query_alias( alias_service, room_alias_str ) if is_known_alias: # the alias exists now so don't query more ASes. return await self.store.get_association_from_room_alias(room_alias) return None
async def _user_can_delete_alias(self, alias: RoomAlias, user_id: str): """Determine whether a user can delete an alias. One of the following must be true: 1. The user created the alias. 2. The user is a server administrator. 3. The user has a power-level sufficient to send a canonical alias event for the current room. """ creator = await self.store.get_room_alias_creator(alias.to_string()) if creator is not None and creator == user_id: return True # Resolve the alias to the corresponding room. room_mapping = await self.get_association(alias) room_id = room_mapping["room_id"] if not room_id: return False res = await self.auth.check_can_change_room_list( room_id, UserID.from_string(user_id) ) return res
async def delete_appservice_association(self, service: ApplicationService, room_alias: RoomAlias) -> None: if not service.is_interested_in_alias(room_alias.to_string()): raise SynapseError( 400, "This application service has not reserved this kind of alias", errcode=Codes.EXCLUSIVE, ) await self._delete_association(room_alias)
def can_modify_alias(self, alias: RoomAlias, user_id: Optional[str] = None) -> bool: # Any application service "interested" in an alias they are regexing on # can modify the alias. # Users can only modify the alias if ALL the interested services have # non-exclusive locks on the alias (or there are no interested services) services = self.store.get_app_services() interested_services = [ s for s in services if s.is_interested_in_alias(alias.to_string()) ] for service in interested_services: if user_id == service.sender: # this user IS the app service so they can do whatever they like return True elif service.is_exclusive_alias(alias.to_string()): # another service has an exclusive lock on this alias. return False # either no interested services, or no service with an exclusive lock return True
async def _update_canonical_alias(self, requester: Requester, user_id: str, room_id: str, room_alias: RoomAlias) -> None: """ Send an updated canonical alias event if the removed alias was set as the canonical alias or listed in the alt_aliases field. Raises: ShadowBanError if the requester has been shadow-banned. """ alias_event = await self.state.get_current_state( room_id, EventTypes.CanonicalAlias, "") # There is no canonical alias, nothing to do. if not alias_event: return # Obtain a mutable version of the event content. content = dict(alias_event.content) send_update = False # Remove the alias property if it matches the removed alias. alias_str = room_alias.to_string() if alias_event.content.get("alias", "") == alias_str: send_update = True content.pop("alias", "") # Filter the alt_aliases property for the removed alias. Note that the # value is not modified if alt_aliases is of an unexpected form. alt_aliases = content.get("alt_aliases") if isinstance(alt_aliases, (list, tuple)) and alias_str in alt_aliases: send_update = True alt_aliases = [ alias for alias in alt_aliases if alias != alias_str ] if alt_aliases: content["alt_aliases"] = alt_aliases else: del content["alt_aliases"] if send_update: await self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.CanonicalAlias, "state_key": "", "room_id": room_id, "sender": user_id, "content": content, }, ratelimit=False, )
async def create_room_alias_association( self, room_alias: RoomAlias, room_id: str, servers: Iterable[str], creator: Optional[str] = None, ) -> None: """Creates an association between a room alias and room_id/servers Args: room_alias: The alias to create. room_id: The target of the alias. servers: A list of servers through which it may be possible to join the room creator: Optional user_id of creator. """ def alias_txn(txn: LoggingTransaction) -> None: self.db_pool.simple_insert_txn( txn, "room_aliases", { "room_alias": room_alias.to_string(), "room_id": room_id, "creator": creator, }, ) self.db_pool.simple_insert_many_txn( txn, table="room_alias_servers", keys=("room_alias", "server"), values=[(room_alias.to_string(), server) for server in servers], ) self._invalidate_cache_and_stream( txn, self.get_aliases_for_room, (room_id,) ) try: await self.db_pool.runInteraction( "create_room_alias_association", alias_txn ) except self.database_engine.module.IntegrityError: raise SynapseError( 409, "Room alias %s already exists" % room_alias.to_string() )
def create_room(self, requester, config, ratelimit=True, creator_join_profile=None): """ Creates a new room. Args: requester (synapse.types.Requester): The user who requested the room creation. config (dict) : A dict of configuration options. ratelimit (bool): set to False to disable the rate limiter creator_join_profile (dict|None): Set to override the displayname and avatar for the creating user in this room. If unset, displayname and avatar will be derived from the user's profile. If set, should contain the values to go in the body of the 'join' event (typically `avatar_url` and/or `displayname`. Returns: Deferred[dict]: a dict containing the keys `room_id` and, if an alias was requested, `room_alias`. Raises: SynapseError if the room ID couldn't be stored, or something went horribly wrong. ResourceLimitError if server is blocked to some resource being exceeded """ user_id = requester.user.to_string() yield self.auth.check_auth_blocking(user_id) if (self._server_notices_mxid is not None and requester.user.to_string() == self._server_notices_mxid): # allow the server notices mxid to create rooms is_requester_admin = True else: is_requester_admin = yield self.auth.is_server_admin( requester.user) # Check whether the third party rules allows/changes the room create # request. yield self.third_party_event_rules.on_create_room( requester, config, is_requester_admin=is_requester_admin) if not is_requester_admin and not self.spam_checker.user_may_create_room( user_id): raise SynapseError(403, "You are not permitted to create rooms") if ratelimit: yield self.ratelimit(requester) room_version = config.get("room_version", self.config.default_room_version.identifier) if not isinstance(room_version, string_types): raise SynapseError(400, "room_version must be a string", Codes.BAD_JSON) if room_version not in KNOWN_ROOM_VERSIONS: raise SynapseError( 400, "Your homeserver does not support this room version", Codes.UNSUPPORTED_ROOM_VERSION, ) if "room_alias_name" in config: for wchar in string.whitespace: if wchar in config["room_alias_name"]: raise SynapseError(400, "Invalid characters in room alias") room_alias = RoomAlias(config["room_alias_name"], self.hs.hostname) mapping = yield self.store.get_association_from_room_alias( room_alias) if mapping: raise SynapseError(400, "Room alias already taken", Codes.ROOM_IN_USE) else: room_alias = None invite_list = config.get("invite", []) for i in invite_list: try: uid = UserID.from_string(i) parse_and_validate_server_name(uid.domain) except Exception: raise SynapseError(400, "Invalid user_id: %s" % (i, )) yield self.event_creation_handler.assert_accepted_privacy_policy( requester) power_level_content_override = config.get( "power_level_content_override") if (power_level_content_override and "users" in power_level_content_override and user_id not in power_level_content_override["users"]): raise SynapseError( 400, "Not a valid power_level_content_override: 'users' did not contain %s" % (user_id, ), ) invite_3pid_list = config.get("invite_3pid", []) visibility = config.get("visibility", None) is_public = visibility == "public" room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public) directory_handler = self.hs.get_handlers().directory_handler if room_alias: yield directory_handler.create_association( requester=requester, room_id=room_id, room_alias=room_alias, servers=[self.hs.hostname], send_event=False, check_membership=False, ) preset_config = config.get( "preset", RoomCreationPreset.PRIVATE_CHAT if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT, ) raw_initial_state = config.get("initial_state", []) initial_state = OrderedDict() for val in raw_initial_state: initial_state[(val["type"], val.get("state_key", ""))] = val["content"] creation_content = config.get("creation_content", {}) # override any attempt to set room versions via the creation_content creation_content["room_version"] = room_version yield self._send_events_for_new_room( requester, room_id, preset_config=preset_config, invite_list=invite_list, initial_state=initial_state, creation_content=creation_content, room_alias=room_alias, power_level_content_override=power_level_content_override, creator_join_profile=creator_join_profile, ) if "name" in config: name = config["name"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Name, "room_id": room_id, "sender": user_id, "state_key": "", "content": { "name": name }, }, ratelimit=False, ) if "topic" in config: topic = config["topic"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Topic, "room_id": room_id, "sender": user_id, "state_key": "", "content": { "topic": topic }, }, ratelimit=False, ) for invitee in invite_list: content = {} is_direct = config.get("is_direct", None) if is_direct: content["is_direct"] = is_direct yield self.room_member_handler.update_membership( requester, UserID.from_string(invitee), room_id, "invite", ratelimit=False, content=content, ) for invite_3pid in invite_3pid_list: id_server = invite_3pid["id_server"] id_access_token = invite_3pid.get("id_access_token") # optional address = invite_3pid["address"] medium = invite_3pid["medium"] yield self.hs.get_room_member_handler().do_3pid_invite( room_id, requester.user, medium, address, id_server, requester, txn_id=None, id_access_token=id_access_token, ) result = {"room_id": room_id} if room_alias: result["room_alias"] = room_alias.to_string() yield directory_handler.send_room_alias_update_event( requester, room_id) return result
def test_build(self): room = RoomAlias("channel", "my.domain") self.assertEquals(room.to_string(), "#channel:my.domain")
async def create_association( self, requester: Requester, room_alias: RoomAlias, room_id: str, servers: Optional[List[str]] = None, check_membership: bool = True, ) -> None: """Attempt to create a new alias Args: requester room_alias room_id servers: Iterable of servers that others servers should try and join via check_membership: Whether to check if the user is in the room before the alias can be set (if the server's config requires it). """ user_id = requester.user.to_string() if len(room_alias.to_string()) > MAX_ALIAS_LENGTH: raise SynapseError( 400, "Can't create aliases longer than %s characters" % MAX_ALIAS_LENGTH, Codes.INVALID_PARAM, ) service = requester.app_service if service: if not service.is_interested_in_alias(room_alias.to_string()): raise SynapseError( 400, "This application service has not reserved this kind of alias.", errcode=Codes.EXCLUSIVE, ) else: # Server admins are not subject to the same constraints as normal # users when creating an alias (e.g. being in the room). is_admin = await self.auth.is_server_admin(requester.user) if (self.require_membership and check_membership) and not is_admin: rooms_for_user = await self.store.get_rooms_for_user(user_id) if room_id not in rooms_for_user: raise AuthError( 403, "You must be in the room to create an alias for it" ) if not self.spam_checker.user_may_create_room_alias(user_id, room_alias): raise AuthError(403, "This user is not permitted to create this alias") if not self.config.is_alias_creation_allowed( user_id, room_id, room_alias.to_string() ): # Lets just return a generic message, as there may be all sorts of # reasons why we said no. TODO: Allow configurable error messages # per alias creation rule? raise SynapseError(403, "Not allowed to create alias") can_create = self.can_modify_alias(room_alias, user_id=user_id) if not can_create: raise AuthError( 400, "This alias is reserved by an application service.", errcode=Codes.EXCLUSIVE, ) await self._create_association(room_alias, room_id, servers, creator=user_id)
def create_room(self, requester, config, ratelimit=True): """ Creates a new room. Args: requester (Requester): The user who requested the room creation. config (dict) : A dict of configuration options. Returns: The new room ID. Raises: SynapseError if the room ID couldn't be stored, or something went horribly wrong. """ user_id = requester.user.to_string() if not self.spam_checker.user_may_create_room(user_id): raise SynapseError(403, "You are not permitted to create rooms") if ratelimit: yield self.ratelimit(requester) if "room_alias_name" in config: for wchar in string.whitespace: if wchar in config["room_alias_name"]: raise SynapseError(400, "Invalid characters in room alias") room_alias = RoomAlias( config["room_alias_name"], self.hs.hostname, ) mapping = yield self.store.get_association_from_room_alias( room_alias) if mapping: raise SynapseError(400, "Room alias already taken") else: room_alias = None invite_list = config.get("invite", []) for i in invite_list: try: UserID.from_string(i) except Exception: raise SynapseError(400, "Invalid user_id: %s" % (i, )) invite_3pid_list = config.get("invite_3pid", []) visibility = config.get("visibility", None) is_public = visibility == "public" # autogen room IDs and try to create it. We may clash, so just # try a few times till one goes through, giving up eventually. attempts = 0 room_id = None while attempts < 5: try: random_string = stringutils.random_string(18) gen_room_id = RoomID( random_string, self.hs.hostname, ) yield self.store.store_room(room_id=gen_room_id.to_string(), room_creator_user_id=user_id, is_public=is_public) room_id = gen_room_id.to_string() break except StoreError: attempts += 1 if not room_id: raise StoreError(500, "Couldn't generate a room ID.") if room_alias: directory_handler = self.hs.get_handlers().directory_handler yield directory_handler.create_association( user_id=user_id, room_id=room_id, room_alias=room_alias, servers=[self.hs.hostname], ) preset_config = config.get( "preset", RoomCreationPreset.PRIVATE_CHAT if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT) raw_initial_state = config.get("initial_state", []) initial_state = OrderedDict() for val in raw_initial_state: initial_state[(val["type"], val.get("state_key", ""))] = val["content"] creation_content = config.get("creation_content", {}) room_member_handler = self.hs.get_room_member_handler() yield self._send_events_for_new_room( requester, room_id, room_member_handler, preset_config=preset_config, invite_list=invite_list, initial_state=initial_state, creation_content=creation_content, room_alias=room_alias, power_level_content_override=config.get( "power_level_content_override", {})) if "name" in config: name = config["name"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Name, "room_id": room_id, "sender": user_id, "state_key": "", "content": { "name": name }, }, ratelimit=False) if "topic" in config: topic = config["topic"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Topic, "room_id": room_id, "sender": user_id, "state_key": "", "content": { "topic": topic }, }, ratelimit=False) for invitee in invite_list: content = {} is_direct = config.get("is_direct", None) if is_direct: content["is_direct"] = is_direct yield room_member_handler.update_membership( requester, UserID.from_string(invitee), room_id, "invite", ratelimit=False, content=content, ) for invite_3pid in invite_3pid_list: id_server = invite_3pid["id_server"] address = invite_3pid["address"] medium = invite_3pid["medium"] yield self.hs.get_room_member_handler().do_3pid_invite( room_id, requester.user, medium, address, id_server, requester, txn_id=None, ) result = {"room_id": room_id} if room_alias: result["room_alias"] = room_alias.to_string() yield directory_handler.send_room_alias_update_event( requester, user_id, room_id) defer.returnValue(result)
def create_room(self, requester, config, ratelimit=True, creator_join_profile=None): """ Creates a new room. Args: requester (synapse.types.Requester): The user who requested the room creation. config (dict) : A dict of configuration options. ratelimit (bool): set to False to disable the rate limiter creator_join_profile (dict|None): Set to override the displayname and avatar for the creating user in this room. If unset, displayname and avatar will be derived from the user's profile. If set, should contain the values to go in the body of the 'join' event (typically `avatar_url` and/or `displayname`. Returns: Deferred[dict]: a dict containing the keys `room_id` and, if an alias was requested, `room_alias`. Raises: SynapseError if the room ID couldn't be stored, or something went horribly wrong. ResourceLimitError if server is blocked to some resource being exceeded """ user_id = requester.user.to_string() self.auth.check_auth_blocking(user_id) if not self.spam_checker.user_may_create_room(user_id): raise SynapseError(403, "You are not permitted to create rooms") if ratelimit: yield self.ratelimit(requester) room_version = config.get("room_version", DEFAULT_ROOM_VERSION) if not isinstance(room_version, string_types): raise SynapseError( 400, "room_version must be a string", Codes.BAD_JSON, ) if room_version not in KNOWN_ROOM_VERSIONS: raise SynapseError( 400, "Your homeserver does not support this room version", Codes.UNSUPPORTED_ROOM_VERSION, ) if "room_alias_name" in config: for wchar in string.whitespace: if wchar in config["room_alias_name"]: raise SynapseError(400, "Invalid characters in room alias") room_alias = RoomAlias( config["room_alias_name"], self.hs.hostname, ) mapping = yield self.store.get_association_from_room_alias( room_alias) if mapping: raise SynapseError(400, "Room alias already taken", Codes.ROOM_IN_USE) else: room_alias = None invite_list = config.get("invite", []) for i in invite_list: try: UserID.from_string(i) except Exception: raise SynapseError(400, "Invalid user_id: %s" % (i, )) yield self.event_creation_handler.assert_accepted_privacy_policy( requester, ) invite_3pid_list = config.get("invite_3pid", []) visibility = config.get("visibility", None) is_public = visibility == "public" # autogen room IDs and try to create it. We may clash, so just # try a few times till one goes through, giving up eventually. attempts = 0 room_id = None while attempts < 5: try: random_string = stringutils.random_string(18) gen_room_id = RoomID( random_string, self.hs.hostname, ) yield self.store.store_room(room_id=gen_room_id.to_string(), room_creator_user_id=user_id, is_public=is_public) room_id = gen_room_id.to_string() break except StoreError: attempts += 1 if not room_id: raise StoreError(500, "Couldn't generate a room ID.") if room_alias: directory_handler = self.hs.get_handlers().directory_handler yield directory_handler.create_association( user_id=user_id, room_id=room_id, room_alias=room_alias, servers=[self.hs.hostname], ) preset_config = config.get( "preset", RoomCreationPreset.PRIVATE_CHAT if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT) raw_initial_state = config.get("initial_state", []) initial_state = OrderedDict() for val in raw_initial_state: initial_state[(val["type"], val.get("state_key", ""))] = val["content"] creation_content = config.get("creation_content", {}) # override any attempt to set room versions via the creation_content creation_content["room_version"] = room_version room_member_handler = self.hs.get_room_member_handler() yield self._send_events_for_new_room( requester, room_id, room_member_handler, preset_config=preset_config, invite_list=invite_list, initial_state=initial_state, creation_content=creation_content, room_alias=room_alias, power_level_content_override=config.get( "power_level_content_override", {}), creator_join_profile=creator_join_profile, ) if "name" in config: name = config["name"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Name, "room_id": room_id, "sender": user_id, "state_key": "", "content": { "name": name }, }, ratelimit=False) if "topic" in config: topic = config["topic"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Topic, "room_id": room_id, "sender": user_id, "state_key": "", "content": { "topic": topic }, }, ratelimit=False) for invitee in invite_list: content = {} is_direct = config.get("is_direct", None) if is_direct: content["is_direct"] = is_direct yield room_member_handler.update_membership( requester, UserID.from_string(invitee), room_id, "invite", ratelimit=False, content=content, ) for invite_3pid in invite_3pid_list: id_server = invite_3pid["id_server"] address = invite_3pid["address"] medium = invite_3pid["medium"] yield self.hs.get_room_member_handler().do_3pid_invite( room_id, requester.user, medium, address, id_server, requester, txn_id=None, ) result = {"room_id": room_id} if room_alias: result["room_alias"] = room_alias.to_string() yield directory_handler.send_room_alias_update_event( requester, user_id, room_id) defer.returnValue(result)
def create_room(self, requester, config, ratelimit=True, creator_join_profile=None): """ Creates a new room. Args: requester (synapse.types.Requester): The user who requested the room creation. config (dict) : A dict of configuration options. ratelimit (bool): set to False to disable the rate limiter creator_join_profile (dict|None): Set to override the displayname and avatar for the creating user in this room. If unset, displayname and avatar will be derived from the user's profile. If set, should contain the values to go in the body of the 'join' event (typically `avatar_url` and/or `displayname`. Returns: Deferred[dict]: a dict containing the keys `room_id` and, if an alias was requested, `room_alias`. Raises: SynapseError if the room ID couldn't be stored, or something went horribly wrong. ResourceLimitError if server is blocked to some resource being exceeded """ user_id = requester.user.to_string() yield self.auth.check_auth_blocking(user_id) if not self.spam_checker.user_may_create_room(user_id): raise SynapseError(403, "You are not permitted to create rooms") if ratelimit: yield self.ratelimit(requester) room_version = config.get("room_version", DEFAULT_ROOM_VERSION.identifier) if not isinstance(room_version, string_types): raise SynapseError( 400, "room_version must be a string", Codes.BAD_JSON, ) if room_version not in KNOWN_ROOM_VERSIONS: raise SynapseError( 400, "Your homeserver does not support this room version", Codes.UNSUPPORTED_ROOM_VERSION, ) if "room_alias_name" in config: for wchar in string.whitespace: if wchar in config["room_alias_name"]: raise SynapseError(400, "Invalid characters in room alias") room_alias = RoomAlias( config["room_alias_name"], self.hs.hostname, ) mapping = yield self.store.get_association_from_room_alias( room_alias ) if mapping: raise SynapseError( 400, "Room alias already taken", Codes.ROOM_IN_USE ) else: room_alias = None invite_list = config.get("invite", []) for i in invite_list: try: UserID.from_string(i) except Exception: raise SynapseError(400, "Invalid user_id: %s" % (i,)) yield self.event_creation_handler.assert_accepted_privacy_policy( requester, ) invite_3pid_list = config.get("invite_3pid", []) visibility = config.get("visibility", None) is_public = visibility == "public" room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public) if room_alias: directory_handler = self.hs.get_handlers().directory_handler yield directory_handler.create_association( requester=requester, room_id=room_id, room_alias=room_alias, servers=[self.hs.hostname], send_event=False, check_membership=False, ) preset_config = config.get( "preset", RoomCreationPreset.PRIVATE_CHAT if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT ) raw_initial_state = config.get("initial_state", []) initial_state = OrderedDict() for val in raw_initial_state: initial_state[(val["type"], val.get("state_key", ""))] = val["content"] creation_content = config.get("creation_content", {}) # override any attempt to set room versions via the creation_content creation_content["room_version"] = room_version yield self._send_events_for_new_room( requester, room_id, preset_config=preset_config, invite_list=invite_list, initial_state=initial_state, creation_content=creation_content, room_alias=room_alias, power_level_content_override=config.get("power_level_content_override"), creator_join_profile=creator_join_profile, ) if "name" in config: name = config["name"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Name, "room_id": room_id, "sender": user_id, "state_key": "", "content": {"name": name}, }, ratelimit=False) if "topic" in config: topic = config["topic"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Topic, "room_id": room_id, "sender": user_id, "state_key": "", "content": {"topic": topic}, }, ratelimit=False) for invitee in invite_list: content = {} is_direct = config.get("is_direct", None) if is_direct: content["is_direct"] = is_direct yield self.room_member_handler.update_membership( requester, UserID.from_string(invitee), room_id, "invite", ratelimit=False, content=content, ) for invite_3pid in invite_3pid_list: id_server = invite_3pid["id_server"] address = invite_3pid["address"] medium = invite_3pid["medium"] yield self.hs.get_room_member_handler().do_3pid_invite( room_id, requester.user, medium, address, id_server, requester, txn_id=None, ) result = {"room_id": room_id} if room_alias: result["room_alias"] = room_alias.to_string() yield directory_handler.send_room_alias_update_event( requester, room_id ) defer.returnValue(result)
def create_room(self, requester, config, ratelimit=True): """ Creates a new room. Args: requester (Requester): The user who requested the room creation. config (dict) : A dict of configuration options. Returns: The new room ID. Raises: SynapseError if the room ID couldn't be stored, or something went horribly wrong. """ user_id = requester.user.to_string() if not self.spam_checker.user_may_create_room(user_id): raise SynapseError(403, "You are not permitted to create rooms") if ratelimit: yield self.ratelimit(requester) if "room_alias_name" in config: for wchar in string.whitespace: if wchar in config["room_alias_name"]: raise SynapseError(400, "Invalid characters in room alias") room_alias = RoomAlias( config["room_alias_name"], self.hs.hostname, ) mapping = yield self.store.get_association_from_room_alias( room_alias ) if mapping: raise SynapseError(400, "Room alias already taken") else: room_alias = None invite_list = config.get("invite", []) for i in invite_list: try: UserID.from_string(i) except Exception: raise SynapseError(400, "Invalid user_id: %s" % (i,)) invite_3pid_list = config.get("invite_3pid", []) visibility = config.get("visibility", None) is_public = visibility == "public" # autogen room IDs and try to create it. We may clash, so just # try a few times till one goes through, giving up eventually. attempts = 0 room_id = None while attempts < 5: try: random_string = stringutils.random_string(18) gen_room_id = RoomID( random_string, self.hs.hostname, ) yield self.store.store_room( room_id=gen_room_id.to_string(), room_creator_user_id=user_id, is_public=is_public ) room_id = gen_room_id.to_string() break except StoreError: attempts += 1 if not room_id: raise StoreError(500, "Couldn't generate a room ID.") if room_alias: directory_handler = self.hs.get_handlers().directory_handler yield directory_handler.create_association( user_id=user_id, room_id=room_id, room_alias=room_alias, servers=[self.hs.hostname], ) preset_config = config.get( "preset", RoomCreationPreset.PRIVATE_CHAT if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT ) raw_initial_state = config.get("initial_state", []) initial_state = OrderedDict() for val in raw_initial_state: initial_state[(val["type"], val.get("state_key", ""))] = val["content"] creation_content = config.get("creation_content", {}) room_member_handler = self.hs.get_room_member_handler() yield self._send_events_for_new_room( requester, room_id, room_member_handler, preset_config=preset_config, invite_list=invite_list, initial_state=initial_state, creation_content=creation_content, room_alias=room_alias, power_level_content_override=config.get("power_level_content_override", {}) ) if "name" in config: name = config["name"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Name, "room_id": room_id, "sender": user_id, "state_key": "", "content": {"name": name}, }, ratelimit=False) if "topic" in config: topic = config["topic"] yield self.event_creation_handler.create_and_send_nonmember_event( requester, { "type": EventTypes.Topic, "room_id": room_id, "sender": user_id, "state_key": "", "content": {"topic": topic}, }, ratelimit=False) for invitee in invite_list: content = {} is_direct = config.get("is_direct", None) if is_direct: content["is_direct"] = is_direct yield room_member_handler.update_membership( requester, UserID.from_string(invitee), room_id, "invite", ratelimit=False, content=content, ) for invite_3pid in invite_3pid_list: id_server = invite_3pid["id_server"] address = invite_3pid["address"] medium = invite_3pid["medium"] yield self.hs.get_room_member_handler().do_3pid_invite( room_id, requester.user, medium, address, id_server, requester, txn_id=None, ) result = {"room_id": room_id} if room_alias: result["room_alias"] = room_alias.to_string() yield directory_handler.send_room_alias_update_event( requester, user_id, room_id ) defer.returnValue(result)