diff --git a/Cargo.lock b/Cargo.lock index 41483941..3480c014 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,12 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +[[package]] +name = "const_panic" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1167,7 +1173,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -1178,6 +1183,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", "hashbrown 0.14.0", + "serde", ] [[package]] @@ -1212,9 +1218,9 @@ checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] @@ -1283,25 +1289,30 @@ dependencies = [ [[package]] name = "konst" -version = "0.2.19" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" +checksum = "1d9a8bb6c7c71d151b25936b03e012a4c00daea99e3a3797c6ead66b0a0d55e2" dependencies = [ - "konst_macro_rules", + "const_panic", + "konst_kernel", "konst_proc_macros", + "typewit", ] [[package]] -name = "konst_macro_rules" -version = "0.2.19" +name = "konst_kernel" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" +checksum = "55d2ab266022e7309df89ed712bddc753e3a3c395c3ced1bb2e4470ec2a8146d" +dependencies = [ + "typewit", +] [[package]] name = "konst_proc_macros" -version = "0.2.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984e109462d46ad18314f10e392c286c3d47bce203088a09012de1015b45b737" +checksum = "4e28ab1dc35e09d60c2b8c90d12a9a8d9666c876c10a3739a3196db0103b6043" [[package]] name = "lazy_static" @@ -2111,7 +2122,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.8.2" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "assign", "js_int", @@ -2129,7 +2140,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.8.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "js_int", "ruma-common", @@ -2140,7 +2151,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.16.2" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "assign", "bytes", @@ -2157,13 +2168,13 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.11.3" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "base64 0.21.2", "bytes", "form_urlencoded", "http", - "indexmap 1.9.3", + "indexmap 2.0.0", "js_int", "js_option", "konst", @@ -2185,7 +2196,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.7.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "js_int", "ruma-common", @@ -2196,7 +2207,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "js_int", "thiserror", @@ -2205,7 +2216,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.7.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "js_int", "ruma-common", @@ -2215,7 +2226,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.11.3" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "once_cell", "proc-macro-crate", @@ -2230,7 +2241,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.7.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "js_int", "ruma-common", @@ -2241,7 +2252,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.13.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "base64 0.21.2", "ed25519-dalek", @@ -2257,7 +2268,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.9.1" -source = "git+https://github.com/ruma/ruma?rev=38294bd5206498c02b1001227d65654eb548308b#38294bd5206498c02b1001227d65654eb548308b" +source = "git+https://github.com/ruma/ruma?rev=07bc06038fded40d4e9180637f056d256f9a1fbc#07bc06038fded40d4e9180637f056d256f9a1fbc" dependencies = [ "itertools", "js_int", @@ -3152,6 +3163,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "typewit" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4061a10d4d8f3081a8ccc025182afd8434302d8d4b4503ec6d8510d09df08c2d" + [[package]] name = "uncased" version = "0.9.9" diff --git a/Cargo.toml b/Cargo.toml index bc29c5a0..ae7de599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,9 @@ tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitiv # Used for matrix spec type definitions and helpers #ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] } -ruma = { git = "https://github.com/ruma/ruma", rev = "38294bd5206498c02b1001227d65654eb548308b", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } -#ruma = { git = "https://github.com/timokoesters/ruma", rev = "50c1db7e0a3a21fc794b0cce3b64285a4c750c71", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] } -#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] } +ruma = { git = "https://github.com/ruma/ruma", rev = "07bc06038fded40d4e9180637f056d256f9a1fbc", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } +#ruma = { git = "https://github.com/timokoesters/ruma", rev = "4ec9c69bb7e09391add2382b3ebac97b6e8f4c64", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } +#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } # Async runtime and utilities tokio = { version = "1.28.1", features = ["fs", "macros", "signal", "sync"] } diff --git a/README.md b/README.md index f73f6aa9..8fabefd6 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Thanks to the contributors to Conduit and all libraries we use, for example: If you run into any question, feel free to - Ask us in `#conduit:fachschaften.org` on Matrix - Write an E-Mail to `conduit@koesters.xyz` -- Send an direct message to `timo@fachschaften.org` on Matrix +- Send an direct message to `timokoesters@fachschaften.org` on Matrix - [Open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new) #### Donate diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index e1322109..df1ac40c 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -203,17 +203,7 @@ pub(crate) async fn get_public_rooms_filtered_helper( Error::bad_database("Invalid canonical alias event in database.") }) })?, - name: services() - .rooms - .state_accessor - .room_state_get(&room_id, &StateEventType::RoomName, "")? - .map_or(Ok(None), |s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomNameEventContent| c.name) - .map_err(|_| { - Error::bad_database("Invalid room name event in database.") - }) - })?, + name: services().rooms.state_accessor.get_name(&room_id)?, num_joined_members: services() .rooms .state_cache diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index dd753470..fed4fb73 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1,4 +1,6 @@ -use crate::{service::rooms::timeline::PduCount, services, Error, Result, Ruma, RumaResponse}; +use crate::{ + service::rooms::timeline::PduCount, services, Error, PduEvent, Result, Ruma, RumaResponse, +}; use ruma::{ api::client::{ filter::{FilterDefinition, LazyLoadOptions}, @@ -8,6 +10,7 @@ use ruma::{ Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice, }, + v4::SlidingOp, DeviceLists, UnreadNotificationsCount, }, uiaa::UiaaResponse, @@ -17,7 +20,7 @@ use ruma::{ StateEventType, TimelineEventType, }, serde::Raw, - DeviceId, OwnedDeviceId, OwnedUserId, RoomId, UserId, + uint, DeviceId, OwnedDeviceId, OwnedUserId, RoomId, UInt, UserId, }; use std::{ collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, @@ -199,7 +202,7 @@ async fn sync_helper( let mut joined_rooms = BTreeMap::new(); let since = body .since - .clone() + .as_ref() .and_then(|string| string.parse().ok()) .unwrap_or(0); let sincecount = PduCount::Normal(since); @@ -581,43 +584,7 @@ async fn load_joined_room( drop(insert_lock); } - let timeline_pdus; - let limited; - if services() - .rooms - .timeline - .last_timeline_count(&sender_user, &room_id)? - > sincecount - { - let mut non_timeline_pdus = services() - .rooms - .timeline - .pdus_until(&sender_user, &room_id, PduCount::max())? - .filter_map(|r| { - // Filter out buggy events - if r.is_err() { - error!("Bad pdu in pdus_since: {:?}", r); - } - r.ok() - }) - .take_while(|(pducount, _)| pducount > &sincecount); - - // Take the last 10 events for the timeline - timeline_pdus = non_timeline_pdus - .by_ref() - .take(10) - .collect::>() - .into_iter() - .rev() - .collect::>(); - - // They /sync response doesn't always return all messages, so we say the output is - // limited unless there are events in non_timeline_pdus - limited = non_timeline_pdus.next().is_some(); - } else { - timeline_pdus = Vec::new(); - limited = false; - } + let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?; let send_notification_counts = !timeline_pdus.is_empty() || services() @@ -1132,6 +1099,52 @@ async fn load_joined_room( }) } +fn load_timeline( + sender_user: &UserId, + room_id: &RoomId, + sincecount: PduCount, + limit: u64, +) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> { + let timeline_pdus; + let limited; + if services() + .rooms + .timeline + .last_timeline_count(&sender_user, &room_id)? + > sincecount + { + let mut non_timeline_pdus = services() + .rooms + .timeline + .pdus_until(&sender_user, &room_id, PduCount::max())? + .filter_map(|r| { + // Filter out buggy events + if r.is_err() { + error!("Bad pdu in pdus_since: {:?}", r); + } + r.ok() + }) + .take_while(|(pducount, _)| pducount > &sincecount); + + // Take the last events for the timeline + timeline_pdus = non_timeline_pdus + .by_ref() + .take(limit as usize) + .collect::>() + .into_iter() + .rev() + .collect::>(); + + // They /sync response doesn't always return all messages, so we say the output is + // limited unless there are events in non_timeline_pdus + limited = non_timeline_pdus.next().is_some(); + } else { + timeline_pdus = Vec::new(); + limited = false; + } + Ok((timeline_pdus, limited)) +} + fn share_encrypted_room( sender_user: &UserId, user_id: &UserId, @@ -1155,3 +1168,190 @@ fn share_encrypted_room( }) .any(|encrypted| encrypted)) } + +pub async fn sync_events_v4_route( + body: Ruma, +) -> Result> { + let sender_user = body.sender_user.expect("user is authenticated"); + let sender_device = body.sender_device.expect("user is authenticated"); + let body = dbg!(body.body); + + // Setup watchers, so if there's no response, we can wait for them + let watcher = services().globals.watch(&sender_user, &sender_device); + + let next_batch = services().globals.current_count()?; + + let since = body + .pos + .as_ref() + .and_then(|string| string.parse().ok()) + .unwrap_or(0); + let sincecount = PduCount::Normal(since); + + let initial = since == 0; + + let all_joined_rooms = services() + .rooms + .state_cache + .rooms_joined(&sender_user) + .filter_map(|r| r.ok()) + .collect::>(); + + let mut lists = BTreeMap::new(); + let mut todo_rooms = BTreeMap::new(); // and required state + + for (list_id, list) in body.lists { + if list.filters.and_then(|f| f.is_invite).unwrap_or(false) { + continue; + } + + lists.insert( + list_id, + sync_events::v4::SyncList { + ops: list + .ranges + .into_iter() + .map(|mut r| { + r.0 = + r.0.clamp(uint!(0), UInt::from(all_joined_rooms.len() as u32 - 1)); + r.1 = + r.1.clamp(r.0, UInt::from(all_joined_rooms.len() as u32 - 1)); + let room_ids = all_joined_rooms + [(u64::from(r.0) as usize)..=(u64::from(r.1) as usize)] + .to_vec(); + todo_rooms.extend(room_ids.iter().cloned().map(|r| { + let limit = list + .room_details + .timeline_limit + .map_or(10, u64::from) + .min(100); + (r, (list.room_details.required_state.clone(), limit)) + })); + sync_events::v4::SyncOp { + op: SlidingOp::Sync, + range: Some(r.clone()), + index: None, + room_ids, + room_id: None, + } + }) + .collect(), + count: UInt::from(all_joined_rooms.len() as u32), + }, + ); + } + + let mut rooms = BTreeMap::new(); + for (room_id, (required_state_request, timeline_limit)) in todo_rooms { + let (timeline_pdus, limited) = + load_timeline(&sender_user, &room_id, sincecount, timeline_limit)?; + + let prev_batch = timeline_pdus + .first() + .map_or(Ok::<_, Error>(None), |(pdu_count, _)| { + Ok(Some(match pdu_count { + PduCount::Backfilled(_) => { + error!("timeline in backfill state?!"); + "0".to_owned() + } + PduCount::Normal(c) => c.to_string(), + })) + })?; + + let room_events: Vec<_> = timeline_pdus + .iter() + .map(|(_, pdu)| pdu.to_sync_room_event()) + .collect(); + + let required_state = required_state_request + .iter() + .map(|state| { + services() + .rooms + .state_accessor + .room_state_get(&room_id, &state.0, &state.1) + }) + .filter_map(|r| r.ok()) + .filter_map(|o| o) + .map(|state| state.to_sync_state_event()) + .collect(); + + rooms.insert( + room_id.clone(), + sync_events::v4::SlidingSyncRoom { + name: services().rooms.state_accessor.get_name(&room_id)?, + initial: Some(initial), + is_dm: None, + invite_state: None, + unread_notifications: UnreadNotificationsCount { + highlight_count: None, + notification_count: None, + }, + timeline: room_events, + required_state, + prev_batch, + limited, + joined_count: Some( + (services() + .rooms + .state_cache + .room_joined_count(&room_id)? + .unwrap_or(0) as u32) + .into(), + ), + invited_count: Some( + (services() + .rooms + .state_cache + .room_invited_count(&room_id)? + .unwrap_or(0) as u32) + .into(), + ), + num_live: None, + }, + ); + } + + if rooms + .iter() + .all(|(_, r)| r.timeline.is_empty() && r.required_state.is_empty()) + { + // Hang a few seconds so requests are not spammed + // Stop hanging if new info arrives + let mut duration = body.timeout.unwrap_or(Duration::from_secs(30)); + if duration.as_secs() > 30 { + duration = Duration::from_secs(30); + } + let _ = tokio::time::timeout(duration, watcher).await; + } + + Ok(dbg!(sync_events::v4::Response { + initial: initial, + txn_id: body.txn_id.clone(), + pos: next_batch.to_string(), + lists, + rooms, + extensions: sync_events::v4::Extensions { + to_device: None, + e2ee: sync_events::v4::E2EE { + device_lists: DeviceLists { + changed: Vec::new(), + left: Vec::new(), + }, + device_one_time_keys_count: BTreeMap::new(), + device_unused_fallback_key_types: None, + }, + account_data: sync_events::v4::AccountData { + global: Vec::new(), + rooms: BTreeMap::new(), + }, + receipts: sync_events::v4::Receipts { + rooms: BTreeMap::new(), + }, + typing: sync_events::v4::Typing { + rooms: BTreeMap::new(), + }, + }, + delta_token: None, + })) +} diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index b4f03f41..797b9529 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -1,8 +1,9 @@ use std::{collections::BTreeMap, iter::FromIterator}; -use ruma::api::client::discovery::get_supported_versions; +use axum::{response::IntoResponse, Json}; +use ruma::api::client::{discovery::get_supported_versions, error::ErrorKind}; -use crate::{Result, Ruma}; +use crate::{services, Error, Result, Ruma}; /// # `GET /_matrix/client/versions` /// @@ -31,3 +32,18 @@ pub async fn get_supported_versions_route( Ok(resp) } + +/// # `GET /.well-known/matrix/client` +pub async fn well_known_client_route( + _body: Ruma, +) -> Result { + let client_url = match services().globals.well_known_client() { + Some(url) => url.clone(), + None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), + }; + + Ok(Json(serde_json::json!({ + "m.homeserver": {"base_url": client_url}, + "org.matrix.msc3575.proxy": {"url": client_url} + }))) +} diff --git a/src/api/server_server.rs b/src/api/server_server.rs index adb5f1fb..0177f2ab 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -123,6 +123,12 @@ where return Err(Error::bad_config("Federation is disabled.")); } + if destination == services().globals.server_name() { + return Err(Error::bad_config( + "Won't send federation request to ourselves", + )); + } + debug!("Preparing to send request to {destination}"); let mut write_destination_to_cache = false; diff --git a/src/config/mod.rs b/src/config/mod.rs index f9222825..4dad9f79 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -54,6 +54,7 @@ pub struct Config { pub allow_unstable_room_versions: bool, #[serde(default = "default_default_room_version")] pub default_room_version: RoomVersionId, + pub well_known_client: Option, #[serde(default = "false_fn")] pub allow_jaeger: bool, #[serde(default = "false_fn")] diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index ab3dfe0e..1e024591 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -1,7 +1,8 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use async_trait::async_trait; use futures_util::{stream::FuturesUnordered, StreamExt}; +use lru_cache::LruCache; use ruma::{ api::federation::discovery::{ServerSigningKeys, VerifyKey}, signatures::Ed25519KeyPair, @@ -148,28 +149,36 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" fn clear_caches(&self, amount: u32) { if amount > 0 { - self.pdu_cache.lock().unwrap().clear(); + let c = &mut *self.pdu_cache.lock().unwrap(); + *c = LruCache::new(c.capacity()); } if amount > 1 { - self.shorteventid_cache.lock().unwrap().clear(); + let c = &mut *self.shorteventid_cache.lock().unwrap(); + *c = LruCache::new(c.capacity()); } if amount > 2 { - self.auth_chain_cache.lock().unwrap().clear(); + let c = &mut *self.auth_chain_cache.lock().unwrap(); + *c = LruCache::new(c.capacity()); } if amount > 3 { - self.eventidshort_cache.lock().unwrap().clear(); + let c = &mut *self.eventidshort_cache.lock().unwrap(); + *c = LruCache::new(c.capacity()); } if amount > 4 { - self.statekeyshort_cache.lock().unwrap().clear(); + let c = &mut *self.statekeyshort_cache.lock().unwrap(); + *c = LruCache::new(c.capacity()); } if amount > 5 { - self.our_real_users_cache.write().unwrap().clear(); + let c = &mut *self.our_real_users_cache.write().unwrap(); + *c = HashMap::new(); } if amount > 6 { - self.appservice_in_room_cache.write().unwrap().clear(); + let c = &mut *self.appservice_in_room_cache.write().unwrap(); + *c = HashMap::new(); } if amount > 7 { - self.lasttimelinecount_cache.lock().unwrap().clear(); + let c = &mut *self.lasttimelinecount_cache.lock().unwrap(); + *c = HashMap::new(); } } diff --git a/src/main.rs b/src/main.rs index ea5572ee..579eeb15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -357,6 +357,7 @@ fn routes() -> Router { .put(client_server::send_state_event_for_empty_key_route), ) .ruma_route(client_server::sync_events_route) + .ruma_route(client_server::sync_events_v4_route) .ruma_route(client_server::get_context_route) .ruma_route(client_server::get_message_events_route) .ruma_route(client_server::search_events_route) diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index e4affde2..5326b7a9 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -342,6 +342,10 @@ impl Service { r } + pub fn well_known_client(&self) -> &Option { + &self.config.well_known_client + } + pub fn shutdown(&self) { self.shutdown.store(true, atomic::Ordering::Relaxed); // On shutdown diff --git a/src/service/pusher/mod.rs b/src/service/pusher/mod.rs index d4acaa59..5e4281d2 100644 --- a/src/service/pusher/mod.rs +++ b/src/service/pusher/mod.rs @@ -270,21 +270,7 @@ impl Service { notifi.sender_display_name = services().users.displayname(&event.sender)?; - let room_name = if let Some(room_name_pdu) = services() - .rooms - .state_accessor - .room_state_get(&event.room_id, &StateEventType::RoomName, "")? - { - serde_json::from_str::(room_name_pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid room name event in database.") - })? - .name - } else { - None - }; - - notifi.room_name = room_name; + notifi.room_name = services().rooms.state_accessor.get_name(&event.room_id)?; self.send_request(&http.url, send_event_notification::v1::Request::new(notifi)) .await?; diff --git a/src/service/rooms/spaces/mod.rs b/src/service/rooms/spaces/mod.rs index 76ba6c57..36fa1fcd 100644 --- a/src/service/rooms/spaces/mod.rs +++ b/src/service/rooms/spaces/mod.rs @@ -5,11 +5,10 @@ use ruma::{ api::{ client::{ error::ErrorKind, - space::{get_hierarchy, SpaceHierarchyRoomsChunk, SpaceRoomJoinRule}, + space::{get_hierarchy, SpaceHierarchyRoomsChunk}, }, federation, }, - directory::PublicRoomJoinRule, events::{ room::{ avatar::RoomAvatarEventContent, @@ -18,11 +17,11 @@ use ruma::{ guest_access::{GuestAccess, RoomGuestAccessEventContent}, history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, join_rules::{JoinRule, RoomJoinRulesEventContent}, - name::RoomNameEventContent, topic::RoomTopicEventContent, }, StateEventType, }, + space::SpaceRoomJoinRule, OwnedRoomId, RoomId, UserId, }; @@ -30,10 +29,15 @@ use tracing::{debug, error, warn}; use crate::{services, Error, PduEvent, Result}; +pub enum CachedJoinRule { + Simplified(SpaceRoomJoinRule), + Full(JoinRule), +} + pub struct CachedSpaceChunk { chunk: SpaceHierarchyRoomsChunk, children: Vec, - join_rule: JoinRule, + join_rule: CachedJoinRule, } pub struct Service { @@ -79,9 +83,15 @@ impl Service { .as_ref() { if let Some(cached) = cached { - if let Some(_join_rule) = - self.handle_join_rule(&cached.join_rule, sender_user, ¤t_room)? - { + let allowed = match &cached.join_rule { + CachedJoinRule::Simplified(s) => { + self.handle_simplified_join_rule(s, sender_user, ¤t_room)? + } + CachedJoinRule::Full(f) => { + self.handle_join_rule(f, sender_user, ¤t_room)? + } + }; + if allowed { if left_to_skip > 0 { left_to_skip -= 1; } else { @@ -152,7 +162,7 @@ impl Service { Some(CachedSpaceChunk { chunk, children: children_ids.clone(), - join_rule, + join_rule: CachedJoinRule::Full(join_rule), }), ); } @@ -182,7 +192,6 @@ impl Service { .await { warn!("Got response from {server} for /hierarchy\n{response:?}"); - let join_rule = self.translate_pjoinrule(&response.room.join_rule)?; let chunk = SpaceHierarchyRoomsChunk { canonical_alias: response.room.canonical_alias, name: response.room.name, @@ -192,7 +201,7 @@ impl Service { world_readable: response.room.world_readable, guest_can_join: response.room.guest_can_join, avatar_url: response.room.avatar_url, - join_rule: self.translate_sjoinrule(&response.room.join_rule)?, + join_rule: response.room.join_rule.clone(), room_type: response.room.room_type, children_state: response.room.children_state, }; @@ -202,9 +211,11 @@ impl Service { .map(|c| c.room_id.clone()) .collect::>(); - if let Some(_join_rule) = - self.handle_join_rule(&join_rule, sender_user, ¤t_room)? - { + if self.handle_simplified_join_rule( + &response.room.join_rule, + sender_user, + ¤t_room, + )? { if left_to_skip > 0 { left_to_skip -= 1; } else { @@ -220,7 +231,7 @@ impl Service { Some(CachedSpaceChunk { chunk, children, - join_rule, + join_rule: CachedJoinRule::Simplified(response.room.join_rule), }), ); @@ -273,15 +284,7 @@ impl Service { Error::bad_database("Invalid canonical alias event in database.") }) })?, - name: services() - .rooms - .state_accessor - .room_state_get(&room_id, &StateEventType::RoomName, "")? - .map_or(Ok(None), |s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomNameEventContent| c.name) - .map_err(|_| Error::bad_database("Invalid room name event in database.")) - })?, + name: services().rooms.state_accessor.get_name(&room_id)?, num_joined_members: services() .rooms .state_cache @@ -357,15 +360,17 @@ impl Service { }) .transpose()? .unwrap_or(JoinRule::Invite); - self.handle_join_rule(&join_rule, sender_user, room_id)? - .ok_or_else(|| { - debug!("User is not allowed to see room {room_id}"); - // This error will be caught later - Error::BadRequest( - ErrorKind::Forbidden, - "User is not allowed to see the room", - ) - })? + + if !self.handle_join_rule(&join_rule, sender_user, room_id)? { + debug!("User is not allowed to see room {room_id}"); + // This error will be caught later + return Err(Error::BadRequest( + ErrorKind::Forbidden, + "User is not allowed to see the room", + )); + } + + self.translate_joinrule(&join_rule)? }, room_type: services() .rooms @@ -386,20 +391,35 @@ impl Service { }) } - fn translate_pjoinrule(&self, join_rule: &PublicRoomJoinRule) -> Result { + fn translate_joinrule(&self, join_rule: &JoinRule) -> Result { match join_rule { - PublicRoomJoinRule::Knock => Ok(JoinRule::Knock), - PublicRoomJoinRule::Public => Ok(JoinRule::Public), + JoinRule::Invite => Ok(SpaceRoomJoinRule::Invite), + JoinRule::Knock => Ok(SpaceRoomJoinRule::Knock), + JoinRule::Private => Ok(SpaceRoomJoinRule::Private), + JoinRule::Restricted(_) => Ok(SpaceRoomJoinRule::Restricted), + JoinRule::KnockRestricted(_) => Ok(SpaceRoomJoinRule::KnockRestricted), + JoinRule::Public => Ok(SpaceRoomJoinRule::Public), _ => Err(Error::BadServerResponse("Unknown join rule")), } } - fn translate_sjoinrule(&self, join_rule: &PublicRoomJoinRule) -> Result { - match join_rule { - PublicRoomJoinRule::Knock => Ok(SpaceRoomJoinRule::Knock), - PublicRoomJoinRule::Public => Ok(SpaceRoomJoinRule::Public), - _ => Err(Error::BadServerResponse("Unknown join rule")), - } + fn handle_simplified_join_rule( + &self, + join_rule: &SpaceRoomJoinRule, + sender_user: &UserId, + room_id: &RoomId, + ) -> Result { + let allowed = match join_rule { + SpaceRoomJoinRule::Public => true, + SpaceRoomJoinRule::Knock => true, + SpaceRoomJoinRule::Invite => services() + .rooms + .state_cache + .is_joined(sender_user, &room_id)?, + _ => false, + }; + + Ok(allowed) } fn handle_join_rule( @@ -407,30 +427,25 @@ impl Service { join_rule: &JoinRule, sender_user: &UserId, room_id: &RoomId, - ) -> Result> { + ) -> Result { + if self.handle_simplified_join_rule( + &self.translate_joinrule(join_rule)?, + sender_user, + room_id, + )? { + return Ok(true); + } + match join_rule { - JoinRule::Public => Ok::<_, Error>(Some(SpaceRoomJoinRule::Public)), - JoinRule::Knock => Ok(Some(SpaceRoomJoinRule::Knock)), - JoinRule::Invite => { - if services() - .rooms - .state_cache - .is_joined(sender_user, &room_id)? - { - Ok(Some(SpaceRoomJoinRule::Invite)) - } else { - Ok(None) - } - } - JoinRule::Restricted(_r) => { + JoinRule::Restricted(_) => { // TODO: Check rules - Ok(None) + Ok(false) } - JoinRule::KnockRestricted(_r) => { + JoinRule::KnockRestricted(_) => { // TODO: Check rules - Ok(None) + Ok(false) } - _ => Ok(None), + _ => Ok(false), } } } diff --git a/src/service/rooms/state_accessor/mod.rs b/src/service/rooms/state_accessor/mod.rs index a25a8b5d..9d071a53 100644 --- a/src/service/rooms/state_accessor/mod.rs +++ b/src/service/rooms/state_accessor/mod.rs @@ -11,6 +11,7 @@ use ruma::{ room::{ history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, member::{MembershipState, RoomMemberEventContent}, + name::RoomNameEventContent, }, StateEventType, }, @@ -269,4 +270,16 @@ impl Service { ) -> Result>> { self.db.room_state_get(room_id, event_type, state_key) } + + pub fn get_name(&self, room_id: &RoomId) -> Result> { + services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomName, "")? + .map_or(Ok(None), |s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomNameEventContent| c.name) + .map_err(|_| Error::bad_database("Invalid room name event in database.")) + }) + } }