use axum::extract::State; use conduit::Err; use ruma::{ api::{client::error::ErrorKind, federation::knock::create_knock_event_template}, events::room::member::{MembershipState, RoomMemberEventContent}, RoomVersionId, }; use serde_json::value::to_raw_value; use tracing::warn; use RoomVersionId::*; use crate::{service::pdu::PduBuilder, Error, Result, Ruma}; /// # `GET /_matrix/federation/v1/make_knock/{roomId}/{userId}` /// /// Creates a knock template. pub(crate) async fn create_knock_event_template_route( State(services): State, body: Ruma, ) -> Result { if !services.rooms.metadata.exists(&body.room_id).await { return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server.")); } if body.user_id.server_name() != body.origin() { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Not allowed to knock on behalf of another server/user", )); } // ACL check origin server services .rooms .event_handler .acl_check(body.origin(), &body.room_id) .await?; if services .globals .config .forbidden_remote_server_names .contains(body.origin()) { warn!( "Server {} for remote user {} tried knocking room ID {} which has a server name that is globally \ forbidden. Rejecting.", body.origin(), &body.user_id, &body.room_id, ); return Err!(Request(Forbidden("Server is banned on this homeserver."))); } if let Some(server) = body.room_id.server_name() { if services .globals .config .forbidden_remote_server_names .contains(&server.to_owned()) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } } let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?; if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6) { return Err(Error::BadRequest( ErrorKind::IncompatibleRoomVersion { room_version: room_version_id, }, "Room version does not support knocking.", )); } if !body.ver.contains(&room_version_id) { return Err(Error::BadRequest( ErrorKind::IncompatibleRoomVersion { room_version: room_version_id, }, "Your homeserver does not support the features required to knock on this room.", )); } let state_lock = services.rooms.state.mutex.lock(&body.room_id).await; let (_pdu, mut pdu_json) = services .rooms .timeline .create_hash_and_sign_event( PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent::new(MembershipState::Knock)), &body.user_id, &body.room_id, &state_lock, ) .await?; drop(state_lock); // room v3 and above removed the "event_id" field from remote PDU format super::maybe_strip_event_id(&mut pdu_json, &room_version_id)?; Ok(create_knock_event_template::v1::Response { room_version: room_version_id, event: to_raw_value(&pdu_json).expect("CanonicalJson can be serialized to JSON"), }) }