Skip to content
Snippets Groups Projects
alias.rs 6.08 KiB
Newer Older
  • Learn to ignore specific revisions
  • Timo Kösters's avatar
    Timo Kösters committed
    use crate::{services, Error, Result, Ruma};
    
    use rand::seq::SliceRandom;
    
    use regex::Regex;
    
    use ruma::{
        api::{
    
            appservice,
    
    Jonathan de Jong's avatar
    Jonathan de Jong committed
                alias::{create_alias, delete_alias, get_alias},
    
                error::ErrorKind,
            },
            federation,
    
    Timo's avatar
    Timo committed
        },
    
    Timo's avatar
    Timo committed
    };
    
    /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}`
    ///
    /// Creates a new room alias on this server.
    
    pub async fn create_alias_route(
    
    Jonas Platte's avatar
    Jonas Platte committed
        body: Ruma<create_alias::v3::Request>,
    
    Jonathan de Jong's avatar
    Jonathan de Jong committed
    ) -> Result<create_alias::v3::Response> {
    
        if body.room_alias.server_name() != services().globals.server_name() {
    
            return Err(Error::BadRequest(
                ErrorKind::InvalidParam,
                "Alias is from another server.",
            ));
        }
    
    
    Timo Kösters's avatar
    Timo Kösters committed
        if services()
            .rooms
            .alias
            .resolve_local_alias(&body.room_alias)?
            .is_some()
        {
    
            return Err(Error::Conflict("Alias already exists."));
        }
    
    
    Timo Kösters's avatar
    Timo Kösters committed
        services()
            .rooms
            .alias
    
            .set_alias(&body.room_alias, &body.room_id)?;
    
    Jonathan de Jong's avatar
    Jonathan de Jong committed
        Ok(create_alias::v3::Response::new())
    
    /// # `DELETE /_matrix/client/r0/directory/room/{roomAlias}`
    ///
    /// Deletes a room alias from this server.
    ///
    /// - TODO: additional access control checks
    /// - TODO: Update canonical alias event
    
    pub async fn delete_alias_route(
    
    Jonas Platte's avatar
    Jonas Platte committed
        body: Ruma<delete_alias::v3::Request>,
    
    Jonathan de Jong's avatar
    Jonathan de Jong committed
    ) -> Result<delete_alias::v3::Response> {
    
        if body.room_alias.server_name() != services().globals.server_name() {
    
            return Err(Error::BadRequest(
                ErrorKind::InvalidParam,
                "Alias is from another server.",
            ));
        }
    
    
        services().rooms.alias.remove_alias(&body.room_alias)?;
    
        // TODO: update alt_aliases?
    
    
    Jonathan de Jong's avatar
    Jonathan de Jong committed
        Ok(delete_alias::v3::Response::new())
    
    /// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
    
    ///
    /// Resolve an alias locally or over federation.
    
    pub async fn get_alias_route(
    
    Jonas Platte's avatar
    Jonas Platte committed
        body: Ruma<get_alias::v3::Request>,
    
    Jonathan de Jong's avatar
    Jonathan de Jong committed
    ) -> Result<get_alias::v3::Response> {
    
    Jonas Platte's avatar
    Jonas Platte committed
        get_alias_helper(body.body.room_alias).await
    
    Jonas Platte's avatar
    Jonas Platte committed
    pub(crate) async fn get_alias_helper(
        room_alias: OwnedRoomAliasId,
    ) -> Result<get_alias::v3::Response> {
    
        if room_alias.server_name() != services().globals.server_name() {
            let response = services()
    
                .sending
                .send_federation_request(
    
                    room_alias.server_name(),
    
    Jonas Platte's avatar
    Jonas Platte committed
                    federation::query::get_room_information::v1::Request {
                        room_alias: room_alias.to_owned(),
                    },
    
                )
                .await?;
    
            let mut servers = response.servers;
    
    
            // find active servers in room state cache to suggest
            for extra_servers in services()
                .rooms
                .state_cache
                .room_servers(&room_id)
                .filter_map(|r| r.ok())
            {
                servers.push(extra_servers);
            }
    
            // shuffle list of servers randomly
    
            servers.shuffle(&mut rand::thread_rng());
    
    
            // insert our server as the very first choice if in list
            if let Some(server_index) = servers
                .clone()
                .into_iter()
                .position(|server| server == services().globals.server_name())
            {
                servers.remove(server_index);
                servers.insert(0, services().globals.server_name().to_owned());
            }
    
            return Ok(get_alias::v3::Response::new(room_id, servers));
    
        let mut room_id = None;
    
    Jonas Platte's avatar
    Jonas Platte committed
        match services().rooms.alias.resolve_local_alias(&room_alias)? {
    
            Some(r) => room_id = Some(r),
            None => {
    
                for (_id, registration) in services().appservice.all()? {
    
                    let aliases = registration
                        .get("namespaces")
                        .and_then(|ns| ns.get("aliases"))
    
                        .and_then(|aliases| aliases.as_sequence())
                        .map_or_else(Vec::new, |aliases| {
                            aliases
                                .iter()
    
                                .filter_map(|aliases| Regex::new(aliases.get("regex")?.as_str()?).ok())
    
                                .collect::<Vec<_>>()
                        });
    
                    if aliases
                        .iter()
                        .any(|aliases| aliases.is_match(room_alias.as_str()))
    
                        && services()
    
                            .sending
                            .send_appservice_request(
                                registration,
    
    Jonas Platte's avatar
    Jonas Platte committed
                                appservice::query::query_room_alias::v1::Request {
                                    room_alias: room_alias.clone(),
                                },
    
    Timo Kösters's avatar
    Timo Kösters committed
                        room_id = Some(
                            services()
                                .rooms
                                .alias
    
    Jonas Platte's avatar
    Jonas Platte committed
                                .resolve_local_alias(&room_alias)?
    
    Timo Kösters's avatar
    Timo Kösters committed
                                .ok_or_else(|| {
                                    Error::bad_config("Appservice lied to us. Room does not exist.")
                                })?,
                        );
    
                        break;
                    }
                }
            }
        };
    
        let room_id = match room_id {
            Some(room_id) => room_id,
            None => {
                return Err(Error::BadRequest(
                    ErrorKind::NotFound,
                    "Room with alias not found.",
                ))
            }
        };
    
        let mut servers: Vec<OwnedServerName> = Vec::new();
    
        // find active servers in room state cache to suggest
        for extra_servers in services()
            .rooms
            .state_cache
            .room_servers(&room_id)
            .filter_map(|r| r.ok())
        {
            servers.push(extra_servers);
        }
    
        // shuffle list of servers randomly
        servers.shuffle(&mut rand::thread_rng());
    
        // insert our server as the very first choice if in list
        if let Some(server_index) = servers
            .clone()
            .into_iter()
            .position(|server| server == services().globals.server_name())
        {
            servers.remove(server_index);
            servers.insert(0, services().globals.server_name().to_owned());
        }
    
        Ok(get_alias::v3::Response::new(room_id, servers))