Skip to content
Snippets Groups Projects
membership.rs 52.9 KiB
Newer Older
use ruma::{
    api::{
        client::{
            error::ErrorKind,
Jonathan de Jong's avatar
Jonathan de Jong committed
            membership::{
                ban_user, forget_room, get_member_events, invite_user, join_room_by_id,
                join_room_by_id_or_alias, joined_members, joined_rooms, kick_user, leave_room,
Jonas Platte's avatar
Jonas Platte committed
                unban_user, ThirdPartySigned,
        federation::{self, membership::create_invite},
    },
Timo Kösters's avatar
Timo Kösters committed
    canonical_json::to_canonical_value,
            join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
            member::{MembershipState, RoomMemberEventContent},
            power_levels::RoomPowerLevelsEventContent,
Kévin Commaille's avatar
Kévin Commaille committed
        StateEventType, TimelineEventType,
Timo Kösters's avatar
Timo Kösters committed
    serde::Base64,
    state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
    OwnedServerName, OwnedUserId, RoomId, RoomVersionId, UserId,
Jonas Platte's avatar
Jonas Platte committed
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
    collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
    sync::{Arc, RwLock},
use tracing::{debug, error, info, warn};
Timo Kösters's avatar
Timo Kösters committed
use crate::{
    service::pdu::{gen_event_id_canonical_json, PduBuilder},
    services, utils, Error, PduEvent, Result, Ruma,
};

use super::get_alias_helper;

/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
///
/// Tries to join the sender user into a room.
///
/// - If the server knowns about this room: creates the join event and does auth rules locally
/// - If the server does not know about the room: asks other servers over federation
pub async fn join_room_by_id_route(
Jonas Platte's avatar
Jonas Platte committed
    body: Ruma<join_room_by_id::v3::Request>,
Jonathan de Jong's avatar
Jonathan de Jong committed
) -> Result<join_room_by_id::v3::Response> {
    let sender_user = body.sender_user.as_ref().expect("user is authenticated");

    let mut servers = Vec::new(); // There is no body.server_name for /roomId/join
    servers.extend(
Timo Kösters's avatar
Timo Kösters committed
        services()
            .rooms
            .state_cache
            .invite_state(sender_user, &body.room_id)?
            .unwrap_or_default()
            .iter()
            .filter_map(|event| serde_json::from_str(event.json().get()).ok())
            .filter_map(|event: serde_json::Value| event.get("sender").cloned())
            .filter_map(|sender| sender.as_str().map(|s| s.to_owned()))
            .filter_map(|sender| UserId::parse(sender).ok())
            .map(|user| user.server_name().to_owned()),
    );

    servers.push(body.room_id.server_name().to_owned());
Nyaaori's avatar
Nyaaori committed
    join_room_by_id_helper(
Jonas Platte's avatar
Jonas Platte committed
        body.sender_user.as_deref(),
        body.third_party_signed.as_ref(),
    )
Nyaaori's avatar
Nyaaori committed
    .await
/// # `POST /_matrix/client/r0/join/{roomIdOrAlias}`
///
/// Tries to join the sender user into a room.
///
/// - If the server knowns about this room: creates the join event and does auth rules locally
/// - If the server does not know about the room: asks other servers over federation
pub async fn join_room_by_id_or_alias_route(
Jonas Platte's avatar
Jonas Platte committed
    body: Ruma<join_room_by_id_or_alias::v3::Request>,
Jonathan de Jong's avatar
Jonathan de Jong committed
) -> Result<join_room_by_id_or_alias::v3::Response> {
    let sender_user = body.sender_user.as_deref().expect("user is authenticated");
    let body = body.body;
Timo Kösters's avatar
Timo Kösters committed
    let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
            let mut servers = body.server_name.clone();
            servers.extend(
Timo Kösters's avatar
Timo Kösters committed
                services()
                    .rooms
                    .state_cache
                    .invite_state(sender_user, &room_id)?
                    .unwrap_or_default()
                    .iter()
                    .filter_map(|event| serde_json::from_str(event.json().get()).ok())
                    .filter_map(|event: serde_json::Value| event.get("sender").cloned())
                    .filter_map(|sender| sender.as_str().map(|s| s.to_owned()))
                    .filter_map(|sender| UserId::parse(sender).ok())
                    .map(|user| user.server_name().to_owned()),
            );
            servers.push(room_id.server_name().to_owned());
        Err(room_alias) => {
Jonas Platte's avatar
Jonas Platte committed
            let response = get_alias_helper(room_alias).await?;
            (response.servers.into_iter().collect(), response.room_id)
    let join_room_response = join_room_by_id_helper(
        Some(sender_user),
        &servers,
        body.third_party_signed.as_ref(),
    )
    .await?;

Jonathan de Jong's avatar
Jonathan de Jong committed
    Ok(join_room_by_id_or_alias::v3::Response {
        room_id: join_room_response.room_id,
    })
/// # `POST /_matrix/client/r0/rooms/{roomId}/leave`
///
/// Tries to leave the sender user from a room.
///
/// - This should always work if the user is currently joined.
pub async fn leave_room_route(
Jonas Platte's avatar
Jonas Platte committed
    body: Ruma<leave_room::v3::Request>,
Jonathan de Jong's avatar
Jonathan de Jong committed
) -> Result<leave_room::v3::Response> {
    let sender_user = body.sender_user.as_ref().expect("user is authenticated");
    leave_room(sender_user, &body.room_id, body.reason.clone()).await?;
Jonathan de Jong's avatar
Jonathan de Jong committed
    Ok(leave_room::v3::Response::new())
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
///
/// Tries to send an invite event into the room.
pub async fn invite_user_route(
Jonas Platte's avatar
Jonas Platte committed
    body: Ruma<invite_user::v3::Request>,
Jonathan de Jong's avatar
Jonathan de Jong committed
) -> Result<invite_user::v3::Response> {
    let sender_user = body.sender_user.as_ref().expect("user is authenticated");
Jonas Platte's avatar
Jonas Platte committed
    if let invite_user::v3::InvitationRecipient::UserId { user_id } = &body.recipient {
        invite_helper(
            sender_user,
            user_id,
            &body.room_id,
            body.reason.clone(),
            false,
        )
        .await?;
Jonathan de Jong's avatar
Jonathan de Jong committed
        Ok(invite_user::v3::Response {})
    } else {
        Err(Error::BadRequest(ErrorKind::NotFound, "User not found."))
    }
}

/// # `POST /_matrix/client/r0/rooms/{roomId}/kick`
///
/// Tries to send a kick event into the room.
pub async fn kick_user_route(
Jonas Platte's avatar
Jonas Platte committed
    body: Ruma<kick_user::v3::Request>,
Jonathan de Jong's avatar
Jonathan de Jong committed
) -> Result<kick_user::v3::Response> {
    let sender_user = body.sender_user.as_ref().expect("user is authenticated");
Jonas Platte's avatar
Jonas Platte committed
    let mut event: RoomMemberEventContent = serde_json::from_str(
Timo Kösters's avatar
Timo Kösters committed
        services()
            .rooms
            .state_accessor
            .room_state_get(
                &body.room_id,
Timo Kösters's avatar
Timo Kösters committed
                &StateEventType::RoomMember,
Nyaaori's avatar
Nyaaori committed
                body.user_id.as_ref(),
            )?
            .ok_or(Error::BadRequest(
                ErrorKind::BadState,
                "Cannot kick member that's not in the room.",
            ))?
Jonas Platte's avatar
Jonas Platte committed
            .get(),
    )
    .map_err(|_| Error::bad_database("Invalid member event in database."))?;

Jonas Platte's avatar
Jonas Platte committed
    event.membership = MembershipState::Leave;
    event.reason = body.reason.clone();
Timo Kösters's avatar
Timo Kösters committed
    let mutex_state = Arc::clone(
Loading
Loading full blame...