diff --git a/src/client_server/account.rs b/src/client_server/account.rs index 4b3ad0d4..fb223d57 100644 --- a/src/client_server/account.rs +++ b/src/client_server/account.rs @@ -34,7 +34,7 @@ use ruma::{ EventType, }, identifiers::RoomName, - push, RoomAliasId, RoomId, RoomVersionId, UserId, + push, RoomAliasId, RoomId, UserId, }; use serde_json::value::to_raw_value; use tracing::info; @@ -282,7 +282,7 @@ pub async fn register_route( let mut content = RoomCreateEventContent::new(conduit_user.clone()); content.federate = true; content.predecessor = None; - content.room_version = RoomVersionId::Version6; + content.room_version = db.globals.default_room_version(); // 1. The room create event db.rooms.build_and_append_pdu( diff --git a/src/client_server/capabilities.rs b/src/client_server/capabilities.rs index f86b23b5..36296b57 100644 --- a/src/client_server/capabilities.rs +++ b/src/client_server/capabilities.rs @@ -1,9 +1,6 @@ -use crate::{ConduitResult, Ruma}; -use ruma::{ - api::client::r0::capabilities::{ - get_capabilities, Capabilities, RoomVersionStability, RoomVersionsCapability, - }, - RoomVersionId, +use crate::{database::DatabaseGuard, ConduitResult, Ruma}; +use ruma::api::client::r0::capabilities::{ + get_capabilities, Capabilities, RoomVersionStability, RoomVersionsCapability, }; use std::collections::BTreeMap; @@ -17,17 +14,28 @@ use rocket::get; feature = "conduit_bin", get("/_matrix/client/r0/capabilities", data = "<_body>") )] -#[tracing::instrument(skip(_body))] +#[tracing::instrument(skip(_body, db))] pub async fn get_capabilities_route( _body: Ruma, + db: DatabaseGuard, ) -> ConduitResult { let mut available = BTreeMap::new(); - available.insert(RoomVersionId::Version5, RoomVersionStability::Stable); - available.insert(RoomVersionId::Version6, RoomVersionStability::Stable); + if db.globals.allow_unstable_room_versions() { + for room_version in &db.globals.unstable_room_versions { + available.insert(room_version.clone(), RoomVersionStability::Stable); + } + } else { + for room_version in &db.globals.unstable_room_versions { + available.insert(room_version.clone(), RoomVersionStability::Unstable); + } + } + for room_version in &db.globals.stable_room_versions { + available.insert(room_version.clone(), RoomVersionStability::Stable); + } let mut capabilities = Capabilities::new(); capabilities.room_versions = RoomVersionsCapability { - default: RoomVersionId::Version6, + default: db.globals.default_room_version(), available, }; diff --git a/src/client_server/membership.rs b/src/client_server/membership.rs index ec685ec9..38a7e9f9 100644 --- a/src/client_server/membership.rs +++ b/src/client_server/membership.rs @@ -551,7 +551,7 @@ async fn join_room_by_id_helper( federation::membership::create_join_event_template::v1::Request { room_id, user_id: sender_user, - ver: &[RoomVersionId::Version5, RoomVersionId::Version6], + ver: &db.globals.supported_room_versions(), }, ) .await; @@ -566,12 +566,7 @@ async fn join_room_by_id_helper( let (make_join_response, remote_server) = make_join_response_and_server?; let room_version = match make_join_response.room_version { - Some(room_version) - if room_version == RoomVersionId::Version5 - || room_version == RoomVersionId::Version6 => - { - room_version - } + Some(room_version) if db.rooms.is_supported_version(&db, &room_version) => room_version, _ => return Err(Error::BadServerResponse("Room version is not supported")), }; @@ -890,9 +885,10 @@ pub(crate) async fn invite_helper<'a>( None }; - // If there was no create event yet, assume we are creating a version 6 room right now + // If there was no create event yet, assume we are creating a room with the default + // version right now let room_version_id = create_event_content - .map_or(RoomVersionId::Version6, |create_event| { + .map_or(db.globals.default_room_version(), |create_event| { create_event.room_version }); let room_version = diff --git a/src/client_server/room.rs b/src/client_server/room.rs index 47c7ee6f..a63b3410 100644 --- a/src/client_server/room.rs +++ b/src/client_server/room.rs @@ -23,7 +23,7 @@ use ruma::{ EventType, }, serde::{CanonicalJsonObject, JsonObject}, - RoomAliasId, RoomId, RoomVersionId, + RoomAliasId, RoomId, }; use serde_json::{json, value::to_raw_value}; use std::{ @@ -109,7 +109,7 @@ pub async fn create_room_route( let room_version = match body.room_version.clone() { Some(room_version) => { - if room_version == RoomVersionId::Version5 || room_version == RoomVersionId::Version6 { + if db.rooms.is_supported_version(&db, &room_version) { room_version } else { return Err(Error::BadRequest( @@ -118,7 +118,7 @@ pub async fn create_room_route( )); } } - None => RoomVersionId::Version6, + None => db.globals.default_room_version(), }; let content = match &body.creation_content { @@ -505,10 +505,7 @@ pub async fn upgrade_room_route( ) -> ConduitResult { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if !matches!( - body.new_version, - RoomVersionId::Version5 | RoomVersionId::Version6 - ) { + if !db.rooms.is_supported_version(&db, &body.new_version) { return Err(Error::BadRequest( ErrorKind::UnsupportedRoomVersion, "This server does not support that room version.", diff --git a/src/database.rs b/src/database.rs index 080e24b3..c533a45e 100644 --- a/src/database.rs +++ b/src/database.rs @@ -24,7 +24,7 @@ use rocket::{ request::{FromRequest, Request}, Shutdown, State, }; -use ruma::{DeviceId, EventId, RoomId, ServerName, UserId}; +use ruma::{DeviceId, EventId, RoomId, RoomVersionId, ServerName, UserId}; use serde::{de::IgnoredAny, Deserialize}; use std::{ collections::{BTreeMap, HashMap, HashSet}, @@ -63,8 +63,12 @@ pub struct Config { allow_federation: bool, #[serde(default = "true_fn")] allow_room_creation: bool, + #[serde(default = "true_fn")] + allow_unstable_room_versions: bool, #[serde(default = "false_fn")] pub allow_jaeger: bool, + #[serde(default = "default_room_version")] + default_room_version: RoomVersionId, #[serde(default = "false_fn")] pub tracing_flame: bool, #[serde(default)] @@ -137,6 +141,10 @@ fn default_max_concurrent_requests() -> u16 { 100 } +fn default_room_version() -> RoomVersionId { + RoomVersionId::Version6 +} + fn default_log() -> String { "info,state_res=warn,rocket=off,_=off,sled=off".to_owned() } diff --git a/src/database/globals.rs b/src/database/globals.rs index 05ecb568..c7aa4aa3 100644 --- a/src/database/globals.rs +++ b/src/database/globals.rs @@ -4,7 +4,8 @@ use ruma::{ client::r0::sync::sync_events, federation::discovery::{ServerSigningKeys, VerifyKey}, }, - DeviceId, EventId, MilliSecondsSinceUnixEpoch, RoomId, ServerName, ServerSigningKeyId, UserId, + DeviceId, EventId, MilliSecondsSinceUnixEpoch, RoomId, RoomVersionId, ServerName, + ServerSigningKeyId, UserId, }; use std::{ collections::{BTreeMap, HashMap}, @@ -39,6 +40,8 @@ pub struct Globals { keypair: Arc, dns_resolver: TokioAsyncResolver, jwt_decoding_key: Option>, + pub stable_room_versions: Vec, + pub unstable_room_versions: Vec, pub(super) server_signingkeys: Arc, pub bad_event_ratelimiter: Arc>>, pub bad_signature_ratelimiter: Arc, RateLimitState>>>, @@ -132,6 +135,11 @@ impl Globals { .as_ref() .map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static()); + // Supported and stable room versions + let stable_room_versions = vec![RoomVersionId::Version6]; + // Experimental, partially supported room versions + let unstable_room_versions = vec![RoomVersionId::Version5]; + let s = Self { globals, config, @@ -143,6 +151,8 @@ impl Globals { tls_name_override, server_signingkeys, jwt_decoding_key, + stable_room_versions, + unstable_room_versions, bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())), servername_ratelimiter: Arc::new(RwLock::new(HashMap::new())), @@ -214,6 +224,22 @@ impl Globals { self.config.allow_room_creation } + pub fn allow_unstable_room_versions(&self) -> bool { + self.config.allow_unstable_room_versions + } + + pub fn default_room_version(&self) -> RoomVersionId { + if self + .supported_room_versions() + .contains(&self.config.default_room_version.clone()) + { + self.config.default_room_version.clone() + } else { + error!("Room version in config isn't supported, falling back to Version 6"); + RoomVersionId::Version6 + } + } + pub fn trusted_servers(&self) -> &[Box] { &self.config.trusted_servers } @@ -246,6 +272,19 @@ impl Globals { &self.config.turn_secret } + pub fn supported_room_versions(&self) -> Vec { + let mut room_versions: Vec = vec![]; + self.stable_room_versions + .iter() + .for_each(|room_version| room_versions.push(room_version.clone())); + if self.allow_unstable_room_versions() { + self.unstable_room_versions + .iter() + .for_each(|room_version| room_versions.push(room_version.clone())); + }; + room_versions + } + /// TODO: the key valid until timestamp is only honored in room version > 4 /// Remove the outdated keys and insert the new ones. /// diff --git a/src/database/rooms.rs b/src/database/rooms.rs index c5b795bd..338b0895 100644 --- a/src/database/rooms.rs +++ b/src/database/rooms.rs @@ -129,6 +129,12 @@ pub struct Rooms { } impl Rooms { + /// Returns true if a given room version is supported + #[tracing::instrument(skip(self, db))] + pub fn is_supported_version(&self, db: &Database, room_version: &RoomVersionId) -> bool { + db.globals.supported_room_versions().contains(room_version) + } + /// Builds a StateMap by iterating over all keys that start /// with state_hash, this gives the full state for the given state_hash. #[tracing::instrument(skip(self))] @@ -1983,9 +1989,10 @@ impl Rooms { None }; - // If there was no create event yet, assume we are creating a version 6 room right now + // If there was no create event yet, assume we are creating a room with the default + // version right now let room_version_id = create_event_content - .map_or(RoomVersionId::Version6, |create_event| { + .map_or(db.globals.default_room_version(), |create_event| { create_event.room_version }); let room_version = RoomVersion::new(&room_version_id).expect("room version is supported"); @@ -2778,11 +2785,7 @@ impl Rooms { let (make_leave_response, remote_server) = make_leave_response_and_server?; let room_version_id = match make_leave_response.room_version { - Some(version) - if version == RoomVersionId::Version5 || version == RoomVersionId::Version6 => - { - version - } + Some(version) if self.is_supported_version(&db, &version) => version, _ => return Err(Error::BadServerResponse("Room version is not supported")), }; diff --git a/src/server_server.rs b/src/server_server.rs index 482edf0f..7709f1ea 100644 --- a/src/server_server.rs +++ b/src/server_server.rs @@ -2670,10 +2670,12 @@ pub fn create_join_event_template_route( None }; - // If there was no create event yet, assume we are creating a version 6 room right now - let room_version_id = create_event_content.map_or(RoomVersionId::Version6, |create_event| { - create_event.room_version - }); + // If there was no create event yet, assume we are creating a room with the default version + // right now + let room_version_id = create_event_content + .map_or(db.globals.default_room_version(), |create_event| { + create_event.room_version + }); let room_version = RoomVersion::new(&room_version_id).expect("room version is supported"); if !body.ver.contains(&room_version_id) { @@ -2937,8 +2939,7 @@ pub async fn create_invite_route( return Err(Error::bad_config("Federation is disabled.")); } - if body.room_version != RoomVersionId::Version5 && body.room_version != RoomVersionId::Version6 - { + if !db.rooms.is_supported_version(&db, &body.room_version) { return Err(Error::BadRequest( ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone(),