Skip to content
Snippets Groups Projects
alias.rs 6.23 KiB
Newer Older
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);
        }

        // 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());
        }

        servers.sort_unstable();
        servers.dedup();

        // shuffle list of servers randomly after sort and dedupe
        servers.shuffle(&mut rand::thread_rng());

        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);
    }

    // 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());
    }

    servers.sort_unstable();
    servers.dedup();

    // shuffle list of servers randomly after sort and dedupe
    servers.shuffle(&mut rand::thread_rng());

    Ok(get_alias::v3::Response::new(room_id, servers))