From 17a0b3430934fbb8370066ee9dc3506102c5b3f6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 20:02:18 -0700 Subject: [PATCH 001/617] remove version checker This reverts commit b8c164dc6027844d665158dc8906dd5c89f9238b. --- src/config/mod.rs | 2 - src/database/key_value/globals.rs | 18 --------- src/database/mod.rs | 62 +------------------------------ src/service/globals/data.rs | 2 - src/service/globals/mod.rs | 14 ------- 5 files changed, 2 insertions(+), 96 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index fb1e2f31..85df6e6a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -27,8 +27,6 @@ pub struct Config { pub db_cache_capacity_mb: f64, #[serde(default = "true_fn")] pub enable_lightning_bolt: bool, - #[serde(default = "true_fn")] - pub allow_check_for_updates: bool, #[serde(default = "default_conduit_cache_capacity_modifier")] pub conduit_cache_capacity_modifier: f64, #[serde(default = "default_rocksdb_max_open_files")] diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 2851ce53..aa13e64f 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -12,7 +12,6 @@ use ruma::{ use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; pub const COUNTER: &[u8] = b"c"; -pub const LAST_CHECK_FOR_UPDATES_COUNT: &[u8] = b"u"; #[async_trait] impl service::globals::Data for KeyValueDatabase { @@ -28,23 +27,6 @@ impl service::globals::Data for KeyValueDatabase { }) } - fn last_check_for_updates_id(&self) -> Result { - self.global - .get(LAST_CHECK_FOR_UPDATES_COUNT)? - .map_or(Ok(0_u64), |bytes| { - utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("last check for updates count has invalid bytes.") - }) - }) - } - - fn update_check_for_updates_id(&self, id: u64) -> Result<()> { - self.global - .insert(LAST_CHECK_FOR_UPDATES_COUNT, &id.to_be_bytes())?; - - Ok(()) - } - async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { let userid_bytes = user_id.as_bytes().to_vec(); let mut userid_prefix = userid_bytes.clone(); diff --git a/src/database/mod.rs b/src/database/mod.rs index 41da857c..7008d56f 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -19,7 +19,6 @@ use ruma::{ CanonicalJsonValue, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, }; -use serde::Deserialize; use std::{ collections::{BTreeMap, HashMap, HashSet}, fs::{self, remove_dir_all}, @@ -27,9 +26,7 @@ use std::{ mem::size_of, path::Path, sync::{Arc, Mutex, RwLock}, - time::Duration, }; -use tokio::time::interval; use tracing::{debug, error, info, warn}; @@ -986,9 +983,6 @@ impl KeyValueDatabase { services().sending.start_handler(); Self::start_cleanup_task().await; - if services().globals.allow_check_for_updates() { - Self::start_check_for_updates_task(); - } Ok(()) } @@ -1004,62 +998,10 @@ impl KeyValueDatabase { res } - #[tracing::instrument] - pub fn start_check_for_updates_task() { - tokio::spawn(async move { - let timer_interval = Duration::from_secs(60 * 60); - let mut i = interval(timer_interval); - loop { - i.tick().await; - let _ = Self::try_handle_updates().await; - } - }); - } - - async fn try_handle_updates() -> Result<()> { - let response = services() - .globals - .default_client() - .get("https://conduit.rs/check-for-updates/stable") - .send() - .await?; - - #[derive(Deserialize)] - struct CheckForUpdatesResponseEntry { - id: u64, - date: String, - message: String, - } - #[derive(Deserialize)] - struct CheckForUpdatesResponse { - updates: Vec, - } - - let response = serde_json::from_str::(&response.text().await?) - .map_err(|_| Error::BadServerResponse("Bad version check response"))?; - - let mut last_update_id = services().globals.last_check_for_updates_id()?; - for update in response.updates { - last_update_id = last_update_id.max(update.id); - if update.id > services().globals.last_check_for_updates_id()? { - println!("{}", update.message); - services() - .admin - .send_message(RoomMessageEventContent::text_plain(format!( - "@room: The following is a message from the Conduit developers. It was sent on '{}':\n\n{}", - update.date, update.message - ))) - } - } - services() - .globals - .update_check_for_updates_id(last_update_id)?; - - Ok(()) - } - #[tracing::instrument] pub async fn start_cleanup_task() { + use tokio::time::interval; + #[cfg(unix)] use tokio::signal::unix::{signal, SignalKind}; diff --git a/src/service/globals/data.rs b/src/service/globals/data.rs index 8a66751b..171b3fec 100644 --- a/src/service/globals/data.rs +++ b/src/service/globals/data.rs @@ -13,8 +13,6 @@ use crate::Result; pub trait Data: Send + Sync { fn next_count(&self) -> Result; fn current_count(&self) -> Result; - fn last_check_for_updates_id(&self) -> Result; - fn update_check_for_updates_id(&self, id: u64) -> Result<()>; async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()>; fn cleanup(&self) -> Result<()>; fn memory_usage(&self) -> String; diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 798c725a..3088ac9b 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -255,16 +255,6 @@ impl Service { self.db.current_count() } - #[tracing::instrument(skip(self))] - pub fn last_check_for_updates_id(&self) -> Result { - self.db.last_check_for_updates_id() - } - - #[tracing::instrument(skip(self))] - pub fn update_check_for_updates_id(&self, id: u64) -> Result<()> { - self.db.update_check_for_updates_id(id) - } - pub async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { self.db.watch(user_id, device_id).await } @@ -313,10 +303,6 @@ impl Service { self.config.enable_lightning_bolt } - pub fn allow_check_for_updates(&self) -> bool { - self.config.allow_check_for_updates - } - pub fn trusted_servers(&self) -> &[OwnedServerName] { &self.config.trusted_servers } From cddf6991f280008b5af5acfab6a9719bb0cfb7f1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 29 Apr 2024 21:34:47 -0700 Subject: [PATCH 002/617] remove optional automatic display name emoji --- src/api/client_server/account.rs | 7 +------ src/config/mod.rs | 6 ------ src/service/admin/mod.rs | 7 +------ src/service/globals/mod.rs | 4 ---- 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 0226abc7..f4dbd280 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -206,12 +206,7 @@ pub async fn register_route(body: Ruma) -> Result bool { - self.config.enable_lightning_bolt - } - pub fn trusted_servers(&self) -> &[OwnedServerName] { &self.config.trusted_servers } From c9945f6bbac6e22af6cf955cfa99826d4b04fe8c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 29 Apr 2024 21:46:46 -0700 Subject: [PATCH 003/617] remove welcome message --- src/service/admin/mod.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 9d66863a..753f961f 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -1301,24 +1301,6 @@ impl Service { &state_lock, ) .await?; - - // Send welcome message - services().rooms.timeline.build_and_append_pdu( - PduBuilder { - event_type: TimelineEventType::RoomMessage, - content: to_raw_value(&RoomMessageEventContent::text_html( - format!("## Thank you for trying out Conduit!\n\nConduit is currently in Beta. This means you can join and participate in most Matrix rooms, but not all features are supported and you might run into bugs from time to time.\n\nHelpful links:\n> Website: https://conduit.rs\n> Git and Documentation: https://gitlab.com/famedly/conduit\n> Report issues: https://gitlab.com/famedly/conduit/-/issues\n\nFor a list of available commands, send the following message in this room: `@conduit:{}: --help`\n\nHere are some rooms you can join (by typing the command):\n\nConduit room (Ask questions and get notified on updates):\n`/join #conduit:fachschaften.org`\n\nConduit lounge (Off-topic, only Conduit users are allowed to join)\n`/join #conduit-lounge:conduit.rs`", services().globals.server_name()), - format!("

Thank you for trying out Conduit!

\n

Conduit is currently in Beta. This means you can join and participate in most Matrix rooms, but not all features are supported and you might run into bugs from time to time.

\n

Helpful links:

\n
\n

Website: https://conduit.rs
Git and Documentation: https://gitlab.com/famedly/conduit
Report issues: https://gitlab.com/famedly/conduit/-/issues

\n
\n

For a list of available commands, send the following message in this room: @conduit:{}: --help

\n

Here are some rooms you can join (by typing the command):

\n

Conduit room (Ask questions and get notified on updates):
/join #conduit:fachschaften.org

\n

Conduit lounge (Off-topic, only Conduit users are allowed to join)
/join #conduit-lounge:conduit.rs

\n", services().globals.server_name()), - )) - .expect("event is valid, we just created it"), - unsigned: None, - state_key: None, - redacts: None, - }, - &conduit_user, - &room_id, - &state_lock, - ).await?; } Ok(()) } From aa51acf1529ad824ca3e0970d365a61ed6ff2e4f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 29 Apr 2024 21:47:19 -0700 Subject: [PATCH 004/617] remove dead code --- Cargo.lock | 43 ------------------------------------------- Cargo.toml | 2 -- src/database/mod.rs | 15 +-------------- 3 files changed, 1 insertion(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0063a3e0..320bc3d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -385,7 +385,6 @@ dependencies = [ "base64", "bytes", "clap", - "directories", "figment", "futures-util", "hmac", @@ -599,26 +598,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "ed25519" version = "2.2.3" @@ -1281,17 +1260,6 @@ dependencies = [ "windows-targets 0.52.4", ] -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.2", - "libc", - "redox_syscall", -] - [[package]] name = "libsqlite3-sys" version = "0.26.0" @@ -1863,17 +1831,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - [[package]] name = "regex" version = "1.10.3" diff --git a/Cargo.toml b/Cargo.toml index 4dfc04f3..6ebdbf6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,8 +51,6 @@ persy = { version = "1.4.4", optional = true, features = ["background_ops"] } # Used for the http request / response body type for Ruma endpoints used with reqwest bytes = "1.4.0" http = "0.2.9" -# Used to find data directory for default db path -directories = "4.0.1" # Used for ruma wrapper serde_json = { version = "1.0.96", features = ["raw_value"] } # Used for appservice registration files diff --git a/src/database/mod.rs b/src/database/mod.rs index 7008d56f..f4cf6a4c 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -6,7 +6,6 @@ use crate::{ SERVICES, }; use abstraction::{KeyValueDatabaseEngine, KvTree}; -use directories::ProjectDirs; use lru_cache::LruCache; use ruma::{ @@ -21,7 +20,7 @@ use ruma::{ }; use std::{ collections::{BTreeMap, HashMap, HashSet}, - fs::{self, remove_dir_all}, + fs, io::Write, mem::size_of, path::Path, @@ -170,18 +169,6 @@ pub struct KeyValueDatabase { } impl KeyValueDatabase { - /// Tries to remove the old database but ignores all errors. - pub fn try_remove(server_name: &str) -> Result<()> { - let mut path = ProjectDirs::from("xyz", "koesters", "conduit") - .ok_or_else(|| Error::bad_config("The OS didn't return a valid home directory path."))? - .data_dir() - .to_path_buf(); - path.push(server_name); - let _ = remove_dir_all(path); - - Ok(()) - } - fn check_db_setup(config: &Config) -> Result<()> { let path = Path::new(&config.database_path); From c765a1634d4ab7f1611b22a8f82be590ddcc1b12 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 29 Apr 2024 21:45:29 -0700 Subject: [PATCH 005/617] remove unused database backends --- Cargo.lock | 57 --------- Cargo.toml | 8 -- complement/Dockerfile | 2 +- src/database/abstraction.rs | 16 +-- src/database/abstraction/heed.rs | 194 ----------------------------- src/database/abstraction/persy.rs | 197 ------------------------------ src/database/abstraction/sled.rs | 127 ------------------- src/database/mod.rs | 17 --- src/utils/error.rs | 30 ----- 9 files changed, 2 insertions(+), 646 deletions(-) delete mode 100644 src/database/abstraction/heed.rs delete mode 100644 src/database/abstraction/persy.rs delete mode 100644 src/database/abstraction/sled.rs diff --git a/Cargo.lock b/Cargo.lock index 320bc3d9..c3429734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,6 @@ dependencies = [ "opentelemetry", "opentelemetry-jaeger", "parking_lot", - "persy", "rand", "regex", "reqwest", @@ -472,21 +471,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" version = "1.4.0" @@ -722,16 +706,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "futures" version = "0.3.30" @@ -1650,22 +1624,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "persy" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef4b7250ab3a90ded0e284b2633469c23ef01ea868fe7cbb64e2f0a7d6f6d02" -dependencies = [ - "crc", - "data-encoding", - "fs2", - "linked-hash-map", - "rand", - "thiserror", - "unsigned-varint", - "zigzag", -] - [[package]] name = "pin-project" version = "1.1.5" @@ -3090,12 +3048,6 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" -[[package]] -name = "unsigned-varint" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" - [[package]] name = "untrusted" version = "0.9.0" @@ -3464,15 +3416,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" -[[package]] -name = "zigzag" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b40401a28d86ce16a330b863b86fd7dbee4d7c940587ab09ab8c019f9e3fdf" -dependencies = [ - "num-traits", -] - [[package]] name = "zstd-sys" version = "2.0.9+zstd.1.5.5" diff --git a/Cargo.toml b/Cargo.toml index 6ebdbf6a..0862b5c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,10 +43,6 @@ ruma = { git = "https://github.com/ruma/ruma", rev = "5495b85aa311c2805302edb0a7 # Async runtime and utilities tokio = { version = "1.28.1", features = ["fs", "macros", "signal", "sync"] } -# Used for storing data permanently -#sled = { version = "0.34.7", features = ["compression", "no_metrics"], optional = true } -#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] } -persy = { version = "1.4.4", optional = true, features = ["background_ops"] } # Used for the http request / response body type for Ruma endpoints used with reqwest bytes = "1.4.0" @@ -91,7 +87,6 @@ parking_lot = { version = "0.12.1", optional = true } # crossbeam = { version = "0.8.2", optional = true } num_cpus = "1.15.0" threadpool = "1.8.1" -# heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true } # Used for ruma wrapper serde_html_form = "0.2.0" @@ -127,10 +122,7 @@ nix = { version = "0.28", features = ["resource"] } [features] default = ["conduit_bin", "backend_sqlite", "backend_rocksdb", "systemd"] -#backend_sled = ["sled"] -backend_persy = ["persy", "parking_lot"] backend_sqlite = ["sqlite"] -#backend_heed = ["heed", "crossbeam"] backend_rocksdb = ["rocksdb"] jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"] sqlite = ["rusqlite", "parking_lot", "tokio/signal"] diff --git a/complement/Dockerfile b/complement/Dockerfile index 813af10e..a327dd8b 100644 --- a/complement/Dockerfile +++ b/complement/Dockerfile @@ -33,7 +33,7 @@ ENV SERVER_NAME=localhost ENV CONDUIT_CONFIG=/workdir/conduit.toml RUN sed -i "s/port = 6167/port = 8008/g" conduit.toml -RUN echo "log = \"warn,_=off,sled=off\"" >> conduit.toml +RUN echo "log = \"warn,_=off\"" >> conduit.toml RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml EXPOSE 8008 8448 diff --git a/src/database/abstraction.rs b/src/database/abstraction.rs index 0a321054..0e3829ba 100644 --- a/src/database/abstraction.rs +++ b/src/database/abstraction.rs @@ -3,27 +3,13 @@ use crate::Result; use std::{future::Future, pin::Pin, sync::Arc}; -#[cfg(feature = "sled")] -pub mod sled; - #[cfg(feature = "sqlite")] pub mod sqlite; -#[cfg(feature = "heed")] -pub mod heed; - #[cfg(feature = "rocksdb")] pub mod rocksdb; -#[cfg(feature = "persy")] -pub mod persy; - -#[cfg(any( - feature = "sqlite", - feature = "rocksdb", - feature = "heed", - feature = "persy" -))] +#[cfg(any(feature = "sqlite", feature = "rocksdb",))] pub mod watchers; pub trait KeyValueDatabaseEngine: Send + Sync { diff --git a/src/database/abstraction/heed.rs b/src/database/abstraction/heed.rs deleted file mode 100644 index 9cca0975..00000000 --- a/src/database/abstraction/heed.rs +++ /dev/null @@ -1,194 +0,0 @@ -use super::{super::Config, watchers::Watchers}; -use crossbeam::channel::{bounded, Sender as ChannelSender}; -use threadpool::ThreadPool; - -use crate::{Error, Result}; -use std::{ - future::Future, - pin::Pin, - sync::{Arc, Mutex}, -}; - -use super::{DatabaseEngine, Tree}; - -type TupleOfBytes = (Vec, Vec); - -pub struct Engine { - env: heed::Env, - iter_pool: Mutex, -} - -pub struct EngineTree { - engine: Arc, - tree: Arc, - watchers: Watchers, -} - -fn convert_error(error: heed::Error) -> Error { - Error::HeedError { - error: error.to_string(), - } -} - -impl DatabaseEngine for Engine { - fn open(config: &Config) -> Result> { - let mut env_builder = heed::EnvOpenOptions::new(); - env_builder.map_size(1024 * 1024 * 1024 * 1024); // 1 Terabyte - env_builder.max_readers(126); - env_builder.max_dbs(128); - unsafe { - env_builder.flag(heed::flags::Flags::MdbWriteMap); - env_builder.flag(heed::flags::Flags::MdbMapAsync); - } - - Ok(Arc::new(Engine { - env: env_builder - .open(&config.database_path) - .map_err(convert_error)?, - iter_pool: Mutex::new(ThreadPool::new(10)), - })) - } - - fn open_tree(self: &Arc, name: &'static str) -> Result> { - // Creates the db if it doesn't exist already - Ok(Arc::new(EngineTree { - engine: Arc::clone(self), - tree: Arc::new( - self.env - .create_database(Some(name)) - .map_err(convert_error)?, - ), - watchers: Default::default(), - })) - } - - fn flush(self: &Arc) -> Result<()> { - self.env.force_sync().map_err(convert_error)?; - Ok(()) - } -} - -impl EngineTree { - fn iter_from_thread( - &self, - tree: Arc, - from: Vec, - backwards: bool, - ) -> Box + Send + Sync> { - let (s, r) = bounded::(100); - let engine = Arc::clone(&self.engine); - - let lock = self.engine.iter_pool.lock().await; - if lock.active_count() < lock.max_count() { - lock.execute(move || { - iter_from_thread_work(tree, &engine.env.read_txn().unwrap(), from, backwards, &s); - }); - } else { - std::thread::spawn(move || { - iter_from_thread_work(tree, &engine.env.read_txn().unwrap(), from, backwards, &s); - }); - } - - Box::new(r.into_iter()) - } -} - -fn iter_from_thread_work( - tree: Arc, - txn: &heed::RoTxn<'_>, - from: Vec, - backwards: bool, - s: &ChannelSender<(Vec, Vec)>, -) { - if backwards { - for (k, v) in tree.rev_range(txn, ..=&*from).unwrap().map(|r| r.unwrap()) { - if s.send((k.to_vec(), v.to_vec())).is_err() { - return; - } - } - } else { - if from.is_empty() { - for (k, v) in tree.iter(txn).unwrap().map(|r| r.unwrap()) { - if s.send((k.to_vec(), v.to_vec())).is_err() { - return; - } - } - } else { - for (k, v) in tree.range(txn, &*from..).unwrap().map(|r| r.unwrap()) { - if s.send((k.to_vec(), v.to_vec())).is_err() { - return; - } - } - } - } -} - -impl Tree for EngineTree { - fn get(&self, key: &[u8]) -> Result>> { - let txn = self.engine.env.read_txn().map_err(convert_error)?; - Ok(self - .tree - .get(&txn, &key) - .map_err(convert_error)? - .map(|s| s.to_vec())) - } - - fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> { - let mut txn = self.engine.env.write_txn().map_err(convert_error)?; - self.tree - .put(&mut txn, &key, &value) - .map_err(convert_error)?; - txn.commit().map_err(convert_error)?; - self.watchers.wake(key); - Ok(()) - } - - fn remove(&self, key: &[u8]) -> Result<()> { - let mut txn = self.engine.env.write_txn().map_err(convert_error)?; - self.tree.delete(&mut txn, &key).map_err(convert_error)?; - txn.commit().map_err(convert_error)?; - Ok(()) - } - - fn iter<'a>(&'a self) -> Box, Vec)> + Send + 'a> { - self.iter_from(&[], false) - } - - fn iter_from( - &self, - from: &[u8], - backwards: bool, - ) -> Box, Vec)> + Send> { - self.iter_from_thread(Arc::clone(&self.tree), from.to_vec(), backwards) - } - - fn increment(&self, key: &[u8]) -> Result> { - let mut txn = self.engine.env.write_txn().map_err(convert_error)?; - - let old = self.tree.get(&txn, &key).map_err(convert_error)?; - let new = - crate::utils::increment(old.as_deref()).expect("utils::increment always returns Some"); - - self.tree - .put(&mut txn, &key, &&*new) - .map_err(convert_error)?; - - txn.commit().map_err(convert_error)?; - - Ok(new) - } - - fn scan_prefix<'a>( - &'a self, - prefix: Vec, - ) -> Box, Vec)> + Send + 'a> { - Box::new( - self.iter_from(&prefix, false) - .take_while(move |(key, _)| key.starts_with(&prefix)), - ) - } - - fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin + Send + 'a>> { - self.watchers.watch(prefix) - } -} diff --git a/src/database/abstraction/persy.rs b/src/database/abstraction/persy.rs deleted file mode 100644 index da7d4cf0..00000000 --- a/src/database/abstraction/persy.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::{ - database::{ - abstraction::{watchers::Watchers, KeyValueDatabaseEngine, KvTree}, - Config, - }, - Result, -}; -use persy::{ByteVec, OpenOptions, Persy, Transaction, TransactionConfig, ValueMode}; - -use std::{future::Future, pin::Pin, sync::Arc}; - -use tracing::warn; - -pub struct Engine { - persy: Persy, -} - -impl KeyValueDatabaseEngine for Arc { - fn open(config: &Config) -> Result { - let mut cfg = persy::Config::new(); - cfg.change_cache_size((config.db_cache_capacity_mb * 1024.0 * 1024.0) as u64); - - let persy = OpenOptions::new() - .create(true) - .config(cfg) - .open(&format!("{}/db.persy", config.database_path))?; - Ok(Arc::new(Engine { persy })) - } - - fn open_tree(&self, name: &'static str) -> Result> { - // Create if it doesn't exist - if !self.persy.exists_index(name)? { - let mut tx = self.persy.begin()?; - tx.create_index::(name, ValueMode::Replace)?; - tx.prepare()?.commit()?; - } - - Ok(Arc::new(PersyTree { - persy: self.persy.clone(), - name: name.to_owned(), - watchers: Watchers::default(), - })) - } - - fn flush(&self) -> Result<()> { - Ok(()) - } -} - -pub struct PersyTree { - persy: Persy, - name: String, - watchers: Watchers, -} - -impl PersyTree { - fn begin(&self) -> Result { - Ok(self - .persy - .begin_with(TransactionConfig::new().set_background_sync(true))?) - } -} - -impl KvTree for PersyTree { - fn get(&self, key: &[u8]) -> Result>> { - let result = self - .persy - .get::(&self.name, &ByteVec::from(key))? - .next() - .map(|v| (*v).to_owned()); - Ok(result) - } - - fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> { - self.insert_batch(&mut Some((key.to_owned(), value.to_owned())).into_iter())?; - self.watchers.wake(key); - Ok(()) - } - - fn insert_batch<'a>(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { - let mut tx = self.begin()?; - for (key, value) in iter { - tx.put::( - &self.name, - ByteVec::from(key.clone()), - ByteVec::from(value), - )?; - } - tx.prepare()?.commit()?; - Ok(()) - } - - fn increment_batch<'a>(&self, iter: &mut dyn Iterator>) -> Result<()> { - let mut tx = self.begin()?; - for key in iter { - let old = tx - .get::(&self.name, &ByteVec::from(key.clone()))? - .next() - .map(|v| (*v).to_owned()); - let new = crate::utils::increment(old.as_deref()).unwrap(); - tx.put::(&self.name, ByteVec::from(key), ByteVec::from(new))?; - } - tx.prepare()?.commit()?; - Ok(()) - } - - fn remove(&self, key: &[u8]) -> Result<()> { - let mut tx = self.begin()?; - tx.remove::(&self.name, ByteVec::from(key), None)?; - tx.prepare()?.commit()?; - Ok(()) - } - - fn iter<'a>(&'a self) -> Box, Vec)> + 'a> { - let iter = self.persy.range::(&self.name, ..); - match iter { - Ok(iter) => Box::new(iter.filter_map(|(k, v)| { - v.into_iter() - .map(|val| ((*k).to_owned(), (*val).to_owned())) - .next() - })), - Err(e) => { - warn!("error iterating {:?}", e); - Box::new(std::iter::empty()) - } - } - } - - fn iter_from<'a>( - &'a self, - from: &[u8], - backwards: bool, - ) -> Box, Vec)> + 'a> { - let range = if backwards { - self.persy - .range::(&self.name, ..=ByteVec::from(from)) - } else { - self.persy - .range::(&self.name, ByteVec::from(from)..) - }; - match range { - Ok(iter) => { - let map = iter.filter_map(|(k, v)| { - v.into_iter() - .map(|val| ((*k).to_owned(), (*val).to_owned())) - .next() - }); - if backwards { - Box::new(map.rev()) - } else { - Box::new(map) - } - } - Err(e) => { - warn!("error iterating with prefix {:?}", e); - Box::new(std::iter::empty()) - } - } - } - - fn increment(&self, key: &[u8]) -> Result> { - self.increment_batch(&mut Some(key.to_owned()).into_iter())?; - Ok(self.get(key)?.unwrap()) - } - - fn scan_prefix<'a>( - &'a self, - prefix: Vec, - ) -> Box, Vec)> + 'a> { - let range_prefix = ByteVec::from(prefix.clone()); - let range = self - .persy - .range::(&self.name, range_prefix..); - - match range { - Ok(iter) => { - let owned_prefix = prefix.clone(); - Box::new( - iter.take_while(move |(k, _)| (*k).starts_with(&owned_prefix)) - .filter_map(|(k, v)| { - v.into_iter() - .map(|val| ((*k).to_owned(), (*val).to_owned())) - .next() - }), - ) - } - Err(e) => { - warn!("error scanning prefix {:?}", e); - Box::new(std::iter::empty()) - } - } - } - - fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin + Send + 'a>> { - self.watchers.watch(prefix) - } -} diff --git a/src/database/abstraction/sled.rs b/src/database/abstraction/sled.rs deleted file mode 100644 index 87defc57..00000000 --- a/src/database/abstraction/sled.rs +++ /dev/null @@ -1,127 +0,0 @@ -use super::super::Config; -use crate::{utils, Result}; -use std::{future::Future, pin::Pin, sync::Arc}; -use tracing::warn; - -use super::{DatabaseEngine, Tree}; - -pub struct Engine(sled::Db); - -pub struct SledEngineTree(sled::Tree); - -impl DatabaseEngine for Engine { - fn open(config: &Config) -> Result> { - Ok(Arc::new(Engine( - sled::Config::default() - .path(&config.database_path) - .cache_capacity((config.db_cache_capacity_mb * 1024.0 * 1024.0) as u64) - .use_compression(true) - .open()?, - ))) - } - - fn open_tree(self: &Arc, name: &'static str) -> Result> { - Ok(Arc::new(SledEngineTree(self.0.open_tree(name)?))) - } - - fn flush(self: &Arc) -> Result<()> { - Ok(()) // noop - } -} - -impl Tree for SledEngineTree { - fn get(&self, key: &[u8]) -> Result>> { - Ok(self.0.get(key)?.map(|v| v.to_vec())) - } - - fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> { - self.0.insert(key, value)?; - Ok(()) - } - - fn insert_batch<'a>(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { - for (key, value) in iter { - self.0.insert(key, value)?; - } - - Ok(()) - } - - fn remove(&self, key: &[u8]) -> Result<()> { - self.0.remove(key)?; - Ok(()) - } - - fn iter<'a>(&'a self) -> Box, Vec)> + 'a> { - Box::new( - self.0 - .iter() - .filter_map(|r| { - if let Err(e) = &r { - warn!("Error: {}", e); - } - r.ok() - }) - .map(|(k, v)| (k.to_vec().into(), v.to_vec().into())), - ) - } - - fn iter_from( - &self, - from: &[u8], - backwards: bool, - ) -> Box, Vec)>> { - let iter = if backwards { - self.0.range(..=from) - } else { - self.0.range(from..) - }; - - let iter = iter - .filter_map(|r| { - if let Err(e) = &r { - warn!("Error: {}", e); - } - r.ok() - }) - .map(|(k, v)| (k.to_vec().into(), v.to_vec().into())); - - if backwards { - Box::new(iter.rev()) - } else { - Box::new(iter) - } - } - - fn increment(&self, key: &[u8]) -> Result> { - Ok(self - .0 - .update_and_fetch(key, utils::increment) - .map(|o| o.expect("increment always sets a value").to_vec())?) - } - - fn scan_prefix<'a>( - &'a self, - prefix: Vec, - ) -> Box, Vec)> + 'a> { - let iter = self - .0 - .scan_prefix(prefix) - .filter_map(|r| { - if let Err(e) = &r { - warn!("Error: {}", e); - } - r.ok() - }) - .map(|(k, v)| (k.to_vec().into(), v.to_vec().into())); - - Box::new(iter) - } - - fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin + Send + 'a>> { - let prefix = prefix.to_vec(); - Box::pin(async move { - self.0.watch_prefix(prefix).await; - }) - } -} diff --git a/src/database/mod.rs b/src/database/mod.rs index f4cf6a4c..eee963d9 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -172,16 +172,11 @@ impl KeyValueDatabase { fn check_db_setup(config: &Config) -> Result<()> { let path = Path::new(&config.database_path); - let sled_exists = path.join("db").exists(); let sqlite_exists = path.join("conduit.db").exists(); let rocksdb_exists = path.join("IDENTITY").exists(); let mut count = 0; - if sled_exists { - count += 1; - } - if sqlite_exists { count += 1; } @@ -195,12 +190,6 @@ impl KeyValueDatabase { return Ok(()); } - if sled_exists && config.database_backend != "sled" { - return Err(Error::bad_config( - "Found sled at database_path, but is not specified in config.", - )); - } - if sqlite_exists && config.database_backend != "sqlite" { return Err(Error::bad_config( "Found sqlite at database_path, but is not specified in config.", @@ -238,12 +227,6 @@ impl KeyValueDatabase { #[cfg(feature = "rocksdb")] Arc::new(Arc::::open(&config)?) } - "persy" => { - #[cfg(not(feature = "persy"))] - return Err(Error::BadConfig("Database backend not found.")); - #[cfg(feature = "persy")] - Arc::new(Arc::::open(&config)?) - } _ => { return Err(Error::BadConfig("Database backend not found.")); } diff --git a/src/utils/error.rs b/src/utils/error.rs index 448f0665..7aa43b9d 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -11,33 +11,18 @@ use ruma::{ use thiserror::Error; use tracing::{error, info}; -#[cfg(feature = "persy")] -use persy::PersyError; - use crate::RumaResponse; pub type Result = std::result::Result; #[derive(Error, Debug)] pub enum Error { - #[cfg(feature = "sled")] - #[error("There was a problem with the connection to the sled database.")] - SledError { - #[from] - source: sled::Error, - }, #[cfg(feature = "sqlite")] #[error("There was a problem with the connection to the sqlite database: {source}")] SqliteError { #[from] source: rusqlite::Error, }, - #[cfg(feature = "persy")] - #[error("There was a problem with the connection to the persy database.")] - PersyError { source: PersyError }, - #[cfg(feature = "heed")] - #[error("There was a problem with the connection to the heed database: {error}")] - HeedError { error: String }, #[cfg(feature = "rocksdb")] #[error("There was a problem with the connection to the rocksdb database: {source}")] RocksDbError { @@ -157,14 +142,8 @@ impl Error { let db_error = String::from("Database or I/O error occurred."); match self { - #[cfg(feature = "sled")] - Self::SledError { .. } => db_error, #[cfg(feature = "sqlite")] Self::SqliteError { .. } => db_error, - #[cfg(feature = "persy")] - Self::PersyError { .. } => db_error, - #[cfg(feature = "heed")] - Self::HeedError => db_error, #[cfg(feature = "rocksdb")] Self::RocksDbError { .. } => db_error, Self::IoError { .. } => db_error, @@ -175,15 +154,6 @@ impl Error { } } -#[cfg(feature = "persy")] -impl> From> for Error { - fn from(err: persy::PE) -> Self { - Error::PersyError { - source: err.error().into(), - } - } -} - impl From for Error { fn from(i: Infallible) -> Self { match i {} From f27941d5108acda250921c6a58499a46568fd030 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 28 Apr 2024 20:01:38 -0700 Subject: [PATCH 006/617] remove half-baked presence implementation But I'm leaving behind the database state for now in case we want it back later, so we won't need to do a migration or whatever. --- src/api/client_server/mod.rs | 2 - src/api/client_server/presence.rs | 90 ----------- src/api/client_server/profile.rs | 44 +---- src/api/client_server/sync.rs | 47 +----- src/database/key_value/rooms/edus/mod.rs | 1 - src/database/key_value/rooms/edus/presence.rs | 152 ------------------ src/database/mod.rs | 9 +- src/main.rs | 2 - src/service/mod.rs | 1 - src/service/rooms/edus/mod.rs | 4 +- src/service/rooms/edus/presence/data.rs | 38 ----- src/service/rooms/edus/presence/mod.rs | 125 -------------- 12 files changed, 8 insertions(+), 507 deletions(-) delete mode 100644 src/api/client_server/presence.rs delete mode 100644 src/database/key_value/rooms/edus/presence.rs delete mode 100644 src/service/rooms/edus/presence/data.rs delete mode 100644 src/service/rooms/edus/presence/mod.rs diff --git a/src/api/client_server/mod.rs b/src/api/client_server/mod.rs index 54c99aa0..c50db8d5 100644 --- a/src/api/client_server/mod.rs +++ b/src/api/client_server/mod.rs @@ -11,7 +11,6 @@ mod keys; mod media; mod membership; mod message; -mod presence; mod profile; mod push; mod read_marker; @@ -46,7 +45,6 @@ pub use keys::*; pub use media::*; pub use membership::*; pub use message::*; -pub use presence::*; pub use profile::*; pub use push::*; pub use read_marker::*; diff --git a/src/api/client_server/presence.rs b/src/api/client_server/presence.rs deleted file mode 100644 index e5cd1b8e..00000000 --- a/src/api/client_server/presence.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{services, utils, Error, Result, Ruma}; -use ruma::api::client::{ - error::ErrorKind, - presence::{get_presence, set_presence}, -}; -use std::time::Duration; - -/// # `PUT /_matrix/client/r0/presence/{userId}/status` -/// -/// Sets the presence state of the sender user. -pub async fn set_presence_route( - body: Ruma, -) -> Result { - let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - - for room_id in services().rooms.state_cache.rooms_joined(sender_user) { - let room_id = room_id?; - - services().rooms.edus.presence.update_presence( - sender_user, - &room_id, - ruma::events::presence::PresenceEvent { - content: ruma::events::presence::PresenceEventContent { - avatar_url: services().users.avatar_url(sender_user)?, - currently_active: None, - displayname: services().users.displayname(sender_user)?, - last_active_ago: Some( - utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"), - ), - presence: body.presence.clone(), - status_msg: body.status_msg.clone(), - }, - sender: sender_user.clone(), - }, - )?; - } - - Ok(set_presence::v3::Response {}) -} - -/// # `GET /_matrix/client/r0/presence/{userId}/status` -/// -/// Gets the presence state of the given user. -/// -/// - Only works if you share a room with the user -pub async fn get_presence_route( - body: Ruma, -) -> Result { - let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - - let mut presence_event = None; - - for room_id in services() - .rooms - .user - .get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])? - { - let room_id = room_id?; - - if let Some(presence) = services() - .rooms - .edus - .presence - .get_last_presence_event(sender_user, &room_id)? - { - presence_event = Some(presence); - break; - } - } - - if let Some(presence) = presence_event { - Ok(get_presence::v3::Response { - // TODO: Should ruma just use the presenceeventcontent type here? - status_msg: presence.content.status_msg, - currently_active: presence.content.currently_active, - last_active_ago: presence - .content - .last_active_ago - .map(|millis| Duration::from_millis(millis.into())), - presence: presence.content.presence, - }) - } else { - Err(Error::BadRequest( - ErrorKind::NotFound, - "Presence state for this user was not found", - )) - } -} diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index cf1db2d7..f1500439 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -1,4 +1,4 @@ -use crate::{service::pdu::PduBuilder, services, utils, Error, Result, Ruma}; +use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma}; use ruma::{ api::{ client::{ @@ -89,27 +89,6 @@ pub async fn set_displayname_route( .timeline .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) .await; - - // Presence update - services().rooms.edus.presence.update_presence( - sender_user, - &room_id, - ruma::events::presence::PresenceEvent { - content: ruma::events::presence::PresenceEventContent { - avatar_url: services().users.avatar_url(sender_user)?, - currently_active: None, - displayname: services().users.displayname(sender_user)?, - last_active_ago: Some( - utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"), - ), - presence: ruma::presence::PresenceState::Online, - status_msg: None, - }, - sender: sender_user.clone(), - }, - )?; } Ok(set_display_name::v3::Response {}) @@ -224,27 +203,6 @@ pub async fn set_avatar_url_route( .timeline .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) .await; - - // Presence update - services().rooms.edus.presence.update_presence( - sender_user, - &room_id, - ruma::events::presence::PresenceEvent { - content: ruma::events::presence::PresenceEventContent { - avatar_url: services().users.avatar_url(sender_user)?, - currently_active: None, - displayname: services().users.displayname(sender_user)?, - last_active_ago: Some( - utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"), - ), - presence: ruma::presence::PresenceState::Online, - status_msg: None, - }, - sender: sender_user.clone(), - }, - )?; } Ok(set_avatar_url::v3::Response {}) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index e0c6e0b9..e561c320 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -21,7 +21,6 @@ use ruma::{ room::member::{MembershipState, RoomMemberEventContent}, StateEventType, TimelineEventType, }, - serde::Raw, uint, DeviceId, EventId, JsOption, OwnedDeviceId, OwnedUserId, RoomId, UInt, UserId, }; use std::{ @@ -174,9 +173,6 @@ async fn sync_helper( body: sync_events::v3::Request, // bool = caching allowed ) -> Result<(sync_events::v3::Response, bool), Error> { - // TODO: match body.set_presence { - services().rooms.edus.presence.ping_presence(&sender_user)?; - // Setup watchers, so if there's no response, we can wait for them let watcher = services().globals.watch(&sender_user, &sender_device); @@ -211,7 +207,6 @@ async fn sync_helper( .unwrap_or(0); let sincecount = PduCount::Normal(since); - let mut presence_updates = HashMap::new(); let mut left_encrypted_users = HashSet::new(); // Users that have left any encrypted rooms the sender was in let mut device_list_updates = HashSet::new(); let mut device_list_left = HashSet::new(); @@ -250,41 +245,6 @@ async fn sync_helper( if !joined_room.is_empty() { joined_rooms.insert(room_id.clone(), joined_room); } - - // Take presence updates from this room - for (user_id, presence) in services() - .rooms - .edus - .presence - .presence_since(&room_id, since)? - { - match presence_updates.entry(user_id) { - Entry::Vacant(v) => { - v.insert(presence); - } - Entry::Occupied(mut o) => { - let p = o.get_mut(); - - // Update existing presence event with more info - p.content.presence = presence.content.presence; - if let Some(status_msg) = presence.content.status_msg { - p.content.status_msg = Some(status_msg); - } - if let Some(last_active_ago) = presence.content.last_active_ago { - p.content.last_active_ago = Some(last_active_ago); - } - if let Some(displayname) = presence.content.displayname { - p.content.displayname = Some(displayname); - } - if let Some(avatar_url) = presence.content.avatar_url { - p.content.avatar_url = Some(avatar_url); - } - if let Some(currently_active) = presence.content.currently_active { - p.content.currently_active = Some(currently_active); - } - } - } - } } } @@ -540,12 +500,7 @@ async fn sync_helper( invite: invited_rooms, knock: BTreeMap::new(), // TODO }, - presence: Presence { - events: presence_updates - .into_values() - .map(|v| Raw::new(&v).expect("PresenceEvent always serializes successfully")) - .collect(), - }, + presence: Presence::default(), account_data: GlobalAccountData { events: services() .account_data diff --git a/src/database/key_value/rooms/edus/mod.rs b/src/database/key_value/rooms/edus/mod.rs index 7abf946f..90f2b24a 100644 --- a/src/database/key_value/rooms/edus/mod.rs +++ b/src/database/key_value/rooms/edus/mod.rs @@ -1,4 +1,3 @@ -mod presence; mod read_receipt; use crate::{database::KeyValueDatabase, service}; diff --git a/src/database/key_value/rooms/edus/presence.rs b/src/database/key_value/rooms/edus/presence.rs deleted file mode 100644 index 904b1c44..00000000 --- a/src/database/key_value/rooms/edus/presence.rs +++ /dev/null @@ -1,152 +0,0 @@ -use std::collections::HashMap; - -use ruma::{ - events::presence::PresenceEvent, presence::PresenceState, OwnedUserId, RoomId, UInt, UserId, -}; - -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; - -impl service::rooms::edus::presence::Data for KeyValueDatabase { - fn update_presence( - &self, - user_id: &UserId, - room_id: &RoomId, - presence: PresenceEvent, - ) -> Result<()> { - // TODO: Remove old entry? Or maybe just wipe completely from time to time? - - let count = services().globals.next_count()?.to_be_bytes(); - - let mut presence_id = room_id.as_bytes().to_vec(); - presence_id.push(0xff); - presence_id.extend_from_slice(&count); - presence_id.push(0xff); - presence_id.extend_from_slice(presence.sender.as_bytes()); - - self.presenceid_presence.insert( - &presence_id, - &serde_json::to_vec(&presence).expect("PresenceEvent can be serialized"), - )?; - - self.userid_lastpresenceupdate.insert( - user_id.as_bytes(), - &utils::millis_since_unix_epoch().to_be_bytes(), - )?; - - Ok(()) - } - - fn ping_presence(&self, user_id: &UserId) -> Result<()> { - self.userid_lastpresenceupdate.insert( - user_id.as_bytes(), - &utils::millis_since_unix_epoch().to_be_bytes(), - )?; - - Ok(()) - } - - fn last_presence_update(&self, user_id: &UserId) -> Result> { - self.userid_lastpresenceupdate - .get(user_id.as_bytes())? - .map(|bytes| { - utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Invalid timestamp in userid_lastpresenceupdate.") - }) - }) - .transpose() - } - - fn get_presence_event( - &self, - room_id: &RoomId, - user_id: &UserId, - count: u64, - ) -> Result> { - let mut presence_id = room_id.as_bytes().to_vec(); - presence_id.push(0xff); - presence_id.extend_from_slice(&count.to_be_bytes()); - presence_id.push(0xff); - presence_id.extend_from_slice(user_id.as_bytes()); - - self.presenceid_presence - .get(&presence_id)? - .map(|value| parse_presence_event(&value)) - .transpose() - } - - fn presence_since( - &self, - room_id: &RoomId, - since: u64, - ) -> Result> { - let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); - - let mut first_possible_edu = prefix.clone(); - first_possible_edu.extend_from_slice(&(since + 1).to_be_bytes()); // +1 so we don't send the event at since - let mut hashmap = HashMap::new(); - - for (key, value) in self - .presenceid_presence - .iter_from(&first_possible_edu, false) - .take_while(|(key, _)| key.starts_with(&prefix)) - { - let user_id = UserId::parse( - utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) - .next() - .expect("rsplit always returns an element"), - ) - .map_err(|_| Error::bad_database("Invalid UserId bytes in presenceid_presence."))?, - ) - .map_err(|_| Error::bad_database("Invalid UserId in presenceid_presence."))?; - - let presence = parse_presence_event(&value)?; - - hashmap.insert(user_id, presence); - } - - Ok(hashmap) - } - - /* - fn presence_maintain(&self, db: Arc>) { - // TODO @M0dEx: move this to a timed tasks module - tokio::spawn(async move { - loop { - select! { - Some(user_id) = self.presence_timers.next() { - // TODO @M0dEx: would it be better to acquire the lock outside the loop? - let guard = db.read().await; - - // TODO @M0dEx: add self.presence_timers - // TODO @M0dEx: maintain presence - } - } - } - }); - } - */ -} - -fn parse_presence_event(bytes: &[u8]) -> Result { - let mut presence: PresenceEvent = serde_json::from_slice(bytes) - .map_err(|_| Error::bad_database("Invalid presence event in db."))?; - - let current_timestamp: UInt = utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"); - - if presence.content.presence == PresenceState::Online { - // Don't set last_active_ago when the user is online - presence.content.last_active_ago = None; - } else { - // Convert from timestamp to duration - presence.content.last_active_ago = presence - .content - .last_active_ago - .map(|timestamp| current_timestamp - timestamp); - } - - Ok(presence) -} diff --git a/src/database/mod.rs b/src/database/mod.rs index eee963d9..4a30465d 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -67,7 +67,11 @@ pub struct KeyValueDatabase { pub(super) readreceiptid_readreceipt: Arc, // ReadReceiptId = RoomId + Count + UserId pub(super) roomuserid_privateread: Arc, // RoomUserId = Room + User, PrivateRead = Count pub(super) roomuserid_lastprivatereadupdate: Arc, // LastPrivateReadUpdate = Count - pub(super) presenceid_presence: Arc, // PresenceId = RoomId + Count + UserId + // This exists in the database already but is currently unused + #[allow(dead_code)] + pub(super) presenceid_presence: Arc, // PresenceId = RoomId + Count + UserId + // This exists in the database already but is currently unused + #[allow(dead_code)] pub(super) userid_lastpresenceupdate: Arc, // LastPresenceUpdate = Count //pub rooms: rooms::Rooms, @@ -929,9 +933,6 @@ impl KeyValueDatabase { ); } - // This data is probably outdated - db.presenceid_presence.clear()?; - services().admin.start_handler(); // Set emergency access for the conduit user diff --git a/src/main.rs b/src/main.rs index 7beeb8ba..938f0d73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -286,8 +286,6 @@ fn routes(config: &Config) -> Router { .ruma_route(client_server::set_avatar_url_route) .ruma_route(client_server::get_avatar_url_route) .ruma_route(client_server::get_profile_route) - .ruma_route(client_server::set_presence_route) - .ruma_route(client_server::get_presence_route) .ruma_route(client_server::upload_keys_route) .ruma_route(client_server::get_keys_route) .ruma_route(client_server::claim_keys_route) diff --git a/src/service/mod.rs b/src/service/mod.rs index 4c11bc18..c96a9041 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -64,7 +64,6 @@ impl Services { auth_chain: rooms::auth_chain::Service { db }, directory: rooms::directory::Service { db }, edus: rooms::edus::Service { - presence: rooms::edus::presence::Service { db }, read_receipt: rooms::edus::read_receipt::Service { db }, typing: rooms::edus::typing::Service { typing: RwLock::new(BTreeMap::new()), diff --git a/src/service/rooms/edus/mod.rs b/src/service/rooms/edus/mod.rs index a6bc3d5b..869865f7 100644 --- a/src/service/rooms/edus/mod.rs +++ b/src/service/rooms/edus/mod.rs @@ -1,11 +1,9 @@ -pub mod presence; pub mod read_receipt; pub mod typing; -pub trait Data: presence::Data + read_receipt::Data + 'static {} +pub trait Data: read_receipt::Data + 'static {} pub struct Service { - pub presence: presence::Service, pub read_receipt: read_receipt::Service, pub typing: typing::Service, } diff --git a/src/service/rooms/edus/presence/data.rs b/src/service/rooms/edus/presence/data.rs deleted file mode 100644 index 53329e08..00000000 --- a/src/service/rooms/edus/presence/data.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::collections::HashMap; - -use crate::Result; -use ruma::{events::presence::PresenceEvent, OwnedUserId, RoomId, UserId}; - -pub trait Data: Send + Sync { - /// Adds a presence event which will be saved until a new event replaces it. - /// - /// Note: This method takes a RoomId because presence updates are always bound to rooms to - /// make sure users outside these rooms can't see them. - fn update_presence( - &self, - user_id: &UserId, - room_id: &RoomId, - presence: PresenceEvent, - ) -> Result<()>; - - /// Resets the presence timeout, so the user will stay in their current presence state. - fn ping_presence(&self, user_id: &UserId) -> Result<()>; - - /// Returns the timestamp of the last presence update of this user in millis since the unix epoch. - fn last_presence_update(&self, user_id: &UserId) -> Result>; - - /// Returns the presence event with correct last_active_ago. - fn get_presence_event( - &self, - room_id: &RoomId, - user_id: &UserId, - count: u64, - ) -> Result>; - - /// Returns the most recent presence updates that happened after the event with id `since`. - fn presence_since( - &self, - room_id: &RoomId, - since: u64, - ) -> Result>; -} diff --git a/src/service/rooms/edus/presence/mod.rs b/src/service/rooms/edus/presence/mod.rs deleted file mode 100644 index 4b929d28..00000000 --- a/src/service/rooms/edus/presence/mod.rs +++ /dev/null @@ -1,125 +0,0 @@ -mod data; -use std::collections::HashMap; - -pub use data::Data; -use ruma::{events::presence::PresenceEvent, OwnedUserId, RoomId, UserId}; - -use crate::Result; - -pub struct Service { - pub db: &'static dyn Data, -} - -impl Service { - /// Adds a presence event which will be saved until a new event replaces it. - /// - /// Note: This method takes a RoomId because presence updates are always bound to rooms to - /// make sure users outside these rooms can't see them. - pub fn update_presence( - &self, - _user_id: &UserId, - _room_id: &RoomId, - _presence: PresenceEvent, - ) -> Result<()> { - // self.db.update_presence(user_id, room_id, presence) - Ok(()) - } - - /// Resets the presence timeout, so the user will stay in their current presence state. - pub fn ping_presence(&self, _user_id: &UserId) -> Result<()> { - // self.db.ping_presence(user_id) - Ok(()) - } - - pub fn get_last_presence_event( - &self, - _user_id: &UserId, - _room_id: &RoomId, - ) -> Result> { - // let last_update = match self.db.last_presence_update(user_id)? { - // Some(last) => last, - // None => return Ok(None), - // }; - - // self.db.get_presence_event(room_id, user_id, last_update) - Ok(None) - } - - /* TODO - /// Sets all users to offline who have been quiet for too long. - fn _presence_maintain( - &self, - rooms: &super::Rooms, - globals: &super::super::globals::Globals, - ) -> Result<()> { - let current_timestamp = utils::millis_since_unix_epoch(); - - for (user_id_bytes, last_timestamp) in self - .userid_lastpresenceupdate - .iter() - .filter_map(|(k, bytes)| { - Some(( - k, - utils::u64_from_bytes(&bytes) - .map_err(|_| { - Error::bad_database("Invalid timestamp in userid_lastpresenceupdate.") - }) - .ok()?, - )) - }) - .take_while(|(_, timestamp)| current_timestamp.saturating_sub(*timestamp) > 5 * 60_000) - // 5 Minutes - { - // Send new presence events to set the user offline - let count = globals.next_count()?.to_be_bytes(); - let user_id: Box<_> = utils::string_from_bytes(&user_id_bytes) - .map_err(|_| { - Error::bad_database("Invalid UserId bytes in userid_lastpresenceupdate.") - })? - .try_into() - .map_err(|_| Error::bad_database("Invalid UserId in userid_lastpresenceupdate."))?; - for room_id in rooms.rooms_joined(&user_id).filter_map(|r| r.ok()) { - let mut presence_id = room_id.as_bytes().to_vec(); - presence_id.push(0xff); - presence_id.extend_from_slice(&count); - presence_id.push(0xff); - presence_id.extend_from_slice(&user_id_bytes); - - self.presenceid_presence.insert( - &presence_id, - &serde_json::to_vec(&PresenceEvent { - content: PresenceEventContent { - avatar_url: None, - currently_active: None, - displayname: None, - last_active_ago: Some( - last_timestamp.try_into().expect("time is valid"), - ), - presence: PresenceState::Offline, - status_msg: None, - }, - sender: user_id.to_owned(), - }) - .expect("PresenceEvent can be serialized"), - )?; - } - - self.userid_lastpresenceupdate.insert( - user_id.as_bytes(), - &utils::millis_since_unix_epoch().to_be_bytes(), - )?; - } - - Ok(()) - }*/ - - /// Returns the most recent presence updates that happened after the event with id `since`. - pub fn presence_since( - &self, - _room_id: &RoomId, - _since: u64, - ) -> Result> { - // self.db.presence_since(room_id, since) - Ok(HashMap::new()) - } -} From 0f7b6d482cd219d3674c56b79ceef160a53dca5c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 20:22:43 -0700 Subject: [PATCH 007/617] remove `conduit_bin` feature --- Cargo.toml | 6 ++---- src/api/ruma_wrapper/mod.rs | 1 - src/utils/error.rs | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0862b5c3..44d2cb11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ workspace = true [dependencies] # Web framework -axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"], optional = true } +axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"] } axum-server = { version = "0.5.1", features = ["tls-rustls"] } tower = { version = "0.4.13", features = ["util"] } tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } @@ -121,18 +121,16 @@ features = [ nix = { version = "0.28", features = ["resource"] } [features] -default = ["conduit_bin", "backend_sqlite", "backend_rocksdb", "systemd"] +default = ["backend_sqlite", "backend_rocksdb", "systemd"] backend_sqlite = ["sqlite"] backend_rocksdb = ["rocksdb"] jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"] sqlite = ["rusqlite", "parking_lot", "tokio/signal"] -conduit_bin = ["axum"] systemd = ["sd-notify"] [[bin]] name = "conduit" path = "src/main.rs" -required-features = ["conduit_bin"] [lib] name = "conduit" diff --git a/src/api/ruma_wrapper/mod.rs b/src/api/ruma_wrapper/mod.rs index 862da1dc..28659dfe 100644 --- a/src/api/ruma_wrapper/mod.rs +++ b/src/api/ruma_wrapper/mod.rs @@ -5,7 +5,6 @@ use ruma::{ }; use std::ops::Deref; -#[cfg(feature = "conduit_bin")] mod axum; /// Extractor for Ruma request structs diff --git a/src/utils/error.rs b/src/utils/error.rs index 7aa43b9d..6fa7f7ec 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -64,10 +64,8 @@ pub enum Error { BadRequest(ErrorKind, &'static str), #[error("{0}")] Conflict(&'static str), // This is only needed for when a room alias already exists - #[cfg(feature = "conduit_bin")] #[error("{0}")] ExtensionError(#[from] axum::extract::rejection::ExtensionRejection), - #[cfg(feature = "conduit_bin")] #[error("{0}")] PathError(#[from] axum::extract::rejection::PathRejection), #[error("{0}")] @@ -160,7 +158,6 @@ impl From for Error { } } -#[cfg(feature = "conduit_bin")] impl axum::response::IntoResponse for Error { fn into_response(self) -> axum::response::Response { self.to_response().into_response() From c097e79e5237059449a9d9383af593b6e39db980 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 21:08:22 -0700 Subject: [PATCH 008/617] remove `src/lib.rs` --- Cargo.toml | 8 -------- src/lib.rs | 26 -------------------------- src/main.rs | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 37 deletions(-) delete mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 44d2cb11..691b140e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,14 +128,6 @@ jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"] sqlite = ["rusqlite", "parking_lot", "tokio/signal"] systemd = ["sd-notify"] -[[bin]] -name = "conduit" -path = "src/main.rs" - -[lib] -name = "conduit" -path = "src/lib.rs" - [package.metadata.deb] name = "matrix-conduit" maintainer = "Paul van Tilburg " diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 5a89f805..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub mod api; -pub mod clap; -mod config; -mod database; -mod service; -mod utils; - -// Not async due to services() being used in many closures, and async closures are not stable as of writing -// This is the case for every other occurence of sync Mutex/RwLock, except for database related ones, where -// the current maintainer (Timo) has asked to not modify those -use std::sync::RwLock; - -pub use api::ruma_wrapper::{Ruma, RumaResponse}; -pub use config::Config; -pub use database::KeyValueDatabase; -pub use service::{pdu::PduEvent, Services}; -pub use utils::error::{Error, Result}; - -pub static SERVICES: RwLock> = RwLock::new(None); - -pub fn services() -> &'static Services { - SERVICES - .read() - .unwrap() - .expect("SERVICES should be initialized when this is called") -} diff --git a/src/main.rs b/src/main.rs index 938f0d73..902b76c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,10 @@ -use std::{future::Future, io, net::SocketAddr, sync::atomic, time::Duration}; +use std::{ + future::Future, + io, + net::SocketAddr, + sync::{atomic, RwLock}, + time::Duration, +}; use axum::{ extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, @@ -7,7 +13,6 @@ use axum::{ Router, }; use axum_server::{bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle}; -use conduit::api::{client_server, server_server}; use figment::{ providers::{Env, Format, Toml}, Figment, @@ -33,7 +38,19 @@ use tower_http::{ use tracing::{debug, error, info, warn}; use tracing_subscriber::{prelude::*, EnvFilter}; -pub use conduit::*; // Re-export everything from the library crate +pub mod api; +pub mod clap; +mod config; +mod database; +mod service; +mod utils; + +pub use api::ruma_wrapper::{Ruma, RumaResponse}; +use api::{client_server, server_server}; +pub use config::Config; +pub use database::KeyValueDatabase; +pub use service::{pdu::PduEvent, Services}; +pub use utils::error::{Error, Result}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] use tikv_jemallocator::Jemalloc; @@ -42,6 +59,18 @@ use tikv_jemallocator::Jemalloc; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; +pub static SERVICES: RwLock> = RwLock::new(None); + +// Not async due to services() being used in many closures, and async closures are not stable as of writing +// This is the case for every other occurence of sync Mutex/RwLock, except for database related ones, where +// the current maintainer (Timo) has asked to not modify those +pub fn services() -> &'static Services { + SERVICES + .read() + .unwrap() + .expect("SERVICES should be initialized when this is called") +} + #[tokio::main] async fn main() { clap::parse(); From c496878afa1d3a2ec95a141dba16a3bafe5c75fc Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 21:48:29 -0700 Subject: [PATCH 009/617] remove unused dependencies --- Cargo.lock | 20 -------------------- Cargo.toml | 5 +---- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3429734..77c11364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -392,7 +392,6 @@ dependencies = [ "hyper", "image", "jsonwebtoken", - "lazy_static", "lru-cache", "nix", "num_cpus", @@ -415,8 +414,6 @@ dependencies = [ "sha-1", "thiserror", "thread_local", - "threadpool", - "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", "tower", @@ -1579,12 +1576,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "pear" version = "0.2.8" @@ -2608,17 +2599,6 @@ dependencies = [ "threadpool", ] -[[package]] -name = "tikv-jemalloc-ctl" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619bfed27d807b54f7f776b9430d4f8060e66ee138a28632ca898584d462c31c" -dependencies = [ - "libc", - "paste", - "tikv-jemalloc-sys", -] - [[package]] name = "tikv-jemalloc-sys" version = "0.5.4+5.3.0-patched" diff --git a/Cargo.toml b/Cargo.toml index 691b140e..3fb0265e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,6 @@ rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } parking_lot = { version = "0.12.1", optional = true } # crossbeam = { version = "0.8.2", optional = true } num_cpus = "1.15.0" -threadpool = "1.8.1" # Used for ruma wrapper serde_html_form = "0.2.0" @@ -100,9 +99,7 @@ futures-util = { version = "0.3.28", default-features = false } # Used for reading the configuration from conduit.toml & environment variables figment = { version = "0.10.8", features = ["env", "toml"] } -tikv-jemalloc-ctl = { version = "0.5.0", features = ["use_std"], optional = true } tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -lazy_static = "1.4.0" async-trait = "0.1.68" sd-notify = { version = "0.4.1", optional = true } @@ -124,7 +121,7 @@ nix = { version = "0.28", features = ["resource"] } default = ["backend_sqlite", "backend_rocksdb", "systemd"] backend_sqlite = ["sqlite"] backend_rocksdb = ["rocksdb"] -jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"] +jemalloc = ["tikv-jemallocator"] sqlite = ["rusqlite", "parking_lot", "tokio/signal"] systemd = ["sd-notify"] From d41f0fbf72dae6562358173f425d23bb0e174ca2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 19:48:28 -0700 Subject: [PATCH 010/617] remove documentation, metadata, etc This drastically reduces the maintenance burden. You may notice the `CODE_OF_CONDUCT.md` is deleted here. This is because I don't feel like updating the relevant parts and I don't think this will ever actually have a community build around it. If that changes for some reason, I'm not opposed to adding a code of conduct again. --- .dockerignore | 28 - .editorconfig | 15 - .gitea/PULL_REQUEST_TEMPLATE.md | 1 - .github/ISSUE_TEMPLATE/Issue.md | 11 - .gitignore | 76 -- .gitlab-ci.yml | 110 +-- .gitlab/CODEOWNERS | 5 - .gitlab/issue_templates/Bug Report.md | 19 - .gitlab/issue_templates/Feature Request.md | 17 - .gitlab/merge_request_templates/MR.md | 8 - .gitlab/route-map.yml | 3 - .gitlab/setup-buildx-remote-builders.sh | 37 - .vscode/extensions.json | 11 - .vscode/launch.json | 35 - CODE_OF_CONDUCT.md | 134 --- Cargo.toml | 77 -- README.md | 77 -- bin/complement | 37 - bin/nix-build-and-cache | 6 +- book.toml | 18 - complement/Dockerfile | 45 - complement/README.md | 11 - complement/caddy.json | 72 -- conduit-example.toml | 67 -- debian/README.md | 37 - debian/config | 17 - debian/matrix-conduit.service | 47 - debian/postinst | 104 --- debian/postrm | 27 - debian/templates | 21 - docker/ci-binaries-packaging.Dockerfile | 84 -- docker/healthcheck.sh | 19 - docs/SUMMARY.md | 12 - docs/appservices.md | 61 -- docs/configuration.md | 110 --- docs/deploying.md | 3 - docs/deploying/debian.md | 1 - docs/deploying/docker-compose.for-traefik.yml | 69 -- docs/deploying/docker-compose.override.yml | 45 - .../deploying/docker-compose.with-traefik.yml | 96 -- docs/deploying/docker-compose.yml | 53 -- docs/deploying/docker.md | 216 ----- docs/deploying/generic.md | 292 ------ docs/deploying/nixos.md | 18 - docs/introduction.md | 13 - docs/turn.md | 25 - engage.toml | 10 - flake.nix | 45 - src/main.rs | 2 +- tests/sytest/are-we-synapse-yet.list | 866 ------------------ tests/sytest/are-we-synapse-yet.py | 266 ------ tests/sytest/show-expected-fail-tests.sh | 105 --- tests/sytest/sytest-blacklist | 7 - tests/sytest/sytest-whitelist | 516 ----------- tests/test-config.toml | 15 - 55 files changed, 5 insertions(+), 4117 deletions(-) delete mode 100644 .dockerignore delete mode 100644 .editorconfig delete mode 100644 .gitea/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/ISSUE_TEMPLATE/Issue.md delete mode 100644 .gitignore delete mode 100644 .gitlab/CODEOWNERS delete mode 100644 .gitlab/issue_templates/Bug Report.md delete mode 100644 .gitlab/issue_templates/Feature Request.md delete mode 100644 .gitlab/merge_request_templates/MR.md delete mode 100644 .gitlab/route-map.yml delete mode 100644 .gitlab/setup-buildx-remote-builders.sh delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/launch.json delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 README.md delete mode 100755 bin/complement delete mode 100644 book.toml delete mode 100644 complement/Dockerfile delete mode 100644 complement/README.md delete mode 100644 complement/caddy.json delete mode 100644 conduit-example.toml delete mode 100644 debian/README.md delete mode 100644 debian/config delete mode 100644 debian/matrix-conduit.service delete mode 100644 debian/postinst delete mode 100644 debian/postrm delete mode 100644 debian/templates delete mode 100644 docker/ci-binaries-packaging.Dockerfile delete mode 100644 docker/healthcheck.sh delete mode 100644 docs/SUMMARY.md delete mode 100644 docs/appservices.md delete mode 100644 docs/configuration.md delete mode 100644 docs/deploying.md delete mode 100644 docs/deploying/debian.md delete mode 100644 docs/deploying/docker-compose.for-traefik.yml delete mode 100644 docs/deploying/docker-compose.override.yml delete mode 100644 docs/deploying/docker-compose.with-traefik.yml delete mode 100644 docs/deploying/docker-compose.yml delete mode 100644 docs/deploying/docker.md delete mode 100644 docs/deploying/generic.md delete mode 100644 docs/deploying/nixos.md delete mode 100644 docs/introduction.md delete mode 100644 docs/turn.md delete mode 100644 tests/sytest/are-we-synapse-yet.list delete mode 100755 tests/sytest/are-we-synapse-yet.py delete mode 100755 tests/sytest/show-expected-fail-tests.sh delete mode 100644 tests/sytest/sytest-blacklist delete mode 100644 tests/sytest/sytest-whitelist delete mode 100644 tests/test-config.toml diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index c78ddbac..00000000 --- a/.dockerignore +++ /dev/null @@ -1,28 +0,0 @@ -# Local build and dev artifacts -target -tests - -# Docker files -Dockerfile* -docker-compose* - -# IDE files -.vscode -.idea -*.iml - -# Git folder -.git -.gitea -.gitlab -.github - -# Dot files -.env -.gitignore - -# Toml files -rustfmt.toml - -# Documentation -#*.md diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index a4e9e432..00000000 --- a/.editorconfig +++ /dev/null @@ -1,15 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -root = true - -[*] -charset = utf-8 -end_of_line = lf -tab_width = 4 -indent_size = 4 -indent_style = space -insert_final_newline = true -max_line_length = 120 - -[*.nix] -indent_size = 2 diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 0e4e01b5..00000000 --- a/.gitea/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1 +0,0 @@ -- [ ] I agree to release my code and all other changes of this PR under the Apache-2.0 license diff --git a/.github/ISSUE_TEMPLATE/Issue.md b/.github/ISSUE_TEMPLATE/Issue.md deleted file mode 100644 index 78896651..00000000 --- a/.github/ISSUE_TEMPLATE/Issue.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: "Issue with / Feature Request for Conduit" -about: "Please file issues on GitLab: https://gitlab.com/famedly/conduit/-/issues/new" -title: "CLOSE ME" ---- - - - -**⚠️ Conduit development does not happen on GitHub. Issues opened here will not be addressed** - -Please open issues on GitLab: https://gitlab.com/famedly/conduit/-/issues/new diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 73ce2e1f..00000000 --- a/.gitignore +++ /dev/null @@ -1,76 +0,0 @@ -# CMake -cmake-build-*/ - -# IntelliJ -.idea/ -out/ -*.iml -modules.xml -*.ipr - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# Linux backup files -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -# Rust -/target/ - -### vscode ### -.vscode/* -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows shortcuts -*.lnk - -# Conduit -conduit.toml -conduit.db - -# Etc. -**/*.rs.bk -cached_target - -# Nix artifacts -/result* - -# Direnv cache -/.direnv - -# Gitlab CI cache -/.gitlab-ci.d - -# mdbook output -public/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8c880b9c..6ff6a097 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,31 +1,17 @@ stages: - ci - artifacts - - publish variables: # Makes some things print in color TERM: ansi -# Avoid duplicate pipelines -# See: https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines -workflow: - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS - when: never - - if: $CI - before_script: # Enable nix-command and flakes - if command -v nix > /dev/null; then echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf; fi # Add our own binary cache - - if command -v nix > /dev/null; then echo "extra-substituters = https://nix.computer.surgery/conduit" >> /etc/nix/nix.conf; fi - - if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=" >> /etc/nix/nix.conf; fi - - # Add alternate binary cache - - if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ]; then echo "extra-substituters = $ATTIC_ENDPOINT" >> /etc/nix/nix.conf; fi + - if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ] && [ -n "$ATTIC_CACHE" ]; then echo "extra-substituters = $ATTIC_ENDPOINT/$ATTIC_CACHE" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi # Add crane binary cache @@ -54,21 +40,9 @@ ci: - direnv exec . engage cache: - key: nix paths: - target - .gitlab-ci.d - rules: - # CI on upstream runners (only available for maintainers) - - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $IS_UPSTREAM_CI == "true" - # Manual CI on unprotected branches that are not MRs - - if: $CI_PIPELINE_SOURCE != "merge_request_event" && $CI_COMMIT_REF_PROTECTED == "false" - when: manual - # Manual CI on forks - - if: $IS_UPSTREAM_CI != "true" - when: manual - - if: $CI - interruptible: true artifacts: stage: artifacts @@ -77,11 +51,6 @@ artifacts: - ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl - cp result/bin/conduit x86_64-unknown-linux-musl - - mkdir -p target/release - - cp result/bin/conduit target/release - - direnv exec . cargo deb --no-build - - mv target/debian/*.deb x86_64-unknown-linux-musl.deb - # Since the OCI image package is based on the binary package, this has the # fun side effect of uploading the normal binary too. Conduit users who are # deploying with Nix can leverage this fact by adding our binary cache to @@ -98,10 +67,6 @@ artifacts: - ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl - cp result oci-image-arm64v8.tar.gz - - - ./bin/nix-build-and-cache .#book - # We can't just copy the symlink, we need to dereference it https://gitlab.com/gitlab-org/gitlab/-/issues/19746 - - cp -r --dereference result public artifacts: paths: - x86_64-unknown-linux-musl @@ -109,76 +74,3 @@ artifacts: - x86_64-unknown-linux-musl.deb - oci-image-amd64.tar.gz - oci-image-arm64v8.tar.gz - - public - rules: - # CI required for all MRs - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - # Optional CI on forks - - if: $IS_UPSTREAM_CI != "true" - when: manual - allow_failure: true - - if: $CI - interruptible: true - -.push-oci-image: - stage: publish - image: docker:25.0.0 - services: - - docker:25.0.0-dind - variables: - IMAGE_SUFFIX_AMD64: amd64 - IMAGE_SUFFIX_ARM64V8: arm64v8 - script: - - docker load -i oci-image-amd64.tar.gz - - IMAGE_ID_AMD64=$(docker images -q conduit:next) - - docker load -i oci-image-arm64v8.tar.gz - - IMAGE_ID_ARM64V8=$(docker images -q conduit:next) - # Tag and push the architecture specific images - - docker tag $IMAGE_ID_AMD64 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 - - docker tag $IMAGE_ID_ARM64V8 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8 - - docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 - - docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8 - # Tag the multi-arch image - - docker manifest create $IMAGE_NAME:$CI_COMMIT_SHA --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8 - - docker manifest push $IMAGE_NAME:$CI_COMMIT_SHA - # Tag and push the git ref - - docker manifest create $IMAGE_NAME:$CI_COMMIT_REF_NAME --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8 - - docker manifest push $IMAGE_NAME:$CI_COMMIT_REF_NAME - # Tag git tags as 'latest' - - | - if [[ -n "$CI_COMMIT_TAG" ]]; then - docker manifest create $IMAGE_NAME:latest --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8 - docker manifest push $IMAGE_NAME:latest - fi - dependencies: - - artifacts - only: - - next - - master - - tags - -oci-image:push-gitlab: - extends: .push-oci-image - variables: - IMAGE_NAME: $CI_REGISTRY_IMAGE/matrix-conduit - before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - -oci-image:push-dockerhub: - extends: .push-oci-image - variables: - IMAGE_NAME: matrixconduit/matrix-conduit - before_script: - - docker login -u $DOCKER_HUB_USER -p $DOCKER_HUB_PASSWORD - -pages: - stage: publish - dependencies: - - artifacts - only: - - next - script: - - "true" - artifacts: - paths: - - public diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS deleted file mode 100644 index 665aaaa7..00000000 --- a/.gitlab/CODEOWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# Nix things -.envrc @CobaltCause -flake.lock @CobaltCause -flake.nix @CobaltCause -nix/ @CobaltCause diff --git a/.gitlab/issue_templates/Bug Report.md b/.gitlab/issue_templates/Bug Report.md deleted file mode 100644 index 3e66d43c..00000000 --- a/.gitlab/issue_templates/Bug Report.md +++ /dev/null @@ -1,19 +0,0 @@ - - -### Description - - -### System Configuration - - -Conduit Version: -Database backend (default is sqlite): sqlite - - -/label ~conduit diff --git a/.gitlab/issue_templates/Feature Request.md b/.gitlab/issue_templates/Feature Request.md deleted file mode 100644 index 3f636e71..00000000 --- a/.gitlab/issue_templates/Feature Request.md +++ /dev/null @@ -1,17 +0,0 @@ - - - -### Is your feature request related to a problem? Please describe. - - - - -### Describe the solution you'd like - - - - -/label ~conduit diff --git a/.gitlab/merge_request_templates/MR.md b/.gitlab/merge_request_templates/MR.md deleted file mode 100644 index c592a3b2..00000000 --- a/.gitlab/merge_request_templates/MR.md +++ /dev/null @@ -1,8 +0,0 @@ - - - ------------------------------------------------------------------------------ - -- [ ] I ran `cargo fmt` and `cargo test` -- [ ] I agree to release my code and all other changes of this MR under the Apache-2.0 license - diff --git a/.gitlab/route-map.yml b/.gitlab/route-map.yml deleted file mode 100644 index 2c23079c..00000000 --- a/.gitlab/route-map.yml +++ /dev/null @@ -1,3 +0,0 @@ -# Docs: Map markdown to html files -- source: /docs/(.+)\.md/ - public: '\1.html' \ No newline at end of file diff --git a/.gitlab/setup-buildx-remote-builders.sh b/.gitlab/setup-buildx-remote-builders.sh deleted file mode 100644 index 29d50dde..00000000 --- a/.gitlab/setup-buildx-remote-builders.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh -set -eux - -# --------------------------------------------------------------------- # -# # -# Configures docker buildx to use a remote server for arm building. # -# Expects $SSH_PRIVATE_KEY to be a valid ssh ed25519 private key with # -# access to the server $ARM_SERVER_USER@$ARM_SERVER_IP # -# # -# This is expected to only be used in the official CI/CD pipeline! # -# # -# Requirements: openssh-client, docker buildx # -# Inspired by: https://depot.dev/blog/building-arm-containers # -# # -# --------------------------------------------------------------------- # - -cat "$BUILD_SERVER_SSH_PRIVATE_KEY" | ssh-add - - -# Test server connections: -ssh "$ARM_SERVER_USER@$ARM_SERVER_IP" "uname -a" -ssh "$AMD_SERVER_USER@$AMD_SERVER_IP" "uname -a" - -# Connect remote arm64 server for all arm builds: -docker buildx create \ - --name "multi" \ - --driver "docker-container" \ - --platform "linux/arm64,linux/arm/v7" \ - "ssh://$ARM_SERVER_USER@$ARM_SERVER_IP" - -# Connect remote amd64 server for adm64 builds: -docker buildx create --append \ - --name "multi" \ - --driver "docker-container" \ - --platform "linux/amd64" \ - "ssh://$AMD_SERVER_USER@$AMD_SERVER_IP" - -docker buildx use multi diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 037f20d7..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "recommendations": [ - "rust-lang.rust-analyzer", - "bungcip.better-toml", - "ms-azuretools.vscode-docker", - "eamodio.gitlens", - "serayuzgur.crates", - "vadimcn.vscode-lldb", - "timonwong.shellcheck" - ] -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index da521604..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug conduit", - "sourceLanguages": ["rust"], - "cargo": { - "args": [ - "build", - "--bin=conduit", - "--package=conduit" - ], - "filter": { - "name": "conduit", - "kind": "bin" - } - }, - "args": [], - "env": { - "RUST_BACKTRACE": "1", - "CONDUIT_CONFIG": "", - "CONDUIT_SERVER_NAME": "localhost", - "CONDUIT_DATABASE_PATH": "/tmp", - "CONDUIT_ADDRESS": "0.0.0.0", - "CONDUIT_PORT": "6167" - }, - "cwd": "${workspaceFolder}" - } - ] -} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 1b060350..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,134 +0,0 @@ - -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement over email at -coc@koesters.xyz or over Matrix at @timo:conduit.rs. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations - diff --git a/Cargo.toml b/Cargo.toml index 3fb0265e..06cbc02c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,68 +13,38 @@ str_to_string = "warn" name = "conduit" description = "A Matrix homeserver written in Rust" license = "Apache-2.0" -authors = ["timokoesters "] -homepage = "https://conduit.rs" -repository = "https://gitlab.com/famedly/conduit" -readme = "README.md" version = "0.7.0" edition = "2021" # See also `rust-toolchain.toml` rust-version = "1.75.0" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [lints] workspace = true [dependencies] -# Web framework axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"] } axum-server = { version = "0.5.1", features = ["tls-rustls"] } tower = { version = "0.4.13", features = ["util"] } tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } - -# 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 = "5495b85aa311c2805302edb0a7de40399e22b397", 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"] } - -# Used for the http request / response body type for Ruma endpoints used with reqwest bytes = "1.4.0" http = "0.2.9" -# Used for ruma wrapper serde_json = { version = "1.0.96", features = ["raw_value"] } -# Used for appservice registration files serde_yaml = "0.9.21" -# Used for pdu definition serde = { version = "1.0.163", features = ["rc"] } -# Used for secure identifiers rand = "0.8.5" -# Used to hash passwords rust-argon2 = "1.0.0" -# Used to send requests hyper = "0.14.26" reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } -# Used for conduit::Error type thiserror = "1.0.40" -# Used to generate thumbnails for images image = { version = "0.24.6", default-features = false, features = ["jpeg", "png", "gif"] } -# Used to encode server public key base64 = "0.21.2" -# Used when hashing the state ring = "0.17.7" -# Used when querying the SRV record of other servers trust-dns-resolver = "0.22.0" -# Used to find matching events for appservices regex = "1.8.1" -# jwt jsonwebtokens jsonwebtoken = "9.2.0" -# Performance measurements tracing = { version = "0.1.37", features = [] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } tracing-flame = "0.2.0" @@ -84,24 +54,16 @@ tracing-opentelemetry = "0.18.0" lru-cache = "0.1.2" rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } parking_lot = { version = "0.12.1", optional = true } -# crossbeam = { version = "0.8.2", optional = true } num_cpus = "1.15.0" -# Used for ruma wrapper serde_html_form = "0.2.0" - thread_local = "1.1.7" -# used for TURN server authentication hmac = "0.12.1" sha-1 = "0.10.1" -# used for conduit's CLI and admin room command parsing clap = { version = "4.3.0", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } futures-util = { version = "0.3.28", default-features = false } -# Used for reading the configuration from conduit.toml & environment variables figment = { version = "0.10.8", features = ["env", "toml"] } - tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } async-trait = "0.1.68" - sd-notify = { version = "0.4.1", optional = true } [dependencies.rocksdb] @@ -124,42 +86,3 @@ backend_rocksdb = ["rocksdb"] jemalloc = ["tikv-jemallocator"] sqlite = ["rusqlite", "parking_lot", "tokio/signal"] systemd = ["sd-notify"] - -[package.metadata.deb] -name = "matrix-conduit" -maintainer = "Paul van Tilburg " -copyright = "2020, Timo Kösters " -license-file = ["LICENSE", "3"] -depends = "$auto, ca-certificates" -extended-description = """\ -A fast Matrix homeserver that is optimized for smaller, personal servers, \ -instead of a server that has high scalability.""" -section = "net" -priority = "optional" -assets = [ - ["debian/README.md", "usr/share/doc/matrix-conduit/README.Debian", "644"], - ["README.md", "usr/share/doc/matrix-conduit/", "644"], - ["target/release/conduit", "usr/sbin/matrix-conduit", "755"], -] -conf-files = [ - "/etc/matrix-conduit/conduit.toml" -] -maintainer-scripts = "debian/" -systemd-units = { unit-name = "matrix-conduit" } - -[profile.dev] -lto = 'off' -incremental = true - -[profile.release] -lto = 'thin' -incremental = true -codegen-units=32 -# If you want to make flamegraphs, enable debug info: -# debug = true - -# For releases also try to max optimizations for dependencies: -[profile.release.build-override] -opt-level = 3 -[profile.release.package."*"] -opt-level = 3 diff --git a/README.md b/README.md deleted file mode 100644 index 474a5247..00000000 --- a/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Conduit - - -### A Matrix homeserver written in Rust - - -Please visit the [Conduit documentation](https://famedly.gitlab.io/conduit) for more information. -Alternatively you can open [docs/introduction.md](docs/introduction.md) in this repository. - - -#### What is Matrix? - -[Matrix](https://matrix.org) is an open network for secure and decentralized -communication. Users from every Matrix homeserver can chat with users from all -other Matrix servers. You can even use bridges (also called Matrix appservices) -to communicate with users outside of Matrix, like a community on Discord. - -#### What is the goal? - -An efficient Matrix homeserver that's easy to set up and just works. You can install -it on a mini-computer like the Raspberry Pi to host Matrix for your family, -friends or company. - -#### Can I try it out? - -Yes! You can test our Conduit instance by opening a client that supports registration tokens such as [Element web](https://app.element.io/), [Nheko](https://matrix.org/ecosystem/clients/nheko/) or [SchildiChat web](https://app.schildi.chat/) and registering on the `conduit.rs` homeserver. The registration token is "for_testing_only". Don't share personal information. Once you have registered, you can use any other [Matrix client](https://matrix.org/ecosystem/clients) to login. - -Server hosting for conduit.rs is donated by the Matrix.org Foundation. - -#### What is the current status? - -Conduit is Beta, meaning you can join and participate in most -Matrix rooms, but not all features are supported and you might run into bugs -from time to time. - -There are still a few important features missing: - -- E2EE emoji comparison over federation (E2EE chat works) -- Outgoing read receipts, typing, presence over federation (incoming works) - - - -#### How can I contribute? - -1. Look for an issue you would like to work on and make sure no one else is currently working on it. -2. Tell us that you are working on the issue (comment on the issue or chat in - [#conduit:fachschaften.org](https://matrix.to/#/#conduit:fachschaften.org)). If it is more complicated, please explain your approach and ask questions. -3. Fork the repo, create a new branch and push commits. -4. Submit a MR - -#### Contact - -If you have any questions, feel free to -- Ask in `#conduit:fachschaften.org` on Matrix -- Write an E-Mail to `conduit@koesters.xyz` -- Send an direct message to `@timokoesters:fachschaften.org` on Matrix -- [Open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new) - -#### Thanks to - -Thanks to FUTO, Famedly, Prototype Fund (DLR and German BMBF) and all individuals for financially supporting this project. - -Thanks to the contributors to Conduit and all libraries we use, for example: - -- Ruma: A clean library for the Matrix Spec in Rust -- axum: A modular web framework - -#### Donate - -- Liberapay: -- Bitcoin: `bc1qnnykf986tw49ur7wx9rpw2tevpsztvar5x8w4n` - -#### Logo - -- Lightning Bolt Logo: -- Logo License: - diff --git a/bin/complement b/bin/complement deleted file mode 100755 index 291953dd..00000000 --- a/bin/complement +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# Path to Complement's source code -COMPLEMENT_SRC="$1" - -# A `.jsonl` file to write test logs to -LOG_FILE="$2" - -# A `.jsonl` file to write test results to -RESULTS_FILE="$3" - -OCI_IMAGE="complement-conduit:dev" - -env \ - -C "$(git rev-parse --show-toplevel)" \ - docker build \ - --tag "$OCI_IMAGE" \ - --file complement/Dockerfile \ - . - -# It's okay (likely, even) that `go test` exits nonzero -set +o pipefail -env \ - -C "$COMPLEMENT_SRC" \ - COMPLEMENT_BASE_IMAGE="$OCI_IMAGE" \ - go test -json ./tests | tee "$LOG_FILE" -set -o pipefail - -# Post-process the results into an easy-to-compare format -cat "$LOG_FILE" | jq -c ' - select( - (.Action == "pass" or .Action == "fail" or .Action == "skip") - and .Test != null - ) | {Action: .Action, Test: .Test} - ' | sort > "$RESULTS_FILE" diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 350e1717..7d21428f 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -11,14 +11,14 @@ nix build "$@" if [ ! -z ${ATTIC_TOKEN+x} ]; then nix run --inputs-from . attic -- \ login \ - conduit \ - "${ATTIC_ENDPOINT:-https://nix.computer.surgery/conduit}" \ + "$ATTIC_SERVER" \ + "$ATTIC_ENDPOINT" \ "$ATTIC_TOKEN" # Push the target installable and its build dependencies nix run --inputs-from . attic -- \ push \ - conduit \ + "$ATTIC_SERVER:$ATTIC_CACHE" \ "$(nix path-info "$INSTALLABLE" --derivation)" \ "$(nix path-info "$INSTALLABLE")" else diff --git a/book.toml b/book.toml deleted file mode 100644 index e25746ca..00000000 --- a/book.toml +++ /dev/null @@ -1,18 +0,0 @@ -[book] -title = "Conduit" -description = "Conduit is a simple, fast and reliable chat server for the Matrix protocol" -language = "en" -multilingual = false -src = "docs" - -[build] -build-dir = "public" -create-missing = true - -[output.html] -git-repository-url = "https://gitlab.com/famedly/conduit" -edit-url-template = "https://gitlab.com/famedly/conduit/-/edit/next/{path}" -git-repository-icon = "fa-git-square" - -[output.html.search] -limit-results = 15 diff --git a/complement/Dockerfile b/complement/Dockerfile deleted file mode 100644 index a327dd8b..00000000 --- a/complement/Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -FROM rust:1.75.0 - -WORKDIR /workdir - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libclang-dev - -COPY Cargo.toml Cargo.toml -COPY Cargo.lock Cargo.lock -COPY src src -RUN cargo build --release \ - && mv target/release/conduit conduit \ - && rm -rf target - -# Install caddy -RUN apt-get update \ - && apt-get install -y \ - debian-keyring \ - debian-archive-keyring \ - apt-transport-https \ - curl \ - && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/gpg.key' \ - | gpg --dearmor -o /usr/share/keyrings/caddy-testing-archive-keyring.gpg \ - && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/debian.deb.txt' \ - | tee /etc/apt/sources.list.d/caddy-testing.list \ - && apt-get update \ - && apt-get install -y caddy - -COPY conduit-example.toml conduit.toml -COPY complement/caddy.json caddy.json - -ENV SERVER_NAME=localhost -ENV CONDUIT_CONFIG=/workdir/conduit.toml - -RUN sed -i "s/port = 6167/port = 8008/g" conduit.toml -RUN echo "log = \"warn,_=off\"" >> conduit.toml -RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml - -EXPOSE 8008 8448 - -CMD uname -a && \ - sed -i "s/#server_name = \"your.server.name\"/server_name = \"${SERVER_NAME}\"/g" conduit.toml && \ - sed -i "s/your.server.name/${SERVER_NAME}/g" caddy.json && \ - caddy start --config caddy.json > /dev/null && \ - /workdir/conduit diff --git a/complement/README.md b/complement/README.md deleted file mode 100644 index 185b251a..00000000 --- a/complement/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Complement - -## What's that? - -Have a look at [its repository](https://github.com/matrix-org/complement). - -## How do I use it with Conduit? - -The script at [`../bin/complement`](../bin/complement) has automation for this. -It takes a few command line arguments, you can read the script to find out what -those are. diff --git a/complement/caddy.json b/complement/caddy.json deleted file mode 100644 index ea52c2c9..00000000 --- a/complement/caddy.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "logging": { - "logs": { - "default": { - "level": "WARN" - } - } - }, - "apps": { - "http": { - "https_port": 8448, - "servers": { - "srv0": { - "listen": [":8448"], - "routes": [{ - "match": [{ - "host": ["your.server.name"] - }], - "handle": [{ - "handler": "subroute", - "routes": [{ - "handle": [{ - "handler": "reverse_proxy", - "upstreams": [{ - "dial": "127.0.0.1:8008" - }] - }] - }] - }], - "terminal": true - }], - "tls_connection_policies": [{ - "match": { - "sni": ["your.server.name"] - } - }] - } - } - }, - "pki": { - "certificate_authorities": { - "local": { - "name": "Complement CA", - "root": { - "certificate": "/complement/ca/ca.crt", - "private_key": "/complement/ca/ca.key" - }, - "intermediate": { - "certificate": "/complement/ca/ca.crt", - "private_key": "/complement/ca/ca.key" - } - } - } - }, - "tls": { - "automation": { - "policies": [{ - "subjects": ["your.server.name"], - "issuers": [{ - "module": "internal" - }], - "on_demand": true - }, { - "issuers": [{ - "module": "internal", - "ca": "local" - }] - }] - } - } - } -} \ No newline at end of file diff --git a/conduit-example.toml b/conduit-example.toml deleted file mode 100644 index c83bce74..00000000 --- a/conduit-example.toml +++ /dev/null @@ -1,67 +0,0 @@ -# ============================================================================= -# This is the official example config for Conduit. -# If you use it for your server, you will need to adjust it to your own needs. -# At the very least, change the server_name field! -# ============================================================================= - - -[global] -# The server_name is the pretty name of this server. It is used as a suffix for user -# and room ids. Examples: matrix.org, conduit.rs - -# The Conduit server needs all /_matrix/ requests to be reachable at -# https://your.server.name/ on port 443 (client-server) and 8448 (federation). - -# If that's not possible for you, you can create /.well-known files to redirect -# requests. See -# https://matrix.org/docs/spec/client_server/latest#get-well-known-matrix-client -# and -# https://matrix.org/docs/spec/server_server/r0.1.4#get-well-known-matrix-server -# for more information - -# YOU NEED TO EDIT THIS -#server_name = "your.server.name" - -# This is the only directory where Conduit will save its data -database_path = "/var/lib/matrix-conduit/" -database_backend = "rocksdb" - -# The port Conduit will be running on. You need to set up a reverse proxy in -# your web server (e.g. apache or nginx), so all requests to /_matrix on port -# 443 and 8448 will be forwarded to the Conduit instance running on this port -# Docker users: Don't change this, you'll need to map an external port to this. -port = 6167 - -# Max size for uploads -max_request_size = 20_000_000 # in bytes - -# Enables registration. If set to false, no users can register on this server. -allow_registration = true - -# A static registration token that new users will have to provide when creating -# an account. YOU NEED TO EDIT THIS. -# - Insert a password that users will have to enter on registration -# - Start the line with '#' to remove the condition -registration_token = "" - -allow_federation = true -allow_check_for_updates = true - -# Enable the display name lightning bolt on registration. -enable_lightning_bolt = true - -# Servers listed here will be used to gather public keys of other servers. -# Generally, copying this exactly should be enough. (Currently, Conduit doesn't -# support batched key requests, so this list should only contain Synapse -# servers.) -trusted_servers = ["matrix.org"] - -#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time - -# Controls the log verbosity. See also [here][0]. -# -# [0]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives -#log = "..." - -address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy -#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it. diff --git a/debian/README.md b/debian/README.md deleted file mode 100644 index 4ddb614d..00000000 --- a/debian/README.md +++ /dev/null @@ -1,37 +0,0 @@ -Conduit for Debian -================== - -Installation ------------- - -Information about downloading, building and deploying the Debian package, see -the "Installing Conduit" section in the Deploying docs. -All following sections until "Setting up the Reverse Proxy" be ignored because -this is handled automatically by the packaging. - -Configuration -------------- - -When installed, Debconf generates the configuration of the homeserver -(host)name, the address and port it listens on. This configuration ends up in -`/etc/matrix-conduit/conduit.toml`. - -You can tweak more detailed settings by uncommenting and setting the variables -in `/etc/matrix-conduit/conduit.toml`. This involves settings such as the maximum -file size for download/upload, enabling federation, etc. - -Running -------- - -The package uses the `matrix-conduit.service` systemd unit file to start and -stop Conduit. It loads the configuration file mentioned above to set up the -environment before running the server. - -This package assumes by default that Conduit will be placed behind a reverse -proxy such as Apache or nginx. This default deployment entails just listening -on `127.0.0.1` and the free port `6167` and is reachable via a client using the URL -. - -At a later stage this packaging may support also setting up TLS and running -stand-alone. In this case, however, you need to set up some certificates and -renewal, for it to work properly. diff --git a/debian/config b/debian/config deleted file mode 100644 index 8710ef97..00000000 --- a/debian/config +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -e - -# Source debconf library. -. /usr/share/debconf/confmodule - -# Ask for the Matrix homeserver name, address and port. -db_input high matrix-conduit/hostname || true -db_go - -db_input low matrix-conduit/address || true -db_go - -db_input medium matrix-conduit/port || true -db_go - -exit 0 diff --git a/debian/matrix-conduit.service b/debian/matrix-conduit.service deleted file mode 100644 index 299f2680..00000000 --- a/debian/matrix-conduit.service +++ /dev/null @@ -1,47 +0,0 @@ -[Unit] -Description=Conduit Matrix homeserver -After=network.target - -[Service] -DynamicUser=yes -User=_matrix-conduit -Group=_matrix-conduit -Type=simple - -AmbientCapabilities= -CapabilityBoundingSet= -LockPersonality=yes -MemoryDenyWriteExecute=yes -NoNewPrivileges=yes -ProtectClock=yes -ProtectControlGroups=yes -ProtectHome=yes -ProtectHostname=yes -ProtectKernelLogs=yes -ProtectKernelModules=yes -ProtectKernelTunables=yes -ProtectSystem=strict -PrivateDevices=yes -PrivateMounts=yes -PrivateTmp=yes -PrivateUsers=yes -RemoveIPC=yes -RestrictAddressFamilies=AF_INET AF_INET6 -RestrictNamespaces=yes -RestrictRealtime=yes -RestrictSUIDSGID=yes -SystemCallArchitectures=native -SystemCallFilter=@system-service -SystemCallErrorNumber=EPERM -StateDirectory=matrix-conduit - -Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml" - -ExecStart=/usr/sbin/matrix-conduit -Restart=on-failure -RestartSec=10 -StartLimitInterval=1m -StartLimitBurst=5 - -[Install] -WantedBy=multi-user.target diff --git a/debian/postinst b/debian/postinst deleted file mode 100644 index 6361af5a..00000000 --- a/debian/postinst +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh -set -e - -. /usr/share/debconf/confmodule - -CONDUIT_CONFIG_PATH=/etc/matrix-conduit -CONDUIT_CONFIG_FILE="${CONDUIT_CONFIG_PATH}/conduit.toml" -CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit/ - -case "$1" in - configure) - # Create the `_matrix-conduit` user if it does not exist yet. - if ! getent passwd _matrix-conduit > /dev/null ; then - echo 'Adding system user for the Conduit Matrix homeserver' 1>&2 - adduser --system --group --quiet \ - --home "$CONDUIT_DATABASE_PATH" \ - --disabled-login \ - --force-badname \ - _matrix-conduit - fi - - # Create the database path if it does not exist yet and fix up ownership - # and permissions. - mkdir -p "$CONDUIT_DATABASE_PATH" - chown _matrix-conduit "$CONDUIT_DATABASE_PATH" - chmod 700 "$CONDUIT_DATABASE_PATH" - - if [ ! -e "$CONDUIT_CONFIG_FILE" ]; then - # Write the debconf values in the config. - db_get matrix-conduit/hostname - CONDUIT_SERVER_NAME="$RET" - db_get matrix-conduit/address - CONDUIT_ADDRESS="$RET" - db_get matrix-conduit/port - CONDUIT_PORT="$RET" - mkdir -p "$CONDUIT_CONFIG_PATH" - cat > "$CONDUIT_CONFIG_FILE" << EOF -[global] -# The server_name is the pretty name of this server. It is used as a suffix for -# user and room ids. Examples: matrix.org, conduit.rs - -# The Conduit server needs all /_matrix/ requests to be reachable at -# https://your.server.name/ on port 443 (client-server) and 8448 (federation). - -# If that's not possible for you, you can create /.well-known files to redirect -# requests. See -# https://matrix.org/docs/spec/client_server/latest#get-well-known-matrix-client -# and -# https://matrix.org/docs/spec/server_server/r0.1.4#get-well-known-matrix-server -# for more information - -server_name = "${CONDUIT_SERVER_NAME}" - -# This is the only directory where Conduit will save its data. -database_path = "${CONDUIT_DATABASE_PATH}" -database_backend = "rocksdb" - -# The address Conduit will be listening on. -# By default the server listens on address 0.0.0.0. Change this to 127.0.0.1 to -# only listen on the localhost when using a reverse proxy. -address = "${CONDUIT_ADDRESS}" - -# The port Conduit will be running on. You need to set up a reverse proxy in -# your web server (e.g. apache or nginx), so all requests to /_matrix on port -# 443 and 8448 will be forwarded to the Conduit instance running on this port -# Docker users: Don't change this, you'll need to map an external port to this. -port = ${CONDUIT_PORT} - -# Max size for uploads -max_request_size = 20_000_000 # in bytes - -# Enables registration. If set to false, no users can register on this server. -allow_registration = true - -# A static registration token that new users will have to provide when creating -# an account. -# - Insert a password that users will have to enter on registration -# - Start the line with '#' to remove the condition -#registration_token = "" - -allow_federation = true -allow_check_for_updates = true - -# Enable the display name lightning bolt on registration. -enable_lightning_bolt = true - -# Servers listed here will be used to gather public keys of other servers. -# Generally, copying this exactly should be enough. (Currently, Conduit doesn't -# support batched key requests, so this list should only contain Synapse -# servers.) -trusted_servers = ["matrix.org"] - -#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time - -# Controls the log verbosity. See also [here][0]. -# -# [0]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives -#log = "..." -EOF - fi - ;; -esac - -#DEBHELPER# diff --git a/debian/postrm b/debian/postrm deleted file mode 100644 index 28949091..00000000 --- a/debian/postrm +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -set -e - -. /usr/share/debconf/confmodule - -CONDUIT_CONFIG_PATH=/etc/matrix-conduit -CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit - -case $1 in - purge) - # Remove debconf changes from the db - db_purge - - # Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior - # "configuration files must be preserved when the package is removed, and - # only deleted when the package is purged." - if [ -d "$CONDUIT_CONFIG_PATH" ]; then - rm -r "$CONDUIT_CONFIG_PATH" - fi - - if [ -d "$CONDUIT_DATABASE_PATH" ]; then - rm -r "$CONDUIT_DATABASE_PATH" - fi - ;; -esac - -#DEBHELPER# diff --git a/debian/templates b/debian/templates deleted file mode 100644 index c4281ad3..00000000 --- a/debian/templates +++ /dev/null @@ -1,21 +0,0 @@ -Template: matrix-conduit/hostname -Type: string -Default: localhost -Description: The server (host)name of the Matrix homeserver - This is the hostname the homeserver will be reachable at via a client. - . - If set to "localhost", you can connect with a client locally and clients - from other hosts and also other homeservers will not be able to reach you! - -Template: matrix-conduit/address -Type: string -Default: 127.0.0.1 -Description: The listen address of the Matrix homeserver - This is the address the homeserver will listen on. Leave it set to 127.0.0.1 - when using a reverse proxy. - -Template: matrix-conduit/port -Type: string -Default: 6167 -Description: The port of the Matrix homeserver - This port is most often just accessed by a reverse proxy. diff --git a/docker/ci-binaries-packaging.Dockerfile b/docker/ci-binaries-packaging.Dockerfile deleted file mode 100644 index 4c1199ed..00000000 --- a/docker/ci-binaries-packaging.Dockerfile +++ /dev/null @@ -1,84 +0,0 @@ -# syntax=docker/dockerfile:1 -# --------------------------------------------------------------------------------------------------------- -# This Dockerfile is intended to be built as part of Conduit's CI pipeline. -# It does not build Conduit in Docker, but just copies the matching build artifact from the build jobs. -# -# It is mostly based on the normal Conduit Dockerfile, but adjusted in a few places to maximise caching. -# Credit's for the original Dockerfile: Weasy666. -# --------------------------------------------------------------------------------------------------------- - -FROM docker.io/alpine:3.16.0@sha256:4ff3ca91275773af45cb4b0834e12b7eb47d1c18f770a0b151381cd227f4c253 AS runner - - -# Standard port on which Conduit launches. -# You still need to map the port when using the docker command or docker-compose. -EXPOSE 6167 - -# Users are expected to mount a volume to this directory: -ARG DEFAULT_DB_PATH=/var/lib/matrix-conduit - -ENV CONDUIT_PORT=6167 \ - CONDUIT_ADDRESS="0.0.0.0" \ - CONDUIT_DATABASE_PATH=${DEFAULT_DB_PATH} \ - CONDUIT_CONFIG='' -# └─> Set no config file to do all configuration with env vars - -# Conduit needs: -# ca-certificates: for https -# iproute2: for `ss` for the healthcheck script -RUN apk add --no-cache \ - ca-certificates \ - iproute2 - -ARG CREATED -ARG VERSION -ARG GIT_REF -# Labels according to https://github.com/opencontainers/image-spec/blob/master/annotations.md -# including a custom label specifying the build command -LABEL org.opencontainers.image.created=${CREATED} \ - org.opencontainers.image.authors="Conduit Contributors" \ - org.opencontainers.image.title="Conduit" \ - org.opencontainers.image.version=${VERSION} \ - org.opencontainers.image.vendor="Conduit Contributors" \ - org.opencontainers.image.description="A Matrix homeserver written in Rust" \ - org.opencontainers.image.url="https://conduit.rs/" \ - org.opencontainers.image.revision=${GIT_REF} \ - org.opencontainers.image.source="https://gitlab.com/famedly/conduit.git" \ - org.opencontainers.image.licenses="Apache-2.0" \ - org.opencontainers.image.documentation="https://gitlab.com/famedly/conduit" \ - org.opencontainers.image.ref.name="" - - -# Test if Conduit is still alive, uses the same endpoint as Element -COPY ./docker/healthcheck.sh /srv/conduit/healthcheck.sh -HEALTHCHECK --start-period=5s --interval=5s CMD ./healthcheck.sh - -# Improve security: Don't run stuff as root, that does not need to run as root: -# Most distros also use 1000:1000 for the first real user, so this should resolve volume mounting problems. -ARG USER_ID=1000 -ARG GROUP_ID=1000 -RUN set -x ; \ - deluser --remove-home www-data ; \ - addgroup -S -g ${GROUP_ID} conduit 2>/dev/null ; \ - adduser -S -u ${USER_ID} -D -H -h /srv/conduit -G conduit -g conduit conduit 2>/dev/null ; \ - addgroup conduit conduit 2>/dev/null && exit 0 ; exit 1 - -# Change ownership of Conduit files to conduit user and group -RUN chown -cR conduit:conduit /srv/conduit && \ - chmod +x /srv/conduit/healthcheck.sh && \ - mkdir -p ${DEFAULT_DB_PATH} && \ - chown -cR conduit:conduit ${DEFAULT_DB_PATH} - -# Change user to conduit -USER conduit -# Set container home directory -WORKDIR /srv/conduit - -# Run Conduit and print backtraces on panics -ENV RUST_BACKTRACE=1 -ENTRYPOINT [ "/srv/conduit/conduit" ] - -# Depending on the target platform (e.g. "linux/arm/v7", "linux/arm64/v8", or "linux/amd64") -# copy the matching binary into this docker image -ARG TARGETPLATFORM -COPY --chown=conduit:conduit ./$TARGETPLATFORM /srv/conduit/conduit diff --git a/docker/healthcheck.sh b/docker/healthcheck.sh deleted file mode 100644 index 62f2f987..00000000 --- a/docker/healthcheck.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -# If the config file does not contain a default port and the CONDUIT_PORT env is not set, create -# try to get port from process list -if [ -z "${CONDUIT_PORT}" ]; then - CONDUIT_PORT=$(ss -tlpn | grep conduit | grep -m1 -o ':[0-9]*' | grep -m1 -o '[0-9]*') -fi - -# If CONDUIT_ADDRESS is not set try to get the address from the process list -if [ -z "${CONDUIT_ADDRESS}" ]; then - CONDUIT_ADDRESS=$(ss -tlpn | awk -F ' +|:' '/conduit/ { print $4 }') -fi - -# The actual health check. -# We try to first get a response on HTTP and when that fails on HTTPS and when that fails, we exit with code 1. -# TODO: Change this to a single wget call. Do we have a config value that we can check for that? -wget --no-verbose --tries=1 --spider "http://${CONDUIT_ADDRESS}:${CONDUIT_PORT}/_matrix/client/versions" || \ - wget --no-verbose --tries=1 --spider "https://${CONDUIT_ADDRESS}:${CONDUIT_PORT}/_matrix/client/versions" || \ - exit 1 diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md deleted file mode 100644 index 2caca3ee..00000000 --- a/docs/SUMMARY.md +++ /dev/null @@ -1,12 +0,0 @@ -# Summary - -- [Introduction](introduction.md) - -- [Configuration](configuration.md) -- [Deploying](deploying.md) - - [Generic](deploying/generic.md) - - [Debian](deploying/debian.md) - - [Docker](deploying/docker.md) - - [NixOS](deploying/nixos.md) -- [TURN](turn.md) -- [Appservices](appservices.md) diff --git a/docs/appservices.md b/docs/appservices.md deleted file mode 100644 index 8ca015a0..00000000 --- a/docs/appservices.md +++ /dev/null @@ -1,61 +0,0 @@ -# Setting up Appservices - -## Getting help - -If you run into any problems while setting up an Appservice, write an email to `timo@koesters.xyz`, ask us in [#conduit:fachschaften.org](https://matrix.to/#/#conduit:fachschaften.org) or [open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new). - -## Set up the appservice - general instructions - -Follow whatever instructions are given by the appservice. This usually includes -downloading, changing its config (setting domain, homeserver url, port etc.) -and later starting it. - -At some point the appservice guide should ask you to add a registration yaml -file to the homeserver. In Synapse you would do this by adding the path to the -homeserver.yaml, but in Conduit you can do this from within Matrix: - -First, go into the #admins room of your homeserver. The first person that -registered on the homeserver automatically joins it. Then send a message into -the room like this: - - @conduit:your.server.name: register-appservice - ``` - paste - the - contents - of - the - yaml - registration - here - ``` - -You can confirm it worked by sending a message like this: -`@conduit:your.server.name: list-appservices` - -The @conduit bot should answer with `Appservices (1): your-bridge` - -Then you are done. Conduit will send messages to the appservices and the -appservice can send requests to the homeserver. You don't need to restart -Conduit, but if it doesn't work, restarting while the appservice is running -could help. - -## Appservice-specific instructions - -### Remove an appservice - -To remove an appservice go to your admin room and execute - -`@conduit:your.server.name: unregister-appservice ` - -where `` one of the output of `list-appservices`. - -### Tested appservices - -These appservices have been tested and work with Conduit without any extra steps: - -- [matrix-appservice-discord](https://github.com/Half-Shot/matrix-appservice-discord) -- [mautrix-hangouts](https://github.com/mautrix/hangouts/) -- [mautrix-telegram](https://github.com/mautrix/telegram/) -- [mautrix-signal](https://github.com/mautrix/signal/) from version `0.2.2` forward. -- [heisenbridge](https://github.com/hifi/heisenbridge/) diff --git a/docs/configuration.md b/docs/configuration.md deleted file mode 100644 index efa080dc..00000000 --- a/docs/configuration.md +++ /dev/null @@ -1,110 +0,0 @@ -# Configuration - -**Conduit** is configured using a TOML file. The configuration file is loaded from the path specified by the `CONDUIT_CONFIG` environment variable. - -> **Note:** The configuration file is required to run Conduit. If the `CONDUIT_CONFIG` environment variable is not set, Conduit will exit with an error. - -> **Note:** If you update the configuration file, you must restart Conduit for the changes to take effect - -Conduit's configuration file is divided into the following sections: - -- [Global](#global) - - [TLS](#tls) - - [Proxy](#proxy) - - -## Global - -The `global` section contains the following fields: - -> **Note:** The `*` symbol indicates that the field is required, and the values in **parentheses** are the possible values - -| Field | Type | Description | Default | -| --- | --- | --- | --- | -| `address` | `string` | The address to bind to | `"127.0.0.1"` | -| `port` | `integer` | The port to bind to | `8000` | -| `tls` | `table` | See the [TLS configuration](#tls) | N/A | -| `server_name`_*_ | `string` | The server name | N/A | -| `database_backend`_*_ | `string` | The database backend to use (`"rocksdb"` *recommended*, `"sqlite"`) | N/A | -| `database_path`_*_ | `string` | The path to the database file/dir | N/A | -| `db_cache_capacity_mb` | `float` | The cache capacity, in MB | `300.0` | -| `enable_lightning_bolt` | `boolean` | Add `⚡️` emoji to end of user's display name | `true` | -| `allow_check_for_updates` | `boolean` | Allow Conduit to check for updates | `true` | -| `conduit_cache_capacity_modifier` | `float` | The value to multiply the default cache capacity by | `1.0` | -| `rocksdb_max_open_files` | `integer` | The maximum number of open files | `1000` | -| `pdu_cache_capacity` | `integer` | The maximum number of Persisted Data Units (PDUs) to cache | `150000` | -| `cleanup_second_interval` | `integer` | How often conduit should clean up the database, in seconds | `60` | -| `max_request_size` | `integer` | The maximum request size, in bytes | `20971520` (20 MiB) | -| `max_concurrent_requests` | `integer` | The maximum number of concurrent requests | `100` | -| `max_fetch_prev_events` | `integer` | The maximum number of previous events to fetch per request if conduit notices events are missing | `100` | -| `allow_registration` | `boolean` | Opens your homeserver to public registration | `false` | -| `registration_token` | `string` | The token users need to have when registering to your homeserver | N/A | -| `allow_encryption` | `boolean` | Allow users to enable encryption in their rooms | `true` | -| `allow_federation` | `boolean` | Allow federation with other servers | `true` | -| `allow_room_creation` | `boolean` | Allow users to create rooms | `true` | -| `allow_unstable_room_versions` | `boolean` | Allow users to create and join rooms with unstable versions | `true` | -| `default_room_version` | `string` | The default room version (`"6"`-`"10"`)| `"10"` | -| `allow_jaeger` | `boolean` | Allow Jaeger tracing | `false` | -| `tracing_flame` | `boolean` | Enable flame tracing | `false` | -| `proxy` | `table` | See the [Proxy configuration](#proxy) | N/A | -| `jwt_secret` | `string` | The secret used in the JWT to enable JWT login without it a 400 error will be returned | N/A | -| `trusted_servers` | `array` | The list of trusted servers to gather public keys of offline servers | `["matrix.org"]` | -| `log` | `string` | The log verbosity to use | `"warn"` | -| `turn_username` | `string` | The TURN username | `""` | -| `turn_password` | `string` | The TURN password | `""` | -| `turn_uris` | `array` | The TURN URIs | `[]` | -| `turn_secret` | `string` | The TURN secret | `""` | -| `turn_ttl` | `integer` | The TURN TTL in seconds | `86400` | -| `emergency_password` | `string` | Set a password to login as the `conduit` user in case of emergency | N/A | - - -### TLS -The `tls` table contains the following fields: -- `certs`: The path to the public PEM certificate -- `key`: The path to the PEM private key - -#### Example -```toml -[global.tls] -certs = "/path/to/cert.pem" -key = "/path/to/key.pem" -``` - - -### Proxy -You can choose what requests conduit should proxy (if any). The `proxy` table contains the following fields - -#### Global -The global option will proxy all outgoing requests. The `global` table contains the following fields: -- `url`: The URL of the proxy server -##### Example -```toml -[global.proxy.global] -url = "https://example.com" -``` - -#### By domain -An array of tables that contain the following fields: -- `url`: The URL of the proxy server -- `include`: Domains that should be proxied (assumed to be `["*"]` if unset) -- `exclude`: Domains that should not be proxied (takes precedent over `include`) - -Both `include` and `exclude` allow for glob pattern matching. -##### Example -In this example, all requests to domains ending in `.onion` and `matrix.secretly-an-onion-domain.xyz` -will be proxied via `socks://localhost:9050`, except for domains ending in `.myspecial.onion`. You can add as many `by_domain` tables as you need. -```toml -[[global.proxy.by_domain]] -url = "socks5://localhost:9050" -include = ["*.onion", "matrix.secretly-an-onion-domain.xyz"] -exclude = ["*.clearnet.onion"] -``` - -### Example - -> **Note:** The following example is a minimal configuration file. You should replace the values with your own. - -```toml -[global] -{{#include ../conduit-example.toml:22:}} -``` diff --git a/docs/deploying.md b/docs/deploying.md deleted file mode 100644 index 136e6538..00000000 --- a/docs/deploying.md +++ /dev/null @@ -1,3 +0,0 @@ -# Deploying - -This chapter describes various ways to deploy Conduit. diff --git a/docs/deploying/debian.md b/docs/deploying/debian.md deleted file mode 100644 index 2e8a544a..00000000 --- a/docs/deploying/debian.md +++ /dev/null @@ -1 +0,0 @@ -{{#include ../../debian/README.md}} diff --git a/docs/deploying/docker-compose.for-traefik.yml b/docs/deploying/docker-compose.for-traefik.yml deleted file mode 100644 index c0bb042e..00000000 --- a/docs/deploying/docker-compose.for-traefik.yml +++ /dev/null @@ -1,69 +0,0 @@ -# Conduit - Behind Traefik Reverse Proxy -version: '3' - -services: - homeserver: - ### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image, - ### then you are ready to go. - image: matrixconduit/matrix-conduit:latest - ### If you want to build a fresh image from the sources, then comment the image line and uncomment the - ### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this: - ### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d - # build: - # context: . - # args: - # CREATED: '2021-03-16T08:18:27Z' - # VERSION: '0.1.0' - # LOCAL: 'false' - # GIT_REF: origin/master - restart: unless-stopped - volumes: - - db:/var/lib/matrix-conduit/ - networks: - - proxy - environment: - CONDUIT_SERVER_NAME: your.server.name # EDIT THIS - CONDUIT_DATABASE_PATH: /var/lib/matrix-conduit/ - CONDUIT_DATABASE_BACKEND: rocksdb - CONDUIT_PORT: 6167 - CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB - CONDUIT_ALLOW_REGISTRATION: 'true' - #CONDUIT_REGISTRATION_TOKEN: '' # require password for registration - CONDUIT_ALLOW_FEDERATION: 'true' - CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true' - CONDUIT_TRUSTED_SERVERS: '["matrix.org"]' - #CONDUIT_MAX_CONCURRENT_REQUESTS: 100 - CONDUIT_ADDRESS: 0.0.0.0 - CONDUIT_CONFIG: '' # Ignore this - - # We need some way to server the client and server .well-known json. The simplest way is to use a nginx container - # to serve those two as static files. If you want to use a different way, delete or comment the below service, here - # and in the docker-compose override file. - well-known: - image: nginx:latest - restart: unless-stopped - volumes: - - ./nginx/matrix.conf:/etc/nginx/conf.d/matrix.conf # the config to serve the .well-known/matrix files - - ./nginx/www:/var/www/ # location of the client and server .well-known-files - ### Uncomment if you want to use your own Element-Web App. - ### Note: You need to provide a config.json for Element and you also need a second - ### Domain or Subdomain for the communication between Element and Conduit - ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md - # element-web: - # image: vectorim/element-web:latest - # restart: unless-stopped - # volumes: - # - ./element_config.json:/app/config.json - # networks: - # - proxy - # depends_on: - # - homeserver - -volumes: - db: - -networks: - # This is the network Traefik listens to, if your network has a different - # name, don't forget to change it here and in the docker-compose.override.yml - proxy: - external: true diff --git a/docs/deploying/docker-compose.override.yml b/docs/deploying/docker-compose.override.yml deleted file mode 100644 index 9525078d..00000000 --- a/docs/deploying/docker-compose.override.yml +++ /dev/null @@ -1,45 +0,0 @@ -# Conduit - Traefik Reverse Proxy Labels -version: '3' - -services: - homeserver: - labels: - - "traefik.enable=true" - - "traefik.docker.network=proxy" # Change this to the name of your Traefik docker proxy network - - - "traefik.http.routers.to-conduit.rule=Host(`.`)" # Change to the address on which Conduit is hosted - - "traefik.http.routers.to-conduit.tls=true" - - "traefik.http.routers.to-conduit.tls.certresolver=letsencrypt" - - "traefik.http.routers.to-conduit.middlewares=cors-headers@docker" - - - "traefik.http.middlewares.cors-headers.headers.accessControlAllowOriginList=*" - - "traefik.http.middlewares.cors-headers.headers.accessControlAllowHeaders=Origin, X-Requested-With, Content-Type, Accept, Authorization" - - "traefik.http.middlewares.cors-headers.headers.accessControlAllowMethods=GET, POST, PUT, DELETE, OPTIONS" - - # We need some way to server the client and server .well-known json. The simplest way is to use a nginx container - # to serve those two as static files. If you want to use a different way, delete or comment the below service, here - # and in the docker-compose file. - well-known: - labels: - - "traefik.enable=true" - - "traefik.docker.network=proxy" - - - "traefik.http.routers.to-matrix-wellknown.rule=Host(`.`) && PathPrefix(`/.well-known/matrix`)" - - "traefik.http.routers.to-matrix-wellknown.tls=true" - - "traefik.http.routers.to-matrix-wellknown.tls.certresolver=letsencrypt" - - "traefik.http.routers.to-matrix-wellknown.middlewares=cors-headers@docker" - - - "traefik.http.middlewares.cors-headers.headers.accessControlAllowOriginList=*" - - "traefik.http.middlewares.cors-headers.headers.accessControlAllowHeaders=Origin, X-Requested-With, Content-Type, Accept, Authorization" - - "traefik.http.middlewares.cors-headers.headers.accessControlAllowMethods=GET, POST, PUT, DELETE, OPTIONS" - - - ### Uncomment this if you uncommented Element-Web App in the docker-compose.yml - # element-web: - # labels: - # - "traefik.enable=true" - # - "traefik.docker.network=proxy" # Change this to the name of your Traefik docker proxy network - - # - "traefik.http.routers.to-element-web.rule=Host(`.`)" # Change to the address on which Element-Web is hosted - # - "traefik.http.routers.to-element-web.tls=true" - # - "traefik.http.routers.to-element-web.tls.certresolver=letsencrypt" diff --git a/docs/deploying/docker-compose.with-traefik.yml b/docs/deploying/docker-compose.with-traefik.yml deleted file mode 100644 index 8ce3ad46..00000000 --- a/docs/deploying/docker-compose.with-traefik.yml +++ /dev/null @@ -1,96 +0,0 @@ -# Conduit - Behind Traefik Reverse Proxy -version: '3' - -services: - homeserver: - ### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image, - ### then you are ready to go. - image: matrixconduit/matrix-conduit:latest - ### If you want to build a fresh image from the sources, then comment the image line and uncomment the - ### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this: - ### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d - # build: - # context: . - # args: - # CREATED: '2021-03-16T08:18:27Z' - # VERSION: '0.1.0' - # LOCAL: 'false' - # GIT_REF: origin/master - restart: unless-stopped - volumes: - - db:/srv/conduit/.local/share/conduit - ### Uncomment if you want to use conduit.toml to configure Conduit - ### Note: Set env vars will override conduit.toml values - # - ./conduit.toml:/srv/conduit/conduit.toml - networks: - - proxy - environment: - CONDUIT_SERVER_NAME: localhost:6167 # replace with your own name - CONDUIT_TRUSTED_SERVERS: '["matrix.org"]' - CONDUIT_ALLOW_REGISTRATION : 'true' - ### Uncomment and change values as desired - # CONDUIT_ADDRESS: 0.0.0.0 - # CONDUIT_PORT: 6167 - # CONDUIT_REGISTRATION_TOKEN: '' # require password for registration - # CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if you want to configure purely by env vars, set this to an empty string '' - # Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging - # CONDUIT_ALLOW_ENCRYPTION: 'true' - # CONDUIT_ALLOW_FEDERATION: 'true' - # CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true' - # CONDUIT_DATABASE_PATH: /srv/conduit/.local/share/conduit - # CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB - - # We need some way to server the client and server .well-known json. The simplest way is to use a nginx container - # to serve those two as static files. If you want to use a different way, delete or comment the below service, here - # and in the docker-compose override file. - well-known: - image: nginx:latest - restart: unless-stopped - volumes: - - ./nginx/matrix.conf:/etc/nginx/conf.d/matrix.conf # the config to serve the .well-known/matrix files - - ./nginx/www:/var/www/ # location of the client and server .well-known-files - - ### Uncomment if you want to use your own Element-Web App. - ### Note: You need to provide a config.json for Element and you also need a second - ### Domain or Subdomain for the communication between Element and Conduit - ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md - # element-web: - # image: vectorim/element-web:latest - # restart: unless-stopped - # volumes: - # - ./element_config.json:/app/config.json - # networks: - # - proxy - # depends_on: - # - homeserver - - traefik: - image: "traefik:latest" - container_name: "traefik" - restart: "unless-stopped" - ports: - - "80:80" - - "443:443" - volumes: - - "/var/run/docker.sock:/var/run/docker.sock" - # - "./traefik_config:/etc/traefik" - - "acme:/etc/traefik/acme" - labels: - - "traefik.enable=true" - - # middleware redirect - - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - # global redirect to https - - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)" - - "traefik.http.routers.redirs.entrypoints=http" - - "traefik.http.routers.redirs.middlewares=redirect-to-https" - - networks: - - proxy - -volumes: - db: - acme: - -networks: - proxy: diff --git a/docs/deploying/docker-compose.yml b/docs/deploying/docker-compose.yml deleted file mode 100644 index 97f91daf..00000000 --- a/docs/deploying/docker-compose.yml +++ /dev/null @@ -1,53 +0,0 @@ -# Conduit -version: '3' - -services: - homeserver: - ### If you already built the Conduit image with 'docker build' or want to use a registry image, - ### then you are ready to go. - image: matrixconduit/matrix-conduit:latest - ### If you want to build a fresh image from the sources, then comment the image line and uncomment the - ### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this: - ### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d - # build: - # context: . - # args: - # CREATED: '2021-03-16T08:18:27Z' - # VERSION: '0.1.0' - # LOCAL: 'false' - # GIT_REF: origin/master - restart: unless-stopped - ports: - - 8448:6167 - volumes: - - db:/var/lib/matrix-conduit/ - environment: - CONDUIT_SERVER_NAME: your.server.name # EDIT THIS - CONDUIT_DATABASE_PATH: /var/lib/matrix-conduit/ - CONDUIT_DATABASE_BACKEND: rocksdb - CONDUIT_PORT: 6167 - CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB - CONDUIT_ALLOW_REGISTRATION: 'true' - CONDUIT_ALLOW_FEDERATION: 'true' - CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true' - CONDUIT_TRUSTED_SERVERS: '["matrix.org"]' - #CONDUIT_MAX_CONCURRENT_REQUESTS: 100 - CONDUIT_ADDRESS: 0.0.0.0 - CONDUIT_CONFIG: '' # Ignore this - # - ### Uncomment if you want to use your own Element-Web App. - ### Note: You need to provide a config.json for Element and you also need a second - ### Domain or Subdomain for the communication between Element and Conduit - ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md - # element-web: - # image: vectorim/element-web:latest - # restart: unless-stopped - # ports: - # - 8009:80 - # volumes: - # - ./element_config.json:/app/config.json - # depends_on: - # - homeserver - -volumes: - db: diff --git a/docs/deploying/docker.md b/docs/deploying/docker.md deleted file mode 100644 index c19ef51b..00000000 --- a/docs/deploying/docker.md +++ /dev/null @@ -1,216 +0,0 @@ -# Conduit for Docker - -> **Note:** To run and use Conduit you should probably use it with a Domain or Subdomain behind a reverse proxy (like Nginx, Traefik, Apache, ...) with a Lets Encrypt certificate. - -## Docker - -To run Conduit with Docker you can either build the image yourself or pull it from a registry. - - -### Use a registry - -OCI images for Conduit are available in the registries listed below. We recommend using the image tagged as `latest` from GitLab's own registry. - -| Registry | Image | Size | Notes | -| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- | -| GitLab Registry | [registry.gitlab.com/famedly/conduit/matrix-conduit:latest][gl] | ![Image Size][shield-latest] | Stable image. | -| Docker Hub | [docker.io/matrixconduit/matrix-conduit:latest][dh] | ![Image Size][shield-latest] | Stable image. | -| GitLab Registry | [registry.gitlab.com/famedly/conduit/matrix-conduit:next][gl] | ![Image Size][shield-next] | Development version. | -| Docker Hub | [docker.io/matrixconduit/matrix-conduit:next][dh] | ![Image Size][shield-next] | Development version. | - - -[dh]: https://hub.docker.com/r/matrixconduit/matrix-conduit -[gl]: https://gitlab.com/famedly/conduit/container_registry/2497937 -[shield-latest]: https://img.shields.io/docker/image-size/matrixconduit/matrix-conduit/latest -[shield-next]: https://img.shields.io/docker/image-size/matrixconduit/matrix-conduit/next - - -Use -```bash -docker image pull -``` -to pull it to your machine. - - - -### Build using a dockerfile - -The Dockerfile provided by Conduit has two stages, each of which creates an image. - -1. **Builder:** Builds the binary from local context or by cloning a git revision from the official repository. -2. **Runner:** Copies the built binary from **Builder** and sets up the runtime environment, like creating a volume to persist the database and applying the correct permissions. - -To build the image you can use the following command - -```bash -docker build --tag matrixconduit/matrix-conduit:latest . -``` - -which also will tag the resulting image as `matrixconduit/matrix-conduit:latest`. - - - -### Run - -When you have the image you can simply run it with - -```bash -docker run -d -p 8448:6167 \ - -v db:/var/lib/matrix-conduit/ \ - -e CONDUIT_SERVER_NAME="your.server.name" \ - -e CONDUIT_DATABASE_BACKEND="rocksdb" \ - -e CONDUIT_ALLOW_REGISTRATION=true \ - -e CONDUIT_ALLOW_FEDERATION=true \ - -e CONDUIT_MAX_REQUEST_SIZE="20_000_000" \ - -e CONDUIT_TRUSTED_SERVERS="[\"matrix.org\"]" \ - -e CONDUIT_MAX_CONCURRENT_REQUESTS="100" \ - --name conduit -``` - -or you can use [docker-compose](#docker-compose). - -The `-d` flag lets the container run in detached mode. You now need to supply a `conduit.toml` config file, an example can be found [here](../configuration.md). -You can pass in different env vars to change config values on the fly. You can even configure Conduit completely by using env vars, but for that you need -to pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible values, please take a look at the `docker-compose.yml` file. - -If you just want to test Conduit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it. - -### Docker-compose - -If the `docker run` command is not for you or your setup, you can also use one of the provided `docker-compose` files. - -Depending on your proxy setup, you can use one of the following files; -- If you already have a `traefik` instance set up, use [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) -- If you don't have a `traefik` instance set up (or any other reverse proxy), use [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml) -- For any other reverse proxy, use [`docker-compose.yml`](docker-compose.yml) - -When picking the traefik-related compose file, rename it so it matches `docker-compose.yml`, and -rename the override file to `docker-compose.override.yml`. Edit the latter with the values you want -for your server. -Additional info about deploying Conduit can be found [here](generic.md). - -### Build - -To build the Conduit image with docker-compose, you first need to open and modify the `docker-compose.yml` file. There you need to comment the `image:` option and uncomment the `build:` option. Then call docker-compose with: - -```bash -docker-compose up -``` - -This will also start the container right afterwards, so if want it to run in detached mode, you also should use the `-d` flag. - -### Run - -If you already have built the image or want to use one from the registries, you can just start the container and everything else in the compose file in detached mode with: - -```bash -docker-compose up -d -``` - -> **Note:** Don't forget to modify and adjust the compose file to your needs. - -### Use Traefik as Proxy - -As a container user, you probably know about Traefik. It is a easy to use reverse proxy for making -containerized app and services available through the web. With the two provided files, -[`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or -[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and -[`docker-compose.override.yml`](docker-compose.override.yml), it is equally easy to deploy -and use Conduit, with a little caveat. If you already took a look at the files, then you should have -seen the `well-known` service, and that is the little caveat. Traefik is simply a proxy and -loadbalancer and is not able to serve any kind of content, but for Conduit to federate, we need to -either expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/client` and -`.well-known/matrix/server`. - -With the service `well-known` we use a single `nginx` container that will serve those two files. - -So...step by step: - -1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or -[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml) from the repository and remove `.for-traefik` (or `.with-traefik`) from the filename. -2. Open both files and modify/adjust them to your needs. Meaning, change the `CONDUIT_SERVER_NAME` and the volume host mappings according to your needs. -3. Create the `conduit.toml` config file, an example can be found [here](../configuration.md), or set `CONDUIT_CONFIG=""` and configure Conduit per env vars. -4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`. -5. Create the files needed by the `well-known` service. - - - `./nginx/matrix.conf` (relative to the compose file, you can change this, but then also need to change the volume mapping) - - ```nginx - server { - server_name .; - listen 80 default_server; - - location /.well-known/matrix/server { - return 200 '{"m.server": ".:443"}'; - types { } default_type "application/json; charset=utf-8"; - } - - location /.well-known/matrix/client { - return 200 '{"m.homeserver": {"base_url": "https://."}}'; - types { } default_type "application/json; charset=utf-8"; - add_header "Access-Control-Allow-Origin" *; - } - - location / { - return 404; - } - } - ``` - -6. Run `docker-compose up -d` -7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin. - - - - -## Voice communication - -In order to make or receive calls, a TURN server is required. Conduit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place. - -### Configuration - -Create a configuration file called `coturn.conf` containing: - -```conf -use-auth-secret -static-auth-secret= -realm= -``` -A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`. - -These same values need to be set in conduit. You can either modify conduit.toml to include these lines: -``` -turn_uris = ["turn:?transport=udp", "turn:?transport=tcp"] -turn_secret = "" -``` -or append the following to the docker environment variables dependig on which configuration method you used earlier: -```yml -CONDUIT_TURN_URIS: '["turn:?transport=udp", "turn:?transport=tcp"]' -CONDUIT_TURN_SECRET: "" -``` -Restart Conduit to apply these changes. - -### Run -Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using -```bash -docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn -``` - -or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml` -and run `docker-compose up -d` in the same directory. - -```yml -version: 3 -services: - turn: - container_name: coturn-server - image: docker.io/coturn/coturn - restart: unless-stopped - network_mode: "host" - volumes: - - ./coturn.conf:/etc/coturn/turnserver.conf -``` - -To understand why the host networking mode is used and explore alternative configuration options, please visit the following link: https://github.com/coturn/coturn/blob/master/docker/coturn/README.md. -For security recommendations see Synapse's [Coturn documentation](https://github.com/matrix-org/synapse/blob/develop/docs/setup/turn/coturn.md#configuration). - diff --git a/docs/deploying/generic.md b/docs/deploying/generic.md deleted file mode 100644 index 307de359..00000000 --- a/docs/deploying/generic.md +++ /dev/null @@ -1,292 +0,0 @@ -# Generic deployment documentation - -> ## Getting help -> -> If you run into any problems while setting up Conduit, write an email to `conduit@koesters.xyz`, ask us -> in `#conduit:fachschaften.org` or [open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new). - -## Installing Conduit - -Although you might be able to compile Conduit for Windows, we do recommend running it on a Linux server. We therefore -only offer Linux binaries. - -You may simply download the binary that fits your machine. Run `uname -m` to see what you need. Now copy the appropriate url: - -**Stable versions:** - -| CPU Architecture | Download stable version | -| ------------------------------------------- | --------------------------------------------------------------- | -| x84_64 / amd64 (Most servers and computers) | [Binary][x84_64-glibc-master] / [.deb][x84_64-glibc-master-deb] | -| armv7 (e.g. Raspberry Pi by default) | [Binary][armv7-glibc-master] / [.deb][armv7-glibc-master-deb] | -| armv8 / aarch64 | [Binary][armv8-glibc-master] / [.deb][armv8-glibc-master-deb] | - -These builds were created on and linked against the glibc version shipped with Debian bullseye. -If you use a system with an older glibc version (e.g. RHEL8), you might need to compile Conduit yourself. - -[x84_64-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit?job=docker:master -[armv7-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit?job=docker:master -[armv8-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit?job=docker:master -[x84_64-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit.deb?job=docker:master -[armv7-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit.deb?job=docker:master -[armv8-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit.deb?job=docker:master - -**Latest versions:** - -| Target | Type | Download | -|-|-|-| -| `x86_64-unknown-linux-musl` | Statically linked Debian package | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/x86_64-unknown-linux-musl.deb?job=artifacts) | -| `x86_64-unknown-linux-musl` | Statically linked binary | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/x86_64-unknown-linux-musl?job=artifacts) | -| `aarch64-unknown-linux-musl` | Statically linked binary | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/aarch64-unknown-linux-musl?job=artifacts) | -| `x86_64-unknown-linux-gnu` | OCI image | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/oci-image-amd64.tar.gz?job=artifacts) | -| `aarch64-unknown-linux-musl` | OCI image | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/oci-image-arm64v8.tar.gz?job=artifacts) | - -```bash -$ sudo wget -O /usr/local/bin/matrix-conduit -$ sudo chmod +x /usr/local/bin/matrix-conduit -``` - -Alternatively, you may compile the binary yourself. First, install any dependencies: - -```bash -# Debian -$ sudo apt install libclang-dev build-essential - -# RHEL -$ sudo dnf install clang -``` -Then, `cd` into the source tree of conduit-next and run: -```bash -$ cargo build --release -``` - -## Adding a Conduit user - -While Conduit can run as any user it is usually better to use dedicated users for different services. This also allows -you to make sure that the file permissions are correctly set up. - -In Debian or RHEL, you can use this command to create a Conduit user: - -```bash -sudo adduser --system conduit --group --disabled-login --no-create-home -``` - -## Forwarding ports in the firewall or the router - -Conduit uses the ports 443 and 8448 both of which need to be open in the firewall. - -If Conduit runs behind a router or in a container and has a different public IP address than the host system these public ports need to be forwarded directly or indirectly to the port mentioned in the config. - -## Optional: Avoid port 8448 - -If Conduit runs behind Cloudflare reverse proxy, which doesn't support port 8448 on free plans, [delegation](https://matrix-org.github.io/synapse/latest/delegate.html) can be set up to have federation traffic routed to port 443: -```apache -# .well-known delegation on Apache - - ErrorDocument 200 '{"m.server": "your.server.name:443"}' - Header always set Content-Type application/json - Header always set Access-Control-Allow-Origin * - -``` -[SRV DNS record](https://spec.matrix.org/latest/server-server-api/#resolving-server-names) delegation is also [possible](https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-srv-record/). - -## Setting up a systemd service - -Now we'll set up a systemd service for Conduit, so it's easy to start/stop Conduit and set it to autostart when your -server reboots. Simply paste the default systemd service you can find below into -`/etc/systemd/system/conduit.service`. - -```systemd -[Unit] -Description=Conduit Matrix Server -After=network.target - -[Service] -Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml" -User=conduit -Group=conduit -Restart=always -ExecStart=/usr/local/bin/matrix-conduit - -[Install] -WantedBy=multi-user.target -``` - -Finally, run - -```bash -$ sudo systemctl daemon-reload -``` - -## Creating the Conduit configuration file - -Now we need to create the Conduit's config file in -`/etc/matrix-conduit/conduit.toml`. Paste in the contents of -[`conduit-example.toml`](../configuration.md) **and take a moment to read it. -You need to change at least the server name.** -You can also choose to use a different database backend, but right now only `rocksdb` and `sqlite` are recommended. - -## Setting the correct file permissions - -As we are using a Conduit specific user we need to allow it to read the config. To do that you can run this command on -Debian or RHEL: - -```bash -sudo chown -R root:root /etc/matrix-conduit -sudo chmod 755 /etc/matrix-conduit -``` - -If you use the default database path you also need to run this: - -```bash -sudo mkdir -p /var/lib/matrix-conduit/ -sudo chown -R conduit:conduit /var/lib/matrix-conduit/ -sudo chmod 700 /var/lib/matrix-conduit/ -``` - -## Setting up the Reverse Proxy - -This depends on whether you use Apache, Caddy, Nginx or another web server. - -### Apache - -Create `/etc/apache2/sites-enabled/050-conduit.conf` and copy-and-paste this: - -```apache -# Requires mod_proxy and mod_proxy_http -# -# On Apache instance compiled from source, -# paste into httpd-ssl.conf or httpd.conf - -Listen 8448 - - - -ServerName your.server.name # EDIT THIS - -AllowEncodedSlashes NoDecode -ProxyPass /_matrix/ http://127.0.0.1:6167/_matrix/ timeout=300 nocanon -ProxyPassReverse /_matrix/ http://127.0.0.1:6167/_matrix/ - - -``` - -**You need to make some edits again.** When you are done, run - -```bash -# Debian -$ sudo systemctl reload apache2 - -# Installed from source -$ sudo apachectl -k graceful -``` - -### Caddy - -Create `/etc/caddy/conf.d/conduit_caddyfile` and enter this (substitute for your server name). - -```caddy -your.server.name, your.server.name:8448 { - reverse_proxy /_matrix/* 127.0.0.1:6167 -} -``` - -That's it! Just start or enable the service and you're set. - -```bash -$ sudo systemctl enable caddy -``` - -### Nginx - -If you use Nginx and not Apache, add the following server section inside the http section of `/etc/nginx/nginx.conf` - -```nginx -server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - listen 8448 ssl http2; - listen [::]:8448 ssl http2; - server_name your.server.name; # EDIT THIS - merge_slashes off; - - # Nginx defaults to only allow 1MB uploads - # Increase this to allow posting large files such as videos - client_max_body_size 20M; - - location /_matrix/ { - proxy_pass http://127.0.0.1:6167; - proxy_set_header Host $http_host; - proxy_buffering off; - proxy_read_timeout 5m; - } - - ssl_certificate /etc/letsencrypt/live/your.server.name/fullchain.pem; # EDIT THIS - ssl_certificate_key /etc/letsencrypt/live/your.server.name/privkey.pem; # EDIT THIS - ssl_trusted_certificate /etc/letsencrypt/live/your.server.name/chain.pem; # EDIT THIS - include /etc/letsencrypt/options-ssl-nginx.conf; -} -``` - -**You need to make some edits again.** When you are done, run - -```bash -$ sudo systemctl reload nginx -``` - -## SSL Certificate - -If you chose Caddy as your web proxy SSL certificates are handled automatically and you can skip this step. - -The easiest way to get an SSL certificate, if you don't have one already, is to [install](https://certbot.eff.org/instructions) `certbot` and run this: - -```bash -# To use ECC for the private key, -# paste into /etc/letsencrypt/cli.ini: -# key-type = ecdsa -# elliptic-curve = secp384r1 - -$ sudo certbot -d your.server.name -``` -[Automated renewal](https://eff-certbot.readthedocs.io/en/stable/using.html#automated-renewals) is usually preconfigured. - -If using Cloudflare, configure instead the edge and origin certificates in dashboard. In case you’re already running a website on the same Apache server, you can just copy-and-paste the SSL configuration from your main virtual host on port 443 into the above-mentioned vhost. - -## You're done! - -Now you can start Conduit with: - -```bash -$ sudo systemctl start conduit -``` - -Set it to start automatically when your system boots with: - -```bash -$ sudo systemctl enable conduit -``` - -## How do I know it works? - -You can open [a Matrix client](https://matrix.org/ecosystem/clients), enter your homeserver and try to register. If you are using a registration token, use [Element web](https://app.element.io/), [Nheko](https://matrix.org/ecosystem/clients/nheko/) or [SchildiChat web](https://app.schildi.chat/), as they support this feature. - -You can also use these commands as a quick health check. - -```bash -$ curl https://your.server.name/_matrix/client/versions - -# If using port 8448 -$ curl https://your.server.name:8448/_matrix/client/versions -``` - -- To check if your server can talk with other homeservers, you can use the [Matrix Federation Tester](https://federationtester.matrix.org/). - If you can register but cannot join federated rooms check your config again and also check if the port 8448 is open and forwarded correctly. - -# What's next? - -## Audio/Video calls - -For Audio/Video call functionality see the [TURN Guide](../turn.md). - -## Appservices - -If you want to set up an appservice, take a look at the [Appservice Guide](../appservices.md). diff --git a/docs/deploying/nixos.md b/docs/deploying/nixos.md deleted file mode 100644 index bf9b1a11..00000000 --- a/docs/deploying/nixos.md +++ /dev/null @@ -1,18 +0,0 @@ -# Conduit for NixOS - -Conduit can be acquired by Nix from various places: - -* The `flake.nix` at the root of the repo -* The `default.nix` at the root of the repo -* From Nixpkgs - -The `flake.nix` and `default.nix` do not (currently) provide a NixOS module, so -(for now) [`services.matrix-conduit`][module] from Nixpkgs should be used to -configure Conduit. - -If you want to run the latest code, you should get Conduit from the `flake.nix` -or `default.nix` and set [`services.matrix-conduit.package`][package] -appropriately. - -[module]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit -[package]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit.package diff --git a/docs/introduction.md b/docs/introduction.md deleted file mode 100644 index da34ab6e..00000000 --- a/docs/introduction.md +++ /dev/null @@ -1,13 +0,0 @@ -# Conduit - -{{#include ../README.md:catchphrase}} - -{{#include ../README.md:body}} - -#### How can I deploy my own? - -- [Deployment options](deploying.md) - -If you want to connect an Appservice to Conduit, take a look at the [appservices documentation](appservices.md). - -{{#include ../README.md:footer}} diff --git a/docs/turn.md b/docs/turn.md deleted file mode 100644 index a61f1b13..00000000 --- a/docs/turn.md +++ /dev/null @@ -1,25 +0,0 @@ -# Setting up TURN/STURN - -## General instructions - -* It is assumed you have a [Coturn server](https://github.com/coturn/coturn) up and running. See [Synapse reference implementation](https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md). - -## Edit/Add a few settings to your existing conduit.toml - -``` -# Refer to your Coturn settings. -# `your.turn.url` has to match the REALM setting of your Coturn as well as `transport`. -turn_uris = ["turn:your.turn.url?transport=udp", "turn:your.turn.url?transport=tcp"] - -# static-auth-secret of your turnserver -turn_secret = "ADD SECRET HERE" - -# If you have your TURN server configured to use a username and password -# you can provide these information too. In this case comment out `turn_secret above`! -#turn_username = "" -#turn_password = "" -``` - -## Apply settings - -Restart Conduit. diff --git a/engage.toml b/engage.toml index cb284167..3e8884eb 100644 --- a/engage.toml +++ b/engage.toml @@ -30,11 +30,6 @@ name = "cargo-clippy" group = "versions" script = "cargo clippy -- --version" -[[task]] -name = "lychee" -group = "versions" -script = "lychee --version" - [[task]] name = "cargo-fmt" group = "lints" @@ -56,11 +51,6 @@ name = "cargo-clippy" group = "lints" script = "cargo clippy --workspace --all-targets --color=always -- -D warnings" -[[task]] -name = "lychee" -group = "lints" -script = "lychee --offline docs" - [[task]] name = "cargo" group = "tests" diff --git a/flake.nix b/flake.nix index 114e221c..721f6c07 100644 --- a/flake.nix +++ b/flake.nix @@ -203,35 +203,6 @@ packages = { default = package pkgsHost; oci-image = mkOciImage pkgsHost self.packages.${system}.default; - - book = - let - package = self.packages.${system}.default; - in - pkgsHost.stdenv.mkDerivation { - pname = "${package.pname}-book"; - version = package.version; - - src = nix-filter { - root = ./.; - include = [ - "book.toml" - "conduit-example.toml" - "README.md" - "debian/README.md" - "docs" - ]; - }; - - nativeBuildInputs = (with pkgsHost; [ - mdbook - ]); - - buildPhase = '' - mdbook build - mv public $out - ''; - }; } // builtins.listToAttrs @@ -290,22 +261,6 @@ toolchain ] ++ (with pkgsHost; [ engage - - # Needed for producing Debian packages - cargo-deb - - # Needed for Complement - go - olm - - # Needed for our script for Complement - jq - - # Needed for finding broken markdown links - lychee - - # Useful for editing the book locally - mdbook ]); }; }); diff --git a/src/main.rs b/src/main.rs index 902b76c7..d246401b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ pub static SERVICES: RwLock> = RwLock::new(None); // Not async due to services() being used in many closures, and async closures are not stable as of writing // This is the case for every other occurence of sync Mutex/RwLock, except for database related ones, where -// the current maintainer (Timo) has asked to not modify those +// the previous maintainer has asked to not modify those pub fn services() -> &'static Services { SERVICES .read() diff --git a/tests/sytest/are-we-synapse-yet.list b/tests/sytest/are-we-synapse-yet.list deleted file mode 100644 index 99091989..00000000 --- a/tests/sytest/are-we-synapse-yet.list +++ /dev/null @@ -1,866 +0,0 @@ -reg GET /register yields a set of flows -reg POST /register can create a user -reg POST /register downcases capitals in usernames -reg POST /register returns the same device_id as that in the request -reg POST /register rejects registration of usernames with '!' -reg POST /register rejects registration of usernames with '"' -reg POST /register rejects registration of usernames with ':' -reg POST /register rejects registration of usernames with '?' -reg POST /register rejects registration of usernames with '\' -reg POST /register rejects registration of usernames with '@' -reg POST /register rejects registration of usernames with '[' -reg POST /register rejects registration of usernames with ']' -reg POST /register rejects registration of usernames with '{' -reg POST /register rejects registration of usernames with '|' -reg POST /register rejects registration of usernames with '}' -reg POST /register rejects registration of usernames with '£' -reg POST /register rejects registration of usernames with 'é' -reg POST /register rejects registration of usernames with '\n' -reg POST /register rejects registration of usernames with ''' -reg POST /r0/admin/register with shared secret -reg POST /r0/admin/register admin with shared secret -reg POST /r0/admin/register with shared secret downcases capitals -reg POST /r0/admin/register with shared secret disallows symbols -reg POST rejects invalid utf-8 in JSON -log GET /login yields a set of flows -log POST /login can log in as a user -log POST /login returns the same device_id as that in the request -log POST /login can log in as a user with just the local part of the id -log POST /login as non-existing user is rejected -log POST /login wrong password is rejected -log Interactive authentication types include SSO -log Can perform interactive authentication with SSO -log The user must be consistent through an interactive authentication session with SSO -log The operation must be consistent through an interactive authentication session -v1s GET /events initially -v1s GET /initialSync initially -csa Version responds 200 OK with valid structure -pro PUT /profile/:user_id/displayname sets my name -pro GET /profile/:user_id/displayname publicly accessible -pro PUT /profile/:user_id/avatar_url sets my avatar -pro GET /profile/:user_id/avatar_url publicly accessible -dev GET /device/{deviceId} -dev GET /device/{deviceId} gives a 404 for unknown devices -dev GET /devices -dev PUT /device/{deviceId} updates device fields -dev PUT /device/{deviceId} gives a 404 for unknown devices -dev DELETE /device/{deviceId} -dev DELETE /device/{deviceId} requires UI auth user to match device owner -dev DELETE /device/{deviceId} with no body gives a 401 -dev The deleted device must be consistent through an interactive auth session -dev Users receive device_list updates for their own devices -pre GET /presence/:user_id/status fetches initial status -pre PUT /presence/:user_id/status updates my presence -crm POST /createRoom makes a public room -crm POST /createRoom makes a private room -crm POST /createRoom makes a private room with invites -crm POST /createRoom makes a room with a name -crm POST /createRoom makes a room with a topic -syn Can /sync newly created room -crm POST /createRoom creates a room with the given version -crm POST /createRoom rejects attempts to create rooms with numeric versions -crm POST /createRoom rejects attempts to create rooms with unknown versions -crm POST /createRoom ignores attempts to set the room version via creation_content -mem GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership -mem GET /rooms/:room_id/state/m.room.member/:user_id?format=event fetches my membership event -rst GET /rooms/:room_id/state/m.room.power_levels fetches powerlevels -mem GET /rooms/:room_id/joined_members fetches my membership -v1s GET /rooms/:room_id/initialSync fetches initial sync state -pub GET /publicRooms lists newly-created room -ali GET /directory/room/:room_alias yields room ID -mem GET /joined_rooms lists newly-created room -rst POST /rooms/:room_id/state/m.room.name sets name -rst GET /rooms/:room_id/state/m.room.name gets name -rst POST /rooms/:room_id/state/m.room.topic sets topic -rst GET /rooms/:room_id/state/m.room.topic gets topic -rst GET /rooms/:room_id/state fetches entire room state -crm POST /createRoom with creation content -ali PUT /directory/room/:room_alias creates alias -nsp GET /rooms/:room_id/aliases lists aliases -jon POST /rooms/:room_id/join can join a room -jon POST /join/:room_alias can join a room -jon POST /join/:room_id can join a room -jon POST /join/:room_id can join a room with custom content -jon POST /join/:room_alias can join a room with custom content -lev POST /rooms/:room_id/leave can leave a room -inv POST /rooms/:room_id/invite can send an invite -ban POST /rooms/:room_id/ban can ban a user -snd POST /rooms/:room_id/send/:event_type sends a message -snd PUT /rooms/:room_id/send/:event_type/:txn_id sends a message -snd PUT /rooms/:room_id/send/:event_type/:txn_id deduplicates the same txn id -get GET /rooms/:room_id/messages returns a message -get GET /rooms/:room_id/messages lazy loads members correctly -typ PUT /rooms/:room_id/typing/:user_id sets typing notification -typ Typing notifications don't leak (3 subtests) -rst GET /rooms/:room_id/state/m.room.power_levels can fetch levels -rst PUT /rooms/:room_id/state/m.room.power_levels can set levels -rst PUT power_levels should not explode if the old power levels were empty -rst Both GET and PUT work -rct POST /rooms/:room_id/receipt can create receipts -red POST /rooms/:room_id/read_markers can create read marker -med POST /media/r0/upload can create an upload -med GET /media/r0/download can fetch the value again -cap GET /capabilities is present and well formed for registered user -cap GET /r0/capabilities is not public -reg Register with a recaptcha -reg registration is idempotent, without username specified -reg registration is idempotent, with username specified -reg registration remembers parameters -reg registration accepts non-ascii passwords -reg registration with inhibit_login inhibits login -reg User signups are forbidden from starting with '_' -reg Can register using an email address -log Can login with 3pid and password using m.login.password -log login types include SSO -log /login/cas/redirect redirects if the old m.login.cas login type is listed -log Can login with new user via CAS -lox Can logout current device -lox Can logout all devices -lox Request to logout with invalid an access token is rejected -lox Request to logout without an access token is rejected -log After changing password, can't log in with old password -log After changing password, can log in with new password -log After changing password, existing session still works -log After changing password, a different session no longer works by default -log After changing password, different sessions can optionally be kept -psh Pushers created with a different access token are deleted on password change -psh Pushers created with a the same access token are not deleted on password change -acc Can deactivate account -acc Can't deactivate account with wrong password -acc After deactivating account, can't log in with password -acc After deactivating account, can't log in with an email -v1s initialSync sees my presence status -pre Presence change reports an event to myself -pre Friends presence changes reports events -crm Room creation reports m.room.create to myself -crm Room creation reports m.room.member to myself -rst Setting room topic reports m.room.topic to myself -v1s Global initialSync -v1s Global initialSync with limit=0 gives no messages -v1s Room initialSync -v1s Room initialSync with limit=0 gives no messages -rst Setting state twice is idempotent -jon Joining room twice is idempotent -syn New room members see their own join event -v1s New room members see existing users' presence in room initialSync -syn Existing members see new members' join events -syn Existing members see new members' presence -v1s All room members see all room members' presence in global initialSync -f,jon Remote users can join room by alias -syn New room members see their own join event -v1s New room members see existing members' presence in room initialSync -syn Existing members see new members' join events -syn Existing members see new member's presence -v1s New room members see first user's profile information in global initialSync -v1s New room members see first user's profile information in per-room initialSync -f,jon Remote users may not join unfederated rooms -syn Local room members see posted message events -v1s Fetching eventstream a second time doesn't yield the message again -syn Local non-members don't see posted message events -get Local room members can get room messages -f,syn Remote room members also see posted message events -f,get Remote room members can get room messages -get Message history can be paginated -f,get Message history can be paginated over federation -eph Ephemeral messages received from clients are correctly expired -ali Room aliases can contain Unicode -f,ali Remote room alias queries can handle Unicode -ali Canonical alias can be set -ali Canonical alias can include alt_aliases -ali Regular users can add and delete aliases in the default room configuration -ali Regular users can add and delete aliases when m.room.aliases is restricted -ali Deleting a non-existent alias should return a 404 -ali Users can't delete other's aliases -ali Users with sufficient power-level can delete other's aliases -ali Can delete canonical alias -ali Alias creators can delete alias with no ops -ali Alias creators can delete canonical alias with no ops -ali Only room members can list aliases of a room -inv Can invite users to invite-only rooms -inv Uninvited users cannot join the room -inv Invited user can reject invite -f,inv Invited user can reject invite over federation -f,inv Invited user can reject invite over federation several times -inv Invited user can reject invite for empty room -f,inv Invited user can reject invite over federation for empty room -inv Invited user can reject local invite after originator leaves -inv Invited user can see room metadata -f,inv Remote invited user can see room metadata -inv Users cannot invite themselves to a room -inv Users cannot invite a user that is already in the room -ban Banned user is kicked and may not rejoin until unbanned -f,ban Remote banned user is kicked and may not rejoin until unbanned -ban 'ban' event respects room powerlevel -plv setting 'm.room.name' respects room powerlevel -plv setting 'm.room.power_levels' respects room powerlevel (2 subtests) -plv Unprivileged users can set m.room.topic if it only needs level 0 -plv Users cannot set ban powerlevel higher than their own (2 subtests) -plv Users cannot set kick powerlevel higher than their own (2 subtests) -plv Users cannot set redact powerlevel higher than their own (2 subtests) -v1s Check that event streams started after a client joined a room work (SYT-1) -v1s Event stream catches up fully after many messages -xxx POST /rooms/:room_id/redact/:event_id as power user redacts message -xxx POST /rooms/:room_id/redact/:event_id as original message sender redacts message -xxx POST /rooms/:room_id/redact/:event_id as random user does not redact message -xxx POST /redact disallows redaction of event in different room -xxx Redaction of a redaction redacts the redaction reason -v1s A departed room is still included in /initialSync (SPEC-216) -v1s Can get rooms/{roomId}/initialSync for a departed room (SPEC-216) -rst Can get rooms/{roomId}/state for a departed room (SPEC-216) -mem Can get rooms/{roomId}/members for a departed room (SPEC-216) -get Can get rooms/{roomId}/messages for a departed room (SPEC-216) -rst Can get 'm.room.name' state for a departed room (SPEC-216) -syn Getting messages going forward is limited for a departed room (SPEC-216) -3pd Can invite existing 3pid -3pd Can invite existing 3pid with no ops into a private room -3pd Can invite existing 3pid in createRoom -3pd Can invite unbound 3pid -f,3pd Can invite unbound 3pid over federation -3pd Can invite unbound 3pid with no ops into a private room -f,3pd Can invite unbound 3pid over federation with no ops into a private room -f,3pd Can invite unbound 3pid over federation with users from both servers -3pd Can accept unbound 3pid invite after inviter leaves -3pd Can accept third party invite with /join -3pd 3pid invite join with wrong but valid signature are rejected -3pd 3pid invite join valid signature but revoked keys are rejected -3pd 3pid invite join valid signature but unreachable ID server are rejected -gst Guest user cannot call /events globally -gst Guest users can join guest_access rooms -gst Guest users can send messages to guest_access rooms if joined -gst Guest user calling /events doesn't tightloop -gst Guest users are kicked from guest_access rooms on revocation of guest_access -gst Guest user can set display names -gst Guest users are kicked from guest_access rooms on revocation of guest_access over federation -gst Guest user can upgrade to fully featured user -gst Guest user cannot upgrade other users -pub GET /publicRooms lists rooms -pub GET /publicRooms includes avatar URLs -gst Guest users can accept invites to private rooms over federation -gst Guest users denied access over federation if guest access prohibited -mem Room members can override their displayname on a room-specific basis -mem Room members can join a room with an overridden displayname -mem Users cannot kick users from a room they are not in -mem Users cannot kick users who have already left a room -typ Typing notification sent to local room members -f,typ Typing notifications also sent to remote room members -typ Typing can be explicitly stopped -rct Read receipts are visible to /initialSync -rct Read receipts are sent as events -rct Receipts must be m.read -pro displayname updates affect room member events -pro avatar_url updates affect room member events -gst m.room.history_visibility == "world_readable" allows/forbids appropriately for Guest users -gst m.room.history_visibility == "shared" allows/forbids appropriately for Guest users -gst m.room.history_visibility == "invited" allows/forbids appropriately for Guest users -gst m.room.history_visibility == "joined" allows/forbids appropriately for Guest users -gst m.room.history_visibility == "default" allows/forbids appropriately for Guest users -gst Guest non-joined user cannot call /events on shared room -gst Guest non-joined user cannot call /events on invited room -gst Guest non-joined user cannot call /events on joined room -gst Guest non-joined user cannot call /events on default room -gst Guest non-joined user can call /events on world_readable room -gst Guest non-joined users can get state for world_readable rooms -gst Guest non-joined users can get individual state for world_readable rooms -gst Guest non-joined users cannot room initalSync for non-world_readable rooms -gst Guest non-joined users can room initialSync for world_readable rooms -gst Guest non-joined users can get individual state for world_readable rooms after leaving -gst Guest non-joined users cannot send messages to guest_access rooms if not joined -gst Guest users can sync from world_readable guest_access rooms if joined -gst Guest users can sync from shared guest_access rooms if joined -gst Guest users can sync from invited guest_access rooms if joined -gst Guest users can sync from joined guest_access rooms if joined -gst Guest users can sync from default guest_access rooms if joined -ath m.room.history_visibility == "world_readable" allows/forbids appropriately for Real users -ath m.room.history_visibility == "shared" allows/forbids appropriately for Real users -ath m.room.history_visibility == "invited" allows/forbids appropriately for Real users -ath m.room.history_visibility == "joined" allows/forbids appropriately for Real users -ath m.room.history_visibility == "default" allows/forbids appropriately for Real users -ath Real non-joined user cannot call /events on shared room -ath Real non-joined user cannot call /events on invited room -ath Real non-joined user cannot call /events on joined room -ath Real non-joined user cannot call /events on default room -ath Real non-joined user can call /events on world_readable room -ath Real non-joined users can get state for world_readable rooms -ath Real non-joined users can get individual state for world_readable rooms -ath Real non-joined users cannot room initalSync for non-world_readable rooms -ath Real non-joined users can room initialSync for world_readable rooms -ath Real non-joined users can get individual state for world_readable rooms after leaving -ath Real non-joined users cannot send messages to guest_access rooms if not joined -ath Real users can sync from world_readable guest_access rooms if joined -ath Real users can sync from shared guest_access rooms if joined -ath Real users can sync from invited guest_access rooms if joined -ath Real users can sync from joined guest_access rooms if joined -ath Real users can sync from default guest_access rooms if joined -ath Only see history_visibility changes on boundaries -f,ath Backfill works correctly with history visibility set to joined -fgt Forgotten room messages cannot be paginated -fgt Forgetting room does not show up in v2 /sync -fgt Can forget room you've been kicked from -fgt Can't forget room you're still in -fgt Can re-join room if re-invited -ath Only original members of the room can see messages from erased users -mem /joined_rooms returns only joined rooms -mem /joined_members return joined members -ctx /context/ on joined room works -ctx /context/ on non world readable room does not work -ctx /context/ returns correct number of events -ctx /context/ with lazy_load_members filter works -get /event/ on joined room works -get /event/ on non world readable room does not work -get /event/ does not allow access to events before the user joined -mem Can get rooms/{roomId}/members -mem Can get rooms/{roomId}/members at a given point -mem Can filter rooms/{roomId}/members -upg /upgrade creates a new room -upg /upgrade should preserve room visibility for public rooms -upg /upgrade should preserve room visibility for private rooms -upg /upgrade copies >100 power levels to the new room -upg /upgrade copies the power levels to the new room -upg /upgrade preserves the power level of the upgrading user in old and new rooms -upg /upgrade copies important state to the new room -upg /upgrade copies ban events to the new room -upg local user has push rules copied to upgraded room -f,upg remote user has push rules copied to upgraded room -upg /upgrade moves aliases to the new room -upg /upgrade moves remote aliases to the new room -upg /upgrade preserves direct room state -upg /upgrade preserves room federation ability -upg /upgrade restricts power levels in the old room -upg /upgrade restricts power levels in the old room when the old PLs are unusual -upg /upgrade to an unknown version is rejected -upg /upgrade is rejected if the user can't send state events -upg /upgrade of a bogus room fails gracefully -upg Cannot send tombstone event that points to the same room -f,upg Local and remote users' homeservers remove a room from their public directory on upgrade -rst Name/topic keys are correct -f,pub Can get remote public room list -pub Can paginate public room list -pub Can search public room list -syn Can create filter -syn Can download filter -syn Can sync -syn Can sync a joined room -syn Full state sync includes joined rooms -syn Newly joined room is included in an incremental sync -syn Newly joined room has correct timeline in incremental sync -syn Newly joined room includes presence in incremental sync -syn Get presence for newly joined members in incremental sync -syn Can sync a room with a single message -syn Can sync a room with a message with a transaction id -syn A message sent after an initial sync appears in the timeline of an incremental sync. -syn A filtered timeline reaches its limit -syn Syncing a new room with a large timeline limit isn't limited -syn A full_state incremental update returns only recent timeline -syn A prev_batch token can be used in the v1 messages API -syn A next_batch token can be used in the v1 messages API -syn User sees their own presence in a sync -syn User is offline if they set_presence=offline in their sync -syn User sees updates to presence from other users in the incremental sync. -syn State is included in the timeline in the initial sync -f,syn State from remote users is included in the state in the initial sync -syn Changes to state are included in an incremental sync -syn Changes to state are included in an gapped incremental sync -f,syn State from remote users is included in the timeline in an incremental sync -syn A full_state incremental update returns all state -syn When user joins a room the state is included in the next sync -syn A change to displayname should not result in a full state sync -syn A change to displayname should appear in incremental /sync -syn When user joins a room the state is included in a gapped sync -syn When user joins and leaves a room in the same batch, the full state is still included in the next sync -syn Current state appears in timeline in private history -syn Current state appears in timeline in private history with many messages before -syn Current state appears in timeline in private history with many messages after -syn Rooms a user is invited to appear in an initial sync -syn Rooms a user is invited to appear in an incremental sync -syn Newly joined room is included in an incremental sync after invite -syn Sync can be polled for updates -syn Sync is woken up for leaves -syn Left rooms appear in the leave section of sync -syn Newly left rooms appear in the leave section of incremental sync -syn We should see our own leave event, even if history_visibility is restricted (SYN-662) -syn We should see our own leave event when rejecting an invite, even if history_visibility is restricted (riot-web/3462) -syn Newly left rooms appear in the leave section of gapped sync -syn Previously left rooms don't appear in the leave section of sync -syn Left rooms appear in the leave section of full state sync -syn Archived rooms only contain history from before the user left -syn Banned rooms appear in the leave section of sync -syn Newly banned rooms appear in the leave section of incremental sync -syn Newly banned rooms appear in the leave section of incremental sync -syn Typing events appear in initial sync -syn Typing events appear in incremental sync -syn Typing events appear in gapped sync -syn Read receipts appear in initial v2 /sync -syn New read receipts appear in incremental v2 /sync -syn Can pass a JSON filter as a query parameter -syn Can request federation format via the filter -syn Read markers appear in incremental v2 /sync -syn Read markers appear in initial v2 /sync -syn Read markers can be updated -syn Lazy loading parameters in the filter are strictly boolean -syn The only membership state included in an initial sync is for all the senders in the timeline -syn The only membership state included in an incremental sync is for senders in the timeline -syn The only membership state included in a gapped incremental sync is for senders in the timeline -syn Gapped incremental syncs include all state changes -syn Old leaves are present in gapped incremental syncs -syn Leaves are present in non-gapped incremental syncs -syn Old members are included in gappy incr LL sync if they start speaking -syn Members from the gap are included in gappy incr LL sync -syn We don't send redundant membership state across incremental syncs by default -syn We do send redundant membership state across incremental syncs if asked -syn Unnamed room comes with a name summary -syn Named room comes with just joined member count summary -syn Room summary only has 5 heroes -syn Room summary counts change when membership changes -rmv User can create and send/receive messages in a room with version 1 -rmv User can create and send/receive messages in a room with version 1 (2 subtests) -rmv local user can join room with version 1 -rmv User can invite local user to room with version 1 -rmv remote user can join room with version 1 -rmv User can invite remote user to room with version 1 -rmv Remote user can backfill in a room with version 1 -rmv Can reject invites over federation for rooms with version 1 -rmv Can receive redactions from regular users over federation in room version 1 -rmv User can create and send/receive messages in a room with version 2 -rmv User can create and send/receive messages in a room with version 2 (2 subtests) -rmv local user can join room with version 2 -rmv User can invite local user to room with version 2 -rmv remote user can join room with version 2 -rmv User can invite remote user to room with version 2 -rmv Remote user can backfill in a room with version 2 -rmv Can reject invites over federation for rooms with version 2 -rmv Can receive redactions from regular users over federation in room version 2 -rmv User can create and send/receive messages in a room with version 3 -rmv User can create and send/receive messages in a room with version 3 (2 subtests) -rmv local user can join room with version 3 -rmv User can invite local user to room with version 3 -rmv remote user can join room with version 3 -rmv User can invite remote user to room with version 3 -rmv Remote user can backfill in a room with version 3 -rmv Can reject invites over federation for rooms with version 3 -rmv Can receive redactions from regular users over federation in room version 3 -rmv User can create and send/receive messages in a room with version 4 -rmv User can create and send/receive messages in a room with version 4 (2 subtests) -rmv local user can join room with version 4 -rmv User can invite local user to room with version 4 -rmv remote user can join room with version 4 -rmv User can invite remote user to room with version 4 -rmv Remote user can backfill in a room with version 4 -rmv Can reject invites over federation for rooms with version 4 -rmv Can receive redactions from regular users over federation in room version 4 -rmv User can create and send/receive messages in a room with version 5 -rmv User can create and send/receive messages in a room with version 5 (2 subtests) -rmv local user can join room with version 5 -rmv User can invite local user to room with version 5 -rmv remote user can join room with version 5 -rmv User can invite remote user to room with version 5 -rmv Remote user can backfill in a room with version 5 -rmv Can reject invites over federation for rooms with version 5 -rmv Can receive redactions from regular users over federation in room version 5 -rmv User can create and send/receive messages in a room with version 6 -rmv User can create and send/receive messages in a room with version 6 (2 subtests) -rmv local user can join room with version 6 -rmv User can invite local user to room with version 6 -rmv remote user can join room with version 6 -rmv User can invite remote user to room with version 6 -rmv Remote user can backfill in a room with version 6 -rmv Can reject invites over federation for rooms with version 6 -rmv Can receive redactions from regular users over federation in room version 6 -rmv Inbound federation rejects invites which include invalid JSON for room version 6 -rmv Outbound federation rejects invite response which include invalid JSON for room version 6 -rmv Inbound federation rejects invite rejections which include invalid JSON for room version 6 -rmv Server rejects invalid JSON in a version 6 room -pre Presence changes are reported to local room members -f,pre Presence changes are also reported to remote room members -pre Presence changes to UNAVAILABLE are reported to local room members -f,pre Presence changes to UNAVAILABLE are reported to remote room members -v1s Newly created users see their own presence in /initialSync (SYT-34) -dvk Can upload device keys -dvk Should reject keys claiming to belong to a different user -dvk Can query device keys using POST -dvk Can query specific device keys using POST -dvk query for user with no keys returns empty key dict -dvk Can claim one time key using POST -f,dvk Can query remote device keys using POST -f,dvk Can claim remote one time key using POST -dvk Local device key changes appear in v2 /sync -dvk Local new device changes appear in v2 /sync -dvk Local delete device changes appear in v2 /sync -dvk Local update device changes appear in v2 /sync -dvk Can query remote device keys using POST after notification -f,dev Device deletion propagates over federation -f,dev If remote user leaves room, changes device and rejoins we see update in sync -f,dev If remote user leaves room we no longer receive device updates -dvk Local device key changes appear in /keys/changes -dvk New users appear in /keys/changes -f,dvk If remote user leaves room, changes device and rejoins we see update in /keys/changes -dvk Get left notifs in sync and /keys/changes when other user leaves -dvk Get left notifs for other users in sync and /keys/changes when user leaves -f,dvk If user leaves room, remote user changes device and rejoins we see update in /sync and /keys/changes -dkb Can create backup version -dkb Can update backup version -dkb Responds correctly when backup is empty -dkb Can backup keys -dkb Can update keys with better versions -dkb Will not update keys with worse versions -dkb Will not back up to an old backup version -dkb Can delete backup -dkb Deleted & recreated backups are empty -dkb Can create more than 10 backup versions -xsk Can upload self-signing keys -xsk Fails to upload self-signing keys with no auth -xsk Fails to upload self-signing key without master key -xsk Changing master key notifies local users -xsk Changing user-signing key notifies local users -f,xsk can fetch self-signing keys over federation -f,xsk uploading self-signing key notifies over federation -f,xsk uploading signed devices gets propagated over federation -tag Can add tag -tag Can remove tag -tag Can list tags for a room -v1s Tags appear in the v1 /events stream -v1s Tags appear in the v1 /initalSync -v1s Tags appear in the v1 room initial sync -tag Tags appear in an initial v2 /sync -tag Newly updated tags appear in an incremental v2 /sync -tag Deleted tags appear in an incremental v2 /sync -tag local user has tags copied to the new room -f,tag remote user has tags copied to the new room -sch Can search for an event by body -sch Can get context around search results -sch Can back-paginate search results -sch Search works across an upgraded room and its predecessor -sch Search results with rank ordering do not include redacted events -sch Search results with recent ordering do not include redacted events -acc Can add account data -acc Can add account data to room -acc Can get account data without syncing -acc Can get room account data without syncing -v1s Latest account data comes down in /initialSync -v1s Latest account data comes down in room initialSync -v1s Account data appears in v1 /events stream -v1s Room account data appears in v1 /events stream -acc Latest account data appears in v2 /sync -acc New account data appears in incremental v2 /sync -oid Can generate a openid access_token that can be exchanged for information about a user -oid Invalid openid access tokens are rejected -oid Requests to userinfo without access tokens are rejected -std Can send a message directly to a device using PUT /sendToDevice -std Can recv a device message using /sync -std Can recv device messages until they are acknowledged -std Device messages with the same txn_id are deduplicated -std Device messages wake up /sync -std Can recv device messages over federation -fsd Device messages over federation wake up /sync -std Can send messages with a wildcard device id -std Can send messages with a wildcard device id to two devices -std Wildcard device messages wake up /sync -fsd Wildcard device messages over federation wake up /sync -adm /whois -nsp /purge_history -nsp /purge_history by ts -nsp Can backfill purged history -nsp Shutdown room -ign Ignore user in existing room -ign Ignore invite in full sync -ign Ignore invite in incremental sync -fky Checking local federation server -fky Federation key API allows unsigned requests for keys -fky Federation key API can act as a notary server via a GET request -fky Federation key API can act as a notary server via a POST request -fky Key notary server should return an expired key if it can't find any others -fky Key notary server must not overwrite a valid key with a spurious result from the origin server -fqu Non-numeric ports in server names are rejected -fqu Outbound federation can query profile data -fqu Inbound federation can query profile data -fqu Outbound federation can query room alias directory -fqu Inbound federation can query room alias directory -fsj Outbound federation can query v1 /send_join -fsj Outbound federation can query v2 /send_join -fmj Outbound federation passes make_join failures through to the client -fsj Inbound federation can receive v1 /send_join -fsj Inbound federation can receive v2 /send_join -fmj Inbound /v1/make_join rejects remote attempts to join local users to rooms -fsj Inbound /v1/send_join rejects incorrectly-signed joins -fsj Inbound /v1/send_join rejects joins from other servers -fau Inbound federation rejects remote attempts to kick local users to rooms -frv Inbound federation rejects attempts to join v1 rooms from servers without v1 support -frv Inbound federation rejects attempts to join v2 rooms from servers lacking version support -frv Inbound federation rejects attempts to join v2 rooms from servers only supporting v1 -frv Inbound federation accepts attempts to join v2 rooms from servers with support -frv Outbound federation correctly handles unsupported room versions -frv A pair of servers can establish a join in a v2 room -fsj Outbound federation rejects send_join responses with no m.room.create event -frv Outbound federation rejects m.room.create events with an unknown room version -fsj Event with an invalid signature in the send_join response should not cause room join to fail -fsj Inbound: send_join rejects invalid JSON for room version 6 -fed Outbound federation can send events -fed Inbound federation can receive events -fed Inbound federation can receive redacted events -fed Ephemeral messages received from servers are correctly expired -fed Events whose auth_events are in the wrong room do not mess up the room state -fed Inbound federation can return events -fed Inbound federation redacts events from erased users -fme Outbound federation can request missing events -fme Inbound federation can return missing events for world_readable visibility -fme Inbound federation can return missing events for shared visibility -fme Inbound federation can return missing events for invite visibility -fme Inbound federation can return missing events for joined visibility -fme outliers whose auth_events are in a different room are correctly rejected -fbk Outbound federation can backfill events -fbk Inbound federation can backfill events -fbk Backfill checks the events requested belong to the room -fbk Backfilled events whose prev_events are in a different room do not allow cross-room back-pagination -fiv Outbound federation can send invites via v1 API -fiv Outbound federation can send invites via v2 API -fiv Inbound federation can receive invites via v1 API -fiv Inbound federation can receive invites via v2 API -fiv Inbound federation can receive invite and reject when remote replies with a 403 -fiv Inbound federation can receive invite and reject when remote replies with a 500 -fiv Inbound federation can receive invite and reject when remote is unreachable -fiv Inbound federation rejects invites which are not signed by the sender -fiv Inbound federation can receive invite rejections -fiv Inbound federation rejects incorrectly-signed invite rejections -fsl Inbound /v1/send_leave rejects leaves from other servers -fst Inbound federation can get state for a room -fst Inbound federation of state requires event_id as a mandatory paramater -fst Inbound federation can get state_ids for a room -fst Inbound federation of state_ids requires event_id as a mandatory paramater -fst Federation rejects inbound events where the prev_events cannot be found -fst Room state at a rejected message event is the same as its predecessor -fst Room state at a rejected state event is the same as its predecessor -fst Outbound federation requests missing prev_events and then asks for /state_ids and resolves the state -fst Federation handles empty auth_events in state_ids sanely -fst Getting state checks the events requested belong to the room -fst Getting state IDs checks the events requested belong to the room -fst Should not be able to take over the room by pretending there is no PL event -fpb Inbound federation can get public room list -fed Outbound federation sends receipts -fed Inbound federation rejects receipts from wrong remote -fed Inbound federation ignores redactions from invalid servers room > v3 -fed An event which redacts an event in a different room should be ignored -fed An event which redacts itself should be ignored -fed A pair of events which redact each other should be ignored -fdk Local device key changes get to remote servers -fdk Server correctly handles incoming m.device_list_update -fdk Server correctly resyncs when client query keys and there is no remote cache -fdk Server correctly resyncs when server leaves and rejoins a room -fdk Local device key changes get to remote servers with correct prev_id -fdk Device list doesn't change if remote server is down -fdk If a device list update goes missing, the server resyncs on the next one -fst Name/topic keys are correct -fau Remote servers cannot set power levels in rooms without existing powerlevels -fau Remote servers should reject attempts by non-creators to set the power levels -fau Inbound federation rejects typing notifications from wrong remote -fau Users cannot set notifications powerlevel higher than their own -fed Forward extremities remain so even after the next events are populated as outliers -fau Banned servers cannot send events -fau Banned servers cannot /make_join -fau Banned servers cannot /send_join -fau Banned servers cannot /make_leave -fau Banned servers cannot /send_leave -fau Banned servers cannot /invite -fau Banned servers cannot get room state -fau Banned servers cannot get room state ids -fau Banned servers cannot backfill -fau Banned servers cannot /event_auth -fau Banned servers cannot get missing events -fau Server correctly handles transactions that break edu limits -fau Inbound federation correctly soft fails events -fau Inbound federation accepts a second soft-failed event -fau Inbound federation correctly handles soft failed events as extremities -med Can upload with Unicode file name -med Can download with Unicode file name locally -f,med Can download with Unicode file name over federation -med Alternative server names do not cause a routing loop -med Can download specifying a different Unicode file name -med Can upload without a file name -med Can download without a file name locally -f,med Can download without a file name over federation -med Can upload with ASCII file name -med Can download file 'ascii' -med Can download file 'name with spaces' -med Can download file 'name;with;semicolons' -med Can download specifying a different ASCII file name -med Can send image in room message -med Can fetch images in room -med POSTed media can be thumbnailed -f,med Remote media can be thumbnailed -med Test URL preview -med Can read configuration endpoint -nsp Can quarantine media in rooms -udr User appears in user directory -udr User in private room doesn't appear in user directory -udr User joining then leaving public room appears and dissappears from directory -udr Users appear/disappear from directory when join_rules are changed -udr Users appear/disappear from directory when history_visibility are changed -udr Users stay in directory when join_rules are changed but history_visibility is world_readable -f,udr User in remote room doesn't appear in user directory after server left room -udr User directory correctly update on display name change -udr User in shared private room does appear in user directory -udr User in shared private room does appear in user directory until leave -udr User in dir while user still shares private rooms -nsp Create group -nsp Add group rooms -nsp Remove group rooms -nsp Get local group profile -nsp Get local group users -nsp Add/remove local group rooms -nsp Get local group summary -nsp Get remote group profile -nsp Get remote group users -nsp Add/remove remote group rooms -nsp Get remote group summary -nsp Add local group users -nsp Remove self from local group -nsp Remove other from local group -nsp Add remote group users -nsp Remove self from remote group -nsp Listing invited users of a remote group when not a member returns a 403 -nsp Add group category -nsp Remove group category -nsp Get group categories -nsp Add group role -nsp Remove group role -nsp Get group roles -nsp Add room to group summary -nsp Adding room to group summary keeps room_id when fetching rooms in group -nsp Adding multiple rooms to group summary have correct order -nsp Remove room from group summary -nsp Add room to group summary with category -nsp Remove room from group summary with category -nsp Add user to group summary -nsp Adding multiple users to group summary have correct order -nsp Remove user from group summary -nsp Add user to group summary with role -nsp Remove user from group summary with role -nsp Local group invites come down sync -nsp Group creator sees group in sync -nsp Group creator sees group in initial sync -nsp Get/set local group publicity -nsp Bulk get group publicity -nsp Joinability comes down summary -nsp Set group joinable and join it -nsp Group is not joinable by default -nsp Group is joinable over federation -nsp Room is transitioned on local and remote groups upon room upgrade -3pd Can bind 3PID via home server -3pd Can bind and unbind 3PID via homeserver -3pd Can unbind 3PID via homeserver when bound out of band -3pd 3PIDs are unbound after account deactivation -3pd Can bind and unbind 3PID via /unbind by specifying the identity server -3pd Can bind and unbind 3PID via /unbind without specifying the identity server -app AS can create a user -app AS can create a user with an underscore -app AS can create a user with inhibit_login -app AS cannot create users outside its own namespace -app Regular users cannot register within the AS namespace -app AS can make room aliases -app Regular users cannot create room aliases within the AS namespace -app AS-ghosted users can use rooms via AS -app AS-ghosted users can use rooms themselves -app Ghost user must register before joining room -app AS can set avatar for ghosted users -app AS can set displayname for ghosted users -app AS can't set displayname for random users -app Inviting an AS-hosted user asks the AS server -app Accesing an AS-hosted room alias asks the AS server -app Events in rooms with AS-hosted room aliases are sent to AS server -app AS user (not ghost) can join room without registering -app AS user (not ghost) can join room without registering, with user_id query param -app HS provides query metadata -app HS can provide query metadata on a single protocol -app HS will proxy request for 3PU mapping -app HS will proxy request for 3PL mapping -app AS can publish rooms in their own list -app AS and main public room lists are separate -app AS can deactivate a user -psh Test that a message is pushed -psh Invites are pushed -psh Rooms with names are correctly named in pushed -psh Rooms with canonical alias are correctly named in pushed -psh Rooms with many users are correctly pushed -psh Don't get pushed for rooms you've muted -psh Rejected events are not pushed -psh Can add global push rule for room -psh Can add global push rule for sender -psh Can add global push rule for content -psh Can add global push rule for override -psh Can add global push rule for underride -psh Can add global push rule for content -psh New rules appear before old rules by default -psh Can add global push rule before an existing rule -psh Can add global push rule after an existing rule -psh Can delete a push rule -psh Can disable a push rule -psh Adding the same push rule twice is idempotent -psh Messages that notify from another user increment unread notification count -psh Messages that highlight from another user increment unread highlight count -psh Can change the actions of default rules -psh Changing the actions of an unknown default rule fails with 404 -psh Can change the actions of a user specified rule -psh Changing the actions of an unknown rule fails with 404 -psh Can fetch a user's pushers -psh Push rules come down in an initial /sync -psh Adding a push rule wakes up an incremental /sync -psh Disabling a push rule wakes up an incremental /sync -psh Enabling a push rule wakes up an incremental /sync -psh Setting actions for a push rule wakes up an incremental /sync -psh Can enable/disable default rules -psh Enabling an unknown default rule fails with 404 -psh Test that rejected pushers are removed. -psh Notifications can be viewed with GET /notifications -psh Trying to add push rule with no scope fails with 400 -psh Trying to add push rule with invalid scope fails with 400 -psh Trying to add push rule with missing template fails with 400 -psh Trying to add push rule with missing rule_id fails with 400 -psh Trying to add push rule with empty rule_id fails with 400 -psh Trying to add push rule with invalid template fails with 400 -psh Trying to add push rule with rule_id with slashes fails with 400 -psh Trying to add push rule with override rule without conditions fails with 400 -psh Trying to add push rule with underride rule without conditions fails with 400 -psh Trying to add push rule with condition without kind fails with 400 -psh Trying to add push rule with content rule without pattern fails with 400 -psh Trying to add push rule with no actions fails with 400 -psh Trying to add push rule with invalid action fails with 400 -psh Trying to add push rule with invalid attr fails with 400 -psh Trying to add push rule with invalid value for enabled fails with 400 -psh Trying to get push rules with no trailing slash fails with 400 -psh Trying to get push rules with scope without trailing slash fails with 400 -psh Trying to get push rules with template without tailing slash fails with 400 -psh Trying to get push rules with unknown scope fails with 400 -psh Trying to get push rules with unknown template fails with 400 -psh Trying to get push rules with unknown attribute fails with 400 -psh Trying to get push rules with unknown rule_id fails with 404 -psh Rooms with names are correctly named in pushes -v1s GET /initialSync with non-numeric 'limit' -v1s GET /events with non-numeric 'limit' -v1s GET /events with negative 'limit' -v1s GET /events with non-numeric 'timeout' -ath Event size limits -syn Check creating invalid filters returns 4xx -f,pre New federated private chats get full presence information (SYN-115) -pre Left room members do not cause problems for presence -crm Rooms can be created with an initial invite list (SYN-205) (1 subtests) -typ Typing notifications don't leak -ban Non-present room members cannot ban others -psh Getting push rules doesn't corrupt the cache SYN-390 -inv Test that we can be reinvited to a room we created -syn Multiple calls to /sync should not cause 500 errors -gst Guest user can call /events on another world_readable room (SYN-606) -gst Real user can call /events on another world_readable room (SYN-606) -gst Events come down the correct room -pub Asking for a remote rooms list, but supplying the local server's name, returns the local rooms list -std Can send a to-device message to two users which both receive it using /sync -fme Outbound federation will ignore a missing event with bad JSON for room version 6 -fbk Outbound federation rejects backfill containing invalid JSON for events in room version 6 -jso Invalid JSON integers -jso Invalid JSON floats -jso Invalid JSON special values -inv Can invite users to invite-only rooms (2 subtests) -plv setting 'm.room.name' respects room powerlevel (2 subtests) -psh Messages that notify from another user increment notification_count -psh Messages that org.matrix.msc2625.mark_unread from another user increment org.matrix.msc2625.unread_count -dvk Can claim one time key using POST (2 subtests) -fdk Can query remote device keys using POST (1 subtests) -fdk Can claim remote one time key using POST (2 subtests) -fmj Inbound /make_join rejects attempts to join rooms where all users have left \ No newline at end of file diff --git a/tests/sytest/are-we-synapse-yet.py b/tests/sytest/are-we-synapse-yet.py deleted file mode 100755 index 3d21fa41..00000000 --- a/tests/sytest/are-we-synapse-yet.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import division -import argparse -import re -import sys - -# Usage: $ ./are-we-synapse-yet.py [-v] results.tap -# This script scans a results.tap file from Dendrite's CI process and spits out -# a rating of how close we are to Synapse parity, based purely on SyTests. -# The main complexity is grouping tests sensibly into features like 'Registration' -# and 'Federation'. Then it just checks the ones which are passing and calculates -# percentages for each group. Produces results like: -# -# Client-Server APIs: 29% (196/666 tests) -# ------------------- -# Registration : 62% (20/32 tests) -# Login : 7% (1/15 tests) -# V1 CS APIs : 10% (3/30 tests) -# ... -# -# or in verbose mode: -# -# Client-Server APIs: 29% (196/666 tests) -# ------------------- -# Registration : 62% (20/32 tests) -# ✓ GET /register yields a set of flows -# ✓ POST /register can create a user -# ✓ POST /register downcases capitals in usernames -# ... -# -# You can also tack `-v` on to see exactly which tests each category falls under. - -test_mappings = { - "nsp": "Non-Spec API", - "unk": "Unknown API (no group specified)", - "app": "Application Services API", - "f": "Federation", # flag to mark test involves federation - - "federation_apis": { - "fky": "Key API", - "fsj": "send_join API", - "fmj": "make_join API", - "fsl": "send_leave API", - "fiv": "Invite API", - "fqu": "Query API", - "frv": "room versions", - "fau": "Auth", - "fbk": "Backfill API", - "fme": "get_missing_events API", - "fst": "State APIs", - "fpb": "Public Room API", - "fdk": "Device Key APIs", - "fed": "Federation API", - "fsd": "Send-to-Device APIs", - }, - - "client_apis": { - "reg": "Registration", - "log": "Login", - "lox": "Logout", - "v1s": "V1 CS APIs", - "csa": "Misc CS APIs", - "pro": "Profile", - "dev": "Devices", - "dvk": "Device Keys", - "dkb": "Device Key Backup", - "xsk": "Cross-signing Keys", - "pre": "Presence", - "crm": "Create Room", - "syn": "Sync API", - "rmv": "Room Versions", - "rst": "Room State APIs", - "pub": "Public Room APIs", - "mem": "Room Membership", - "ali": "Room Aliases", - "jon": "Joining Rooms", - "lev": "Leaving Rooms", - "inv": "Inviting users to Rooms", - "ban": "Banning users", - "snd": "Sending events", - "get": "Getting events for Rooms", - "rct": "Receipts", - "red": "Read markers", - "med": "Media APIs", - "cap": "Capabilities API", - "typ": "Typing API", - "psh": "Push APIs", - "acc": "Account APIs", - "eph": "Ephemeral Events", - "plv": "Power Levels", - "xxx": "Redaction", - "3pd": "Third-Party ID APIs", - "gst": "Guest APIs", - "ath": "Room Auth", - "fgt": "Forget APIs", - "ctx": "Context APIs", - "upg": "Room Upgrade APIs", - "tag": "Tagging APIs", - "sch": "Search APIs", - "oid": "OpenID API", - "std": "Send-to-Device APIs", - "adm": "Server Admin API", - "ign": "Ignore Users", - "udr": "User Directory APIs", - "jso": "Enforced canonical JSON", - }, -} - -# optional 'not ' with test number then anything but '#' -re_testname = re.compile(r"^(not )?ok [0-9]+ ([^#]+)") - -# Parses lines like the following: -# -# SUCCESS: ok 3 POST /register downcases capitals in usernames -# FAIL: not ok 54 (expected fail) POST /createRoom creates a room with the given version -# SKIP: ok 821 Multiple calls to /sync should not cause 500 errors # skip lack of can_post_room_receipts -# EXPECT FAIL: not ok 822 (expected fail) Guest user can call /events on another world_readable room (SYN-606) # TODO expected fail -# -# Only SUCCESS lines are treated as success, the rest are not implemented. -# -# Returns a dict like: -# { name: "...", ok: True } -def parse_test_line(line): - if not line.startswith("ok ") and not line.startswith("not ok "): - return - re_match = re_testname.match(line) - test_name = re_match.groups()[1].replace("(expected fail) ", "").strip() - test_pass = False - if line.startswith("ok ") and not "# skip " in line: - test_pass = True - return { - "name": test_name, - "ok": test_pass, - } - -# Prints the stats for a complete section. -# header_name => "Client-Server APIs" -# gid_to_tests => { gid: { : True|False }} -# gid_to_name => { gid: "Group Name" } -# verbose => True|False -# Produces: -# Client-Server APIs: 29% (196/666 tests) -# ------------------- -# Registration : 62% (20/32 tests) -# Login : 7% (1/15 tests) -# V1 CS APIs : 10% (3/30 tests) -# ... -# or in verbose mode: -# Client-Server APIs: 29% (196/666 tests) -# ------------------- -# Registration : 62% (20/32 tests) -# ✓ GET /register yields a set of flows -# ✓ POST /register can create a user -# ✓ POST /register downcases capitals in usernames -# ... -def print_stats(header_name, gid_to_tests, gid_to_name, verbose): - subsections = [] # Registration: 100% (13/13 tests) - subsection_test_names = {} # 'subsection name': ["✓ Test 1", "✓ Test 2", "× Test 3"] - total_passing = 0 - total_tests = 0 - for gid, tests in gid_to_tests.items(): - group_total = len(tests) - if group_total == 0: - continue - group_passing = 0 - test_names_and_marks = [] - for name, passing in tests.items(): - if passing: - group_passing += 1 - test_names_and_marks.append(f"{'✓' if passing else '×'} {name}") - - total_tests += group_total - total_passing += group_passing - pct = "{0:.0f}%".format(group_passing/group_total * 100) - line = "%s: %s (%d/%d tests)" % (gid_to_name[gid].ljust(25, ' '), pct.rjust(4, ' '), group_passing, group_total) - subsections.append(line) - subsection_test_names[line] = test_names_and_marks - - pct = "{0:.0f}%".format(total_passing/total_tests * 100) - print("%s: %s (%d/%d tests)" % (header_name, pct, total_passing, total_tests)) - print("-" * (len(header_name)+1)) - for line in subsections: - print(" %s" % (line,)) - if verbose: - for test_name_and_pass_mark in subsection_test_names[line]: - print(" %s" % (test_name_and_pass_mark,)) - print("") - print("") - -def main(results_tap_path, verbose): - # Load up test mappings - test_name_to_group_id = {} - fed_tests = set() - client_tests = set() - with open("./are-we-synapse-yet.list", "r") as f: - for line in f.readlines(): - test_name = " ".join(line.split(" ")[1:]).strip() - groups = line.split(" ")[0].split(",") - for gid in groups: - if gid == "f" or gid in test_mappings["federation_apis"]: - fed_tests.add(test_name) - else: - client_tests.add(test_name) - if gid == "f": - continue # we expect another group ID - test_name_to_group_id[test_name] = gid - - # parse results.tap - summary = { - "client": { - # gid: { - # test_name: OK - # } - }, - "federation": { - # gid: { - # test_name: OK - # } - }, - "appservice": { - "app": {}, - }, - "nonspec": { - "nsp": {}, - "unk": {} - }, - } - with open(results_tap_path, "r") as f: - for line in f.readlines(): - test_result = parse_test_line(line) - if not test_result: - continue - name = test_result["name"] - group_id = test_name_to_group_id.get(name) - if not group_id: - summary["nonspec"]["unk"][name] = test_result["ok"] - if group_id == "nsp": - summary["nonspec"]["nsp"][name] = test_result["ok"] - elif group_id == "app": - summary["appservice"]["app"][name] = test_result["ok"] - elif group_id in test_mappings["federation_apis"]: - group = summary["federation"].get(group_id, {}) - group[name] = test_result["ok"] - summary["federation"][group_id] = group - elif group_id in test_mappings["client_apis"]: - group = summary["client"].get(group_id, {}) - group[name] = test_result["ok"] - summary["client"][group_id] = group - - print("Are We Synapse Yet?") - print("===================") - print("") - print_stats("Non-Spec APIs", summary["nonspec"], test_mappings, verbose) - print_stats("Client-Server APIs", summary["client"], test_mappings["client_apis"], verbose) - print_stats("Federation APIs", summary["federation"], test_mappings["federation_apis"], verbose) - print_stats("Application Services APIs", summary["appservice"], test_mappings, verbose) - - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("tap_file", help="path to results.tap") - parser.add_argument("-v", action="store_true", help="show individual test names in output") - args = parser.parse_args() - main(args.tap_file, args.v) \ No newline at end of file diff --git a/tests/sytest/show-expected-fail-tests.sh b/tests/sytest/show-expected-fail-tests.sh deleted file mode 100755 index 320d4ebd..00000000 --- a/tests/sytest/show-expected-fail-tests.sh +++ /dev/null @@ -1,105 +0,0 @@ -#! /bin/bash -# -# Parses a results.tap file from SyTest output and a file containing test names (a test whitelist) -# and checks whether a test name that exists in the whitelist (that should pass), failed or not. -# -# An optional blacklist file can be added, also containing test names, where if a test name is -# present, the script will not error even if the test is in the whitelist file and failed -# -# For each of these files, lines starting with '#' are ignored. -# -# Usage ./show-expected-fail-tests.sh results.tap whitelist [blacklist] - -results_file=$1 -whitelist_file=$2 -blacklist_file=$3 - -fail_build=0 - -if [ $# -lt 2 ]; then - echo "Usage: $0 results.tap whitelist [blacklist]" - exit 1 -fi - -if [ ! -f "$results_file" ]; then - echo "ERROR: Specified results file '${results_file}' doesn't exist." - fail_build=1 -fi - -if [ ! -f "$whitelist_file" ]; then - echo "ERROR: Specified test whitelist '${whitelist_file}' doesn't exist." - fail_build=1 -fi - -blacklisted_tests=() - -# Check if a blacklist file was provided -if [ $# -eq 3 ]; then - # Read test blacklist file - if [ ! -f "$blacklist_file" ]; then - echo "ERROR: Specified test blacklist file '${blacklist_file}' doesn't exist." - fail_build=1 - fi - - # Read each line, ignoring those that start with '#' - blacklisted_tests="" - search_non_comments=$(grep -v '^#' ${blacklist_file}) - while read -r line ; do - # Record the blacklisted test name - blacklisted_tests+=("${line}") - done <<< "${search_non_comments}" # This allows us to edit blacklisted_tests in the while loop -fi - -[ "$fail_build" = 0 ] || exit 1 - -passed_but_expected_fail=$(grep ' # TODO passed but expected fail' ${results_file} | sed -E 's/^ok [0-9]+ (\(expected fail\) )?//' | sed -E 's/( \([0-9]+ subtests\))? # TODO passed but expected fail$//') -tests_to_add="" -already_in_whitelist="" - -while read -r test_name; do - # Ignore empty lines - [ "${test_name}" = "" ] && continue - - grep "^${test_name}" "${whitelist_file}" > /dev/null 2>&1 - if [ "$?" != "0" ]; then - # Check if this test name is blacklisted - if printf '%s\n' "${blacklisted_tests[@]}" | grep -q -P "^${test_name}$"; then - # Don't notify about this test - continue - fi - - # Append this test_name to the existing list - tests_to_add="${tests_to_add}${test_name}\n" - fail_build=1 - else - already_in_whitelist="${already_in_whitelist}${test_name}\n" - fi -done <<< "${passed_but_expected_fail}" - -# TODO: Check that the same test doesn't exist in both the whitelist and blacklist -# TODO: Check that the same test doesn't appear twice in the whitelist|blacklist - -# Trim test output strings -tests_to_add=$(IFS=$'\n' echo "${tests_to_add[*]%%'\n'}") -already_in_whitelist=$(IFS=$'\n' echo "${already_in_whitelist[*]%%'\n'}") - -# Format output with markdown for buildkite annotation rendering purposes -if [ -n "${tests_to_add}" ] && [ -n "${already_in_whitelist}" ]; then - echo "### 📜 SyTest Whitelist Maintenance" -fi - -if [ -n "${tests_to_add}" ]; then - echo "**ERROR**: The following tests passed but are not present in \`$2\`. Please append them to the file:" - echo "\`\`\`" - echo -e "${tests_to_add}" - echo "\`\`\`" -fi - -if [ -n "${already_in_whitelist}" ]; then - echo "**WARN**: Tests in the whitelist still marked as **expected fail**:" - echo "\`\`\`" - echo -e "${already_in_whitelist}" - echo "\`\`\`" -fi - -exit ${fail_build} diff --git a/tests/sytest/sytest-blacklist b/tests/sytest/sytest-blacklist deleted file mode 100644 index 009de225..00000000 --- a/tests/sytest/sytest-blacklist +++ /dev/null @@ -1,7 +0,0 @@ -# This test checks for a room-alias key in the response which is not in the spec, we must add it back in whitelist when https://github.com/matrix-org/sytest/pull/880 is merged -POST /createRoom makes a public room -# These fails because they use a endpoint which is not in the spec, we must add them back in whitelist when https://github.com/matrix-org/sytest/issues/878 is closed -POST /createRoom makes a room with a name -POST /createRoom makes a room with a topic -Can /sync newly created room -POST /createRoom ignores attempts to set the room version via creation_content \ No newline at end of file diff --git a/tests/sytest/sytest-whitelist b/tests/sytest/sytest-whitelist deleted file mode 100644 index 1c969dba..00000000 --- a/tests/sytest/sytest-whitelist +++ /dev/null @@ -1,516 +0,0 @@ -/event/ does not allow access to events before the user joined -/event/ on joined room works -/event/ on non world readable room does not work -/joined_members return joined members -/joined_rooms returns only joined rooms -/whois -3pid invite join valid signature but revoked keys are rejected -3pid invite join valid signature but unreachable ID server are rejected -3pid invite join with wrong but valid signature are rejected -A change to displayname should appear in incremental /sync -A full_state incremental update returns all state -A full_state incremental update returns only recent timeline -A message sent after an initial sync appears in the timeline of an incremental sync. -A next_batch token can be used in the v1 messages API -A pair of events which redact each other should be ignored -A pair of servers can establish a join in a v2 room -A prev_batch token can be used in the v1 messages API -AS can create a user -AS can create a user with an underscore -AS can create a user with inhibit_login -AS can set avatar for ghosted users -AS can set displayname for ghosted users -AS can't set displayname for random users -AS cannot create users outside its own namespace -AS user (not ghost) can join room without registering -AS user (not ghost) can join room without registering, with user_id query param -After changing password, a different session no longer works by default -After changing password, can log in with new password -After changing password, can't log in with old password -After changing password, different sessions can optionally be kept -After changing password, existing session still works -After deactivating account, can't log in with an email -After deactivating account, can't log in with password -Alias creators can delete alias with no ops -Alias creators can delete canonical alias with no ops -Alternative server names do not cause a routing loop -An event which redacts an event in a different room should be ignored -An event which redacts itself should be ignored -Asking for a remote rooms list, but supplying the local server's name, returns the local rooms list -Backfill checks the events requested belong to the room -Backfill works correctly with history visibility set to joined -Backfilled events whose prev_events are in a different room do not allow cross-room back-pagination -Banned servers cannot /event_auth -Banned servers cannot /invite -Banned servers cannot /make_join -Banned servers cannot /make_leave -Banned servers cannot /send_join -Banned servers cannot /send_leave -Banned servers cannot backfill -Banned servers cannot get missing events -Banned servers cannot get room state -Banned servers cannot get room state ids -Banned servers cannot send events -Banned user is kicked and may not rejoin until unbanned -Both GET and PUT work -Can /sync newly created room -Can add account data -Can add account data to room -Can add tag -Can claim one time key using POST -Can claim remote one time key using POST -Can create filter -Can deactivate account -Can delete canonical alias -Can download file 'ascii' -Can download file 'name with spaces' -Can download file 'name;with;semicolons' -Can download filter -Can download specifying a different ASCII file name -Can download specifying a different Unicode file name -Can download with Unicode file name locally -Can download with Unicode file name over federation -Can download without a file name locally -Can download without a file name over federation -Can forget room you've been kicked from -Can get 'm.room.name' state for a departed room (SPEC-216) -Can get account data without syncing -Can get remote public room list -Can get room account data without syncing -Can get rooms/{roomId}/members -Can get rooms/{roomId}/members for a departed room (SPEC-216) -Can get rooms/{roomId}/state for a departed room (SPEC-216) -Can invite users to invite-only rooms -Can list tags for a room -Can logout all devices -Can logout current device -Can paginate public room list -Can pass a JSON filter as a query parameter -Can query device keys using POST -Can query remote device keys using POST -Can query specific device keys using POST -Can re-join room if re-invited -Can read configuration endpoint -Can receive redactions from regular users over federation in room version 1 -Can receive redactions from regular users over federation in room version 2 -Can receive redactions from regular users over federation in room version 3 -Can receive redactions from regular users over federation in room version 4 -Can receive redactions from regular users over federation in room version 5 -Can receive redactions from regular users over federation in room version 6 -Can recv a device message using /sync -Can recv a device message using /sync -Can recv device messages over federation -Can recv device messages until they are acknowledged -Can recv device messages until they are acknowledged -Can reject invites over federation for rooms with version 1 -Can reject invites over federation for rooms with version 2 -Can reject invites over federation for rooms with version 3 -Can reject invites over federation for rooms with version 4 -Can reject invites over federation for rooms with version 5 -Can reject invites over federation for rooms with version 6 -Can remove tag -Can search public room list -Can send a message directly to a device using PUT /sendToDevice -Can send a message directly to a device using PUT /sendToDevice -Can send a to-device message to two users which both receive it using /sync -Can send image in room message -Can send messages with a wildcard device id -Can send messages with a wildcard device id -Can send messages with a wildcard device id to two devices -Can send messages with a wildcard device id to two devices -Can sync -Can sync a joined room -Can sync a room with a message with a transaction id -Can sync a room with a single message -Can upload device keys -Can upload with ASCII file name -Can upload with Unicode file name -Can upload without a file name -Can't deactivate account with wrong password -Can't forget room you're still in -Changes to state are included in an gapped incremental sync -Changes to state are included in an incremental sync -Changing the actions of an unknown default rule fails with 404 -Changing the actions of an unknown rule fails with 404 -Checking local federation server -Creators can delete alias -Current state appears in timeline in private history -Current state appears in timeline in private history with many messages before -DELETE /device/{deviceId} -DELETE /device/{deviceId} requires UI auth user to match device owner -DELETE /device/{deviceId} with no body gives a 401 -Deleted tags appear in an incremental v2 /sync -Deleting a non-existent alias should return a 404 -Device list doesn't change if remote server is down -Device messages over federation wake up /sync -Device messages wake up /sync -Device messages wake up /sync -Device messages with the same txn_id are deduplicated -Device messages with the same txn_id are deduplicated -Enabling an unknown default rule fails with 404 -Event size limits -Event with an invalid signature in the send_join response should not cause room join to fail -Events come down the correct room -Events whose auth_events are in the wrong room do not mess up the room state -Existing members see new members' join events -Federation key API allows unsigned requests for keys -Federation key API can act as a notary server via a GET request -Federation key API can act as a notary server via a POST request -Federation rejects inbound events where the prev_events cannot be found -Fetching eventstream a second time doesn't yield the message again -Forgetting room does not show up in v2 /sync -Full state sync includes joined rooms -GET /capabilities is present and well formed for registered user -GET /device/{deviceId} -GET /device/{deviceId} gives a 404 for unknown devices -GET /devices -GET /directory/room/:room_alias yields room ID -GET /events initially -GET /events with negative 'limit' -GET /events with non-numeric 'limit' -GET /events with non-numeric 'timeout' -GET /initialSync initially -GET /joined_rooms lists newly-created room -GET /login yields a set of flows -GET /media/r0/download can fetch the value again -GET /profile/:user_id/avatar_url publicly accessible -GET /profile/:user_id/displayname publicly accessible -GET /publicRooms includes avatar URLs -GET /publicRooms lists newly-created room -GET /publicRooms lists rooms -GET /r0/capabilities is not public -GET /register yields a set of flows -GET /rooms/:room_id/joined_members fetches my membership -GET /rooms/:room_id/messages returns a message -GET /rooms/:room_id/state fetches entire room state -GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership -GET /rooms/:room_id/state/m.room.member/:user_id?format=event fetches my membership event -GET /rooms/:room_id/state/m.room.name gets name -GET /rooms/:room_id/state/m.room.power_levels can fetch levels -GET /rooms/:room_id/state/m.room.power_levels fetches powerlevels -GET /rooms/:room_id/state/m.room.topic gets topic -Get left notifs for other users in sync and /keys/changes when user leaves -Getting messages going forward is limited for a departed room (SPEC-216) -Getting push rules doesn't corrupt the cache SYN-390 -Getting state IDs checks the events requested belong to the room -Getting state checks the events requested belong to the room -Ghost user must register before joining room -Guest non-joined user cannot call /events on default room -Guest non-joined user cannot call /events on invited room -Guest non-joined user cannot call /events on joined room -Guest non-joined user cannot call /events on shared room -Guest non-joined users can get individual state for world_readable rooms -Guest non-joined users can get individual state for world_readable rooms after leaving -Guest non-joined users can get state for world_readable rooms -Guest non-joined users cannot room initalSync for non-world_readable rooms -Guest non-joined users cannot send messages to guest_access rooms if not joined -Guest user can set display names -Guest user cannot call /events globally -Guest user cannot upgrade other users -Guest users can accept invites to private rooms over federation -Guest users can join guest_access rooms -Guest users can send messages to guest_access rooms if joined -If a device list update goes missing, the server resyncs on the next one -If remote user leaves room we no longer receive device updates -If remote user leaves room, changes device and rejoins we see update in /keys/changes -If remote user leaves room, changes device and rejoins we see update in sync -Inbound /make_join rejects attempts to join rooms where all users have left -Inbound /v1/make_join rejects remote attempts to join local users to rooms -Inbound /v1/send_join rejects incorrectly-signed joins -Inbound /v1/send_join rejects joins from other servers -Inbound /v1/send_leave rejects leaves from other servers -Inbound federation accepts a second soft-failed event -Inbound federation accepts attempts to join v2 rooms from servers with support -Inbound federation can backfill events -Inbound federation can get public room list -Inbound federation can get state for a room -Inbound federation can get state_ids for a room -Inbound federation can query profile data -Inbound federation can query room alias directory -Inbound federation can receive events -Inbound federation can receive invites via v1 API -Inbound federation can receive invites via v2 API -Inbound federation can receive redacted events -Inbound federation can receive v1 /send_join -Inbound federation can receive v2 /send_join -Inbound federation can return events -Inbound federation can return missing events for invite visibility -Inbound federation can return missing events for world_readable visibility -Inbound federation correctly soft fails events -Inbound federation of state requires event_id as a mandatory paramater -Inbound federation of state_ids requires event_id as a mandatory paramater -Inbound federation rejects attempts to join v1 rooms from servers without v1 support -Inbound federation rejects attempts to join v2 rooms from servers lacking version support -Inbound federation rejects attempts to join v2 rooms from servers only supporting v1 -Inbound federation rejects invite rejections which include invalid JSON for room version 6 -Inbound federation rejects invites which include invalid JSON for room version 6 -Inbound federation rejects receipts from wrong remote -Inbound federation rejects remote attempts to join local users to rooms -Inbound federation rejects remote attempts to kick local users to rooms -Inbound federation rejects typing notifications from wrong remote -Inbound: send_join rejects invalid JSON for room version 6 -Invalid JSON floats -Invalid JSON integers -Invalid JSON special values -Invited user can reject invite -Invited user can reject invite over federation -Invited user can reject invite over federation for empty room -Invited user can reject invite over federation several times -Invited user can see room metadata -Inviting an AS-hosted user asks the AS server -Lazy loading parameters in the filter are strictly boolean -Left rooms appear in the leave section of full state sync -Local delete device changes appear in v2 /sync -Local device key changes appear in /keys/changes -Local device key changes appear in v2 /sync -Local device key changes get to remote servers -Local new device changes appear in v2 /sync -Local non-members don't see posted message events -Local room members can get room messages -Local room members see posted message events -Local update device changes appear in v2 /sync -Local users can peek by room alias -Local users can peek into world_readable rooms by room ID -Message history can be paginated -Message history can be paginated over federation -Name/topic keys are correct -New account data appears in incremental v2 /sync -New read receipts appear in incremental v2 /sync -New room members see their own join event -New users appear in /keys/changes -Newly banned rooms appear in the leave section of incremental sync -Newly joined room is included in an incremental sync -Newly joined room is included in an incremental sync after invite -Newly left rooms appear in the leave section of gapped sync -Newly left rooms appear in the leave section of incremental sync -Newly updated tags appear in an incremental v2 /sync -Non-numeric ports in server names are rejected -Outbound federation can backfill events -Outbound federation can query profile data -Outbound federation can query room alias directory -Outbound federation can query v1 /send_join -Outbound federation can query v2 /send_join -Outbound federation can request missing events -Outbound federation can send events -Outbound federation can send invites via v1 API -Outbound federation can send invites via v2 API -Outbound federation can send room-join requests -Outbound federation correctly handles unsupported room versions -Outbound federation passes make_join failures through to the client -Outbound federation rejects backfill containing invalid JSON for events in room version 6 -Outbound federation rejects m.room.create events with an unknown room version -Outbound federation rejects send_join responses with no m.room.create event -Outbound federation sends receipts -Outbound federation will ignore a missing event with bad JSON for room version 6 -POST /createRoom creates a room with the given version -POST /createRoom ignores attempts to set the room version via creation_content -POST /createRoom makes a private room -POST /createRoom makes a private room with invites -POST /createRoom makes a public room -POST /createRoom makes a room with a name -POST /createRoom makes a room with a topic -POST /createRoom rejects attempts to create rooms with numeric versions -POST /createRoom rejects attempts to create rooms with unknown versions -POST /createRoom with creation content -POST /join/:room_alias can join a room -POST /join/:room_alias can join a room with custom content -POST /join/:room_id can join a room -POST /join/:room_id can join a room with custom content -POST /login as non-existing user is rejected -POST /login can log in as a user -POST /login can log in as a user with just the local part of the id -POST /login returns the same device_id as that in the request -POST /login wrong password is rejected -POST /media/r0/upload can create an upload -POST /redact disallows redaction of event in different room -POST /register allows registration of usernames with '-' -POST /register allows registration of usernames with '.' -POST /register allows registration of usernames with '/' -POST /register allows registration of usernames with '3' -POST /register allows registration of usernames with '=' -POST /register allows registration of usernames with '_' -POST /register allows registration of usernames with 'q' -POST /register can create a user -POST /register downcases capitals in usernames -POST /register rejects registration of usernames with '!' -POST /register rejects registration of usernames with '"' -POST /register rejects registration of usernames with ''' -POST /register rejects registration of usernames with ':' -POST /register rejects registration of usernames with '?' -POST /register rejects registration of usernames with '@' -POST /register rejects registration of usernames with '[' -POST /register rejects registration of usernames with '\' -POST /register rejects registration of usernames with '\n' -POST /register rejects registration of usernames with ']' -POST /register rejects registration of usernames with '{' -POST /register rejects registration of usernames with '|' -POST /register rejects registration of usernames with '}' -POST /register rejects registration of usernames with '£' -POST /register rejects registration of usernames with 'é' -POST /register returns the same device_id as that in the request -POST /rooms/:room_id/ban can ban a user -POST /rooms/:room_id/invite can send an invite -POST /rooms/:room_id/join can join a room -POST /rooms/:room_id/leave can leave a room -POST /rooms/:room_id/read_markers can create read marker -POST /rooms/:room_id/receipt can create receipts -POST /rooms/:room_id/redact/:event_id as original message sender redacts message -POST /rooms/:room_id/redact/:event_id as power user redacts message -POST /rooms/:room_id/redact/:event_id as random user does not redact message -POST /rooms/:room_id/send/:event_type sends a message -POST /rooms/:room_id/state/m.room.name sets name -POST /rooms/:room_id/state/m.room.topic sets topic -POST /rooms/:room_id/upgrade can upgrade a room version -POST rejects invalid utf-8 in JSON -POSTed media can be thumbnailed -PUT /device/{deviceId} gives a 404 for unknown devices -PUT /device/{deviceId} updates device fields -PUT /directory/room/:room_alias creates alias -PUT /profile/:user_id/avatar_url sets my avatar -PUT /profile/:user_id/displayname sets my name -PUT /rooms/:room_id/send/:event_type/:txn_id deduplicates the same txn id -PUT /rooms/:room_id/send/:event_type/:txn_id sends a message -PUT /rooms/:room_id/state/m.room.power_levels can set levels -PUT /rooms/:room_id/typing/:user_id sets typing notification -PUT power_levels should not explode if the old power levels were empty -Peeked rooms only turn up in the sync for the device who peeked them -Previously left rooms don't appear in the leave section of sync -Push rules come down in an initial /sync -Read markers appear in incremental v2 /sync -Read markers appear in initial v2 /sync -Read markers can be updated -Read receipts appear in initial v2 /sync -Real non-joined user cannot call /events on default room -Real non-joined user cannot call /events on invited room -Real non-joined user cannot call /events on joined room -Real non-joined user cannot call /events on shared room -Real non-joined users can get individual state for world_readable rooms -Real non-joined users can get individual state for world_readable rooms after leaving -Real non-joined users can get state for world_readable rooms -Real non-joined users cannot room initalSync for non-world_readable rooms -Real non-joined users cannot send messages to guest_access rooms if not joined -Receipts must be m.read -Redaction of a redaction redacts the redaction reason -Regular users can add and delete aliases in the default room configuration -Regular users can add and delete aliases when m.room.aliases is restricted -Regular users cannot create room aliases within the AS namespace -Regular users cannot register within the AS namespace -Remote media can be thumbnailed -Remote room alias queries can handle Unicode -Remote room members also see posted message events -Remote room members can get room messages -Remote user can backfill in a room with version 1 -Remote user can backfill in a room with version 2 -Remote user can backfill in a room with version 3 -Remote user can backfill in a room with version 4 -Remote user can backfill in a room with version 5 -Remote user can backfill in a room with version 6 -Remote users can join room by alias -Remote users may not join unfederated rooms -Request to logout with invalid an access token is rejected -Request to logout without an access token is rejected -Room aliases can contain Unicode -Room creation reports m.room.create to myself -Room creation reports m.room.member to myself -Room members can join a room with an overridden displayname -Room members can override their displayname on a room-specific basis -Room state at a rejected message event is the same as its predecessor -Room state at a rejected state event is the same as its predecessor -Rooms a user is invited to appear in an incremental sync -Rooms a user is invited to appear in an initial sync -Rooms can be created with an initial invite list (SYN-205) -Server correctly handles incoming m.device_list_update -Server correctly handles transactions that break edu limits -Server correctly resyncs when client query keys and there is no remote cache -Server correctly resyncs when server leaves and rejoins a room -Server rejects invalid JSON in a version 6 room -Setting room topic reports m.room.topic to myself -Should not be able to take over the room by pretending there is no PL event -Should reject keys claiming to belong to a different user -State from remote users is included in the state in the initial sync -State from remote users is included in the timeline in an incremental sync -State is included in the timeline in the initial sync -Sync can be polled for updates -Sync is woken up for leaves -Syncing a new room with a large timeline limit isn't limited -Tags appear in an initial v2 /sync -Trying to get push rules with unknown rule_id fails with 404 -Typing can be explicitly stopped -Typing events appear in gapped sync -Typing events appear in incremental sync -Typing events appear in initial sync -Typing notification sent to local room members -Typing notifications also sent to remote room members -Typing notifications don't leak -Uninvited users cannot join the room -Unprivileged users can set m.room.topic if it only needs level 0 -User appears in user directory -User in private room doesn't appear in user directory -User joining then leaving public room appears and dissappears from directory -User in shared private room does appear in user directory until leave -User can create and send/receive messages in a room with version 1 -User can create and send/receive messages in a room with version 2 -User can create and send/receive messages in a room with version 3 -User can create and send/receive messages in a room with version 4 -User can create and send/receive messages in a room with version 5 -User can create and send/receive messages in a room with version 6 -User can invite local user to room with version 1 -User can invite local user to room with version 2 -User can invite local user to room with version 3 -User can invite local user to room with version 4 -User can invite local user to room with version 5 -User can invite local user to room with version 6 -User can invite remote user to room with version 1 -User can invite remote user to room with version 2 -User can invite remote user to room with version 3 -User can invite remote user to room with version 4 -User can invite remote user to room with version 5 -User can invite remote user to room with version 6 -User directory correctly update on display name change -User in dir while user still shares private rooms -User in shared private room does appear in user directory -User is offline if they set_presence=offline in their sync -User signups are forbidden from starting with '_' -Users can't delete other's aliases -Users cannot invite a user that is already in the room -Users cannot invite themselves to a room -Users cannot kick users from a room they are not in -Users cannot kick users who have already left a room -Users cannot set ban powerlevel higher than their own -Users cannot set kick powerlevel higher than their own -Users cannot set notifications powerlevel higher than their own -Users cannot set redact powerlevel higher than their own -Users receive device_list updates for their own devices -Users with sufficient power-level can delete other's aliases -Version responds 200 OK with valid structure -We can't peek into rooms with invited history_visibility -We can't peek into rooms with joined history_visibility -We can't peek into rooms with shared history_visibility -We don't send redundant membership state across incremental syncs by default -We should see our own leave event when rejecting an invite, even if history_visibility is restricted (riot-web/3462) -We should see our own leave event, even if history_visibility is restricted (SYN-662) -Wildcard device messages over federation wake up /sync -Wildcard device messages wake up /sync -Wildcard device messages wake up /sync -avatar_url updates affect room member events -displayname updates affect room member events -local user can join room with version 1 -local user can join room with version 2 -local user can join room with version 3 -local user can join room with version 4 -local user can join room with version 5 -local user can join room with version 6 -m.room.history_visibility == "joined" allows/forbids appropriately for Guest users -m.room.history_visibility == "joined" allows/forbids appropriately for Real users -m.room.history_visibility == "world_readable" allows/forbids appropriately for Guest users -m.room.history_visibility == "world_readable" allows/forbids appropriately for Real users -query for user with no keys returns empty key dict -remote user can join room with version 1 -remote user can join room with version 2 -remote user can join room with version 3 -remote user can join room with version 4 -remote user can join room with version 5 -remote user can join room with version 6 -setting 'm.room.name' respects room powerlevel -setting 'm.room.power_levels' respects room powerlevel -Federation publicRoom Name/topic keys are correct diff --git a/tests/test-config.toml b/tests/test-config.toml deleted file mode 100644 index c4666878..00000000 --- a/tests/test-config.toml +++ /dev/null @@ -1,15 +0,0 @@ -[global] - -# Server runs in same container as tests do, so localhost is fine -server_name = "localhost" - -# With a bit of luck /tmp is a RAM disk, so that the file system does not become the bottleneck while testing -database_path = "/tmp" - -# All the other settings are left at their defaults: -port = 6167 -max_request_size = 20_000_000 -allow_registration = true -trusted_servers = ["matrix.org"] -address = "127.0.0.1" -proxy = "none" \ No newline at end of file From 5619d7e3180661731800e253b558b88b407d2ae7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 20:50:24 -0700 Subject: [PATCH 011/617] remove program name from config option This is technically a breaking change but this is really silly, I can't not get rid of this. --- src/config/mod.rs | 8 ++++---- src/database/mod.rs | 10 +++++----- src/service/mod.rs | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 4b532f2b..762504da 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -25,8 +25,8 @@ pub struct Config { pub database_path: String, #[serde(default = "default_db_cache_capacity_mb")] pub db_cache_capacity_mb: f64, - #[serde(default = "default_conduit_cache_capacity_modifier")] - pub conduit_cache_capacity_modifier: f64, + #[serde(default = "default_cache_capacity_modifier")] + pub cache_capacity_modifier: f64, #[serde(default = "default_rocksdb_max_open_files")] pub rocksdb_max_open_files: i32, #[serde(default = "default_pdu_cache_capacity")] @@ -120,7 +120,7 @@ impl fmt::Display for Config { ), ( "Cache capacity modifier", - &self.conduit_cache_capacity_modifier.to_string(), + &self.cache_capacity_modifier.to_string(), ), #[cfg(feature = "rocksdb")] ( @@ -218,7 +218,7 @@ fn default_db_cache_capacity_mb() -> f64 { 300.0 } -fn default_conduit_cache_capacity_modifier() -> f64 { +fn default_cache_capacity_modifier() -> f64 { 1.0 } diff --git a/src/database/mod.rs b/src/database/mod.rs index 4a30465d..58ab4a51 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -346,19 +346,19 @@ impl KeyValueDatabase { .expect("pdu cache capacity fits into usize"), )), auth_chain_cache: Mutex::new(LruCache::new( - (100_000.0 * config.conduit_cache_capacity_modifier) as usize, + (100_000.0 * config.cache_capacity_modifier) as usize, )), shorteventid_cache: Mutex::new(LruCache::new( - (100_000.0 * config.conduit_cache_capacity_modifier) as usize, + (100_000.0 * config.cache_capacity_modifier) as usize, )), eventidshort_cache: Mutex::new(LruCache::new( - (100_000.0 * config.conduit_cache_capacity_modifier) as usize, + (100_000.0 * config.cache_capacity_modifier) as usize, )), shortstatekey_cache: Mutex::new(LruCache::new( - (100_000.0 * config.conduit_cache_capacity_modifier) as usize, + (100_000.0 * config.cache_capacity_modifier) as usize, )), statekeyshort_cache: Mutex::new(LruCache::new( - (100_000.0 * config.conduit_cache_capacity_modifier) as usize, + (100_000.0 * config.cache_capacity_modifier) as usize, )), our_real_users_cache: RwLock::new(HashMap::new()), appservice_in_room_cache: RwLock::new(HashMap::new()), diff --git a/src/service/mod.rs b/src/service/mod.rs index c96a9041..045168eb 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -85,17 +85,17 @@ impl Services { state_accessor: rooms::state_accessor::Service { db, server_visibility_cache: StdMutex::new(LruCache::new( - (100.0 * config.conduit_cache_capacity_modifier) as usize, + (100.0 * config.cache_capacity_modifier) as usize, )), user_visibility_cache: StdMutex::new(LruCache::new( - (100.0 * config.conduit_cache_capacity_modifier) as usize, + (100.0 * config.cache_capacity_modifier) as usize, )), }, state_cache: rooms::state_cache::Service { db }, state_compressor: rooms::state_compressor::Service { db, stateinfo_cache: StdMutex::new(LruCache::new( - (100.0 * config.conduit_cache_capacity_modifier) as usize, + (100.0 * config.cache_capacity_modifier) as usize, )), }, timeline: rooms::timeline::Service { From 360e020b644bd012ed438708b661a25fbd124f68 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 20:43:59 -0700 Subject: [PATCH 012/617] rename conduit to grapevine --- .gitlab-ci.yml | 6 +- Cargo.lock | 100 ++++++++++++++--------------- Cargo.toml | 4 +- flake.nix | 2 +- src/api/client_server/account.rs | 2 +- src/api/ruma_wrapper/axum.rs | 2 +- src/api/server_server.rs | 14 ++-- src/clap.rs | 4 +- src/config/mod.rs | 2 +- src/database/abstraction/sqlite.rs | 2 +- src/database/mod.rs | 33 +++++----- src/main.rs | 21 +++--- src/service/admin/mod.rs | 89 ++++++++++++------------- src/service/rooms/timeline/mod.rs | 24 +++---- 14 files changed, 153 insertions(+), 152 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ff6a097..fef354ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,11 +49,11 @@ artifacts: image: nixos/nix:2.20.4 script: - ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl - - cp result/bin/conduit x86_64-unknown-linux-musl + - cp result/bin/grapevine x86_64-unknown-linux-musl # Since the OCI image package is based on the binary package, this has the - # fun side effect of uploading the normal binary too. Conduit users who are # deploying with Nix can leverage this fact by adding our binary cache to + # fun side effect of uploading the normal binary too. Grapevine users who are # their systems. # # Note that although we have an `oci-image-x86_64-unknown-linux-musl` @@ -63,7 +63,7 @@ artifacts: - cp result oci-image-amd64.tar.gz - ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl - - cp result/bin/conduit aarch64-unknown-linux-musl + - cp result/bin/grapevine aarch64-unknown-linux-musl - ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl - cp result oci-image-arm64v8.tar.gz diff --git a/Cargo.lock b/Cargo.lock index 77c11364..885f2486 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -375,56 +375,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "conduit" -version = "0.7.0" -dependencies = [ - "async-trait", - "axum", - "axum-server", - "base64", - "bytes", - "clap", - "figment", - "futures-util", - "hmac", - "http", - "hyper", - "image", - "jsonwebtoken", - "lru-cache", - "nix", - "num_cpus", - "opentelemetry", - "opentelemetry-jaeger", - "parking_lot", - "rand", - "regex", - "reqwest", - "ring", - "ruma", - "rusqlite", - "rust-argon2", - "rust-rocksdb", - "sd-notify", - "serde", - "serde_html_form", - "serde_json", - "serde_yaml", - "sha-1", - "thiserror", - "thread_local", - "tikv-jemallocator", - "tokio", - "tower", - "tower-http", - "tracing", - "tracing-flame", - "tracing-opentelemetry", - "tracing-subscriber", - "trust-dns-resolver", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -837,6 +787,56 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "grapevine" +version = "0.1.0" +dependencies = [ + "async-trait", + "axum", + "axum-server", + "base64", + "bytes", + "clap", + "figment", + "futures-util", + "hmac", + "http", + "hyper", + "image", + "jsonwebtoken", + "lru-cache", + "nix", + "num_cpus", + "opentelemetry", + "opentelemetry-jaeger", + "parking_lot", + "rand", + "regex", + "reqwest", + "ring", + "ruma", + "rusqlite", + "rust-argon2", + "rust-rocksdb", + "sd-notify", + "serde", + "serde_html_form", + "serde_json", + "serde_yaml", + "sha-1", + "thiserror", + "thread_local", + "tikv-jemallocator", + "tokio", + "tower", + "tower-http", + "tracing", + "tracing-flame", + "tracing-opentelemetry", + "tracing-subscriber", + "trust-dns-resolver", +] + [[package]] name = "h2" version = "0.3.24" diff --git a/Cargo.toml b/Cargo.toml index 06cbc02c..bc2ae658 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,10 @@ dbg_macro = "warn" str_to_string = "warn" [package] -name = "conduit" +name = "grapevine" description = "A Matrix homeserver written in Rust" license = "Apache-2.0" -version = "0.7.0" +version = "0.1.0" edition = "2021" # See also `rust-toolchain.toml` diff --git a/flake.nix b/flake.nix index 721f6c07..568e75ab 100644 --- a/flake.nix +++ b/flake.nix @@ -68,7 +68,7 @@ }); env = pkgs: { - CONDUIT_VERSION_EXTRA = self.shortRev or self.dirtyShortRev; + GRAPEVINE_VERSION_EXTRA = self.shortRev or self.dirtyShortRev; ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include"; ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib"; } diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index f4dbd280..8f639687 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -265,7 +265,7 @@ pub async fn register_route(body: Ruma) -> Result Result<(), std::net::AddrParseError> { /// FedDest::Literal("198.51.100.3:8448".parse()?); /// FedDest::Literal("[2001:db8::4:5]:443".parse()?); @@ -529,7 +529,7 @@ pub async fn get_server_version_route( ) -> Result { Ok(get_server_version::v1::Response { server: Some(get_server_version::v1::Server { - name: Some("Conduit".to_owned()), + name: Some(env!("CARGO_PKG_NAME").to_owned()), version: Some(env!("CARGO_PKG_VERSION").to_owned()), }), }) @@ -1042,7 +1042,7 @@ pub async fn get_backfill_route( let all_events = services() .rooms .timeline - .pdus_until(user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)? + .pdus_until(user_id!("@doesntmatter:grapevine"), &body.room_id, until)? .take(limit.try_into().unwrap()); let events = all_events @@ -1379,7 +1379,7 @@ pub async fn create_join_event_template_route( ); let state_lock = mutex_state.lock().await; - // TODO: Conduit does not implement restricted join rules yet, we always reject + // TODO: Grapevine does not implement restricted join rules yet, we always reject let join_rules_event = services().rooms.state_accessor.room_state_get( &body.room_id, &StateEventType::RoomJoinRules, @@ -1403,7 +1403,7 @@ pub async fn create_join_event_template_route( ) { return Err(Error::BadRequest( ErrorKind::UnableToAuthorizeJoin, - "Conduit does not support restricted rooms yet.", + "Grapevine does not support restricted rooms yet.", )); } } @@ -1470,7 +1470,7 @@ async fn create_join_event( .event_handler .acl_check(sender_servername, room_id)?; - // TODO: Conduit does not implement restricted join rules yet, we always reject + // TODO: Grapevine does not implement restricted join rules yet, we always reject let join_rules_event = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomJoinRules, @@ -1494,7 +1494,7 @@ async fn create_join_event( ) { return Err(Error::BadRequest( ErrorKind::UnableToAuthorizeJoin, - "Conduit does not support restricted rooms yet.", + "Grapevine does not support restricted rooms yet.", )); } } diff --git a/src/clap.rs b/src/clap.rs index 170d2a17..01563aee 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -4,13 +4,13 @@ use clap::Parser; /// Returns the current version of the crate with extra info if supplied /// -/// Set the environment variable `CONDUIT_VERSION_EXTRA` to any UTF-8 string to +/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string to /// include it in parenthesis after the SemVer version. A common value are git /// commit hashes. fn version() -> String { let cargo_pkg_version = env!("CARGO_PKG_VERSION"); - match option_env!("CONDUIT_VERSION_EXTRA") { + match option_env!("GRAPEVINE_VERSION_EXTRA") { Some(x) => format!("{} ({})", cargo_pkg_version, x), None => cargo_pkg_version.to_owned(), } diff --git a/src/config/mod.rs b/src/config/mod.rs index 762504da..a000773f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -102,7 +102,7 @@ impl Config { } if was_deprecated { - warn!("Read conduit documentation and check your configuration if any new configuration parameters should be adjusted"); + warn!("Read grapevine documentation and check your configuration if any new configuration parameters should be adjusted"); } } } diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 222a8433..eae5e5c8 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -82,7 +82,7 @@ impl Engine { impl KeyValueDatabaseEngine for Arc { fn open(config: &Config) -> Result { - let path = Path::new(&config.database_path).join("conduit.db"); + let path = Path::new(&config.database_path).join("grapevine.db"); // calculates cache-size per permanent connection // 1. convert MB to KiB diff --git a/src/database/mod.rs b/src/database/mod.rs index 58ab4a51..dadf9cbc 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -176,7 +176,7 @@ impl KeyValueDatabase { fn check_db_setup(config: &Config) -> Result<()> { let path = Path::new(&config.database_path); - let sqlite_exists = path.join("conduit.db").exists(); + let sqlite_exists = path.join("grapevine.db").exists(); let rocksdb_exists = path.join("IDENTITY").exists(); let mut count = 0; @@ -375,14 +375,14 @@ impl KeyValueDatabase { // Matrix resource ownership is based on the server name; changing it // requires recreating the database from scratch. if services().users.count()? > 0 { - let conduit_user = - UserId::parse_with_server_name("conduit", services().globals.server_name()) - .expect("@conduit:server_name is valid"); + let grapevine_user = + UserId::parse_with_server_name("grapevine", services().globals.server_name()) + .expect("@grapevine:server_name is valid"); - if !services().users.exists(&conduit_user)? { + if !services().users.exists(&grapevine_user)? { error!( "The {} server user does not exist, and the database is not new.", - conduit_user + grapevine_user ); return Err(Error::bad_database( "Cannot reuse an existing database after changing the server name, please delete the old one first." @@ -935,17 +935,17 @@ impl KeyValueDatabase { services().admin.start_handler(); - // Set emergency access for the conduit user + // Set emergency access for the grapevine user match set_emergency_access() { Ok(pwd_set) => { if pwd_set { - warn!("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!"); - services().admin.send_message(RoomMessageEventContent::text_plain("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!")); + warn!("The Grapevine account emergency password is set! Please unset it as soon as you finish admin account recovery!"); + services().admin.send_message(RoomMessageEventContent::text_plain("The Grapevine account emergency password is set! Please unset it as soon as you finish admin account recovery!")); } } Err(e) => { error!( - "Could not set the configured emergency password for the conduit user: {}", + "Could not set the configured emergency password for the grapevine user: {}", e ) } @@ -1013,24 +1013,25 @@ impl KeyValueDatabase { } } -/// Sets the emergency password and push rules for the @conduit account in case emergency password is set +/// Sets the emergency password and push rules for the @grapevine account in case emergency password is set fn set_emergency_access() -> Result { - let conduit_user = UserId::parse_with_server_name("conduit", services().globals.server_name()) - .expect("@conduit:server_name is a valid UserId"); + let grapevine_user = + UserId::parse_with_server_name("grapevine", services().globals.server_name()) + .expect("@grapevine:server_name is a valid UserId"); services().users.set_password( - &conduit_user, + &grapevine_user, services().globals.emergency_password().as_deref(), )?; let (ruleset, res) = match services().globals.emergency_password() { - Some(_) => (Ruleset::server_default(&conduit_user), Ok(true)), + Some(_) => (Ruleset::server_default(&grapevine_user), Ok(true)), None => (Ruleset::new(), Ok(false)), }; services().account_data.update( None, - &conduit_user, + &grapevine_user, GlobalAccountDataEventType::PushRules.to_string().into(), &serde_json::to_value(&GlobalAccountDataEvent { content: PushRulesEventContent { global: ruleset }, diff --git a/src/main.rs b/src/main.rs index d246401b..d466c1c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,15 +76,14 @@ async fn main() { clap::parse(); // Initialize config - let raw_config = - Figment::new() - .merge( - Toml::file(Env::var("CONDUIT_CONFIG").expect( - "The CONDUIT_CONFIG env var needs to be set. Example: /etc/conduit.toml", - )) - .nested(), - ) - .merge(Env::prefixed("CONDUIT_").global()); + let raw_config = Figment::new() + .merge( + Toml::file(Env::var("GRAPEVINE_CONFIG").expect( + "The GRAPEVINE_CONFIG env var needs to be set. Example: /etc/grapevine.toml", + )) + .nested(), + ) + .merge(Env::prefixed("GRAPEVINE_").global()); let config = match raw_config.extract::() { Ok(s) => s, @@ -100,7 +99,7 @@ async fn main() { opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); let tracer = opentelemetry_jaeger::new_agent_pipeline() .with_auto_split_batch(true) - .with_service_name("conduit") + .with_service_name("grapevine") .install_batch(opentelemetry::runtime::Tokio) .unwrap(); let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); @@ -515,7 +514,7 @@ async fn initial_sync(_uri: Uri) -> impl IntoResponse { } async fn it_works() -> &'static str { - "Hello from Conduit!" + "Hello from Grapevine!" } trait RouterExt { diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 753f961f..315dbdcf 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -40,7 +40,7 @@ use super::pdu::PduBuilder; #[cfg_attr(test, derive(Debug))] #[derive(Parser)] -#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))] +#[command(name = "@grapevine:server.name:", version = env!("CARGO_PKG_VERSION"))] enum AdminCommand { #[command(verbatim_doc_comment)] /// Register an appservice using its registration YAML @@ -128,7 +128,7 @@ enum AdminCommand { /// # ``` ParsePdu, - /// Retrieve and print a PDU by ID from the Conduit database + /// Retrieve and print a PDU by ID from the Grapevine database GetPdu { /// An event ID (a $ followed by the base64 reference hash) event_id: Box, @@ -137,10 +137,10 @@ enum AdminCommand { /// Print database memory usage statistics MemoryUsage, - /// Clears all of Conduit's database caches with index smaller than the amount + /// Clears all of Grapevine's database caches with index smaller than the amount ClearDatabaseCaches { amount: u32 }, - /// Clears all of Conduit's service caches with index smaller than the amount + /// Clears all of Grapevine's service caches with index smaller than the amount ClearServiceCaches { amount: u32 }, /// Show configuration values @@ -212,10 +212,11 @@ impl Service { // TODO: Use futures when we have long admin commands //let mut futures = FuturesUnordered::new(); - let conduit_user = UserId::parse(format!("@conduit:{}", services().globals.server_name())) - .expect("@conduit:server_name is valid"); + let grapevine_user = + UserId::parse(format!("@grapevine:{}", services().globals.server_name())) + .expect("@grapevine:server_name is valid"); - if let Ok(Some(conduit_room)) = services().admin.get_admin_room() { + if let Ok(Some(grapevine_room)) = services().admin.get_admin_room() { loop { tokio::select! { Some(event) = receiver.recv() => { @@ -229,7 +230,7 @@ impl Service { .roomid_mutex_state .write() .await - .entry(conduit_room.to_owned()) + .entry(grapevine_room.to_owned()) .or_default(), ); @@ -247,8 +248,8 @@ impl Service { state_key: None, redacts: None, }, - &conduit_user, - &conduit_room, + &grapevine_user, + &grapevine_room, &state_lock, ) .await.unwrap(); @@ -306,7 +307,7 @@ impl Service { // Parse chat messages from the admin room into an AdminCommand object fn parse_admin_command(&self, command_line: &str) -> std::result::Result { - // Note: argv[0] is `@conduit:servername:`, which is treated as the main command + // Note: argv[0] is `@grapevine:servername:`, which is treated as the main command let mut argv: Vec<_> = command_line.split_whitespace().collect(); // Replace `help command` with `command --help` @@ -566,10 +567,10 @@ impl Service { if !services().users.exists(&user_id)? || user_id == UserId::parse_with_server_name( - "conduit", + "grapevine", services().globals.server_name(), ) - .expect("conduit user exists") + .expect("grapevine user exists") { return Ok(RoomMessageEventContent::text_plain( "The specified user does not exist!", @@ -854,13 +855,13 @@ impl Service { // Utility to turn clap's `--help` text to HTML. fn usage_to_html(&self, text: &str, server_name: &ServerName) -> String { - // Replace `@conduit:servername:-subcmdname` with `@conduit:servername: subcmdname` + // Replace `@grapevine:servername:-subcmdname` with `@grapevine:servername: subcmdname` let text = text.replace( - &format!("@conduit:{server_name}:-"), - &format!("@conduit:{server_name}: "), + &format!("@grapevine:{server_name}:-"), + &format!("@grapevine:{server_name}: "), ); - // For the conduit admin room, subcommands become main commands + // For the grapevine admin room, subcommands become main commands let text = text.replace("SUBCOMMAND", "COMMAND"); let text = text.replace("subcommand", "command"); @@ -914,7 +915,7 @@ impl Service { // Improve the usage section let text = if command_body.is_empty() { // Wrap the usage line in code tags - let re = Regex::new("(?m)^USAGE:\n (@conduit:.*)$") + let re = Regex::new("(?m)^USAGE:\n (@grapevine:.*)$") .expect("Regex compilation should not fail"); re.replace_all(&text, "USAGE:\n$1").to_string() } else { @@ -935,7 +936,7 @@ impl Service { /// Create the admin room. /// - /// Users in this room are considered admins by conduit, and the room can be + /// Users in this room are considered admins by grapevine, and the room can be /// used to issue admin commands by talking to the server user inside it. pub(crate) async fn create_admin_room(&self) -> Result<()> { let room_id = RoomId::new(services().globals.server_name()); @@ -954,11 +955,11 @@ impl Service { let state_lock = mutex_state.lock().await; // Create a user for the server - let conduit_user = - UserId::parse_with_server_name("conduit", services().globals.server_name()) - .expect("@conduit:server_name is valid"); + let grapevine_user = + UserId::parse_with_server_name("grapevine", services().globals.server_name()) + .expect("@grapevine:server_name is valid"); - services().users.create(&conduit_user, None)?; + services().users.create(&grapevine_user, None)?; let room_version = services().globals.default_room_version(); let mut content = match room_version { @@ -971,7 +972,7 @@ impl Service { | RoomVersionId::V7 | RoomVersionId::V8 | RoomVersionId::V9 - | RoomVersionId::V10 => RoomCreateEventContent::new_v1(conduit_user.clone()), + | RoomVersionId::V10 => RoomCreateEventContent::new_v1(grapevine_user.clone()), RoomVersionId::V11 => RoomCreateEventContent::new_v11(), _ => unreachable!("Validity of room version already checked"), }; @@ -991,13 +992,13 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) .await?; - // 2. Make conduit bot join + // 2. Make grapevine bot join services() .rooms .timeline @@ -1016,10 +1017,10 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some(conduit_user.to_string()), + state_key: Some(grapevine_user.to_string()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1027,7 +1028,7 @@ impl Service { // 3. Power levels let mut users = BTreeMap::new(); - users.insert(conduit_user.clone(), 100.into()); + users.insert(grapevine_user.clone(), 100.into()); services() .rooms @@ -1044,7 +1045,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1063,7 +1064,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1084,7 +1085,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1105,7 +1106,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1125,7 +1126,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1145,7 +1146,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1171,7 +1172,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1197,9 +1198,9 @@ impl Service { .resolve_local_alias(&admin_room_alias) } - /// Invite the user to the conduit admin room. + /// Invite the user to the grapevine admin room. /// - /// In conduit, this is equivalent to granting admin privileges. + /// In grapevine, this is equivalent to granting admin privileges. pub(crate) async fn make_user_admin( &self, user_id: &UserId, @@ -1218,9 +1219,9 @@ impl Service { let state_lock = mutex_state.lock().await; // Use the server user to grant the new admin's power level - let conduit_user = - UserId::parse_with_server_name("conduit", services().globals.server_name()) - .expect("@conduit:server_name is valid"); + let grapevine_user = + UserId::parse_with_server_name("grapevine", services().globals.server_name()) + .expect("@grapevine:server_name is valid"); // Invite and join the real user services() @@ -1244,7 +1245,7 @@ impl Service { state_key: Some(user_id.to_string()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) @@ -1278,7 +1279,7 @@ impl Service { // Set power level let mut users = BTreeMap::new(); - users.insert(conduit_user.to_owned(), 100.into()); + users.insert(grapevine_user.to_owned(), 100.into()); users.insert(user_id.to_owned(), 100.into()); services() @@ -1296,7 +1297,7 @@ impl Service { state_key: Some("".to_owned()), redacts: None, }, - &conduit_user, + &grapevine_user, &room_id, &state_lock, ) diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index acb00d01..6c15b751 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -98,7 +98,7 @@ pub struct Service { impl Service { #[tracing::instrument(skip(self))] pub fn first_pdu_in_room(&self, room_id: &RoomId) -> Result>> { - self.all_pdus(user_id!("@doesntmatter:conduit.rs"), room_id)? + self.all_pdus(user_id!("@doesntmatter:grapevine"), room_id)? .next() .map(|o| o.map(|(_, p)| Arc::new(p))) .transpose() @@ -485,20 +485,20 @@ impl Service { .search .index_pdu(shortroomid, &pdu_id, &body)?; - let server_user = format!("@conduit:{}", services().globals.server_name()); + let server_user = format!("@grapevine:{}", services().globals.server_name()); - let to_conduit = body.starts_with(&format!("{server_user}: ")) + let to_grapevine = body.starts_with(&format!("{server_user}: ")) || body.starts_with(&format!("{server_user} ")) || body == format!("{server_user}:") || body == server_user; // This will evaluate to false if the emergency password is set up so that - // the administrator can execute commands as conduit - let from_conduit = pdu.sender == server_user + // the administrator can execute commands as grapevine + let from_grapevine = pdu.sender == server_user && services().globals.emergency_password().is_none(); if let Some(admin_room) = services().admin.get_admin_room()? { - if to_conduit && !from_conduit && admin_room == pdu.room_id { + if to_grapevine && !from_grapevine && admin_room == pdu.room_id { services().admin.process_message(body); } } @@ -838,16 +838,16 @@ impl Service { .filter(|v| v.starts_with('@')) .unwrap_or(sender.as_str()); let server_name = services().globals.server_name(); - let server_user = format!("@conduit:{}", server_name); + let server_user = format!("@grapevine:{}", server_name); let content = serde_json::from_str::(pdu.content.get()) .map_err(|_| Error::bad_database("Invalid content in pdu."))?; if content.membership == MembershipState::Leave { if target == server_user { - warn!("Conduit user cannot leave from admins room"); + warn!("Grapevine user cannot leave from admins room"); return Err(Error::BadRequest( ErrorKind::Forbidden, - "Conduit user cannot leave from admins room.", + "Grapevine user cannot leave from admins room.", )); } @@ -870,10 +870,10 @@ impl Service { if content.membership == MembershipState::Ban && pdu.state_key().is_some() { if target == server_user { - warn!("Conduit user cannot be banned in admins room"); + warn!("Grapevine user cannot be banned in admins room"); return Err(Error::BadRequest( ErrorKind::Forbidden, - "Conduit user cannot be banned in admins room.", + "Grapevine user cannot be banned in admins room.", )); } @@ -1102,7 +1102,7 @@ impl Service { #[tracing::instrument(skip(self, room_id))] pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result<()> { let first_pdu = self - .all_pdus(user_id!("@doesntmatter:conduit.rs"), room_id)? + .all_pdus(user_id!("@doesntmatter:grapevine"), room_id)? .next() .expect("Room is not empty")?; From f437a9ab888547ed50bc1df0fe0db551f5e54fbe Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 29 Apr 2024 23:59:00 -0700 Subject: [PATCH 013/617] add gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5d4e599f --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Cargo artifacts +target + +# Direnv cache +/.direnv + +# Nix artifacts +result* + +# GitLab CI cache +/.gitlab-ci.d From c27fc4e1772c63288f9e792600c6d3536e393476 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 20:52:22 -0700 Subject: [PATCH 014/617] convert license to markdown --- LICENSE | 176 ----------------------------------------------------- LICENSE.md | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 176 deletions(-) delete mode 100644 LICENSE create mode 100644 LICENSE.md diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d9a10c0d..00000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..6f0d0bab --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,172 @@ +# Apache License + +Version 2.0, January 2004, + +## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +### 1. Definitions + +* **"License"** shall mean the terms and conditions for use, reproduction, and + distribution as defined by Sections 1 through 9 of this document. + +* **"Licensor"** shall mean the copyright owner or entity authorized by the + copyright owner that is granting the License. + +* **"Legal Entity"** shall mean the union of the acting entity and all other + entities that control, are controlled by, or are under common control with + that entity. For the purposes of this definition, "control" means (i) the + power, direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (ii) ownership of fifty percent + (50%) or more of the outstanding shares, or (iii) beneficial ownership of such + entity. + +* **"You"** (or **"Your"**) shall mean an individual or Legal Entity exercising + permissions granted by this License. + +* **"Source"** form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation source, and + configuration files. + +* **"Object"** form shall mean any form resulting from mechanical transformation + or translation of a Source form, including but not limited to compiled object + code, generated documentation, and conversions to other media types. + +* **"Work"** shall mean the work of authorship, whether in Source or Object + form, made available under the License, as indicated by a copyright notice + that is included in or attached to the work (an example is provided in the + Appendix below). + +* **"Derivative Works"** shall mean any work, whether in Source or Object form, + that is based on (or derived from) the Work and for which the editorial + revisions, annotations, elaborations, or other modifications represent, as + a whole, an original work of authorship. For the purposes of this License, + Derivative Works shall not include works that remain separable from, or merely + link (or bind by name) to the interfaces of, the Work and Derivative Works + thereof. + +* **"Contribution"** shall mean any work of authorship, including the original + version of the Work and any modifications or additions to that Work or + Derivative Works thereof, that is intentionally submitted to Licensor for + inclusion in the Work by the copyright owner or by an individual or Legal + Entity authorized to submit on behalf of the copyright owner. For the purposes + of this definition, "submitted" means any form of electronic, verbal, or + written communication sent to the Licensor or its representatives, including + but not limited to communication on electronic mailing lists, source code + control systems, and issue tracking systems that are managed by, or on behalf + of, the Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise designated + in writing by the copyright owner as "Not a Contribution." + +* **"Contributor"** shall mean Licensor and any individual or Legal Entity on + behalf of whom a Contribution has been received by Licensor and subsequently + incorporated within the Work. + +### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including +a cross-claim or counterclaim in a lawsuit) alleging that the Work or +a Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of + this License; and + +2. You must cause any modified files to carry prominent notices stating that You + changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from + the Source form of the Work, excluding those notices that do not pertain to + any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those + notices that do not pertain to any part of the Derivative Works, in at least + one of the following places: within a NOTICE text file distributed as part of + the Derivative Works; within the Source form or documentation, if provided + along with the Derivative Works; or, within a display generated by the + Derivative Works, if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only and do + not modify the License. You may add Your own attribution notices within + Derivative Works that You distribute, alongside or as an addendum to the + NOTICE text from the Work, provided that such additional attribution notices + cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. From daf7b1e67826c559f6c599cd0cbe0163a58dd887 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 21:01:04 -0700 Subject: [PATCH 015/617] sort keys in Cargo.toml --- Cargo.toml | 86 +++++++++++++++++++++++++----------------------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc2ae658..155121e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,67 +22,61 @@ rust-version = "1.75.0" [lints] workspace = true +# Keep sorted [dependencies] +async-trait = "0.1.68" axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"] } axum-server = { version = "0.5.1", features = ["tls-rustls"] } -tower = { version = "0.4.13", features = ["util"] } -tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } -ruma = { git = "https://github.com/ruma/ruma", rev = "5495b85aa311c2805302edb0a7de40399e22b397", 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" ] } -tokio = { version = "1.28.1", features = ["fs", "macros", "signal", "sync"] } -bytes = "1.4.0" -http = "0.2.9" -serde_json = { version = "1.0.96", features = ["raw_value"] } -serde_yaml = "0.9.21" -serde = { version = "1.0.163", features = ["rc"] } -rand = "0.8.5" -rust-argon2 = "1.0.0" -hyper = "0.14.26" -reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } -thiserror = "1.0.40" -image = { version = "0.24.6", default-features = false, features = ["jpeg", "png", "gif"] } base64 = "0.21.2" -ring = "0.17.7" -trust-dns-resolver = "0.22.0" -regex = "1.8.1" +bytes = "1.4.0" +clap = { version = "4.3.0", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } +figment = { version = "0.10.8", features = ["env", "toml"] } +futures-util = { version = "0.3.28", default-features = false } +hmac = "0.12.1" +http = "0.2.9" +hyper = "0.14.26" +image = { version = "0.24.6", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.2.0" -tracing = { version = "0.1.37", features = [] } -tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } -tracing-flame = "0.2.0" +lru-cache = "0.1.2" +num_cpus = "1.15.0" opentelemetry = { version = "0.18.0", features = ["rt-tokio"] } opentelemetry-jaeger = { version = "0.17.0", features = ["rt-tokio"] } -tracing-opentelemetry = "0.18.0" -lru-cache = "0.1.2" -rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } parking_lot = { version = "0.12.1", optional = true } -num_cpus = "1.15.0" -serde_html_form = "0.2.0" -thread_local = "1.1.7" -hmac = "0.12.1" -sha-1 = "0.10.1" -clap = { version = "4.3.0", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } -futures-util = { version = "0.3.28", default-features = false } -figment = { version = "0.10.8", features = ["env", "toml"] } -tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -async-trait = "0.1.68" +rand = "0.8.5" +regex = "1.8.1" +reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } +ring = "0.17.7" +rocksdb = { package = "rust-rocksdb", version = "0.24.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +ruma = { git = "https://github.com/ruma/ruma", rev = "5495b85aa311c2805302edb0a7de40399e22b397", 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" ] } +rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } +rust-argon2 = "1.0.0" sd-notify = { version = "0.4.1", optional = true } - -[dependencies.rocksdb] -package = "rust-rocksdb" -version = "0.24.0" -optional = true -features = [ - "multi-threaded-cf", - "zstd", - "lz4", -] +serde = { version = "1.0.163", features = ["rc"] } +serde_html_form = "0.2.0" +serde_json = { version = "1.0.96", features = ["raw_value"] } +serde_yaml = "0.9.21" +sha-1 = "0.10.1" +thiserror = "1.0.40" +thread_local = "1.1.7" +tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } +tokio = { version = "1.28.1", features = ["fs", "macros", "signal", "sync"] } +tower = { version = "0.4.13", features = ["util"] } +tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } +tracing = { version = "0.1.37", features = [] } +tracing-flame = "0.2.0" +tracing-opentelemetry = "0.18.0" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +trust-dns-resolver = "0.22.0" [target.'cfg(unix)'.dependencies] nix = { version = "0.28", features = ["resource"] } [features] -default = ["backend_sqlite", "backend_rocksdb", "systemd"] -backend_sqlite = ["sqlite"] +default = ["backend_rocksdb", "backend_sqlite", "systemd"] + +# Keep sorted backend_rocksdb = ["rocksdb"] +backend_sqlite = ["sqlite"] jemalloc = ["tikv-jemallocator"] sqlite = ["rusqlite", "parking_lot", "tokio/signal"] systemd = ["sd-notify"] From c0972c2b57d13a44688d56a692dbabe7c17a79b9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 21:54:24 -0700 Subject: [PATCH 016/617] use `dep:` syntax in cargo features --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 155121e5..ced1bb96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,8 +75,8 @@ nix = { version = "0.28", features = ["resource"] } default = ["backend_rocksdb", "backend_sqlite", "systemd"] # Keep sorted -backend_rocksdb = ["rocksdb"] +backend_rocksdb = ["dep:rocksdb"] backend_sqlite = ["sqlite"] -jemalloc = ["tikv-jemallocator"] -sqlite = ["rusqlite", "parking_lot", "tokio/signal"] -systemd = ["sd-notify"] +jemalloc = ["dep:tikv-jemallocator"] +sqlite = ["dep:rusqlite", "dep:parking_lot", "tokio/signal"] +systemd = ["dep:sd-notify"] From d21221d54eb2c5104f1cd06c2c6955e888d44b46 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 21:37:57 -0700 Subject: [PATCH 017/617] remove backend_* features that do nothing --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ced1bb96..394be101 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,11 +72,10 @@ trust-dns-resolver = "0.22.0" nix = { version = "0.28", features = ["resource"] } [features] -default = ["backend_rocksdb", "backend_sqlite", "systemd"] +default = ["rocksdb", "sqlite", "systemd"] # Keep sorted -backend_rocksdb = ["dep:rocksdb"] -backend_sqlite = ["sqlite"] jemalloc = ["dep:tikv-jemallocator"] +rocksdb = ["dep:rocksdb"] sqlite = ["dep:rusqlite", "dep:parking_lot", "tokio/signal"] systemd = ["dep:sd-notify"] From 806fabc4c7aa1ff2982fdf949324050a6f46bd11 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 20:23:59 -0700 Subject: [PATCH 018/617] run clippy for no, default, and all features This should be good enough for now. --- engage.toml | 28 +++++++++++++++++++++++++++- src/database/mod.rs | 24 ++++++++++++------------ src/utils/mod.rs | 1 + 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/engage.toml b/engage.toml index 3e8884eb..ec3b4066 100644 --- a/engage.toml +++ b/engage.toml @@ -47,10 +47,36 @@ RUSTDOCFLAGS="-D warnings" cargo doc \ """ [[task]] -name = "cargo-clippy" +name = "cargo-clippy/none" +group = "lints" +script = """ +cargo clippy \ + --workspace \ + --all-targets \ + --no-default-features \ + --color=always \ + -- \ + -D warnings +""" + +[[task]] +name = "cargo-clippy/default" group = "lints" script = "cargo clippy --workspace --all-targets --color=always -- -D warnings" +[[task]] +name = "cargo-clippy/all" +group = "lints" +script = """ +cargo clippy \ + --workspace \ + --all-targets \ + --all-features \ + --color=always \ + -- \ + -D warnings +""" + [[task]] name = "cargo" group = "tests" diff --git a/src/database/mod.rs b/src/database/mod.rs index dadf9cbc..6941b22b 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -210,6 +210,10 @@ impl KeyValueDatabase { } /// Load an existing database or create a new one. + #[cfg_attr( + not(any(feature = "rocksdb", feature = "sqlite")), + allow(unreachable_code) + )] pub async fn load_or_create(config: Config) -> Result<()> { Self::check_db_setup(&config)?; @@ -218,19 +222,15 @@ impl KeyValueDatabase { .map_err(|_| Error::BadConfig("Database folder doesn't exists and couldn't be created (e.g. due to missing permissions). Please create the database folder yourself."))?; } + #[cfg_attr( + not(any(feature = "rocksdb", feature = "sqlite")), + allow(unused_variables) + )] let builder: Arc = match &*config.database_backend { - "sqlite" => { - #[cfg(not(feature = "sqlite"))] - return Err(Error::BadConfig("Database backend not found.")); - #[cfg(feature = "sqlite")] - Arc::new(Arc::::open(&config)?) - } - "rocksdb" => { - #[cfg(not(feature = "rocksdb"))] - return Err(Error::BadConfig("Database backend not found.")); - #[cfg(feature = "rocksdb")] - Arc::new(Arc::::open(&config)?) - } + #[cfg(feature = "sqlite")] + "sqlite" => Arc::new(Arc::::open(&config)?), + #[cfg(feature = "rocksdb")] + "rocksdb" => Arc::new(Arc::::open(&config)?), _ => { return Err(Error::BadConfig("Database backend not found.")); } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 0b5b1ae4..0ac81b62 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -18,6 +18,7 @@ pub fn millis_since_unix_epoch() -> u64 { .as_millis() as u64 } +#[cfg(any(feature = "rocksdb", feature = "sqlite"))] pub fn increment(old: Option<&[u8]>) -> Option> { let number = match old.map(|bytes| bytes.try_into()) { Some(Ok(bytes)) => { From 242d9e84cc45e850e6b9649a3ed3682c03ef6609 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 21:33:18 -0700 Subject: [PATCH 019/617] enable more lints that aren't already violated These are good lints to have and we want them enabled, and luckily no existing code needs to be modified to keep the linters happy for these. --- Cargo.toml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 394be101..1a13de8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,50 @@ # Keep alphabetically sorted [workspace.lints.rust] +elided_lifetimes_in_paths = "warn" explicit_outlives_requirements = "warn" +macro_use_extern_crate = "warn" +missing_abi = "warn" +noop_method_call = "warn" +pointer_structural_match = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_extern_crates = "warn" +unused_import_braces = "warn" +unused_macro_rules = "warn" unused_qualifications = "warn" # Keep alphabetically sorted [workspace.lints.clippy] +assertions_on_result_states = "warn" cloned_instead_of_copied = "warn" dbg_macro = "warn" +default_union_representation = "warn" +empty_drop = "warn" +filetype_is_file = "warn" +float_cmp_const = "warn" +get_unwrap = "warn" +lossy_float_literal = "warn" +mem_forget = "warn" +mutex_atomic = "warn" +negative_feature_names = "warn" +pub_without_shorthand = "warn" +rc_buffer = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +redundant_type_annotations = "warn" +rest_pat_in_fully_bound_structs = "warn" +semicolon_inside_block = "warn" str_to_string = "warn" +string_lit_chars_any = "warn" +string_to_string = "warn" +suspicious_xor_used_as_pow = "warn" +tests_outside_test_module = "warn" +try_err = "warn" +unnecessary_safety_comment = "warn" +unnecessary_safety_doc = "warn" +unnecessary_self_imports = "warn" +unseparated_literal_suffix = "warn" +verbose_file_reads = "warn" +wildcard_dependencies = "warn" [package] name = "grapevine" From a626e7b0f0b55b17f366e70c9189da95dd6a2338 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 27 Apr 2024 22:05:49 -0700 Subject: [PATCH 020/617] enable `mod_module_files` lint --- Cargo.toml | 1 + src/{api/mod.rs => api.rs} | 0 src/api/{client_server/mod.rs => client_server.rs} | 0 src/api/{ruma_wrapper/mod.rs => ruma_wrapper.rs} | 0 src/{config/mod.rs => config.rs} | 0 src/{database/mod.rs => database.rs} | 0 src/database/{key_value/mod.rs => key_value.rs} | 0 src/database/key_value/{rooms/mod.rs => rooms.rs} | 0 src/database/key_value/rooms/{edus/mod.rs => edus.rs} | 0 src/{service/mod.rs => service.rs} | 0 src/service/{account_data/mod.rs => account_data.rs} | 0 src/service/{admin/mod.rs => admin.rs} | 0 src/service/{appservice/mod.rs => appservice.rs} | 0 src/service/{globals/mod.rs => globals.rs} | 0 src/service/{key_backups/mod.rs => key_backups.rs} | 0 src/service/{media/mod.rs => media.rs} | 0 src/service/{pusher/mod.rs => pusher.rs} | 0 src/service/{rooms/mod.rs => rooms.rs} | 0 src/service/rooms/{alias/mod.rs => alias.rs} | 0 src/service/rooms/{auth_chain/mod.rs => auth_chain.rs} | 0 src/service/rooms/{directory/mod.rs => directory.rs} | 0 src/service/rooms/{edus/mod.rs => edus.rs} | 0 src/service/rooms/edus/{read_receipt/mod.rs => read_receipt.rs} | 0 src/service/rooms/edus/{typing/mod.rs => typing.rs} | 0 src/service/rooms/{event_handler/mod.rs => event_handler.rs} | 0 src/service/rooms/{lazy_loading/mod.rs => lazy_loading.rs} | 0 src/service/rooms/{metadata/mod.rs => metadata.rs} | 0 src/service/rooms/{outlier/mod.rs => outlier.rs} | 0 src/service/rooms/{pdu_metadata/mod.rs => pdu_metadata.rs} | 0 src/service/rooms/{search/mod.rs => search.rs} | 0 src/service/rooms/{short/mod.rs => short.rs} | 0 src/service/rooms/{spaces/mod.rs => spaces.rs} | 0 src/service/rooms/{state/mod.rs => state.rs} | 0 src/service/rooms/{state_accessor/mod.rs => state_accessor.rs} | 0 src/service/rooms/{state_cache/mod.rs => state_cache.rs} | 0 .../rooms/{state_compressor/mod.rs => state_compressor.rs} | 0 src/service/rooms/{threads/mod.rs => threads.rs} | 0 src/service/rooms/{timeline/mod.rs => timeline.rs} | 0 src/service/rooms/{user/mod.rs => user.rs} | 0 src/service/{sending/mod.rs => sending.rs} | 0 src/service/{transaction_ids/mod.rs => transaction_ids.rs} | 0 src/service/{uiaa/mod.rs => uiaa.rs} | 0 src/service/{users/mod.rs => users.rs} | 0 src/{utils/mod.rs => utils.rs} | 0 44 files changed, 1 insertion(+) rename src/{api/mod.rs => api.rs} (100%) rename src/api/{client_server/mod.rs => client_server.rs} (100%) rename src/api/{ruma_wrapper/mod.rs => ruma_wrapper.rs} (100%) rename src/{config/mod.rs => config.rs} (100%) rename src/{database/mod.rs => database.rs} (100%) rename src/database/{key_value/mod.rs => key_value.rs} (100%) rename src/database/key_value/{rooms/mod.rs => rooms.rs} (100%) rename src/database/key_value/rooms/{edus/mod.rs => edus.rs} (100%) rename src/{service/mod.rs => service.rs} (100%) rename src/service/{account_data/mod.rs => account_data.rs} (100%) rename src/service/{admin/mod.rs => admin.rs} (100%) rename src/service/{appservice/mod.rs => appservice.rs} (100%) rename src/service/{globals/mod.rs => globals.rs} (100%) rename src/service/{key_backups/mod.rs => key_backups.rs} (100%) rename src/service/{media/mod.rs => media.rs} (100%) rename src/service/{pusher/mod.rs => pusher.rs} (100%) rename src/service/{rooms/mod.rs => rooms.rs} (100%) rename src/service/rooms/{alias/mod.rs => alias.rs} (100%) rename src/service/rooms/{auth_chain/mod.rs => auth_chain.rs} (100%) rename src/service/rooms/{directory/mod.rs => directory.rs} (100%) rename src/service/rooms/{edus/mod.rs => edus.rs} (100%) rename src/service/rooms/edus/{read_receipt/mod.rs => read_receipt.rs} (100%) rename src/service/rooms/edus/{typing/mod.rs => typing.rs} (100%) rename src/service/rooms/{event_handler/mod.rs => event_handler.rs} (100%) rename src/service/rooms/{lazy_loading/mod.rs => lazy_loading.rs} (100%) rename src/service/rooms/{metadata/mod.rs => metadata.rs} (100%) rename src/service/rooms/{outlier/mod.rs => outlier.rs} (100%) rename src/service/rooms/{pdu_metadata/mod.rs => pdu_metadata.rs} (100%) rename src/service/rooms/{search/mod.rs => search.rs} (100%) rename src/service/rooms/{short/mod.rs => short.rs} (100%) rename src/service/rooms/{spaces/mod.rs => spaces.rs} (100%) rename src/service/rooms/{state/mod.rs => state.rs} (100%) rename src/service/rooms/{state_accessor/mod.rs => state_accessor.rs} (100%) rename src/service/rooms/{state_cache/mod.rs => state_cache.rs} (100%) rename src/service/rooms/{state_compressor/mod.rs => state_compressor.rs} (100%) rename src/service/rooms/{threads/mod.rs => threads.rs} (100%) rename src/service/rooms/{timeline/mod.rs => timeline.rs} (100%) rename src/service/rooms/{user/mod.rs => user.rs} (100%) rename src/service/{sending/mod.rs => sending.rs} (100%) rename src/service/{transaction_ids/mod.rs => transaction_ids.rs} (100%) rename src/service/{uiaa/mod.rs => uiaa.rs} (100%) rename src/service/{users/mod.rs => users.rs} (100%) rename src/{utils/mod.rs => utils.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 1a13de8f..1229e72a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ float_cmp_const = "warn" get_unwrap = "warn" lossy_float_literal = "warn" mem_forget = "warn" +mod_module_files = "warn" mutex_atomic = "warn" negative_feature_names = "warn" pub_without_shorthand = "warn" diff --git a/src/api/mod.rs b/src/api.rs similarity index 100% rename from src/api/mod.rs rename to src/api.rs diff --git a/src/api/client_server/mod.rs b/src/api/client_server.rs similarity index 100% rename from src/api/client_server/mod.rs rename to src/api/client_server.rs diff --git a/src/api/ruma_wrapper/mod.rs b/src/api/ruma_wrapper.rs similarity index 100% rename from src/api/ruma_wrapper/mod.rs rename to src/api/ruma_wrapper.rs diff --git a/src/config/mod.rs b/src/config.rs similarity index 100% rename from src/config/mod.rs rename to src/config.rs diff --git a/src/database/mod.rs b/src/database.rs similarity index 100% rename from src/database/mod.rs rename to src/database.rs diff --git a/src/database/key_value/mod.rs b/src/database/key_value.rs similarity index 100% rename from src/database/key_value/mod.rs rename to src/database/key_value.rs diff --git a/src/database/key_value/rooms/mod.rs b/src/database/key_value/rooms.rs similarity index 100% rename from src/database/key_value/rooms/mod.rs rename to src/database/key_value/rooms.rs diff --git a/src/database/key_value/rooms/edus/mod.rs b/src/database/key_value/rooms/edus.rs similarity index 100% rename from src/database/key_value/rooms/edus/mod.rs rename to src/database/key_value/rooms/edus.rs diff --git a/src/service/mod.rs b/src/service.rs similarity index 100% rename from src/service/mod.rs rename to src/service.rs diff --git a/src/service/account_data/mod.rs b/src/service/account_data.rs similarity index 100% rename from src/service/account_data/mod.rs rename to src/service/account_data.rs diff --git a/src/service/admin/mod.rs b/src/service/admin.rs similarity index 100% rename from src/service/admin/mod.rs rename to src/service/admin.rs diff --git a/src/service/appservice/mod.rs b/src/service/appservice.rs similarity index 100% rename from src/service/appservice/mod.rs rename to src/service/appservice.rs diff --git a/src/service/globals/mod.rs b/src/service/globals.rs similarity index 100% rename from src/service/globals/mod.rs rename to src/service/globals.rs diff --git a/src/service/key_backups/mod.rs b/src/service/key_backups.rs similarity index 100% rename from src/service/key_backups/mod.rs rename to src/service/key_backups.rs diff --git a/src/service/media/mod.rs b/src/service/media.rs similarity index 100% rename from src/service/media/mod.rs rename to src/service/media.rs diff --git a/src/service/pusher/mod.rs b/src/service/pusher.rs similarity index 100% rename from src/service/pusher/mod.rs rename to src/service/pusher.rs diff --git a/src/service/rooms/mod.rs b/src/service/rooms.rs similarity index 100% rename from src/service/rooms/mod.rs rename to src/service/rooms.rs diff --git a/src/service/rooms/alias/mod.rs b/src/service/rooms/alias.rs similarity index 100% rename from src/service/rooms/alias/mod.rs rename to src/service/rooms/alias.rs diff --git a/src/service/rooms/auth_chain/mod.rs b/src/service/rooms/auth_chain.rs similarity index 100% rename from src/service/rooms/auth_chain/mod.rs rename to src/service/rooms/auth_chain.rs diff --git a/src/service/rooms/directory/mod.rs b/src/service/rooms/directory.rs similarity index 100% rename from src/service/rooms/directory/mod.rs rename to src/service/rooms/directory.rs diff --git a/src/service/rooms/edus/mod.rs b/src/service/rooms/edus.rs similarity index 100% rename from src/service/rooms/edus/mod.rs rename to src/service/rooms/edus.rs diff --git a/src/service/rooms/edus/read_receipt/mod.rs b/src/service/rooms/edus/read_receipt.rs similarity index 100% rename from src/service/rooms/edus/read_receipt/mod.rs rename to src/service/rooms/edus/read_receipt.rs diff --git a/src/service/rooms/edus/typing/mod.rs b/src/service/rooms/edus/typing.rs similarity index 100% rename from src/service/rooms/edus/typing/mod.rs rename to src/service/rooms/edus/typing.rs diff --git a/src/service/rooms/event_handler/mod.rs b/src/service/rooms/event_handler.rs similarity index 100% rename from src/service/rooms/event_handler/mod.rs rename to src/service/rooms/event_handler.rs diff --git a/src/service/rooms/lazy_loading/mod.rs b/src/service/rooms/lazy_loading.rs similarity index 100% rename from src/service/rooms/lazy_loading/mod.rs rename to src/service/rooms/lazy_loading.rs diff --git a/src/service/rooms/metadata/mod.rs b/src/service/rooms/metadata.rs similarity index 100% rename from src/service/rooms/metadata/mod.rs rename to src/service/rooms/metadata.rs diff --git a/src/service/rooms/outlier/mod.rs b/src/service/rooms/outlier.rs similarity index 100% rename from src/service/rooms/outlier/mod.rs rename to src/service/rooms/outlier.rs diff --git a/src/service/rooms/pdu_metadata/mod.rs b/src/service/rooms/pdu_metadata.rs similarity index 100% rename from src/service/rooms/pdu_metadata/mod.rs rename to src/service/rooms/pdu_metadata.rs diff --git a/src/service/rooms/search/mod.rs b/src/service/rooms/search.rs similarity index 100% rename from src/service/rooms/search/mod.rs rename to src/service/rooms/search.rs diff --git a/src/service/rooms/short/mod.rs b/src/service/rooms/short.rs similarity index 100% rename from src/service/rooms/short/mod.rs rename to src/service/rooms/short.rs diff --git a/src/service/rooms/spaces/mod.rs b/src/service/rooms/spaces.rs similarity index 100% rename from src/service/rooms/spaces/mod.rs rename to src/service/rooms/spaces.rs diff --git a/src/service/rooms/state/mod.rs b/src/service/rooms/state.rs similarity index 100% rename from src/service/rooms/state/mod.rs rename to src/service/rooms/state.rs diff --git a/src/service/rooms/state_accessor/mod.rs b/src/service/rooms/state_accessor.rs similarity index 100% rename from src/service/rooms/state_accessor/mod.rs rename to src/service/rooms/state_accessor.rs diff --git a/src/service/rooms/state_cache/mod.rs b/src/service/rooms/state_cache.rs similarity index 100% rename from src/service/rooms/state_cache/mod.rs rename to src/service/rooms/state_cache.rs diff --git a/src/service/rooms/state_compressor/mod.rs b/src/service/rooms/state_compressor.rs similarity index 100% rename from src/service/rooms/state_compressor/mod.rs rename to src/service/rooms/state_compressor.rs diff --git a/src/service/rooms/threads/mod.rs b/src/service/rooms/threads.rs similarity index 100% rename from src/service/rooms/threads/mod.rs rename to src/service/rooms/threads.rs diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline.rs similarity index 100% rename from src/service/rooms/timeline/mod.rs rename to src/service/rooms/timeline.rs diff --git a/src/service/rooms/user/mod.rs b/src/service/rooms/user.rs similarity index 100% rename from src/service/rooms/user/mod.rs rename to src/service/rooms/user.rs diff --git a/src/service/sending/mod.rs b/src/service/sending.rs similarity index 100% rename from src/service/sending/mod.rs rename to src/service/sending.rs diff --git a/src/service/transaction_ids/mod.rs b/src/service/transaction_ids.rs similarity index 100% rename from src/service/transaction_ids/mod.rs rename to src/service/transaction_ids.rs diff --git a/src/service/uiaa/mod.rs b/src/service/uiaa.rs similarity index 100% rename from src/service/uiaa/mod.rs rename to src/service/uiaa.rs diff --git a/src/service/users/mod.rs b/src/service/users.rs similarity index 100% rename from src/service/users/mod.rs rename to src/service/users.rs diff --git a/src/utils/mod.rs b/src/utils.rs similarity index 100% rename from src/utils/mod.rs rename to src/utils.rs From d748544f0e7f35c7a4149130bb76549bc3c8f985 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 22:26:21 -0700 Subject: [PATCH 021/617] enable `unreachable_pub` lint This causes some other lints to start firing too (which is good), but I'm going to fix them in follow-up commits to keep things organized. --- Cargo.toml | 7 ++ src/api.rs | 8 +- src/api/client_server.rs | 74 +++++------ src/api/client_server/account.rs | 18 +-- src/api/client_server/alias.rs | 6 +- src/api/client_server/backup.rs | 28 ++--- src/api/client_server/capabilities.rs | 2 +- src/api/client_server/config.rs | 8 +- src/api/client_server/context.rs | 2 +- src/api/client_server/device.rs | 10 +- src/api/client_server/directory.rs | 8 +- src/api/client_server/filter.rs | 4 +- src/api/client_server/keys.rs | 14 ++- src/api/client_server/media.rs | 12 +- src/api/client_server/membership.rs | 32 +++-- src/api/client_server/message.rs | 4 +- src/api/client_server/profile.rs | 10 +- src/api/client_server/push.rs | 20 +-- src/api/client_server/read_marker.rs | 4 +- src/api/client_server/redact.rs | 2 +- src/api/client_server/relations.rs | 6 +- src/api/client_server/report.rs | 2 +- src/api/client_server/room.rs | 8 +- src/api/client_server/search.rs | 2 +- src/api/client_server/session.rs | 8 +- src/api/client_server/space.rs | 2 +- src/api/client_server/state.rs | 10 +- src/api/client_server/sync.rs | 4 +- src/api/client_server/tag.rs | 8 +- src/api/client_server/thirdparty.rs | 2 +- src/api/client_server/threads.rs | 2 +- src/api/client_server/to_device.rs | 2 +- src/api/client_server/typing.rs | 2 +- src/api/client_server/unversioned.rs | 4 +- src/api/client_server/user_directory.rs | 2 +- src/api/client_server/voip.rs | 2 +- src/api/ruma_wrapper.rs | 16 +-- src/api/server_server.rs | 48 ++++---- src/clap.rs | 4 +- src/config.rs | 82 ++++++------ src/config/proxy.rs | 14 +-- src/database.rs | 36 +++--- src/database/abstraction.rs | 10 +- src/database/abstraction/rocksdb.rs | 4 +- src/database/abstraction/sqlite.rs | 12 +- src/database/key_value/globals.rs | 2 +- src/main.rs | 20 +-- src/service.rs | 54 ++++---- src/service/account_data.rs | 12 +- src/service/account_data/data.rs | 2 +- src/service/admin.rs | 14 +-- src/service/appservice.rs | 50 ++++---- src/service/appservice/data.rs | 2 +- src/service/globals.rs | 122 +++++++++--------- src/service/globals/data.rs | 2 +- src/service/key_backups.rs | 36 +++--- src/service/key_backups/data.rs | 2 +- src/service/media.rs | 24 ++-- src/service/media/data.rs | 2 +- src/service/pdu.rs | 76 ++++++------ src/service/pusher.rs | 24 ++-- src/service/pusher/data.rs | 2 +- src/service/rooms.rs | 80 ++++++------ src/service/rooms/alias.rs | 14 +-- src/service/rooms/alias/data.rs | 2 +- src/service/rooms/auth_chain.rs | 19 ++- src/service/rooms/auth_chain/data.rs | 2 +- src/service/rooms/directory.rs | 14 +-- src/service/rooms/directory/data.rs | 2 +- src/service/rooms/edus.rs | 12 +- src/service/rooms/edus/read_receipt.rs | 29 +++-- src/service/rooms/edus/read_receipt/data.rs | 2 +- src/service/rooms/edus/typing.rs | 23 ++-- src/service/rooms/event_handler.rs | 8 +- src/service/rooms/lazy_loading.rs | 16 +-- src/service/rooms/lazy_loading/data.rs | 2 +- src/service/rooms/metadata.rs | 14 +-- src/service/rooms/metadata/data.rs | 2 +- src/service/rooms/outlier.rs | 19 ++- src/service/rooms/outlier/data.rs | 2 +- src/service/rooms/pdu_metadata.rs | 24 ++-- src/service/rooms/pdu_metadata/data.rs | 2 +- src/service/rooms/search.rs | 15 ++- src/service/rooms/search/data.rs | 2 +- src/service/rooms/short.rs | 25 ++-- src/service/rooms/short/data.rs | 2 +- src/service/rooms/spaces.rs | 10 +- src/service/rooms/state.rs | 29 +++-- src/service/rooms/state/data.rs | 2 +- src/service/rooms/state_accessor.rs | 49 ++++---- src/service/rooms/state_accessor/data.rs | 2 +- src/service/rooms/state_cache.rs | 62 ++++++---- src/service/rooms/state_cache/data.rs | 2 +- src/service/rooms/state_compressor.rs | 22 ++-- src/service/rooms/state_compressor/data.rs | 10 +- src/service/rooms/threads.rs | 10 +- src/service/rooms/threads/data.rs | 2 +- src/service/rooms/timeline.rs | 73 ++++++----- src/service/rooms/timeline/data.rs | 2 +- src/service/rooms/user.rs | 28 +++-- src/service/rooms/user/data.rs | 2 +- src/service/sending.rs | 37 +++--- src/service/sending/data.rs | 2 +- src/service/transaction_ids.rs | 10 +- src/service/transaction_ids/data.rs | 2 +- src/service/uiaa.rs | 12 +- src/service/uiaa/data.rs | 2 +- src/service/users.rs | 130 +++++++++++--------- src/service/users/data.rs | 2 +- src/utils.rs | 26 ++-- src/utils/error.rs | 12 +- 111 files changed, 1007 insertions(+), 876 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1229e72a..b185ccc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,12 +6,16 @@ macro_use_extern_crate = "warn" missing_abi = "warn" noop_method_call = "warn" pointer_structural_match = "warn" +unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_extern_crates = "warn" unused_import_braces = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" +# TODO: Remove these +dead_code = "allow" + # Keep alphabetically sorted [workspace.lints.clippy] assertions_on_result_states = "warn" @@ -47,6 +51,9 @@ unseparated_literal_suffix = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" +# TODO: Remove these +enum_variant_names = "allow" + [package] name = "grapevine" description = "A Matrix homeserver written in Rust" diff --git a/src/api.rs b/src/api.rs index 0d2cd664..442d2f2b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,4 +1,4 @@ -pub mod appservice_server; -pub mod client_server; -pub mod ruma_wrapper; -pub mod server_server; +pub(crate) mod appservice_server; +pub(crate) mod client_server; +pub(crate) mod ruma_wrapper; +pub(crate) mod server_server; diff --git a/src/api/client_server.rs b/src/api/client_server.rs index c50db8d5..65c5985d 100644 --- a/src/api/client_server.rs +++ b/src/api/client_server.rs @@ -32,41 +32,41 @@ mod unversioned; mod user_directory; mod voip; -pub use account::*; -pub use alias::*; -pub use backup::*; -pub use capabilities::*; -pub use config::*; -pub use context::*; -pub use device::*; -pub use directory::*; -pub use filter::*; -pub use keys::*; -pub use media::*; -pub use membership::*; -pub use message::*; -pub use profile::*; -pub use push::*; -pub use read_marker::*; -pub use redact::*; -pub use relations::*; -pub use report::*; -pub use room::*; -pub use search::*; -pub use session::*; -pub use space::*; -pub use state::*; -pub use sync::*; -pub use tag::*; -pub use thirdparty::*; -pub use threads::*; -pub use to_device::*; -pub use typing::*; -pub use unversioned::*; -pub use user_directory::*; -pub use voip::*; +pub(crate) use account::*; +pub(crate) use alias::*; +pub(crate) use backup::*; +pub(crate) use capabilities::*; +pub(crate) use config::*; +pub(crate) use context::*; +pub(crate) use device::*; +pub(crate) use directory::*; +pub(crate) use filter::*; +pub(crate) use keys::*; +pub(crate) use media::*; +pub(crate) use membership::*; +pub(crate) use message::*; +pub(crate) use profile::*; +pub(crate) use push::*; +pub(crate) use read_marker::*; +pub(crate) use redact::*; +pub(crate) use relations::*; +pub(crate) use report::*; +pub(crate) use room::*; +pub(crate) use search::*; +pub(crate) use session::*; +pub(crate) use space::*; +pub(crate) use state::*; +pub(crate) use sync::*; +pub(crate) use tag::*; +pub(crate) use thirdparty::*; +pub(crate) use threads::*; +pub(crate) use to_device::*; +pub(crate) use typing::*; +pub(crate) use unversioned::*; +pub(crate) use user_directory::*; +pub(crate) use voip::*; -pub const DEVICE_ID_LENGTH: usize = 10; -pub const TOKEN_LENGTH: usize = 32; -pub const SESSION_ID_LENGTH: usize = 32; -pub const AUTO_GEN_PASSWORD_LENGTH: usize = 15; +pub(crate) const DEVICE_ID_LENGTH: usize = 10; +pub(crate) const TOKEN_LENGTH: usize = 32; +pub(crate) const SESSION_ID_LENGTH: usize = 32; +pub(crate) const AUTO_GEN_PASSWORD_LENGTH: usize = 15; diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 8f639687..9c3d2083 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -30,7 +30,7 @@ const RANDOM_USER_ID_LENGTH: usize = 10; /// - No user or appservice on this server already claimed this username /// /// Note: This will not reserve the username, so the username might become invalid when trying to register -pub async fn get_register_available_route( +pub(crate) async fn get_register_available_route( body: Ruma, ) -> Result { // Validate user id @@ -74,7 +74,9 @@ pub async fn get_register_available_route( /// - If type is not guest and no username is given: Always fails after UIAA check /// - Creates a new account and populates it with default account data /// - If `inhibit_login` is false: Creates a device and returns device id and access_token -pub async fn register_route(body: Ruma) -> Result { +pub(crate) async fn register_route( + body: Ruma, +) -> Result { if !services().globals.allow_registration() && body.appservice_info.is_none() { return Err(Error::BadRequest( ErrorKind::Forbidden, @@ -307,7 +309,7 @@ pub async fn register_route(body: Ruma) -> Result, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -373,7 +375,7 @@ pub async fn change_password_route( /// Get user_id of the sender user. /// /// Note: Also works for Application Services -pub async fn whoami_route(body: Ruma) -> Result { +pub(crate) async fn whoami_route(body: Ruma) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device_id = body.sender_device.as_ref().cloned(); @@ -394,7 +396,7 @@ pub async fn whoami_route(body: Ruma) -> Result, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -452,7 +454,7 @@ pub async fn deactivate_route( /// Get a list of third party identifiers associated with this account. /// /// - Currently always returns empty list -pub async fn third_party_route( +pub(crate) async fn third_party_route( body: Ruma, ) -> Result { let _sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -465,7 +467,7 @@ pub async fn third_party_route( /// "This API should be used to request validation tokens when adding an email address to an account" /// /// - 403 signals that The homeserver does not allow the third party identifier as a contact option. -pub async fn request_3pid_management_token_via_email_route( +pub(crate) async fn request_3pid_management_token_via_email_route( _body: Ruma, ) -> Result { Err(Error::BadRequest( @@ -479,7 +481,7 @@ pub async fn request_3pid_management_token_via_email_route( /// "This API should be used to request validation tokens when adding an phone number to an account" /// /// - 403 signals that The homeserver does not allow the third party identifier as a contact option. -pub async fn request_3pid_management_token_via_msisdn_route( +pub(crate) async fn request_3pid_management_token_via_msisdn_route( _body: Ruma, ) -> Result { Err(Error::BadRequest( diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 7cbe9fa1..6e198e26 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -15,7 +15,7 @@ use ruma::{ /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}` /// /// Creates a new room alias on this server. -pub async fn create_alias_route( +pub(crate) async fn create_alias_route( body: Ruma, ) -> Result { if body.room_alias.server_name() != services().globals.server_name() { @@ -66,7 +66,7 @@ pub async fn create_alias_route( /// /// - TODO: additional access control checks /// - TODO: Update canonical alias event -pub async fn delete_alias_route( +pub(crate) async fn delete_alias_route( body: Ruma, ) -> Result { if body.room_alias.server_name() != services().globals.server_name() { @@ -106,7 +106,7 @@ pub async fn delete_alias_route( /// Resolve an alias locally or over federation. /// /// - TODO: Suggest more servers to join via -pub async fn get_alias_route( +pub(crate) async fn get_alias_route( body: Ruma, ) -> Result { get_alias_helper(body.body.room_alias).await diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index 115cba7c..0f9c8082 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -13,7 +13,7 @@ use ruma::api::client::{ /// # `POST /_matrix/client/r0/room_keys/version` /// /// Creates a new backup. -pub async fn create_backup_version_route( +pub(crate) async fn create_backup_version_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -27,7 +27,7 @@ pub async fn create_backup_version_route( /// # `PUT /_matrix/client/r0/room_keys/version/{version}` /// /// Update information about an existing backup. Only `auth_data` can be modified. -pub async fn update_backup_version_route( +pub(crate) async fn update_backup_version_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -41,7 +41,7 @@ pub async fn update_backup_version_route( /// # `GET /_matrix/client/r0/room_keys/version` /// /// Get information about the latest backup version. -pub async fn get_latest_backup_info_route( +pub(crate) async fn get_latest_backup_info_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -65,7 +65,7 @@ pub async fn get_latest_backup_info_route( /// # `GET /_matrix/client/r0/room_keys/version` /// /// Get information about an existing backup. -pub async fn get_backup_info_route( +pub(crate) async fn get_backup_info_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -95,7 +95,7 @@ pub async fn get_backup_info_route( /// Delete an existing key backup. /// /// - Deletes both information about the backup, as well as all key data related to the backup -pub async fn delete_backup_version_route( +pub(crate) async fn delete_backup_version_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -114,7 +114,7 @@ pub async fn delete_backup_version_route( /// - Only manipulating the most recently created version of the backup is allowed /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag -pub async fn add_backup_keys_route( +pub(crate) async fn add_backup_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -161,7 +161,7 @@ pub async fn add_backup_keys_route( /// - Only manipulating the most recently created version of the backup is allowed /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag -pub async fn add_backup_keys_for_room_route( +pub(crate) async fn add_backup_keys_for_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -206,7 +206,7 @@ pub async fn add_backup_keys_for_room_route( /// - Only manipulating the most recently created version of the backup is allowed /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag -pub async fn add_backup_keys_for_session_route( +pub(crate) async fn add_backup_keys_for_session_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -245,7 +245,7 @@ pub async fn add_backup_keys_for_session_route( /// # `GET /_matrix/client/r0/room_keys/keys` /// /// Retrieves all keys from the backup. -pub async fn get_backup_keys_route( +pub(crate) async fn get_backup_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -258,7 +258,7 @@ pub async fn get_backup_keys_route( /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}` /// /// Retrieves all keys from the backup for a given room. -pub async fn get_backup_keys_for_room_route( +pub(crate) async fn get_backup_keys_for_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -273,7 +273,7 @@ pub async fn get_backup_keys_for_room_route( /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}` /// /// Retrieves a key from the backup. -pub async fn get_backup_keys_for_session_route( +pub(crate) async fn get_backup_keys_for_session_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -292,7 +292,7 @@ pub async fn get_backup_keys_for_session_route( /// # `DELETE /_matrix/client/r0/room_keys/keys` /// /// Delete the keys from the backup. -pub async fn delete_backup_keys_route( +pub(crate) async fn delete_backup_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -315,7 +315,7 @@ pub async fn delete_backup_keys_route( /// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}` /// /// Delete the keys from the backup for a given room. -pub async fn delete_backup_keys_for_room_route( +pub(crate) async fn delete_backup_keys_for_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -338,7 +338,7 @@ pub async fn delete_backup_keys_for_room_route( /// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}` /// /// Delete a key from the backup. -pub async fn delete_backup_keys_for_session_route( +pub(crate) async fn delete_backup_keys_for_session_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index 233e3c9c..f248d1b9 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -7,7 +7,7 @@ use std::collections::BTreeMap; /// # `GET /_matrix/client/r0/capabilities` /// /// Get information on the supported feature set and other relevent capabilities of this server. -pub async fn get_capabilities_route( +pub(crate) async fn get_capabilities_route( _body: Ruma, ) -> Result { let mut available = BTreeMap::new(); diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index 37279e35..c650de1f 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -16,7 +16,7 @@ use serde_json::{json, value::RawValue as RawJsonValue}; /// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}` /// /// Sets some account data for the sender user. -pub async fn set_global_account_data_route( +pub(crate) async fn set_global_account_data_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -42,7 +42,7 @@ pub async fn set_global_account_data_route( /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}` /// /// Sets some room account data for the sender user. -pub async fn set_room_account_data_route( +pub(crate) async fn set_room_account_data_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -68,7 +68,7 @@ pub async fn set_room_account_data_route( /// # `GET /_matrix/client/r0/user/{userId}/account_data/{type}` /// /// Gets some account data for the sender user. -pub async fn get_global_account_data_route( +pub(crate) async fn get_global_account_data_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -88,7 +88,7 @@ pub async fn get_global_account_data_route( /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}` /// /// Gets some room account data for the sender user. -pub async fn get_room_account_data_route( +pub(crate) async fn get_room_account_data_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 8e193e6b..205ecc3a 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -12,7 +12,7 @@ use tracing::error; /// /// - Only works if the user is joined (TODO: always allow, but only show events if the user was /// joined, depending on history_visibility) -pub async fn get_context_route( +pub(crate) async fn get_context_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index aba061b2..03f55d77 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -10,7 +10,7 @@ use super::SESSION_ID_LENGTH; /// # `GET /_matrix/client/r0/devices` /// /// Get metadata on all devices of the sender user. -pub async fn get_devices_route( +pub(crate) async fn get_devices_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -27,7 +27,7 @@ pub async fn get_devices_route( /// # `GET /_matrix/client/r0/devices/{deviceId}` /// /// Get metadata on a single device of the sender user. -pub async fn get_device_route( +pub(crate) async fn get_device_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -43,7 +43,7 @@ pub async fn get_device_route( /// # `PUT /_matrix/client/r0/devices/{deviceId}` /// /// Updates the metadata on a given device of the sender user. -pub async fn update_device_route( +pub(crate) async fn update_device_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -71,7 +71,7 @@ pub async fn update_device_route( /// - Deletes device metadata (device id, device display name, last seen ip, last seen ts) /// - Forgets to-device events /// - Triggers device list updates -pub async fn delete_device_route( +pub(crate) async fn delete_device_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -125,7 +125,7 @@ pub async fn delete_device_route( /// - Deletes device metadata (device id, device display name, last seen ip, last seen ts) /// - Forgets to-device events /// - Triggers device list updates -pub async fn delete_devices_route( +pub(crate) async fn delete_devices_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 50ae9f15..feba5ff3 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -33,7 +33,7 @@ use tracing::{error, info, warn}; /// Lists the public rooms on this server. /// /// - Rooms are ordered by the number of joined members -pub async fn get_public_rooms_filtered_route( +pub(crate) async fn get_public_rooms_filtered_route( body: Ruma, ) -> Result { get_public_rooms_filtered_helper( @@ -51,7 +51,7 @@ pub async fn get_public_rooms_filtered_route( /// Lists the public rooms on this server. /// /// - Rooms are ordered by the number of joined members -pub async fn get_public_rooms_route( +pub(crate) async fn get_public_rooms_route( body: Ruma, ) -> Result { let response = get_public_rooms_filtered_helper( @@ -76,7 +76,7 @@ pub async fn get_public_rooms_route( /// Sets the visibility of a given room in the room directory. /// /// - TODO: Access control checks -pub async fn set_room_visibility_route( +pub(crate) async fn set_room_visibility_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -106,7 +106,7 @@ pub async fn set_room_visibility_route( /// # `GET /_matrix/client/r0/directory/list/room/{roomId}` /// /// Gets the visibility of a given room in the room directory. -pub async fn get_room_visibility_route( +pub(crate) async fn get_room_visibility_route( body: Ruma, ) -> Result { if !services().rooms.metadata.exists(&body.room_id)? { diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index e9a359d6..5293a95e 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -9,7 +9,7 @@ use ruma::api::client::{ /// Loads a filter that was previously created. /// /// - A user can only access their own filters -pub async fn get_filter_route( +pub(crate) async fn get_filter_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -24,7 +24,7 @@ pub async fn get_filter_route( /// # `PUT /_matrix/client/r0/user/{userId}/filter` /// /// Creates a new filter to be used by other endpoints. -pub async fn create_filter_route( +pub(crate) async fn create_filter_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 4af8890d..3ea66d7d 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -29,7 +29,7 @@ use tracing::debug; /// /// - Adds one time keys /// - If there are no device keys yet: Adds device keys (TODO: merge with existing keys?) -pub async fn upload_keys_route( +pub(crate) async fn upload_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -69,7 +69,9 @@ pub async fn upload_keys_route( /// - Always fetches users from other servers over federation /// - Gets master keys, self-signing keys, user signing keys and device keys. /// - The master and self-signing keys contain signatures that the user is allowed to see -pub async fn get_keys_route(body: Ruma) -> Result { +pub(crate) async fn get_keys_route( + body: Ruma, +) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let response = @@ -81,7 +83,7 @@ pub async fn get_keys_route(body: Ruma) -> Result, ) -> Result { let response = claim_keys_helper(&body.one_time_keys).await?; @@ -94,7 +96,7 @@ pub async fn claim_keys_route( /// Uploads end-to-end key information for the sender user. /// /// - Requires UIAA to verify password -pub async fn upload_signing_keys_route( +pub(crate) async fn upload_signing_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -146,7 +148,7 @@ pub async fn upload_signing_keys_route( /// # `POST /_matrix/client/r0/keys/signatures/upload` /// /// Uploads end-to-end key signatures from the sender user. -pub async fn upload_signatures_route( +pub(crate) async fn upload_signatures_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -204,7 +206,7 @@ pub async fn upload_signatures_route( /// Gets a list of users who have updated their device identity keys since the previous sync token. /// /// - TODO: left users -pub async fn get_key_changes_route( +pub(crate) async fn get_key_changes_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 7fc65c26..62fe4c09 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -14,7 +14,7 @@ const MXC_LENGTH: usize = 32; /// # `GET /_matrix/media/r0/config` /// /// Returns max upload size. -pub async fn get_media_config_route( +pub(crate) async fn get_media_config_route( _body: Ruma, ) -> Result { Ok(get_media_config::v3::Response { @@ -28,7 +28,7 @@ pub async fn get_media_config_route( /// /// - Some metadata will be saved in the database /// - Media will be saved in the media/ directory -pub async fn create_content_route( +pub(crate) async fn create_content_route( body: Ruma, ) -> Result { let mxc = format!( @@ -56,7 +56,7 @@ pub async fn create_content_route( }) } -pub async fn get_remote_content( +pub(crate) async fn get_remote_content( mxc: &str, server_name: &ruma::ServerName, media_id: String, @@ -93,7 +93,7 @@ pub async fn get_remote_content( /// Load media from our server or over federation. /// /// - Only allows federation if `allow_remote` is true -pub async fn get_content_route( +pub(crate) async fn get_content_route( body: Ruma, ) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); @@ -124,7 +124,7 @@ pub async fn get_content_route( /// Load media from our server or over federation, permitting desired filename. /// /// - Only allows federation if `allow_remote` is true -pub async fn get_content_as_filename_route( +pub(crate) async fn get_content_as_filename_route( body: Ruma, ) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); @@ -161,7 +161,7 @@ pub async fn get_content_as_filename_route( /// Load media thumbnail from our server or over federation. /// /// - Only allows federation if `allow_remote` is true -pub async fn get_content_thumbnail_route( +pub(crate) async fn get_content_thumbnail_route( body: Ruma, ) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 6fe1e0ea..bc98c00b 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -44,7 +44,7 @@ use super::get_alias_helper; /// /// - 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( +pub(crate) async fn join_room_by_id_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -87,7 +87,7 @@ pub async fn join_room_by_id_route( /// /// - 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( +pub(crate) async fn join_room_by_id_or_alias_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_deref().expect("user is authenticated"); @@ -145,7 +145,7 @@ pub async fn join_room_by_id_or_alias_route( /// 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( +pub(crate) async fn leave_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -158,7 +158,7 @@ pub async fn leave_room_route( /// # `POST /_matrix/client/r0/rooms/{roomId}/invite` /// /// Tries to send an invite event into the room. -pub async fn invite_user_route( +pub(crate) async fn invite_user_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -181,7 +181,7 @@ pub async fn invite_user_route( /// # `POST /_matrix/client/r0/rooms/{roomId}/kick` /// /// Tries to send a kick event into the room. -pub async fn kick_user_route( +pub(crate) async fn kick_user_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -251,7 +251,9 @@ pub async fn kick_user_route( /// # `POST /_matrix/client/r0/rooms/{roomId}/ban` /// /// Tries to send a ban event into the room. -pub async fn ban_user_route(body: Ruma) -> Result { +pub(crate) async fn ban_user_route( + body: Ruma, +) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(Some(membership_event)) = services() @@ -330,7 +332,7 @@ pub async fn ban_user_route(body: Ruma) -> Result, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -407,7 +409,7 @@ pub async fn unban_user_route( /// /// Note: Other devices of the user have no way of knowing the room was forgotten, so this has to /// be called from every device -pub async fn forget_room_route( +pub(crate) async fn forget_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -423,7 +425,7 @@ pub async fn forget_room_route( /// # `POST /_matrix/client/r0/joined_rooms` /// /// Lists all rooms the user has joined. -pub async fn joined_rooms_route( +pub(crate) async fn joined_rooms_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -443,7 +445,7 @@ pub async fn joined_rooms_route( /// Lists all joined users in a room (TODO: at a specific point in time, with a specific membership). /// /// - Only works if the user is currently joined -pub async fn get_member_events_route( +pub(crate) async fn get_member_events_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -478,7 +480,7 @@ pub async fn get_member_events_route( /// /// - The sender user must be in the room /// - TODO: An appservice just needs a puppet joined -pub async fn joined_members_route( +pub(crate) async fn joined_members_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -1378,7 +1380,7 @@ pub(crate) async fn invite_helper<'a>( } // Make a user leave all their joined rooms -pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> { +pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> { let all_rooms = services() .rooms .state_cache @@ -1404,7 +1406,11 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> { Ok(()) } -pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option) -> Result<()> { +pub(crate) async fn leave_room( + user_id: &UserId, + room_id: &RoomId, + reason: Option, +) -> Result<()> { // Ask a remote server if we don't have this room if !services() .rooms diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 89f33591..36890cab 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -21,7 +21,7 @@ use std::{ /// - Is a NOOP if the txn id was already used before and returns the same event id again /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed -pub async fn send_message_event_route( +pub(crate) async fn send_message_event_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -111,7 +111,7 @@ pub async fn send_message_event_route( /// /// - Only works if the user is joined (TODO: always allow, but only show events where the user was /// joined, depending on history_visibility) -pub async fn get_message_events_route( +pub(crate) async fn get_message_events_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index f1500439..eafa726f 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -19,7 +19,7 @@ use std::sync::Arc; /// Updates the displayname. /// /// - Also makes sure other users receive the update using presence EDUs -pub async fn set_displayname_route( +pub(crate) async fn set_displayname_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -99,7 +99,7 @@ pub async fn set_displayname_route( /// Returns the displayname of the user. /// /// - If user is on another server: Fetches displayname over federation -pub async fn get_displayname_route( +pub(crate) async fn get_displayname_route( body: Ruma, ) -> Result { if body.user_id.server_name() != services().globals.server_name() { @@ -129,7 +129,7 @@ pub async fn get_displayname_route( /// Updates the avatar_url and blurhash. /// /// - Also makes sure other users receive the update using presence EDUs -pub async fn set_avatar_url_route( +pub(crate) async fn set_avatar_url_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -213,7 +213,7 @@ pub async fn set_avatar_url_route( /// Returns the avatar_url and blurhash of the user. /// /// - If user is on another server: Fetches avatar_url and blurhash over federation -pub async fn get_avatar_url_route( +pub(crate) async fn get_avatar_url_route( body: Ruma, ) -> Result { if body.user_id.server_name() != services().globals.server_name() { @@ -245,7 +245,7 @@ pub async fn get_avatar_url_route( /// Returns the displayname, avatar_url and blurhash of the user. /// /// - If user is on another server: Fetches profile over federation -pub async fn get_profile_route( +pub(crate) async fn get_profile_route( body: Ruma, ) -> Result { if body.user_id.server_name() != services().globals.server_name() { diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 72768662..cdb8cb82 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -15,7 +15,7 @@ use ruma::{ /// # `GET /_matrix/client/r0/pushrules` /// /// Retrieves the push rules event for this user. -pub async fn get_pushrules_all_route( +pub(crate) async fn get_pushrules_all_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -44,7 +44,7 @@ pub async fn get_pushrules_all_route( /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}` /// /// Retrieves a single specified push rule for this user. -pub async fn get_pushrule_route( +pub(crate) async fn get_pushrule_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -83,7 +83,7 @@ pub async fn get_pushrule_route( /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}` /// /// Creates a single specified push rule for this user. -pub async fn set_pushrule_route( +pub(crate) async fn set_pushrule_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -156,7 +156,7 @@ pub async fn set_pushrule_route( /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions` /// /// Gets the actions of a single specified push rule for this user. -pub async fn get_pushrule_actions_route( +pub(crate) async fn get_pushrule_actions_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -199,7 +199,7 @@ pub async fn get_pushrule_actions_route( /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions` /// /// Sets the actions of a single specified push rule for this user. -pub async fn set_pushrule_actions_route( +pub(crate) async fn set_pushrule_actions_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -251,7 +251,7 @@ pub async fn set_pushrule_actions_route( /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled` /// /// Gets the enabled status of a single specified push rule for this user. -pub async fn get_pushrule_enabled_route( +pub(crate) async fn get_pushrule_enabled_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -293,7 +293,7 @@ pub async fn get_pushrule_enabled_route( /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled` /// /// Sets the enabled status of a single specified push rule for this user. -pub async fn set_pushrule_enabled_route( +pub(crate) async fn set_pushrule_enabled_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -345,7 +345,7 @@ pub async fn set_pushrule_enabled_route( /// # `DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}` /// /// Deletes a single specified push rule for this user. -pub async fn delete_pushrule_route( +pub(crate) async fn delete_pushrule_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -404,7 +404,7 @@ pub async fn delete_pushrule_route( /// # `GET /_matrix/client/r0/pushers` /// /// Gets all currently active pushers for the sender user. -pub async fn get_pushers_route( +pub(crate) async fn get_pushers_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -419,7 +419,7 @@ pub async fn get_pushers_route( /// Adds a pusher for the sender user. /// /// - TODO: Handle `append` -pub async fn set_pushers_route( +pub(crate) async fn set_pushers_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index a5553d25..fb614bc9 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -15,7 +15,7 @@ use std::collections::BTreeMap; /// /// - Updates fully-read account data event to `fully_read` /// - If `read_receipt` is set: Update private marker and public read receipt EDU -pub async fn set_read_marker_route( +pub(crate) async fn set_read_marker_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -98,7 +98,7 @@ pub async fn set_read_marker_route( /// # `POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}` /// /// Sets private read marker and public read receipt EDU. -pub async fn create_receipt_route( +pub(crate) async fn create_receipt_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/redact.rs b/src/api/client_server/redact.rs index f0603f4b..0f774dd9 100644 --- a/src/api/client_server/redact.rs +++ b/src/api/client_server/redact.rs @@ -13,7 +13,7 @@ use serde_json::value::to_raw_value; /// Tries to send a redaction event into the room. /// /// - TODO: Handle txn id -pub async fn redact_event_route( +pub(crate) async fn redact_event_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 124f1310..abf3ea70 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -6,7 +6,7 @@ use ruma::api::client::relations::{ use crate::{service::rooms::timeline::PduCount, services, Result, Ruma}; /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}` -pub async fn get_relating_events_with_rel_type_and_event_type_route( +pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -56,7 +56,7 @@ pub async fn get_relating_events_with_rel_type_and_event_type_route( } /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}` -pub async fn get_relating_events_with_rel_type_route( +pub(crate) async fn get_relating_events_with_rel_type_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -104,7 +104,7 @@ pub async fn get_relating_events_with_rel_type_route( } /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}` -pub async fn get_relating_events_route( +pub(crate) async fn get_relating_events_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index ab5027cd..8e7dbed2 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -9,7 +9,7 @@ use ruma::{ /// /// Reports an inappropriate event to homeserver admins /// -pub async fn report_event_route( +pub(crate) async fn report_event_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index e3e8a746..f1f67076 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -45,7 +45,7 @@ use tracing::{info, warn}; /// - Send events listed in initial state /// - Send events implied by `name` and `topic` /// - Send invite events -pub async fn create_room_route( +pub(crate) async fn create_room_route( body: Ruma, ) -> Result { use create_room::v3::RoomPreset; @@ -502,7 +502,7 @@ pub async fn create_room_route( /// Gets a single event. /// /// - You have to currently be joined to the room (TODO: Respect history visibility) -pub async fn get_room_event_route( +pub(crate) async fn get_room_event_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -540,7 +540,7 @@ pub async fn get_room_event_route( /// Lists all aliases of the room. /// /// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable -pub async fn get_room_aliases_route( +pub(crate) async fn get_room_aliases_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -576,7 +576,7 @@ pub async fn get_room_aliases_route( /// - Transfers some state events /// - Moves local aliases /// - Modifies old room power levels to prevent users from speaking -pub async fn upgrade_room_route( +pub(crate) async fn upgrade_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index e9fac365..1fb70253 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -14,7 +14,7 @@ use std::collections::BTreeMap; /// Searches rooms for messages. /// /// - Only works if the user is currently joined to the room (TODO: Respect history visibility) -pub async fn search_events_route( +pub(crate) async fn search_events_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 3e583fac..d18d384a 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -21,7 +21,7 @@ struct Claims { /// /// Get the supported login types of this server. One of these should be used as the `type` field /// when logging in. -pub async fn get_login_types_route( +pub(crate) async fn get_login_types_route( _body: Ruma, ) -> Result { Ok(get_login_types::v3::Response::new(vec![ @@ -41,7 +41,7 @@ pub async fn get_login_types_route( /// /// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see /// supported login types. -pub async fn login_route(body: Ruma) -> Result { +pub(crate) async fn login_route(body: Ruma) -> Result { // To allow deprecated login methods #![allow(deprecated)] // Validate login method @@ -223,7 +223,7 @@ pub async fn login_route(body: Ruma) -> Result) -> Result { +pub(crate) async fn logout_route(body: Ruma) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -252,7 +252,7 @@ pub async fn logout_route(body: Ruma) -> Result, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index e2ea8c34..ffbd5b8d 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -4,7 +4,7 @@ use ruma::api::client::space::get_hierarchy; /// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`` /// /// Paginates over the space tree in a depth-first manner to locate child rooms of a given space. -pub async fn get_hierarchy_route( +pub(crate) async fn get_hierarchy_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index e62aa013..97d9821e 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -21,7 +21,7 @@ use tracing::log::warn; /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed /// - If event is new canonical_alias: Rejects if alias is incorrect -pub async fn send_state_event_for_key_route( +pub(crate) async fn send_state_event_for_key_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -46,7 +46,7 @@ pub async fn send_state_event_for_key_route( /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed /// - If event is new canonical_alias: Rejects if alias is incorrect -pub async fn send_state_event_for_empty_key_route( +pub(crate) async fn send_state_event_for_empty_key_route( body: Ruma, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -77,7 +77,7 @@ pub async fn send_state_event_for_empty_key_route( /// Get all state events for a room. /// /// - If not joined: Only works if current room history visibility is world readable -pub async fn get_state_events_route( +pub(crate) async fn get_state_events_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -110,7 +110,7 @@ pub async fn get_state_events_route( /// Get single state event of a room. /// /// - If not joined: Only works if current room history visibility is world readable -pub async fn get_state_events_for_key_route( +pub(crate) async fn get_state_events_for_key_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -149,7 +149,7 @@ pub async fn get_state_events_for_key_route( /// Get single state event of a room. /// /// - If not joined: Only works if current room history visibility is world readable -pub async fn get_state_events_for_empty_key_route( +pub(crate) async fn get_state_events_for_empty_key_route( body: Ruma, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index e561c320..e10563f4 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -65,7 +65,7 @@ use tracing::{error, info}; /// /// - Sync is handled in an async task, multiple requests from the same device with the same /// `since` will be cached -pub async fn sync_events_route( +pub(crate) async fn sync_events_route( body: Ruma, ) -> Result> { let sender_user = body.sender_user.expect("user is authenticated"); @@ -1180,7 +1180,7 @@ fn share_encrypted_room( .any(|encrypted| encrypted)) } -pub async fn sync_events_v4_route( +pub(crate) async fn sync_events_v4_route( body: Ruma, ) -> Result> { let sender_user = body.sender_user.expect("user is authenticated"); diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 16f1600f..4c333821 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -13,7 +13,7 @@ use std::collections::BTreeMap; /// Adds a tag to the room. /// /// - Inserts the tag into the tag event of the room account data. -pub async fn update_tag_route( +pub(crate) async fn update_tag_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -57,7 +57,7 @@ pub async fn update_tag_route( /// Deletes a tag from the room. /// /// - Removes the tag from the tag event of the room account data. -pub async fn delete_tag_route( +pub(crate) async fn delete_tag_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -98,7 +98,9 @@ pub async fn delete_tag_route( /// Returns tags on the room. /// /// - Gets the tag event of the room account data. -pub async fn get_tags_route(body: Ruma) -> Result { +pub(crate) async fn get_tags_route( + body: Ruma, +) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( diff --git a/src/api/client_server/thirdparty.rs b/src/api/client_server/thirdparty.rs index c2c1adfd..3c678acf 100644 --- a/src/api/client_server/thirdparty.rs +++ b/src/api/client_server/thirdparty.rs @@ -6,7 +6,7 @@ use std::collections::BTreeMap; /// # `GET /_matrix/client/r0/thirdparty/protocols` /// /// TODO: Fetches all metadata about protocols supported by the homeserver. -pub async fn get_protocols_route( +pub(crate) async fn get_protocols_route( _body: Ruma, ) -> Result { // TODO diff --git a/src/api/client_server/threads.rs b/src/api/client_server/threads.rs index a095b420..6c93644a 100644 --- a/src/api/client_server/threads.rs +++ b/src/api/client_server/threads.rs @@ -3,7 +3,7 @@ use ruma::api::client::{error::ErrorKind, threads::get_threads}; use crate::{services, Error, Result, Ruma}; /// # `GET /_matrix/client/r0/rooms/{roomId}/threads` -pub async fn get_threads_route( +pub(crate) async fn get_threads_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index 31590fc7..bfa79631 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -12,7 +12,7 @@ use ruma::{ /// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}` /// /// Send a to-device event to a set of client devices. -pub async fn send_event_to_device_route( +pub(crate) async fn send_event_to_device_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index e9e93708..7a3dd5c9 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -4,7 +4,7 @@ use ruma::api::client::{error::ErrorKind, typing::create_typing_event}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}` /// /// Sets the typing state of the sender user. -pub async fn create_typing_event_route( +pub(crate) async fn create_typing_event_route( body: Ruma, ) -> Result { use create_typing_event::v3::Typing; diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 70e260ec..81899b68 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -15,7 +15,7 @@ use crate::{services, Error, Result, Ruma}; /// /// Note: Unstable features are used while developing new features. Clients should avoid using /// unstable features in their stable releases -pub async fn get_supported_versions_route( +pub(crate) async fn get_supported_versions_route( _body: Ruma, ) -> Result { let resp = get_supported_versions::Response { @@ -35,7 +35,7 @@ pub async fn get_supported_versions_route( } /// # `GET /.well-known/matrix/client` -pub async fn well_known_client_route( +pub(crate) async fn well_known_client_route( _body: Ruma, ) -> Result { let client_url = match services().globals.well_known_client() { diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index b4d11800..30f7686e 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -13,7 +13,7 @@ use ruma::{ /// /// - Hides any local users that aren't in any public rooms (i.e. those that have the join rule set to public) /// and don't share a room with the sender -pub async fn search_users_route( +pub(crate) async fn search_users_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index f0d91f71..f7bce1a6 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -10,7 +10,7 @@ type HmacSha1 = Hmac; /// # `GET /_matrix/client/r0/voip/turnServer` /// /// TODO: Returns information about the recommended turn server. -pub async fn turn_server_route( +pub(crate) async fn turn_server_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/ruma_wrapper.rs b/src/api/ruma_wrapper.rs index 28659dfe..e3486a73 100644 --- a/src/api/ruma_wrapper.rs +++ b/src/api/ruma_wrapper.rs @@ -8,14 +8,14 @@ use std::ops::Deref; mod axum; /// Extractor for Ruma request structs -pub struct Ruma { - pub body: T, - pub sender_user: Option, - pub sender_device: Option, - pub sender_servername: Option, +pub(crate) struct Ruma { + pub(crate) body: T, + pub(crate) sender_user: Option, + pub(crate) sender_device: Option, + pub(crate) sender_servername: Option, // This is None when body is not a valid string - pub json_body: Option, - pub appservice_info: Option, + pub(crate) json_body: Option, + pub(crate) appservice_info: Option, } impl Deref for Ruma { @@ -27,7 +27,7 @@ impl Deref for Ruma { } #[derive(Clone)] -pub struct RumaResponse(pub T); +pub(crate) struct RumaResponse(pub(crate) T); impl From for RumaResponse { fn from(t: T) -> Self { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index ad330c55..32a9b7f4 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -77,7 +77,7 @@ use tracing::{debug, error, warn}; /// # } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] -pub enum FedDest { +pub(crate) enum FedDest { Literal(SocketAddr), Named(String, String), } @@ -524,7 +524,7 @@ async fn request_well_known(destination: &str) -> Option { /// # `GET /_matrix/federation/v1/version` /// /// Get version information on this server. -pub async fn get_server_version_route( +pub(crate) async fn get_server_version_route( _body: Ruma, ) -> Result { Ok(get_server_version::v1::Response { @@ -542,7 +542,7 @@ pub async fn get_server_version_route( /// - Matrix does not support invalidating public keys, so the key returned by this will be valid /// forever. // Response type for this endpoint is Json because we need to calculate a signature for the response -pub async fn get_server_keys_route() -> Result { +pub(crate) async fn get_server_keys_route() -> Result { let mut verify_keys: BTreeMap = BTreeMap::new(); verify_keys.insert( format!("ed25519:{}", services().globals.keypair().version()) @@ -588,14 +588,14 @@ pub async fn get_server_keys_route() -> Result { /// /// - Matrix does not support invalidating public keys, so the key returned by this will be valid /// forever. -pub async fn get_server_keys_deprecated_route() -> impl IntoResponse { +pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { get_server_keys_route().await } /// # `POST /_matrix/federation/v1/publicRooms` /// /// Lists the public rooms on this server. -pub async fn get_public_rooms_filtered_route( +pub(crate) async fn get_public_rooms_filtered_route( body: Ruma, ) -> Result { let response = client_server::get_public_rooms_filtered_helper( @@ -618,7 +618,7 @@ pub async fn get_public_rooms_filtered_route( /// # `GET /_matrix/federation/v1/publicRooms` /// /// Lists the public rooms on this server. -pub async fn get_public_rooms_route( +pub(crate) async fn get_public_rooms_route( body: Ruma, ) -> Result { let response = client_server::get_public_rooms_filtered_helper( @@ -638,7 +638,7 @@ pub async fn get_public_rooms_route( }) } -pub fn parse_incoming_pdu( +pub(crate) fn parse_incoming_pdu( pdu: &RawJsonValue, ) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { @@ -672,7 +672,7 @@ pub fn parse_incoming_pdu( /// # `PUT /_matrix/federation/v1/send/{txnId}` /// /// Push EDUs and PDUs to this server. -pub async fn send_transaction_message_route( +pub(crate) async fn send_transaction_message_route( body: Ruma, ) -> Result { let sender_servername = body @@ -942,7 +942,7 @@ pub async fn send_transaction_message_route( /// Retrieves a single event from the server. /// /// - Only works if a user of this server is currently invited or joined the room -pub async fn get_event_route( +pub(crate) async fn get_event_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1000,7 +1000,7 @@ pub async fn get_event_route( /// /// Retrieves events from before the sender joined the room, if the room's /// history visibility allows. -pub async fn get_backfill_route( +pub(crate) async fn get_backfill_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1072,7 +1072,7 @@ pub async fn get_backfill_route( /// # `POST /_matrix/federation/v1/get_missing_events/{roomId}` /// /// Retrieves events that the sender is missing. -pub async fn get_missing_events_route( +pub(crate) async fn get_missing_events_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1157,7 +1157,7 @@ pub async fn get_missing_events_route( /// Retrieves the auth chain for a given event. /// /// - This does not include the event itself -pub async fn get_event_authorization_route( +pub(crate) async fn get_event_authorization_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1215,7 +1215,7 @@ pub async fn get_event_authorization_route( /// # `GET /_matrix/federation/v1/state/{roomId}` /// /// Retrieves the current state of the room. -pub async fn get_room_state_route( +pub(crate) async fn get_room_state_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1291,7 +1291,7 @@ pub async fn get_room_state_route( /// # `GET /_matrix/federation/v1/state_ids/{roomId}` /// /// Retrieves the current state of the room. -pub async fn get_room_state_ids_route( +pub(crate) async fn get_room_state_ids_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1348,7 +1348,7 @@ pub async fn get_room_state_ids_route( /// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}` /// /// Creates a join template. -pub async fn create_join_event_template_route( +pub(crate) async fn create_join_event_template_route( body: Ruma, ) -> Result { if !services().rooms.metadata.exists(&body.room_id)? { @@ -1592,7 +1592,7 @@ async fn create_join_event( /// # `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}` /// /// Submits a signed join event. -pub async fn create_join_event_v1_route( +pub(crate) async fn create_join_event_v1_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1608,7 +1608,7 @@ pub async fn create_join_event_v1_route( /// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}` /// /// Submits a signed join event. -pub async fn create_join_event_v2_route( +pub(crate) async fn create_join_event_v2_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1635,7 +1635,7 @@ pub async fn create_join_event_v2_route( /// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}` /// /// Invites a remote user to a room. -pub async fn create_invite_route( +pub(crate) async fn create_invite_route( body: Ruma, ) -> Result { let sender_servername = body @@ -1748,7 +1748,7 @@ pub async fn create_invite_route( /// # `GET /_matrix/federation/v1/user/devices/{userId}` /// /// Gets information on all devices of the user. -pub async fn get_devices_route( +pub(crate) async fn get_devices_route( body: Ruma, ) -> Result { if body.user_id.server_name() != services().globals.server_name() { @@ -1800,7 +1800,7 @@ pub async fn get_devices_route( /// # `GET /_matrix/federation/v1/query/directory` /// /// Resolve a room alias to a room id. -pub async fn get_room_information_route( +pub(crate) async fn get_room_information_route( body: Ruma, ) -> Result { let room_id = services() @@ -1821,7 +1821,7 @@ pub async fn get_room_information_route( /// # `GET /_matrix/federation/v1/query/profile` /// /// Gets information on a profile. -pub async fn get_profile_information_route( +pub(crate) async fn get_profile_information_route( body: Ruma, ) -> Result { if body.user_id.server_name() != services().globals.server_name() { @@ -1862,7 +1862,9 @@ pub async fn get_profile_information_route( /// # `POST /_matrix/federation/v1/user/keys/query` /// /// Gets devices and identity keys for the given users. -pub async fn get_keys_route(body: Ruma) -> Result { +pub(crate) async fn get_keys_route( + body: Ruma, +) -> Result { if body .device_keys .iter() @@ -1889,7 +1891,7 @@ pub async fn get_keys_route(body: Ruma) -> Result, ) -> Result { if body diff --git a/src/clap.rs b/src/clap.rs index 01563aee..7b7a420a 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -19,9 +19,9 @@ fn version() -> String { /// Command line arguments #[derive(Parser)] #[clap(about, version = version())] -pub struct Args {} +pub(crate) struct Args {} /// Parse command line arguments into structured data -pub fn parse() -> Args { +pub(crate) fn parse() -> Args { Args::parse() } diff --git a/src/config.rs b/src/config.rs index a000773f..449e91e4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,84 +13,84 @@ mod proxy; use self::proxy::ProxyConfig; #[derive(Clone, Debug, Deserialize)] -pub struct Config { +pub(crate) struct Config { #[serde(default = "default_address")] - pub address: IpAddr, + pub(crate) address: IpAddr, #[serde(default = "default_port")] - pub port: u16, - pub tls: Option, + pub(crate) port: u16, + pub(crate) tls: Option, - pub server_name: OwnedServerName, - pub database_backend: String, - pub database_path: String, + pub(crate) server_name: OwnedServerName, + pub(crate) database_backend: String, + pub(crate) database_path: String, #[serde(default = "default_db_cache_capacity_mb")] - pub db_cache_capacity_mb: f64, + pub(crate) db_cache_capacity_mb: f64, #[serde(default = "default_cache_capacity_modifier")] - pub cache_capacity_modifier: f64, + pub(crate) cache_capacity_modifier: f64, #[serde(default = "default_rocksdb_max_open_files")] - pub rocksdb_max_open_files: i32, + pub(crate) rocksdb_max_open_files: i32, #[serde(default = "default_pdu_cache_capacity")] - pub pdu_cache_capacity: u32, + pub(crate) pdu_cache_capacity: u32, #[serde(default = "default_cleanup_second_interval")] - pub cleanup_second_interval: u32, + pub(crate) cleanup_second_interval: u32, #[serde(default = "default_max_request_size")] - pub max_request_size: u32, + pub(crate) max_request_size: u32, #[serde(default = "default_max_concurrent_requests")] - pub max_concurrent_requests: u16, + pub(crate) max_concurrent_requests: u16, #[serde(default = "default_max_fetch_prev_events")] - pub max_fetch_prev_events: u16, + pub(crate) max_fetch_prev_events: u16, #[serde(default = "false_fn")] - pub allow_registration: bool, - pub registration_token: Option, + pub(crate) allow_registration: bool, + pub(crate) registration_token: Option, #[serde(default = "true_fn")] - pub allow_encryption: bool, + pub(crate) allow_encryption: bool, #[serde(default = "false_fn")] - pub allow_federation: bool, + pub(crate) allow_federation: bool, #[serde(default = "true_fn")] - pub allow_room_creation: bool, + pub(crate) allow_room_creation: bool, #[serde(default = "true_fn")] - pub allow_unstable_room_versions: bool, + pub(crate) allow_unstable_room_versions: bool, #[serde(default = "default_default_room_version")] - pub default_room_version: RoomVersionId, - pub well_known_client: Option, + pub(crate) default_room_version: RoomVersionId, + pub(crate) well_known_client: Option, #[serde(default = "false_fn")] - pub allow_jaeger: bool, + pub(crate) allow_jaeger: bool, #[serde(default = "false_fn")] - pub tracing_flame: bool, + pub(crate) tracing_flame: bool, #[serde(default)] - pub proxy: ProxyConfig, - pub jwt_secret: Option, + pub(crate) proxy: ProxyConfig, + pub(crate) jwt_secret: Option, #[serde(default = "default_trusted_servers")] - pub trusted_servers: Vec, + pub(crate) trusted_servers: Vec, #[serde(default = "default_log")] - pub log: String, + pub(crate) log: String, #[serde(default)] - pub turn_username: String, + pub(crate) turn_username: String, #[serde(default)] - pub turn_password: String, + pub(crate) turn_password: String, #[serde(default = "Vec::new")] - pub turn_uris: Vec, + pub(crate) turn_uris: Vec, #[serde(default)] - pub turn_secret: String, + pub(crate) turn_secret: String, #[serde(default = "default_turn_ttl")] - pub turn_ttl: u64, + pub(crate) turn_ttl: u64, - pub emergency_password: Option, + pub(crate) emergency_password: Option, #[serde(flatten)] - pub catchall: BTreeMap, + pub(crate) catchall: BTreeMap, } #[derive(Clone, Debug, Deserialize)] -pub struct TlsConfig { - pub certs: String, - pub key: String, +pub(crate) struct TlsConfig { + pub(crate) certs: String, + pub(crate) key: String, } const DEPRECATED_KEYS: &[&str] = &["cache_capacity"]; impl Config { - pub fn warn_deprecated(&self) { + pub(crate) fn warn_deprecated(&self) { let mut was_deprecated = false; for key in self .catchall @@ -259,6 +259,6 @@ fn default_turn_ttl() -> u64 { } // I know, it's a great name -pub fn default_default_room_version() -> RoomVersionId { +pub(crate) fn default_default_room_version() -> RoomVersionId { RoomVersionId::V10 } diff --git a/src/config/proxy.rs b/src/config/proxy.rs index c03463e7..bf725190 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -30,7 +30,7 @@ use crate::Result; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "snake_case")] #[derive(Default)] -pub enum ProxyConfig { +pub(crate) enum ProxyConfig { #[default] None, Global { @@ -40,7 +40,7 @@ pub enum ProxyConfig { ByDomain(Vec), } impl ProxyConfig { - pub fn to_proxy(&self) -> Result> { + pub(crate) fn to_proxy(&self) -> Result> { Ok(match self.clone() { ProxyConfig::None => None, ProxyConfig::Global { url } => Some(Proxy::all(url)?), @@ -52,7 +52,7 @@ impl ProxyConfig { } #[derive(Clone, Debug, Deserialize)] -pub struct PartialProxyConfig { +pub(crate) struct PartialProxyConfig { #[serde(deserialize_with = "crate::utils::deserialize_from_str")] url: Url, #[serde(default)] @@ -61,7 +61,7 @@ pub struct PartialProxyConfig { exclude: Vec, } impl PartialProxyConfig { - pub fn for_url(&self, url: &Url) -> Option<&Url> { + pub(crate) fn for_url(&self, url: &Url) -> Option<&Url> { let domain = url.domain()?; let mut included_because = None; // most specific reason it was included let mut excluded_because = None; // most specific reason it was excluded @@ -95,20 +95,20 @@ impl PartialProxyConfig { /// A domain name, that optionally allows a * as its first subdomain. #[derive(Clone, Debug)] -pub enum WildCardedDomain { +pub(crate) enum WildCardedDomain { WildCard, WildCarded(String), Exact(String), } impl WildCardedDomain { - pub fn matches(&self, domain: &str) -> bool { + pub(crate) fn matches(&self, domain: &str) -> bool { match self { WildCardedDomain::WildCard => true, WildCardedDomain::WildCarded(d) => domain.ends_with(d), WildCardedDomain::Exact(d) => domain == d, } } - pub fn more_specific_than(&self, other: &Self) -> bool { + pub(crate) fn more_specific_than(&self, other: &Self) -> bool { match (self, other) { (WildCardedDomain::WildCard, WildCardedDomain::WildCard) => false, (_, WildCardedDomain::WildCard) => true, diff --git a/src/database.rs b/src/database.rs index 6941b22b..0de879d4 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,5 +1,5 @@ -pub mod abstraction; -pub mod key_value; +pub(crate) mod abstraction; +pub(crate) mod key_value; use crate::{ service::rooms::timeline::PduCount, services, utils, Config, Error, PduEvent, Result, Services, @@ -29,14 +29,14 @@ use std::{ use tracing::{debug, error, info, warn}; -pub struct KeyValueDatabase { +pub(crate) struct KeyValueDatabase { _db: Arc, - //pub globals: globals::Globals, + //pub(crate) globals: globals::Globals, pub(super) global: Arc, pub(super) server_signingkeys: Arc, - //pub users: users::Users, + //pub(crate) users: users::Users, pub(super) userid_password: Arc, pub(super) userid_displayname: Arc, pub(super) userid_avatarurl: Arc, @@ -58,12 +58,12 @@ pub struct KeyValueDatabase { pub(super) todeviceid_events: Arc, // ToDeviceId = UserId + DeviceId + Count - //pub uiaa: uiaa::Uiaa, + //pub(crate) uiaa: uiaa::Uiaa, pub(super) userdevicesessionid_uiaainfo: Arc, // User-interactive authentication pub(super) userdevicesessionid_uiaarequest: RwLock>, - //pub edus: RoomEdus, + //pub(crate) edus: RoomEdus, pub(super) readreceiptid_readreceipt: Arc, // ReadReceiptId = RoomId + Count + UserId pub(super) roomuserid_privateread: Arc, // RoomUserId = Room + User, PrivateRead = Count pub(super) roomuserid_lastprivatereadupdate: Arc, // LastPrivateReadUpdate = Count @@ -74,7 +74,7 @@ pub struct KeyValueDatabase { #[allow(dead_code)] pub(super) userid_lastpresenceupdate: Arc, // LastPresenceUpdate = Count - //pub rooms: rooms::Rooms, + //pub(crate) rooms: rooms::Rooms, pub(super) pduid_pdu: Arc, // PduId = ShortRoomId + Count pub(super) eventid_pduid: Arc, pub(super) roomid_pduleaves: Arc, @@ -137,28 +137,28 @@ pub struct KeyValueDatabase { /// RoomId + EventId -> Parent PDU EventId. pub(super) referencedevents: Arc, - //pub account_data: account_data::AccountData, + //pub(crate) account_data: account_data::AccountData, pub(super) roomuserdataid_accountdata: Arc, // RoomUserDataId = Room + User + Count + Type pub(super) roomusertype_roomuserdataid: Arc, // RoomUserType = Room + User + Type - //pub media: media::Media, + //pub(crate) media: media::Media, pub(super) mediaid_file: Arc, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType - //pub key_backups: key_backups::KeyBackups, + //pub(crate) key_backups: key_backups::KeyBackups, pub(super) backupid_algorithm: Arc, // BackupId = UserId + Version(Count) pub(super) backupid_etag: Arc, // BackupId = UserId + Version(Count) pub(super) backupkeyid_backup: Arc, // BackupKeyId = UserId + Version + RoomId + SessionId - //pub transaction_ids: transaction_ids::TransactionIds, + //pub(crate) transaction_ids: transaction_ids::TransactionIds, pub(super) userdevicetxnid_response: Arc, // Response can be empty (/sendToDevice) or the event id (/send) - //pub sending: sending::Sending, + //pub(crate) sending: sending::Sending, pub(super) servername_educount: Arc, // EduCount: Count of last EDU sync pub(super) servernameevent_data: Arc, // ServernameEvent = (+ / $)SenderKey / ServerName / UserId + PduId / Id (for edus), Data = EDU content pub(super) servercurrentevent_data: Arc, // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / Id (for edus), Data = EDU content - //pub appservice: appservice::Appservice, + //pub(crate) appservice: appservice::Appservice, pub(super) id_appserviceregistrations: Arc, - //pub pusher: pusher::PushData, + //pub(crate) pusher: pusher::PushData, pub(super) senderkey_pusher: Arc, pub(super) pdu_cache: Mutex>>, @@ -214,7 +214,7 @@ impl KeyValueDatabase { not(any(feature = "rocksdb", feature = "sqlite")), allow(unreachable_code) )] - pub async fn load_or_create(config: Config) -> Result<()> { + pub(crate) async fn load_or_create(config: Config) -> Result<()> { Self::check_db_setup(&config)?; if !Path::new(&config.database_path).exists() { @@ -959,7 +959,7 @@ impl KeyValueDatabase { } #[tracing::instrument(skip(self))] - pub fn flush(&self) -> Result<()> { + pub(crate) fn flush(&self) -> Result<()> { let start = std::time::Instant::now(); let res = self._db.flush(); @@ -970,7 +970,7 @@ impl KeyValueDatabase { } #[tracing::instrument] - pub async fn start_cleanup_task() { + pub(crate) async fn start_cleanup_task() { use tokio::time::interval; #[cfg(unix)] diff --git a/src/database/abstraction.rs b/src/database/abstraction.rs index 0e3829ba..3b64332e 100644 --- a/src/database/abstraction.rs +++ b/src/database/abstraction.rs @@ -4,15 +4,15 @@ use crate::Result; use std::{future::Future, pin::Pin, sync::Arc}; #[cfg(feature = "sqlite")] -pub mod sqlite; +pub(crate) mod sqlite; #[cfg(feature = "rocksdb")] -pub mod rocksdb; +pub(crate) mod rocksdb; #[cfg(any(feature = "sqlite", feature = "rocksdb",))] -pub mod watchers; +pub(crate) mod watchers; -pub trait KeyValueDatabaseEngine: Send + Sync { +pub(crate) trait KeyValueDatabaseEngine: Send + Sync { fn open(config: &Config) -> Result where Self: Sized; @@ -27,7 +27,7 @@ pub trait KeyValueDatabaseEngine: Send + Sync { fn clear_caches(&self) {} } -pub trait KvTree: Send + Sync { +pub(crate) trait KvTree: Send + Sync { fn get(&self, key: &[u8]) -> Result>>; fn insert(&self, key: &[u8], value: &[u8]) -> Result<()>; diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 447ee038..78e76524 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -6,14 +6,14 @@ use std::{ sync::{Arc, RwLock}, }; -pub struct Engine { +pub(crate) struct Engine { rocks: rocksdb::DBWithThreadMode, max_open_files: i32, cache: rocksdb::Cache, old_cfs: Vec, } -pub struct RocksDbEngineTree<'a> { +pub(crate) struct RocksDbEngineTree<'a> { db: Arc, name: &'a str, watchers: Watchers, diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index eae5e5c8..a5bfe540 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -18,8 +18,8 @@ thread_local! { } struct PreparedStatementIterator<'a> { - pub iterator: Box + 'a>, - pub _statement_ref: NonAliasingBox>, + pub(crate) iterator: Box + 'a>, + pub(crate) _statement_ref: NonAliasingBox>, } impl Iterator for PreparedStatementIterator<'_> { @@ -37,7 +37,7 @@ impl Drop for NonAliasingBox { } } -pub struct Engine { +pub(crate) struct Engine { writer: Mutex, read_conn_tls: ThreadLocal, read_iterator_conn_tls: ThreadLocal, @@ -73,7 +73,7 @@ impl Engine { .get_or(|| Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap()) } - pub fn flush_wal(self: &Arc) -> Result<()> { + pub(crate) fn flush_wal(self: &Arc) -> Result<()> { self.write_lock() .pragma_update(Some(Main), "wal_checkpoint", "RESTART")?; Ok(()) @@ -125,7 +125,7 @@ impl KeyValueDatabaseEngine for Arc { } } -pub struct SqliteTable { +pub(crate) struct SqliteTable { engine: Arc, name: String, watchers: Watchers, @@ -153,7 +153,7 @@ impl SqliteTable { Ok(()) } - pub fn iter_with_guard<'a>( + pub(crate) fn iter_with_guard<'a>( &'a self, guard: &'a Connection, ) -> Box + 'a> { diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index aa13e64f..1f47a445 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -11,7 +11,7 @@ use ruma::{ use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; -pub const COUNTER: &[u8] = b"c"; +pub(crate) const COUNTER: &[u8] = b"c"; #[async_trait] impl service::globals::Data for KeyValueDatabase { diff --git a/src/main.rs b/src/main.rs index d466c1c5..4b86b111 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,19 +38,19 @@ use tower_http::{ use tracing::{debug, error, info, warn}; use tracing_subscriber::{prelude::*, EnvFilter}; -pub mod api; -pub mod clap; +pub(crate) mod api; +pub(crate) mod clap; mod config; mod database; mod service; mod utils; -pub use api::ruma_wrapper::{Ruma, RumaResponse}; +pub(crate) use api::ruma_wrapper::{Ruma, RumaResponse}; use api::{client_server, server_server}; -pub use config::Config; -pub use database::KeyValueDatabase; -pub use service::{pdu::PduEvent, Services}; -pub use utils::error::{Error, Result}; +pub(crate) use config::Config; +pub(crate) use database::KeyValueDatabase; +pub(crate) use service::{pdu::PduEvent, Services}; +pub(crate) use utils::error::{Error, Result}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] use tikv_jemallocator::Jemalloc; @@ -59,12 +59,12 @@ use tikv_jemallocator::Jemalloc; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -pub static SERVICES: RwLock> = RwLock::new(None); +pub(crate) static SERVICES: RwLock> = RwLock::new(None); // Not async due to services() being used in many closures, and async closures are not stable as of writing // This is the case for every other occurence of sync Mutex/RwLock, except for database related ones, where // the previous maintainer has asked to not modify those -pub fn services() -> &'static Services { +pub(crate) fn services() -> &'static Services { SERVICES .read() .unwrap() @@ -534,7 +534,7 @@ impl RouterExt for Router { } } -pub trait RumaHandler { +pub(crate) trait RumaHandler { // Can't transform to a handler without boxing or relying on the nightly-only // impl-trait-in-traits feature. Moving a small amount of extra logic into the trait // allows bypassing both. diff --git a/src/service.rs b/src/service.rs index 045168eb..4ab89c68 100644 --- a/src/service.rs +++ b/src/service.rs @@ -9,37 +9,37 @@ use tokio::sync::{broadcast, Mutex}; use crate::{Config, Result}; use tokio::sync::RwLock; -pub mod account_data; -pub mod admin; -pub mod appservice; -pub mod globals; -pub mod key_backups; -pub mod media; -pub mod pdu; -pub mod pusher; -pub mod rooms; -pub mod sending; -pub mod transaction_ids; -pub mod uiaa; -pub mod users; +pub(crate) mod account_data; +pub(crate) mod admin; +pub(crate) mod appservice; +pub(crate) mod globals; +pub(crate) mod key_backups; +pub(crate) mod media; +pub(crate) mod pdu; +pub(crate) mod pusher; +pub(crate) mod rooms; +pub(crate) mod sending; +pub(crate) mod transaction_ids; +pub(crate) mod uiaa; +pub(crate) mod users; -pub struct Services { - pub appservice: appservice::Service, - pub pusher: pusher::Service, - pub rooms: rooms::Service, - pub transaction_ids: transaction_ids::Service, - pub uiaa: uiaa::Service, - pub users: users::Service, - pub account_data: account_data::Service, - pub admin: Arc, - pub globals: globals::Service, - pub key_backups: key_backups::Service, - pub media: media::Service, - pub sending: Arc, +pub(crate) struct Services { + pub(crate) appservice: appservice::Service, + pub(crate) pusher: pusher::Service, + pub(crate) rooms: rooms::Service, + pub(crate) transaction_ids: transaction_ids::Service, + pub(crate) uiaa: uiaa::Service, + pub(crate) users: users::Service, + pub(crate) account_data: account_data::Service, + pub(crate) admin: Arc, + pub(crate) globals: globals::Service, + pub(crate) key_backups: key_backups::Service, + pub(crate) media: media::Service, + pub(crate) sending: Arc, } impl Services { - pub fn build< + pub(crate) fn build< D: appservice::Data + pusher::Data + rooms::Data diff --git a/src/service/account_data.rs b/src/service/account_data.rs index f9c49b1a..515b970e 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -1,6 +1,6 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, @@ -12,14 +12,14 @@ use std::collections::HashMap; use crate::Result; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Places one event in the account data of the user and removes the previous entry. #[tracing::instrument(skip(self, room_id, user_id, event_type, data))] - pub fn update( + pub(crate) fn update( &self, room_id: Option<&RoomId>, user_id: &UserId, @@ -31,7 +31,7 @@ impl Service { /// Searches the account data for a specific kind. #[tracing::instrument(skip(self, room_id, user_id, event_type))] - pub fn get( + pub(crate) fn get( &self, room_id: Option<&RoomId>, user_id: &UserId, @@ -42,7 +42,7 @@ impl Service { /// Returns all changes to the account data that happened after `since`. #[tracing::instrument(skip(self, room_id, user_id, since))] - pub fn changes_since( + pub(crate) fn changes_since( &self, room_id: Option<&RoomId>, user_id: &UserId, diff --git a/src/service/account_data/data.rs b/src/service/account_data/data.rs index c7c92981..92a17cab 100644 --- a/src/service/account_data/data.rs +++ b/src/service/account_data/data.rs @@ -7,7 +7,7 @@ use ruma::{ RoomId, UserId, }; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Places one event in the account data of the user and removes the previous entry. fn update( &self, diff --git a/src/service/admin.rs b/src/service/admin.rs index 315dbdcf..f61938a1 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -181,18 +181,18 @@ enum AdminCommand { } #[derive(Debug)] -pub enum AdminRoomEvent { +pub(crate) enum AdminRoomEvent { ProcessMessage(String), SendMessage(RoomMessageEventContent), } -pub struct Service { - pub sender: mpsc::UnboundedSender, +pub(crate) struct Service { + pub(crate) sender: mpsc::UnboundedSender, receiver: Mutex>, } impl Service { - pub fn build() -> Arc { + pub(crate) fn build() -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); Arc::new(Self { sender, @@ -200,7 +200,7 @@ impl Service { }) } - pub fn start_handler(self: &Arc) { + pub(crate) fn start_handler(self: &Arc) { let self2 = Arc::clone(self); tokio::spawn(async move { self2.handler().await; @@ -259,13 +259,13 @@ impl Service { } } - pub fn process_message(&self, room_message: String) { + pub(crate) fn process_message(&self, room_message: String) { self.sender .send(AdminRoomEvent::ProcessMessage(room_message)) .unwrap(); } - pub fn send_message(&self, message_content: RoomMessageEventContent) { + pub(crate) fn send_message(&self, message_content: RoomMessageEventContent) { self.sender .send(AdminRoomEvent::SendMessage(message_content)) .unwrap(); diff --git a/src/service/appservice.rs b/src/service/appservice.rs index 9db6609e..1d5346aa 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -2,7 +2,7 @@ mod data; use std::collections::BTreeMap; -pub use data::Data; +pub(crate) use data::Data; use futures_util::Future; use regex::RegexSet; @@ -16,14 +16,14 @@ use crate::{services, Result}; /// Compiled regular expressions for a namespace. #[derive(Clone, Debug)] -pub struct NamespaceRegex { - pub exclusive: Option, - pub non_exclusive: Option, +pub(crate) struct NamespaceRegex { + pub(crate) exclusive: Option, + pub(crate) non_exclusive: Option, } impl NamespaceRegex { /// Checks if this namespace has rights to a namespace - pub fn is_match(&self, heystack: &str) -> bool { + pub(crate) fn is_match(&self, heystack: &str) -> bool { if self.is_exclusive_match(heystack) { return true; } @@ -37,7 +37,7 @@ impl NamespaceRegex { } /// Checks if this namespace has exlusive rights to a namespace - pub fn is_exclusive_match(&self, heystack: &str) -> bool { + pub(crate) fn is_exclusive_match(&self, heystack: &str) -> bool { if let Some(exclusive) = &self.exclusive { if exclusive.is_match(heystack) { return true; @@ -79,20 +79,20 @@ impl TryFrom> for NamespaceRegex { /// Appservice registration combined with its compiled regular expressions. #[derive(Clone, Debug)] -pub struct RegistrationInfo { - pub registration: Registration, - pub users: NamespaceRegex, - pub aliases: NamespaceRegex, - pub rooms: NamespaceRegex, +pub(crate) struct RegistrationInfo { + pub(crate) registration: Registration, + pub(crate) users: NamespaceRegex, + pub(crate) aliases: NamespaceRegex, + pub(crate) rooms: NamespaceRegex, } impl RegistrationInfo { - pub fn is_user_match(&self, user_id: &UserId) -> bool { + pub(crate) fn is_user_match(&self, user_id: &UserId) -> bool { self.users.is_match(user_id.as_str()) || self.registration.sender_localpart == user_id.localpart() } - pub fn is_exclusive_user_match(&self, user_id: &UserId) -> bool { + pub(crate) fn is_exclusive_user_match(&self, user_id: &UserId) -> bool { self.users.is_exclusive_match(user_id.as_str()) || self.registration.sender_localpart == user_id.localpart() } @@ -111,13 +111,13 @@ impl TryFrom for RegistrationInfo { type Error = regex::Error; } -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, registration_info: RwLock>, } impl Service { - pub fn build(db: &'static dyn Data) -> Result { + pub(crate) fn build(db: &'static dyn Data) -> Result { let mut registration_info = BTreeMap::new(); // Inserting registrations into cache for appservice in db.all()? { @@ -136,7 +136,7 @@ impl Service { }) } /// Registers an appservice and returns the ID to the caller. - pub async fn register_appservice(&self, yaml: Registration) -> Result { + pub(crate) async fn register_appservice(&self, yaml: Registration) -> Result { //TODO: Check for collisions between exclusive appservice namespaces services() .appservice @@ -153,7 +153,7 @@ impl Service { /// # Arguments /// /// * `service_name` - the name you send to register the service previously - pub async fn unregister_appservice(&self, service_name: &str) -> Result<()> { + pub(crate) async fn unregister_appservice(&self, service_name: &str) -> Result<()> { services() .appservice .registration_info @@ -165,7 +165,7 @@ impl Service { self.db.unregister_appservice(service_name) } - pub async fn get_registration(&self, id: &str) -> Option { + pub(crate) async fn get_registration(&self, id: &str) -> Option { self.registration_info .read() .await @@ -174,7 +174,7 @@ impl Service { .map(|info| info.registration) } - pub async fn iter_ids(&self) -> Vec { + pub(crate) async fn iter_ids(&self) -> Vec { self.registration_info .read() .await @@ -183,7 +183,7 @@ impl Service { .collect() } - pub async fn find_from_token(&self, token: &str) -> Option { + pub(crate) async fn find_from_token(&self, token: &str) -> Option { self.read() .await .values() @@ -192,7 +192,7 @@ impl Service { } // Checks if a given user id matches any exclusive appservice regex - pub async fn is_exclusive_user_id(&self, user_id: &UserId) -> bool { + pub(crate) async fn is_exclusive_user_id(&self, user_id: &UserId) -> bool { self.read() .await .values() @@ -200,7 +200,7 @@ impl Service { } // Checks if a given room alias matches any exclusive appservice regex - pub async fn is_exclusive_alias(&self, alias: &RoomAliasId) -> bool { + pub(crate) async fn is_exclusive_alias(&self, alias: &RoomAliasId) -> bool { self.read() .await .values() @@ -208,14 +208,14 @@ impl Service { } // Checks if a given room id matches any exclusive appservice regex - pub async fn is_exclusive_room_id(&self, room_id: &RoomId) -> bool { + pub(crate) async fn is_exclusive_room_id(&self, room_id: &RoomId) -> bool { self.read() .await .values() .any(|info| info.rooms.is_exclusive_match(room_id.as_str())) } - pub fn read( + pub(crate) fn read( &self, ) -> impl Future>> { diff --git a/src/service/appservice/data.rs b/src/service/appservice/data.rs index ab19a50c..92da3a73 100644 --- a/src/service/appservice/data.rs +++ b/src/service/appservice/data.rs @@ -2,7 +2,7 @@ use ruma::api::appservice::Registration; use crate::Result; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Registers an appservice and returns the ID to the caller fn register_appservice(&self, yaml: Registration) -> Result; diff --git a/src/service/globals.rs b/src/service/globals.rs index 6429a2af..d88dda3d 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -1,5 +1,5 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, @@ -49,46 +49,46 @@ type SyncHandle = ( Receiver>>, // rx ); -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, - pub actual_destination_cache: Arc>, // actual_destination, host - pub tls_name_override: Arc>, - pub config: Config, + pub(crate) actual_destination_cache: Arc>, // actual_destination, host + pub(crate) tls_name_override: Arc>, + pub(crate) config: Config, keypair: Arc, dns_resolver: TokioAsyncResolver, jwt_decoding_key: Option, federation_client: reqwest::Client, default_client: reqwest::Client, - pub stable_room_versions: Vec, - pub unstable_room_versions: Vec, - pub bad_event_ratelimiter: Arc>>, - pub bad_signature_ratelimiter: Arc, RateLimitState>>>, - pub bad_query_ratelimiter: Arc>>, - pub servername_ratelimiter: Arc>>>, - pub sync_receivers: RwLock>, - pub roomid_mutex_insert: RwLock>>>, - pub roomid_mutex_state: RwLock>>>, - pub roomid_mutex_federation: RwLock>>>, // this lock will be held longer - pub roomid_federationhandletime: RwLock>, - pub stateres_mutex: Arc>, - pub rotate: RotationHandler, + pub(crate) stable_room_versions: Vec, + pub(crate) unstable_room_versions: Vec, + pub(crate) bad_event_ratelimiter: Arc>>, + pub(crate) bad_signature_ratelimiter: Arc, RateLimitState>>>, + pub(crate) bad_query_ratelimiter: Arc>>, + pub(crate) servername_ratelimiter: Arc>>>, + pub(crate) sync_receivers: RwLock>, + pub(crate) roomid_mutex_insert: RwLock>>>, + pub(crate) roomid_mutex_state: RwLock>>>, + pub(crate) roomid_mutex_federation: RwLock>>>, // this lock will be held longer + pub(crate) roomid_federationhandletime: RwLock>, + pub(crate) stateres_mutex: Arc>, + pub(crate) rotate: RotationHandler, - pub shutdown: AtomicBool, + pub(crate) shutdown: AtomicBool, } /// Handles "rotation" of long-polling requests. "Rotation" in this context is similar to "rotation" of log files and the like. /// /// This is utilized to have sync workers return early and release read locks on the database. -pub struct RotationHandler(broadcast::Sender<()>, broadcast::Receiver<()>); +pub(crate) struct RotationHandler(broadcast::Sender<()>, broadcast::Receiver<()>); impl RotationHandler { - pub fn new() -> Self { + pub(crate) fn new() -> Self { let (s, r) = broadcast::channel(1); Self(s, r) } - pub fn watch(&self) -> impl Future { + pub(crate) fn watch(&self) -> impl Future { let mut r = self.0.subscribe(); async move { @@ -96,7 +96,7 @@ impl RotationHandler { } } - pub fn fire(&self) { + pub(crate) fn fire(&self) { let _ = self.0.send(()); } } @@ -107,13 +107,13 @@ impl Default for RotationHandler { } } -pub struct Resolver { +pub(crate) struct Resolver { inner: GaiResolver, overrides: Arc>, } impl Resolver { - pub fn new(overrides: Arc>) -> Self { + pub(crate) fn new(overrides: Arc>) -> Self { Resolver { inner: GaiResolver::new(), overrides, @@ -147,7 +147,7 @@ impl Resolve for Resolver { } impl Service { - pub fn load(db: &'static dyn Data, config: Config) -> Result { + pub(crate) fn load(db: &'static dyn Data, config: Config) -> Result { let keypair = db.load_keypair(); let keypair = match keypair { @@ -229,113 +229,113 @@ impl Service { } /// Returns this server's keypair. - pub fn keypair(&self) -> &ruma::signatures::Ed25519KeyPair { + pub(crate) fn keypair(&self) -> &ruma::signatures::Ed25519KeyPair { &self.keypair } /// Returns a reqwest client which can be used to send requests - pub fn default_client(&self) -> reqwest::Client { + pub(crate) fn default_client(&self) -> reqwest::Client { // Client is cheap to clone (Arc wrapper) and avoids lifetime issues self.default_client.clone() } /// Returns a client used for resolving .well-knowns - pub fn federation_client(&self) -> reqwest::Client { + pub(crate) fn federation_client(&self) -> reqwest::Client { // Client is cheap to clone (Arc wrapper) and avoids lifetime issues self.federation_client.clone() } #[tracing::instrument(skip(self))] - pub fn next_count(&self) -> Result { + pub(crate) fn next_count(&self) -> Result { self.db.next_count() } #[tracing::instrument(skip(self))] - pub fn current_count(&self) -> Result { + pub(crate) fn current_count(&self) -> Result { self.db.current_count() } - pub async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { + pub(crate) async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { self.db.watch(user_id, device_id).await } - pub fn cleanup(&self) -> Result<()> { + pub(crate) fn cleanup(&self) -> Result<()> { self.db.cleanup() } - pub fn server_name(&self) -> &ServerName { + pub(crate) fn server_name(&self) -> &ServerName { self.config.server_name.as_ref() } - pub fn max_request_size(&self) -> u32 { + pub(crate) fn max_request_size(&self) -> u32 { self.config.max_request_size } - pub fn max_fetch_prev_events(&self) -> u16 { + pub(crate) fn max_fetch_prev_events(&self) -> u16 { self.config.max_fetch_prev_events } - pub fn allow_registration(&self) -> bool { + pub(crate) fn allow_registration(&self) -> bool { self.config.allow_registration } - pub fn allow_encryption(&self) -> bool { + pub(crate) fn allow_encryption(&self) -> bool { self.config.allow_encryption } - pub fn allow_federation(&self) -> bool { + pub(crate) fn allow_federation(&self) -> bool { self.config.allow_federation } - pub fn allow_room_creation(&self) -> bool { + pub(crate) fn allow_room_creation(&self) -> bool { self.config.allow_room_creation } - pub fn allow_unstable_room_versions(&self) -> bool { + pub(crate) fn allow_unstable_room_versions(&self) -> bool { self.config.allow_unstable_room_versions } - pub fn default_room_version(&self) -> RoomVersionId { + pub(crate) fn default_room_version(&self) -> RoomVersionId { self.config.default_room_version.clone() } - pub fn trusted_servers(&self) -> &[OwnedServerName] { + pub(crate) fn trusted_servers(&self) -> &[OwnedServerName] { &self.config.trusted_servers } - pub fn dns_resolver(&self) -> &TokioAsyncResolver { + pub(crate) fn dns_resolver(&self) -> &TokioAsyncResolver { &self.dns_resolver } - pub fn jwt_decoding_key(&self) -> Option<&jsonwebtoken::DecodingKey> { + pub(crate) fn jwt_decoding_key(&self) -> Option<&jsonwebtoken::DecodingKey> { self.jwt_decoding_key.as_ref() } - pub fn turn_password(&self) -> &String { + pub(crate) fn turn_password(&self) -> &String { &self.config.turn_password } - pub fn turn_ttl(&self) -> u64 { + pub(crate) fn turn_ttl(&self) -> u64 { self.config.turn_ttl } - pub fn turn_uris(&self) -> &[String] { + pub(crate) fn turn_uris(&self) -> &[String] { &self.config.turn_uris } - pub fn turn_username(&self) -> &String { + pub(crate) fn turn_username(&self) -> &String { &self.config.turn_username } - pub fn turn_secret(&self) -> &String { + pub(crate) fn turn_secret(&self) -> &String { &self.config.turn_secret } - pub fn emergency_password(&self) -> &Option { + pub(crate) fn emergency_password(&self) -> &Option { &self.config.emergency_password } - pub fn supported_room_versions(&self) -> Vec { + pub(crate) fn supported_room_versions(&self) -> Vec { let mut room_versions: Vec = vec![]; room_versions.extend(self.stable_room_versions.clone()); if self.allow_unstable_room_versions() { @@ -348,7 +348,7 @@ impl Service { /// Remove the outdated keys and insert the new ones. /// /// This doesn't actually check that the keys provided are newer than the old set. - pub fn add_signing_key( + pub(crate) fn add_signing_key( &self, origin: &ServerName, new_keys: ServerSigningKeys, @@ -357,7 +357,7 @@ impl Service { } /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server. - pub fn signing_keys_for( + pub(crate) fn signing_keys_for( &self, origin: &ServerName, ) -> Result> { @@ -376,22 +376,22 @@ impl Service { Ok(keys) } - pub fn database_version(&self) -> Result { + pub(crate) fn database_version(&self) -> Result { self.db.database_version() } - pub fn bump_database_version(&self, new_version: u64) -> Result<()> { + pub(crate) fn bump_database_version(&self, new_version: u64) -> Result<()> { self.db.bump_database_version(new_version) } - pub fn get_media_folder(&self) -> PathBuf { + pub(crate) fn get_media_folder(&self) -> PathBuf { let mut r = PathBuf::new(); r.push(self.config.database_path.clone()); r.push("media"); r } - pub fn get_media_file(&self, key: &[u8]) -> PathBuf { + pub(crate) fn get_media_file(&self, key: &[u8]) -> PathBuf { let mut r = PathBuf::new(); r.push(self.config.database_path.clone()); r.push("media"); @@ -399,11 +399,11 @@ impl Service { r } - pub fn well_known_client(&self) -> &Option { + pub(crate) fn well_known_client(&self) -> &Option { &self.config.well_known_client } - pub fn shutdown(&self) { + pub(crate) fn shutdown(&self) { self.shutdown.store(true, atomic::Ordering::Relaxed); // On shutdown info!(target: "shutdown-sync", "Received shutdown notification, notifying sync helpers..."); diff --git a/src/service/globals/data.rs b/src/service/globals/data.rs index 171b3fec..9d863ca9 100644 --- a/src/service/globals/data.rs +++ b/src/service/globals/data.rs @@ -10,7 +10,7 @@ use ruma::{ use crate::Result; #[async_trait] -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn next_count(&self) -> Result; fn current_count(&self) -> Result; async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()>; diff --git a/src/service/key_backups.rs b/src/service/key_backups.rs index 5fc52ced..542db640 100644 --- a/src/service/key_backups.rs +++ b/src/service/key_backups.rs @@ -1,5 +1,5 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use crate::Result; use ruma::{ @@ -9,12 +9,12 @@ use ruma::{ }; use std::collections::BTreeMap; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn create_backup( + pub(crate) fn create_backup( &self, user_id: &UserId, backup_metadata: &Raw, @@ -22,11 +22,11 @@ impl Service { self.db.create_backup(user_id, backup_metadata) } - pub fn delete_backup(&self, user_id: &UserId, version: &str) -> Result<()> { + pub(crate) fn delete_backup(&self, user_id: &UserId, version: &str) -> Result<()> { self.db.delete_backup(user_id, version) } - pub fn update_backup( + pub(crate) fn update_backup( &self, user_id: &UserId, version: &str, @@ -35,18 +35,18 @@ impl Service { self.db.update_backup(user_id, version, backup_metadata) } - pub fn get_latest_backup_version(&self, user_id: &UserId) -> Result> { + pub(crate) fn get_latest_backup_version(&self, user_id: &UserId) -> Result> { self.db.get_latest_backup_version(user_id) } - pub fn get_latest_backup( + pub(crate) fn get_latest_backup( &self, user_id: &UserId, ) -> Result)>> { self.db.get_latest_backup(user_id) } - pub fn get_backup( + pub(crate) fn get_backup( &self, user_id: &UserId, version: &str, @@ -54,7 +54,7 @@ impl Service { self.db.get_backup(user_id, version) } - pub fn add_key( + pub(crate) fn add_key( &self, user_id: &UserId, version: &str, @@ -66,15 +66,15 @@ impl Service { .add_key(user_id, version, room_id, session_id, key_data) } - pub fn count_keys(&self, user_id: &UserId, version: &str) -> Result { + pub(crate) fn count_keys(&self, user_id: &UserId, version: &str) -> Result { self.db.count_keys(user_id, version) } - pub fn get_etag(&self, user_id: &UserId, version: &str) -> Result { + pub(crate) fn get_etag(&self, user_id: &UserId, version: &str) -> Result { self.db.get_etag(user_id, version) } - pub fn get_all( + pub(crate) fn get_all( &self, user_id: &UserId, version: &str, @@ -82,7 +82,7 @@ impl Service { self.db.get_all(user_id, version) } - pub fn get_room( + pub(crate) fn get_room( &self, user_id: &UserId, version: &str, @@ -91,7 +91,7 @@ impl Service { self.db.get_room(user_id, version, room_id) } - pub fn get_session( + pub(crate) fn get_session( &self, user_id: &UserId, version: &str, @@ -101,11 +101,11 @@ impl Service { self.db.get_session(user_id, version, room_id, session_id) } - pub fn delete_all_keys(&self, user_id: &UserId, version: &str) -> Result<()> { + pub(crate) fn delete_all_keys(&self, user_id: &UserId, version: &str) -> Result<()> { self.db.delete_all_keys(user_id, version) } - pub fn delete_room_keys( + pub(crate) fn delete_room_keys( &self, user_id: &UserId, version: &str, @@ -114,7 +114,7 @@ impl Service { self.db.delete_room_keys(user_id, version, room_id) } - pub fn delete_room_key( + pub(crate) fn delete_room_key( &self, user_id: &UserId, version: &str, diff --git a/src/service/key_backups/data.rs b/src/service/key_backups/data.rs index bf640015..c99fe1d6 100644 --- a/src/service/key_backups/data.rs +++ b/src/service/key_backups/data.rs @@ -7,7 +7,7 @@ use ruma::{ OwnedRoomId, RoomId, UserId, }; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn create_backup( &self, user_id: &UserId, diff --git a/src/service/media.rs b/src/service/media.rs index fc8fa569..9ecd2668 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -1,7 +1,7 @@ mod data; use std::io::Cursor; -pub use data::Data; +pub(crate) use data::Data; use crate::{services, Result}; use image::imageops::FilterType; @@ -11,19 +11,19 @@ use tokio::{ io::{AsyncReadExt, AsyncWriteExt, BufReader}, }; -pub struct FileMeta { - pub content_disposition: Option, - pub content_type: Option, - pub file: Vec, +pub(crate) struct FileMeta { + pub(crate) content_disposition: Option, + pub(crate) content_type: Option, + pub(crate) file: Vec, } -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Uploads a file. - pub async fn create( + pub(crate) async fn create( &self, mxc: String, content_disposition: Option<&str>, @@ -43,7 +43,7 @@ impl Service { /// Uploads or replaces a file thumbnail. #[allow(clippy::too_many_arguments)] - pub async fn upload_thumbnail( + pub(crate) async fn upload_thumbnail( &self, mxc: String, content_disposition: Option<&str>, @@ -64,7 +64,7 @@ impl Service { } /// Downloads a file. - pub async fn get(&self, mxc: String) -> Result> { + pub(crate) async fn get(&self, mxc: String) -> Result> { if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc, 0, 0) { @@ -86,7 +86,7 @@ impl Service { /// Returns width, height of the thumbnail and whether it should be cropped. Returns None when /// the server should send the original file. - pub fn thumbnail_properties(&self, width: u32, height: u32) -> Option<(u32, u32, bool)> { + pub(crate) fn thumbnail_properties(&self, width: u32, height: u32) -> Option<(u32, u32, bool)> { match (width, height) { (0..=32, 0..=32) => Some((32, 32, true)), (0..=96, 0..=96) => Some((96, 96, true)), @@ -107,7 +107,7 @@ impl Service { /// - Server creates the thumbnail and sends it to the user /// /// For width,height <= 96 the server uses another thumbnailing algorithm which crops the image afterwards. - pub async fn get_thumbnail( + pub(crate) async fn get_thumbnail( &self, mxc: String, width: u32, diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 75a682cb..ce2fc76c 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -1,6 +1,6 @@ use crate::Result; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn create_file_metadata( &self, mxc: String, diff --git a/src/service/pdu.rs b/src/service/pdu.rs index a51d7ec5..d535fc3f 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -21,37 +21,37 @@ use tracing::warn; /// Content hashes of a PDU. #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct EventHash { +pub(crate) struct EventHash { /// The SHA-256 hash. - pub sha256: String, + pub(crate) sha256: String, } #[derive(Clone, Deserialize, Debug, Serialize)] -pub struct PduEvent { - pub event_id: Arc, - pub room_id: OwnedRoomId, - pub sender: OwnedUserId, - pub origin_server_ts: UInt, +pub(crate) struct PduEvent { + pub(crate) event_id: Arc, + pub(crate) room_id: OwnedRoomId, + pub(crate) sender: OwnedUserId, + pub(crate) origin_server_ts: UInt, #[serde(rename = "type")] - pub kind: TimelineEventType, - pub content: Box, + pub(crate) kind: TimelineEventType, + pub(crate) content: Box, #[serde(skip_serializing_if = "Option::is_none")] - pub state_key: Option, - pub prev_events: Vec>, - pub depth: UInt, - pub auth_events: Vec>, + pub(crate) state_key: Option, + pub(crate) prev_events: Vec>, + pub(crate) depth: UInt, + pub(crate) auth_events: Vec>, #[serde(skip_serializing_if = "Option::is_none")] - pub redacts: Option>, + pub(crate) redacts: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - pub unsigned: Option>, - pub hashes: EventHash, + pub(crate) unsigned: Option>, + pub(crate) hashes: EventHash, #[serde(default, skip_serializing_if = "Option::is_none")] - pub signatures: Option>, // BTreeMap, BTreeMap> + pub(crate) signatures: Option>, // BTreeMap, BTreeMap> } impl PduEvent { #[tracing::instrument(skip(self))] - pub fn redact( + pub(crate) fn redact( &mut self, room_version_id: RoomVersionId, reason: &PduEvent, @@ -72,7 +72,7 @@ impl PduEvent { Ok(()) } - pub fn remove_transaction_id(&mut self) -> crate::Result<()> { + pub(crate) fn remove_transaction_id(&mut self) -> crate::Result<()> { if let Some(unsigned) = &self.unsigned { let mut unsigned: BTreeMap> = serde_json::from_str(unsigned.get()) @@ -84,7 +84,7 @@ impl PduEvent { Ok(()) } - pub fn add_age(&mut self) -> crate::Result<()> { + pub(crate) fn add_age(&mut self) -> crate::Result<()> { let mut unsigned: BTreeMap> = self .unsigned .as_ref() @@ -109,7 +109,7 @@ impl PduEvent { /// > For improved compatibility with newer clients, servers should add a redacts property /// > to the content of m.room.redaction events in older room versions when serving /// > such events over the Client-Server API. - pub fn copy_redacts(&self) -> (Option>, Box) { + pub(crate) fn copy_redacts(&self) -> (Option>, Box) { if self.kind == TimelineEventType::RoomRedaction { if let Ok(mut content) = serde_json::from_str::(self.content.get()) @@ -130,7 +130,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_sync_room_event(&self) -> Raw { + pub(crate) fn to_sync_room_event(&self) -> Raw { let (redacts, content) = self.copy_redacts(); let mut json = json!({ "content": content, @@ -155,7 +155,7 @@ impl PduEvent { /// This only works for events that are also AnyRoomEvents. #[tracing::instrument(skip(self))] - pub fn to_any_event(&self) -> Raw { + pub(crate) fn to_any_event(&self) -> Raw { let mut json = json!({ "content": self.content, "type": self.kind, @@ -179,7 +179,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_room_event(&self) -> Raw { + pub(crate) fn to_room_event(&self) -> Raw { let (redacts, content) = self.copy_redacts(); let mut json = json!({ "content": content, @@ -204,7 +204,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_message_like_event(&self) -> Raw { + pub(crate) fn to_message_like_event(&self) -> Raw { let (redacts, content) = self.copy_redacts(); let mut json = json!({ "content": content, @@ -229,7 +229,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_state_event(&self) -> Raw { + pub(crate) fn to_state_event(&self) -> Raw { let mut json = json!({ "content": self.content, "type": self.kind, @@ -248,7 +248,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_sync_state_event(&self) -> Raw { + pub(crate) fn to_sync_state_event(&self) -> Raw { let mut json = json!({ "content": self.content, "type": self.kind, @@ -266,7 +266,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_stripped_state_event(&self) -> Raw { + pub(crate) fn to_stripped_state_event(&self) -> Raw { let json = json!({ "content": self.content, "type": self.kind, @@ -278,7 +278,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_stripped_spacechild_state_event(&self) -> Raw { + pub(crate) fn to_stripped_spacechild_state_event(&self) -> Raw { let json = json!({ "content": self.content, "type": self.kind, @@ -291,7 +291,7 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub fn to_member_event(&self) -> Raw> { + pub(crate) fn to_member_event(&self) -> Raw> { let mut json = json!({ "content": self.content, "type": self.kind, @@ -312,7 +312,7 @@ impl PduEvent { /// This does not return a full `Pdu` it is only to satisfy ruma's types. #[tracing::instrument] - pub fn convert_to_outgoing_federation_event( + pub(crate) fn convert_to_outgoing_federation_event( mut pdu_json: CanonicalJsonObject, ) -> Box { if let Some(unsigned) = pdu_json @@ -334,7 +334,7 @@ impl PduEvent { to_raw_value(&pdu_json).expect("CanonicalJson is valid serde_json::Value") } - pub fn from_id_val( + pub(crate) fn from_id_val( event_id: &EventId, mut json: CanonicalJsonObject, ) -> Result { @@ -436,11 +436,11 @@ pub(crate) fn gen_event_id_canonical_json( /// Build the start of a PDU in order to add it to the Database. #[derive(Debug, Deserialize)] -pub struct PduBuilder { +pub(crate) struct PduBuilder { #[serde(rename = "type")] - pub event_type: TimelineEventType, - pub content: Box, - pub unsigned: Option>, - pub state_key: Option, - pub redacts: Option>, + pub(crate) event_type: TimelineEventType, + pub(crate) content: Box, + pub(crate) unsigned: Option>, + pub(crate) state_key: Option, + pub(crate) redacts: Option>, } diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 6ca86be7..1f7bf0a0 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -1,5 +1,5 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{events::AnySyncTimelineEvent, push::PushConditionPowerLevelsCtx}; use crate::{services, Error, PduEvent, Result}; @@ -22,29 +22,33 @@ use ruma::{ use std::{fmt::Debug, mem}; use tracing::{info, warn}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn set_pusher(&self, sender: &UserId, pusher: set_pusher::v3::PusherAction) -> Result<()> { + pub(crate) fn set_pusher( + &self, + sender: &UserId, + pusher: set_pusher::v3::PusherAction, + ) -> Result<()> { self.db.set_pusher(sender, pusher) } - pub fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result> { + pub(crate) fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result> { self.db.get_pusher(sender, pushkey) } - pub fn get_pushers(&self, sender: &UserId) -> Result> { + pub(crate) fn get_pushers(&self, sender: &UserId) -> Result> { self.db.get_pushers(sender) } - pub fn get_pushkeys(&self, sender: &UserId) -> Box>> { + pub(crate) fn get_pushkeys(&self, sender: &UserId) -> Box>> { self.db.get_pushkeys(sender) } #[tracing::instrument(skip(self, destination, request))] - pub async fn send_request( + pub(crate) async fn send_request( &self, destination: &str, request: T, @@ -128,7 +132,7 @@ impl Service { } #[tracing::instrument(skip(self, user, unread, pusher, ruleset, pdu))] - pub async fn send_push_notice( + pub(crate) async fn send_push_notice( &self, user: &UserId, unread: UInt, @@ -184,7 +188,7 @@ impl Service { } #[tracing::instrument(skip(self, user, ruleset, pdu))] - pub fn get_actions<'a>( + pub(crate) fn get_actions<'a>( &self, user: &UserId, ruleset: &'a Ruleset, diff --git a/src/service/pusher/data.rs b/src/service/pusher/data.rs index 2062f567..b13fcc3b 100644 --- a/src/service/pusher/data.rs +++ b/src/service/pusher/data.rs @@ -4,7 +4,7 @@ use ruma::{ UserId, }; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn set_pusher(&self, sender: &UserId, pusher: set_pusher::v3::PusherAction) -> Result<()>; fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result>; diff --git a/src/service/rooms.rs b/src/service/rooms.rs index f0739841..aab3cf00 100644 --- a/src/service/rooms.rs +++ b/src/service/rooms.rs @@ -1,24 +1,24 @@ -pub mod alias; -pub mod auth_chain; -pub mod directory; -pub mod edus; -pub mod event_handler; -pub mod lazy_loading; -pub mod metadata; -pub mod outlier; -pub mod pdu_metadata; -pub mod search; -pub mod short; -pub mod spaces; -pub mod state; -pub mod state_accessor; -pub mod state_cache; -pub mod state_compressor; -pub mod threads; -pub mod timeline; -pub mod user; +pub(crate) mod alias; +pub(crate) mod auth_chain; +pub(crate) mod directory; +pub(crate) mod edus; +pub(crate) mod event_handler; +pub(crate) mod lazy_loading; +pub(crate) mod metadata; +pub(crate) mod outlier; +pub(crate) mod pdu_metadata; +pub(crate) mod search; +pub(crate) mod short; +pub(crate) mod spaces; +pub(crate) mod state; +pub(crate) mod state_accessor; +pub(crate) mod state_cache; +pub(crate) mod state_compressor; +pub(crate) mod threads; +pub(crate) mod timeline; +pub(crate) mod user; -pub trait Data: +pub(crate) trait Data: alias::Data + auth_chain::Data + directory::Data @@ -39,24 +39,24 @@ pub trait Data: { } -pub struct Service { - pub alias: alias::Service, - pub auth_chain: auth_chain::Service, - pub directory: directory::Service, - pub edus: edus::Service, - pub event_handler: event_handler::Service, - pub lazy_loading: lazy_loading::Service, - pub metadata: metadata::Service, - pub outlier: outlier::Service, - pub pdu_metadata: pdu_metadata::Service, - pub search: search::Service, - pub short: short::Service, - pub state: state::Service, - pub state_accessor: state_accessor::Service, - pub state_cache: state_cache::Service, - pub state_compressor: state_compressor::Service, - pub timeline: timeline::Service, - pub threads: threads::Service, - pub spaces: spaces::Service, - pub user: user::Service, +pub(crate) struct Service { + pub(crate) alias: alias::Service, + pub(crate) auth_chain: auth_chain::Service, + pub(crate) directory: directory::Service, + pub(crate) edus: edus::Service, + pub(crate) event_handler: event_handler::Service, + pub(crate) lazy_loading: lazy_loading::Service, + pub(crate) metadata: metadata::Service, + pub(crate) outlier: outlier::Service, + pub(crate) pdu_metadata: pdu_metadata::Service, + pub(crate) search: search::Service, + pub(crate) short: short::Service, + pub(crate) state: state::Service, + pub(crate) state_accessor: state_accessor::Service, + pub(crate) state_cache: state_cache::Service, + pub(crate) state_compressor: state_compressor::Service, + pub(crate) timeline: timeline::Service, + pub(crate) threads: threads::Service, + pub(crate) spaces: spaces::Service, + pub(crate) user: user::Service, } diff --git a/src/service/rooms/alias.rs b/src/service/rooms/alias.rs index d26030c0..d078611a 100644 --- a/src/service/rooms/alias.rs +++ b/src/service/rooms/alias.rs @@ -1,32 +1,32 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use crate::Result; use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { #[tracing::instrument(skip(self))] - pub fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()> { + pub(crate) fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()> { self.db.set_alias(alias, room_id) } #[tracing::instrument(skip(self))] - pub fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { + pub(crate) fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { self.db.remove_alias(alias) } #[tracing::instrument(skip(self))] - pub fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result> { + pub(crate) fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result> { self.db.resolve_local_alias(alias) } #[tracing::instrument(skip(self))] - pub fn local_aliases_for_room<'a>( + pub(crate) fn local_aliases_for_room<'a>( &'a self, room_id: &RoomId, ) -> Box> + 'a> { diff --git a/src/service/rooms/alias/data.rs b/src/service/rooms/alias/data.rs index 629b1ee1..f0f442d0 100644 --- a/src/service/rooms/alias/data.rs +++ b/src/service/rooms/alias/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Creates or updates the alias to the given room id. fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()>; diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index da1944e2..e1dc6527 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -4,28 +4,35 @@ use std::{ sync::Arc, }; -pub use data::Data; +pub(crate) use data::Data; use ruma::{api::client::error::ErrorKind, EventId, RoomId}; use tracing::{debug, error, warn}; use crate::{services, Error, Result}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn get_cached_eventid_authchain(&self, key: &[u64]) -> Result>>> { + pub(crate) fn get_cached_eventid_authchain( + &self, + key: &[u64], + ) -> Result>>> { self.db.get_cached_eventid_authchain(key) } #[tracing::instrument(skip(self))] - pub fn cache_auth_chain(&self, key: Vec, auth_chain: Arc>) -> Result<()> { + pub(crate) fn cache_auth_chain( + &self, + key: Vec, + auth_chain: Arc>, + ) -> Result<()> { self.db.cache_auth_chain(key, auth_chain) } #[tracing::instrument(skip(self, starting_events))] - pub async fn get_auth_chain<'a>( + pub(crate) async fn get_auth_chain<'a>( &self, room_id: &RoomId, starting_events: Vec>, diff --git a/src/service/rooms/auth_chain/data.rs b/src/service/rooms/auth_chain/data.rs index e8c379fc..7a865368 100644 --- a/src/service/rooms/auth_chain/data.rs +++ b/src/service/rooms/auth_chain/data.rs @@ -1,7 +1,7 @@ use crate::Result; use std::{collections::HashSet, sync::Arc}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn get_cached_eventid_authchain( &self, shorteventid: &[u64], diff --git a/src/service/rooms/directory.rs b/src/service/rooms/directory.rs index 10f782bb..9e43ac45 100644 --- a/src/service/rooms/directory.rs +++ b/src/service/rooms/directory.rs @@ -1,32 +1,32 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{OwnedRoomId, RoomId}; use crate::Result; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { #[tracing::instrument(skip(self))] - pub fn set_public(&self, room_id: &RoomId) -> Result<()> { + pub(crate) fn set_public(&self, room_id: &RoomId) -> Result<()> { self.db.set_public(room_id) } #[tracing::instrument(skip(self))] - pub fn set_not_public(&self, room_id: &RoomId) -> Result<()> { + pub(crate) fn set_not_public(&self, room_id: &RoomId) -> Result<()> { self.db.set_not_public(room_id) } #[tracing::instrument(skip(self))] - pub fn is_public_room(&self, room_id: &RoomId) -> Result { + pub(crate) fn is_public_room(&self, room_id: &RoomId) -> Result { self.db.is_public_room(room_id) } #[tracing::instrument(skip(self))] - pub fn public_rooms(&self) -> impl Iterator> + '_ { + pub(crate) fn public_rooms(&self) -> impl Iterator> + '_ { self.db.public_rooms() } } diff --git a/src/service/rooms/directory/data.rs b/src/service/rooms/directory/data.rs index aca731ce..9aefaf6c 100644 --- a/src/service/rooms/directory/data.rs +++ b/src/service/rooms/directory/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{OwnedRoomId, RoomId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Adds the room to the public room directory fn set_public(&self, room_id: &RoomId) -> Result<()>; diff --git a/src/service/rooms/edus.rs b/src/service/rooms/edus.rs index 869865f7..d9483465 100644 --- a/src/service/rooms/edus.rs +++ b/src/service/rooms/edus.rs @@ -1,9 +1,9 @@ -pub mod read_receipt; -pub mod typing; +pub(crate) mod read_receipt; +pub(crate) mod typing; -pub trait Data: read_receipt::Data + 'static {} +pub(crate) trait Data: read_receipt::Data + 'static {} -pub struct Service { - pub read_receipt: read_receipt::Service, - pub typing: typing::Service, +pub(crate) struct Service { + pub(crate) read_receipt: read_receipt::Service, + pub(crate) typing: typing::Service, } diff --git a/src/service/rooms/edus/read_receipt.rs b/src/service/rooms/edus/read_receipt.rs index c6035280..54628203 100644 --- a/src/service/rooms/edus/read_receipt.rs +++ b/src/service/rooms/edus/read_receipt.rs @@ -1,17 +1,17 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use crate::Result; use ruma::{events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Replaces the previous read receipt. - pub fn readreceipt_update( + pub(crate) fn readreceipt_update( &self, user_id: &UserId, room_id: &RoomId, @@ -22,7 +22,7 @@ impl Service { /// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`. #[tracing::instrument(skip(self))] - pub fn readreceipts_since<'a>( + pub(crate) fn readreceipts_since<'a>( &'a self, room_id: &RoomId, since: u64, @@ -38,18 +38,31 @@ impl Service { /// Sets a private read marker at `count`. #[tracing::instrument(skip(self))] - pub fn private_read_set(&self, room_id: &RoomId, user_id: &UserId, count: u64) -> Result<()> { + pub(crate) fn private_read_set( + &self, + room_id: &RoomId, + user_id: &UserId, + count: u64, + ) -> Result<()> { self.db.private_read_set(room_id, user_id, count) } /// Returns the private read marker. #[tracing::instrument(skip(self))] - pub fn private_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + pub(crate) fn private_read_get( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { self.db.private_read_get(room_id, user_id) } /// Returns the count of the last typing update in this room. - pub fn last_privateread_update(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn last_privateread_update( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { self.db.last_privateread_update(user_id, room_id) } } diff --git a/src/service/rooms/edus/read_receipt/data.rs b/src/service/rooms/edus/read_receipt/data.rs index 044dad82..c25af24e 100644 --- a/src/service/rooms/edus/read_receipt/data.rs +++ b/src/service/rooms/edus/read_receipt/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Replaces the previous read receipt. fn readreceipt_update( &self, diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 7546aa84..76be3791 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -4,16 +4,21 @@ use tokio::sync::{broadcast, RwLock}; use crate::{services, utils, Result}; -pub struct Service { - pub typing: RwLock>>, // u64 is unix timestamp of timeout - pub last_typing_update: RwLock>, // timestamp of the last change to typing users - pub typing_update_sender: broadcast::Sender, +pub(crate) struct Service { + pub(crate) typing: RwLock>>, // u64 is unix timestamp of timeout + pub(crate) last_typing_update: RwLock>, // timestamp of the last change to typing users + pub(crate) typing_update_sender: broadcast::Sender, } impl Service { /// Sets a user as typing until the timeout timestamp is reached or roomtyping_remove is /// called. - pub async fn typing_add(&self, user_id: &UserId, room_id: &RoomId, timeout: u64) -> Result<()> { + pub(crate) async fn typing_add( + &self, + user_id: &UserId, + room_id: &RoomId, + timeout: u64, + ) -> Result<()> { self.typing .write() .await @@ -29,7 +34,7 @@ impl Service { } /// Removes a user from typing before the timeout is reached. - pub async fn typing_remove(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { + pub(crate) async fn typing_remove(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { self.typing .write() .await @@ -44,7 +49,7 @@ impl Service { Ok(()) } - pub async fn wait_for_update(&self, room_id: &RoomId) -> Result<()> { + pub(crate) async fn wait_for_update(&self, room_id: &RoomId) -> Result<()> { let mut receiver = self.typing_update_sender.subscribe(); while let Ok(next) = receiver.recv().await { if next == room_id { @@ -87,7 +92,7 @@ impl Service { } /// Returns the count of the last typing update in this room. - pub async fn last_typing_update(&self, room_id: &RoomId) -> Result { + pub(crate) async fn last_typing_update(&self, room_id: &RoomId) -> Result { self.typings_maintain(room_id).await?; Ok(self .last_typing_update @@ -99,7 +104,7 @@ impl Service { } /// Returns a new typing EDU. - pub async fn typings_all( + pub(crate) async fn typings_all( &self, room_id: &RoomId, ) -> Result> { diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index b7817e50..84b210e1 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -43,7 +43,7 @@ use crate::{service::*, services, Error, PduEvent, Result}; use super::state_compressor::CompressedStateEvent; -pub struct Service; +pub(crate) struct Service; impl Service { /// When receiving an event one needs to: @@ -480,7 +480,7 @@ impl Service { } #[tracing::instrument(skip(self, incoming_pdu, val, create_event, pub_key_map))] - pub async fn upgrade_outlier_to_timeline_pdu( + pub(crate) async fn upgrade_outlier_to_timeline_pdu( &self, incoming_pdu: Arc, val: BTreeMap, @@ -1636,7 +1636,7 @@ impl Service { } /// Returns Ok if the acl allows the server - pub fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> { + pub(crate) fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> { let acl_event = match services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomServerAcl, @@ -1677,7 +1677,7 @@ impl Service { /// Search the DB for the signing keys of the given server, if we don't have them /// fetch them from the server and save to our DB. #[tracing::instrument(skip_all)] - pub async fn fetch_signing_keys( + pub(crate) async fn fetch_signing_keys( &self, origin: &ServerName, signature_ids: Vec, diff --git a/src/service/rooms/lazy_loading.rs b/src/service/rooms/lazy_loading.rs index e2594a0a..023e7fc2 100644 --- a/src/service/rooms/lazy_loading.rs +++ b/src/service/rooms/lazy_loading.rs @@ -1,7 +1,7 @@ mod data; use std::collections::{HashMap, HashSet}; -pub use data::Data; +pub(crate) use data::Data; use ruma::{DeviceId, OwnedDeviceId, OwnedRoomId, OwnedUserId, RoomId, UserId}; use tokio::sync::Mutex; @@ -9,17 +9,17 @@ use crate::Result; use super::timeline::PduCount; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub lazy_load_waiting: + pub(crate) lazy_load_waiting: Mutex>>, } impl Service { #[tracing::instrument(skip(self))] - pub fn lazy_load_was_sent_before( + pub(crate) fn lazy_load_was_sent_before( &self, user_id: &UserId, device_id: &DeviceId, @@ -31,7 +31,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub async fn lazy_load_mark_sent( + pub(crate) async fn lazy_load_mark_sent( &self, user_id: &UserId, device_id: &DeviceId, @@ -51,7 +51,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub async fn lazy_load_confirm_delivery( + pub(crate) async fn lazy_load_confirm_delivery( &self, user_id: &UserId, device_id: &DeviceId, @@ -78,7 +78,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn lazy_load_reset( + pub(crate) fn lazy_load_reset( &self, user_id: &UserId, device_id: &DeviceId, diff --git a/src/service/rooms/lazy_loading/data.rs b/src/service/rooms/lazy_loading/data.rs index 9af8e21b..92f694a3 100644 --- a/src/service/rooms/lazy_loading/data.rs +++ b/src/service/rooms/lazy_loading/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{DeviceId, RoomId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn lazy_load_was_sent_before( &self, user_id: &UserId, diff --git a/src/service/rooms/metadata.rs b/src/service/rooms/metadata.rs index d1884691..5205a94a 100644 --- a/src/service/rooms/metadata.rs +++ b/src/service/rooms/metadata.rs @@ -1,30 +1,30 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{OwnedRoomId, RoomId}; use crate::Result; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Checks if a room exists. #[tracing::instrument(skip(self))] - pub fn exists(&self, room_id: &RoomId) -> Result { + pub(crate) fn exists(&self, room_id: &RoomId) -> Result { self.db.exists(room_id) } - pub fn iter_ids<'a>(&'a self) -> Box> + 'a> { + pub(crate) fn iter_ids<'a>(&'a self) -> Box> + 'a> { self.db.iter_ids() } - pub fn is_disabled(&self, room_id: &RoomId) -> Result { + pub(crate) fn is_disabled(&self, room_id: &RoomId) -> Result { self.db.is_disabled(room_id) } - pub fn disable_room(&self, room_id: &RoomId, disabled: bool) -> Result<()> { + pub(crate) fn disable_room(&self, room_id: &RoomId, disabled: bool) -> Result<()> { self.db.disable_room(room_id, disabled) } } diff --git a/src/service/rooms/metadata/data.rs b/src/service/rooms/metadata/data.rs index 339db573..9b49fdeb 100644 --- a/src/service/rooms/metadata/data.rs +++ b/src/service/rooms/metadata/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{OwnedRoomId, RoomId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn exists(&self, room_id: &RoomId) -> Result; fn iter_ids<'a>(&'a self) -> Box> + 'a>; fn is_disabled(&self, room_id: &RoomId) -> Result; diff --git a/src/service/rooms/outlier.rs b/src/service/rooms/outlier.rs index dae41e4b..64665fa5 100644 --- a/src/service/rooms/outlier.rs +++ b/src/service/rooms/outlier.rs @@ -1,28 +1,35 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{CanonicalJsonObject, EventId}; use crate::{PduEvent, Result}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Returns the pdu from the outlier tree. - pub fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_outlier_pdu_json( + &self, + event_id: &EventId, + ) -> Result> { self.db.get_outlier_pdu_json(event_id) } /// Returns the pdu from the outlier tree. - pub fn get_pdu_outlier(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_pdu_outlier(&self, event_id: &EventId) -> Result> { self.db.get_outlier_pdu(event_id) } /// Append the PDU as an outlier. #[tracing::instrument(skip(self, pdu))] - pub fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()> { + pub(crate) fn add_pdu_outlier( + &self, + event_id: &EventId, + pdu: &CanonicalJsonObject, + ) -> Result<()> { self.db.add_pdu_outlier(event_id, pdu) } } diff --git a/src/service/rooms/outlier/data.rs b/src/service/rooms/outlier/data.rs index 0ed521dd..34505640 100644 --- a/src/service/rooms/outlier/data.rs +++ b/src/service/rooms/outlier/data.rs @@ -2,7 +2,7 @@ use ruma::{CanonicalJsonObject, EventId}; use crate::{PduEvent, Result}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result>; fn get_outlier_pdu(&self, event_id: &EventId) -> Result>; fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()>; diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 411f4f54..ee904e07 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -1,7 +1,7 @@ mod data; use std::sync::Arc; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ api::client::relations::get_relating_events, events::{relation::RelationType, TimelineEventType}, @@ -13,8 +13,8 @@ use crate::{services, PduEvent, Result}; use super::timeline::PduCount; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } #[derive(Clone, Debug, Deserialize)] @@ -29,7 +29,7 @@ struct ExtractRelatesToEventId { impl Service { #[tracing::instrument(skip(self, from, to))] - pub fn add_relation(&self, from: PduCount, to: PduCount) -> Result<()> { + pub(crate) fn add_relation(&self, from: PduCount, to: PduCount) -> Result<()> { match (from, to) { (PduCount::Normal(f), PduCount::Normal(t)) => self.db.add_relation(f, t), _ => { @@ -41,7 +41,7 @@ impl Service { } #[allow(clippy::too_many_arguments)] - pub fn paginate_relations_with_filter( + pub(crate) fn paginate_relations_with_filter( &self, sender_user: &UserId, room_id: &RoomId, @@ -152,7 +152,7 @@ impl Service { } } - pub fn relations_until<'a>( + pub(crate) fn relations_until<'a>( &'a self, user_id: &'a UserId, room_id: &'a RoomId, @@ -169,22 +169,26 @@ impl Service { } #[tracing::instrument(skip(self, room_id, event_ids))] - pub fn mark_as_referenced(&self, room_id: &RoomId, event_ids: &[Arc]) -> Result<()> { + pub(crate) fn mark_as_referenced( + &self, + room_id: &RoomId, + event_ids: &[Arc], + ) -> Result<()> { self.db.mark_as_referenced(room_id, event_ids) } #[tracing::instrument(skip(self))] - pub fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result { + pub(crate) fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result { self.db.is_event_referenced(room_id, event_id) } #[tracing::instrument(skip(self))] - pub fn mark_event_soft_failed(&self, event_id: &EventId) -> Result<()> { + pub(crate) fn mark_event_soft_failed(&self, event_id: &EventId) -> Result<()> { self.db.mark_event_soft_failed(event_id) } #[tracing::instrument(skip(self))] - pub fn is_event_soft_failed(&self, event_id: &EventId) -> Result { + pub(crate) fn is_event_soft_failed(&self, event_id: &EventId) -> Result { self.db.is_event_soft_failed(event_id) } } diff --git a/src/service/rooms/pdu_metadata/data.rs b/src/service/rooms/pdu_metadata/data.rs index a4df34cc..4e28474a 100644 --- a/src/service/rooms/pdu_metadata/data.rs +++ b/src/service/rooms/pdu_metadata/data.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::{service::rooms::timeline::PduCount, PduEvent, Result}; use ruma::{EventId, RoomId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn add_relation(&self, from: u64, to: u64) -> Result<()>; #[allow(clippy::type_complexity)] fn relations_until<'a>( diff --git a/src/service/rooms/search.rs b/src/service/rooms/search.rs index b6f35e79..48dacca2 100644 --- a/src/service/rooms/search.rs +++ b/src/service/rooms/search.rs @@ -1,22 +1,27 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use crate::Result; use ruma::RoomId; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { #[tracing::instrument(skip(self))] - pub fn index_pdu<'a>(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { + pub(crate) fn index_pdu<'a>( + &self, + shortroomid: u64, + pdu_id: &[u8], + message_body: &str, + ) -> Result<()> { self.db.index_pdu(shortroomid, pdu_id, message_body) } #[tracing::instrument(skip(self))] - pub fn search_pdus<'a>( + pub(crate) fn search_pdus<'a>( &'a self, room_id: &RoomId, search_string: &str, diff --git a/src/service/rooms/search/data.rs b/src/service/rooms/search/data.rs index 7ea7e3d1..9e68fe62 100644 --- a/src/service/rooms/search/data.rs +++ b/src/service/rooms/search/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::RoomId; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()>; #[allow(clippy::type_complexity)] diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 45fadd74..b2ad4fc5 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,21 +1,21 @@ mod data; use std::sync::Arc; -pub use data::Data; +pub(crate) use data::Data; use ruma::{events::StateEventType, EventId, RoomId}; use crate::Result; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { + pub(crate) fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { self.db.get_or_create_shorteventid(event_id) } - pub fn get_shortstatekey( + pub(crate) fn get_shortstatekey( &self, event_type: &StateEventType, state_key: &str, @@ -23,7 +23,7 @@ impl Service { self.db.get_shortstatekey(event_type, state_key) } - pub fn get_or_create_shortstatekey( + pub(crate) fn get_or_create_shortstatekey( &self, event_type: &StateEventType, state_key: &str, @@ -31,24 +31,27 @@ impl Service { self.db.get_or_create_shortstatekey(event_type, state_key) } - pub fn get_eventid_from_short(&self, shorteventid: u64) -> Result> { + pub(crate) fn get_eventid_from_short(&self, shorteventid: u64) -> Result> { self.db.get_eventid_from_short(shorteventid) } - pub fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)> { + pub(crate) fn get_statekey_from_short( + &self, + shortstatekey: u64, + ) -> Result<(StateEventType, String)> { self.db.get_statekey_from_short(shortstatekey) } /// Returns (shortstatehash, already_existed) - pub fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { + pub(crate) fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { self.db.get_or_create_shortstatehash(state_hash) } - pub fn get_shortroomid(&self, room_id: &RoomId) -> Result> { + pub(crate) fn get_shortroomid(&self, room_id: &RoomId) -> Result> { self.db.get_shortroomid(room_id) } - pub fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { + pub(crate) fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { self.db.get_or_create_shortroomid(room_id) } } diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index 652c525b..2b7465f1 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::Result; use ruma::{events::StateEventType, EventId, RoomId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result; fn get_shortstatekey( diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 981d4a37..27d00c30 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -31,23 +31,23 @@ use tracing::{debug, error, warn}; use crate::{services, Error, PduEvent, Result}; -pub enum CachedJoinRule { +pub(crate) enum CachedJoinRule { //Simplified(SpaceRoomJoinRule), Full(JoinRule), } -pub struct CachedSpaceChunk { +pub(crate) struct CachedSpaceChunk { chunk: SpaceHierarchyRoomsChunk, children: Vec, join_rule: CachedJoinRule, } -pub struct Service { - pub roomid_spacechunk_cache: Mutex>>, +pub(crate) struct Service { + pub(crate) roomid_spacechunk_cache: Mutex>>, } impl Service { - pub async fn get_hierarchy( + pub(crate) async fn get_hierarchy( &self, sender_user: &UserId, room_id: &RoomId, diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index f6581bb5..2b3da501 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -4,7 +4,7 @@ use std::{ sync::Arc, }; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ api::client::error::ErrorKind, events::{ @@ -23,13 +23,13 @@ use crate::{services, utils::calculate_hash, Error, PduEvent, Result}; use super::state_compressor::CompressedStateEvent; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Set the room to the given statehash and update caches. - pub async fn force_state( + pub(crate) async fn force_state( &self, room_id: &RoomId, shortstatehash: u64, @@ -115,7 +115,7 @@ impl Service { /// This adds all current state events (not including the incoming event) /// to `stateid_pduid` and adds the incoming event to `eventid_statehash`. #[tracing::instrument(skip(self, state_ids_compressed))] - pub fn set_event_state( + pub(crate) fn set_event_state( &self, event_id: &EventId, room_id: &RoomId, @@ -187,7 +187,7 @@ impl Service { /// This adds all current state events (not including the incoming event) /// to `stateid_pduid` and adds the incoming event to `eventid_statehash`. #[tracing::instrument(skip(self, new_pdu))] - pub fn append_to_state(&self, new_pdu: &PduEvent) -> Result { + pub(crate) fn append_to_state(&self, new_pdu: &PduEvent) -> Result { let shorteventid = services() .rooms .short @@ -259,7 +259,7 @@ impl Service { } #[tracing::instrument(skip(self, invite_event))] - pub fn calculate_invite_state( + pub(crate) fn calculate_invite_state( &self, invite_event: &PduEvent, ) -> Result>> { @@ -314,7 +314,7 @@ impl Service { /// Set the state hash to a new version, but does not update state_cache. #[tracing::instrument(skip(self))] - pub fn set_room_state( + pub(crate) fn set_room_state( &self, room_id: &RoomId, shortstatehash: u64, @@ -325,7 +325,7 @@ impl Service { /// Returns the room's version. #[tracing::instrument(skip(self))] - pub fn get_room_version(&self, room_id: &RoomId) -> Result { + pub(crate) fn get_room_version(&self, room_id: &RoomId) -> Result { let create_event = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomCreate, @@ -346,15 +346,18 @@ impl Service { Ok(create_event_content.room_version) } - pub fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result> { + pub(crate) fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result> { self.db.get_room_shortstatehash(room_id) } - pub fn get_forward_extremities(&self, room_id: &RoomId) -> Result>> { + pub(crate) fn get_forward_extremities( + &self, + room_id: &RoomId, + ) -> Result>> { self.db.get_forward_extremities(room_id) } - pub fn set_forward_extremities( + pub(crate) fn set_forward_extremities( &self, room_id: &RoomId, event_ids: Vec, @@ -366,7 +369,7 @@ impl Service { /// This fetches auth events from the current state. #[tracing::instrument(skip(self))] - pub fn get_auth_events( + pub(crate) fn get_auth_events( &self, room_id: &RoomId, kind: &TimelineEventType, diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index 96116b02..b8dda66b 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -3,7 +3,7 @@ use ruma::{EventId, OwnedEventId, RoomId}; use std::{collections::HashSet, sync::Arc}; use tokio::sync::MutexGuard; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Returns the last state hash key added to the db for the given room. fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result>; diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 53e3176f..6026677e 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -4,7 +4,7 @@ use std::{ sync::{Arc, Mutex}, }; -pub use data::Data; +pub(crate) use data::Data; use lru_cache::LruCache; use ruma::{ events::{ @@ -26,21 +26,24 @@ use tracing::{error, warn}; use crate::{service::pdu::PduBuilder, services, Error, PduEvent, Result}; -pub struct Service { - pub db: &'static dyn Data, - pub server_visibility_cache: Mutex>, - pub user_visibility_cache: Mutex>, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, + pub(crate) server_visibility_cache: Mutex>, + pub(crate) user_visibility_cache: Mutex>, } impl Service { /// 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))] - pub async fn state_full_ids(&self, shortstatehash: u64) -> Result>> { + pub(crate) async fn state_full_ids( + &self, + shortstatehash: u64, + ) -> Result>> { self.db.state_full_ids(shortstatehash).await } - pub async fn state_full( + pub(crate) async fn state_full( &self, shortstatehash: u64, ) -> Result>> { @@ -49,7 +52,7 @@ impl Service { /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). #[tracing::instrument(skip(self))] - pub fn state_get_id( + pub(crate) fn state_get_id( &self, shortstatehash: u64, event_type: &StateEventType, @@ -59,7 +62,7 @@ impl Service { } /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). - pub fn state_get( + pub(crate) fn state_get( &self, shortstatehash: u64, event_type: &StateEventType, @@ -100,7 +103,7 @@ impl Service { /// Whether a server is allowed to see an event through federation, based on /// the room's history_visibility at that event's state. #[tracing::instrument(skip(self, origin, room_id, event_id))] - pub fn server_can_see_event( + pub(crate) fn server_can_see_event( &self, origin: &ServerName, room_id: &RoomId, @@ -164,7 +167,7 @@ impl Service { /// Whether a user is allowed to see an event, based on /// the room's history_visibility at that event's state. #[tracing::instrument(skip(self, user_id, room_id, event_id))] - pub fn user_can_see_event( + pub(crate) fn user_can_see_event( &self, user_id: &UserId, room_id: &RoomId, @@ -224,7 +227,11 @@ impl Service { /// Whether a user is allowed to see an event, based on /// the room's history_visibility at that event's state. #[tracing::instrument(skip(self, user_id, room_id))] - pub fn user_can_see_state_events(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn user_can_see_state_events( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?; let history_visibility = self @@ -241,13 +248,13 @@ impl Service { } /// Returns the state hash for this pdu. - pub fn pdu_shortstatehash(&self, event_id: &EventId) -> Result> { + pub(crate) fn pdu_shortstatehash(&self, event_id: &EventId) -> Result> { self.db.pdu_shortstatehash(event_id) } /// Returns the full room state. #[tracing::instrument(skip(self))] - pub async fn room_state_full( + pub(crate) async fn room_state_full( &self, room_id: &RoomId, ) -> Result>> { @@ -256,7 +263,7 @@ impl Service { /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). #[tracing::instrument(skip(self))] - pub fn room_state_get_id( + pub(crate) fn room_state_get_id( &self, room_id: &RoomId, event_type: &StateEventType, @@ -267,7 +274,7 @@ impl Service { /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). #[tracing::instrument(skip(self))] - pub fn room_state_get( + pub(crate) fn room_state_get( &self, room_id: &RoomId, event_type: &StateEventType, @@ -276,7 +283,7 @@ impl Service { self.db.room_state_get(room_id, event_type, state_key) } - pub fn get_name(&self, room_id: &RoomId) -> Result> { + pub(crate) fn get_name(&self, room_id: &RoomId) -> Result> { services() .rooms .state_accessor @@ -294,7 +301,7 @@ impl Service { }) } - pub fn get_avatar(&self, room_id: &RoomId) -> Result> { + pub(crate) fn get_avatar(&self, room_id: &RoomId) -> Result> { services() .rooms .state_accessor @@ -305,7 +312,7 @@ impl Service { }) } - pub async fn user_can_invite( + pub(crate) async fn user_can_invite( &self, room_id: &RoomId, sender: &UserId, @@ -330,7 +337,7 @@ impl Service { .is_ok()) } - pub fn get_member( + pub(crate) fn get_member( &self, room_id: &RoomId, user_id: &UserId, @@ -350,7 +357,7 @@ impl Service { /// If `federation` is `true`, it allows redaction events from any user of the same server /// as the original event sender, [as required by room versions >= /// v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions) - pub fn user_can_redact( + pub(crate) fn user_can_redact( &self, redacts: &EventId, sender: &UserId, diff --git a/src/service/rooms/state_accessor/data.rs b/src/service/rooms/state_accessor/data.rs index f3ae3c21..a57cdb9d 100644 --- a/src/service/rooms/state_accessor/data.rs +++ b/src/service/rooms/state_accessor/data.rs @@ -6,7 +6,7 @@ use ruma::{events::StateEventType, EventId, RoomId}; use crate::{PduEvent, Result}; #[async_trait] -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Builds a StateMap by iterating over all keys that start /// with state_hash, this gives the full state for the given state_hash. async fn state_full_ids(&self, shortstatehash: u64) -> Result>>; diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index c108695d..8b844f1e 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -1,7 +1,7 @@ mod data; use std::{collections::HashSet, sync::Arc}; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ events::{ @@ -18,14 +18,14 @@ use tracing::warn; use crate::{service::appservice::RegistrationInfo, services, Error, Result}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Update current membership data. #[tracing::instrument(skip(self, last_state))] - pub fn update_membership( + pub(crate) fn update_membership( &self, room_id: &RoomId, user_id: &UserId, @@ -192,17 +192,17 @@ impl Service { } #[tracing::instrument(skip(self, room_id))] - pub fn update_joined_count(&self, room_id: &RoomId) -> Result<()> { + pub(crate) fn update_joined_count(&self, room_id: &RoomId) -> Result<()> { self.db.update_joined_count(room_id) } #[tracing::instrument(skip(self, room_id))] - pub fn get_our_real_users(&self, room_id: &RoomId) -> Result>> { + pub(crate) fn get_our_real_users(&self, room_id: &RoomId) -> Result>> { self.db.get_our_real_users(room_id) } #[tracing::instrument(skip(self, room_id, appservice))] - pub fn appservice_in_room( + pub(crate) fn appservice_in_room( &self, room_id: &RoomId, appservice: &RegistrationInfo, @@ -212,13 +212,13 @@ impl Service { /// Makes a user forget a room. #[tracing::instrument(skip(self))] - pub fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()> { + pub(crate) fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()> { self.db.forget(room_id, user_id) } /// Returns an iterator of all servers participating in this room. #[tracing::instrument(skip(self))] - pub fn room_servers<'a>( + pub(crate) fn room_servers<'a>( &'a self, room_id: &RoomId, ) -> impl Iterator> + 'a { @@ -226,13 +226,17 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn server_in_room<'a>(&'a self, server: &ServerName, room_id: &RoomId) -> Result { + pub(crate) fn server_in_room<'a>( + &'a self, + server: &ServerName, + room_id: &RoomId, + ) -> Result { self.db.server_in_room(server, room_id) } /// Returns an iterator of all rooms a server participates in (as far as we know). #[tracing::instrument(skip(self))] - pub fn server_rooms<'a>( + pub(crate) fn server_rooms<'a>( &'a self, server: &ServerName, ) -> impl Iterator> + 'a { @@ -241,7 +245,7 @@ impl Service { /// Returns an iterator over all joined members of a room. #[tracing::instrument(skip(self))] - pub fn room_members<'a>( + pub(crate) fn room_members<'a>( &'a self, room_id: &RoomId, ) -> impl Iterator> + 'a { @@ -249,18 +253,18 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn room_joined_count(&self, room_id: &RoomId) -> Result> { + pub(crate) fn room_joined_count(&self, room_id: &RoomId) -> Result> { self.db.room_joined_count(room_id) } #[tracing::instrument(skip(self))] - pub fn room_invited_count(&self, room_id: &RoomId) -> Result> { + pub(crate) fn room_invited_count(&self, room_id: &RoomId) -> Result> { self.db.room_invited_count(room_id) } /// Returns an iterator over all User IDs who ever joined a room. #[tracing::instrument(skip(self))] - pub fn room_useroncejoined<'a>( + pub(crate) fn room_useroncejoined<'a>( &'a self, room_id: &RoomId, ) -> impl Iterator> + 'a { @@ -269,7 +273,7 @@ impl Service { /// Returns an iterator over all invited members of a room. #[tracing::instrument(skip(self))] - pub fn room_members_invited<'a>( + pub(crate) fn room_members_invited<'a>( &'a self, room_id: &RoomId, ) -> impl Iterator> + 'a { @@ -277,18 +281,22 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn get_invite_count(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + pub(crate) fn get_invite_count( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { self.db.get_invite_count(room_id, user_id) } #[tracing::instrument(skip(self))] - pub fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + pub(crate) fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result> { self.db.get_left_count(room_id, user_id) } /// Returns an iterator over all rooms this user joined. #[tracing::instrument(skip(self))] - pub fn rooms_joined<'a>( + pub(crate) fn rooms_joined<'a>( &'a self, user_id: &UserId, ) -> impl Iterator> + 'a { @@ -297,7 +305,7 @@ impl Service { /// Returns an iterator over all rooms a user was invited to. #[tracing::instrument(skip(self))] - pub fn rooms_invited<'a>( + pub(crate) fn rooms_invited<'a>( &'a self, user_id: &UserId, ) -> impl Iterator>)>> + 'a { @@ -305,7 +313,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn invite_state( + pub(crate) fn invite_state( &self, user_id: &UserId, room_id: &RoomId, @@ -314,7 +322,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn left_state( + pub(crate) fn left_state( &self, user_id: &UserId, room_id: &RoomId, @@ -324,7 +332,7 @@ impl Service { /// Returns an iterator over all rooms a user left. #[tracing::instrument(skip(self))] - pub fn rooms_left<'a>( + pub(crate) fn rooms_left<'a>( &'a self, user_id: &UserId, ) -> impl Iterator>)>> + 'a { @@ -332,22 +340,22 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.once_joined(user_id, room_id) } #[tracing::instrument(skip(self))] - pub fn is_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn is_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.is_joined(user_id, room_id) } #[tracing::instrument(skip(self))] - pub fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.is_invited(user_id, room_id) } #[tracing::instrument(skip(self))] - pub fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.is_left(user_id, room_id) } } diff --git a/src/service/rooms/state_cache/data.rs b/src/service/rooms/state_cache/data.rs index b511919a..c92ec848 100644 --- a/src/service/rooms/state_cache/data.rs +++ b/src/service/rooms/state_cache/data.rs @@ -7,7 +7,7 @@ use ruma::{ OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, }; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn mark_as_once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; fn mark_as_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; fn mark_as_invited( diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 6118e06b..8ba07634 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -1,11 +1,11 @@ -pub mod data; +pub(crate) mod data; use std::{ collections::HashSet, mem::size_of, sync::{Arc, Mutex}, }; -pub use data::Data; +pub(crate) use data::Data; use lru_cache::LruCache; use ruma::{EventId, RoomId}; @@ -13,11 +13,11 @@ use crate::{services, utils, Result}; use self::data::StateDiff; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub stateinfo_cache: Mutex< + pub(crate) stateinfo_cache: Mutex< LruCache< u64, Vec<( @@ -30,13 +30,13 @@ pub struct Service { >, } -pub type CompressedStateEvent = [u8; 2 * size_of::()]; +pub(crate) type CompressedStateEvent = [u8; 2 * size_of::()]; impl Service { /// Returns a stack with info on shortstatehash, full state, added diff and removed diff for the selected shortstatehash and each parent layer. #[allow(clippy::type_complexity)] #[tracing::instrument(skip(self))] - pub fn load_shortstatehash_info( + pub(crate) fn load_shortstatehash_info( &self, shortstatehash: u64, ) -> Result< @@ -89,7 +89,7 @@ impl Service { } } - pub fn compress_state_event( + pub(crate) fn compress_state_event( &self, shortstatekey: u64, event_id: &EventId, @@ -106,7 +106,7 @@ impl Service { } /// Returns shortstatekey, event id - pub fn parse_compressed_state_event( + pub(crate) fn parse_compressed_state_event( &self, compressed_event: &CompressedStateEvent, ) -> Result<(u64, Arc)> { @@ -141,7 +141,7 @@ impl Service { diff_to_sibling, parent_states ))] - pub fn save_state_from_diff( + pub(crate) fn save_state_from_diff( &self, shortstatehash: u64, statediffnew: Arc>, @@ -257,7 +257,7 @@ impl Service { /// Returns the new shortstatehash, and the state diff from the previous room state #[allow(clippy::type_complexity)] - pub fn save_state( + pub(crate) fn save_state( &self, room_id: &RoomId, new_state_ids_compressed: Arc>, diff --git a/src/service/rooms/state_compressor/data.rs b/src/service/rooms/state_compressor/data.rs index d221d576..3000b4ad 100644 --- a/src/service/rooms/state_compressor/data.rs +++ b/src/service/rooms/state_compressor/data.rs @@ -3,13 +3,13 @@ use std::{collections::HashSet, sync::Arc}; use super::CompressedStateEvent; use crate::Result; -pub struct StateDiff { - pub parent: Option, - pub added: Arc>, - pub removed: Arc>, +pub(crate) struct StateDiff { + pub(crate) parent: Option, + pub(crate) added: Arc>, + pub(crate) removed: Arc>, } -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn get_statediff(&self, shortstatehash: u64) -> Result; fn save_statediff(&self, shortstatehash: u64, diff: StateDiff) -> Result<()>; } diff --git a/src/service/rooms/threads.rs b/src/service/rooms/threads.rs index c6193bc8..d7690c08 100644 --- a/src/service/rooms/threads.rs +++ b/src/service/rooms/threads.rs @@ -1,6 +1,6 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ api::client::{error::ErrorKind, threads::get_threads::v1::IncludeThreads}, events::relation::BundledThread, @@ -11,12 +11,12 @@ use serde_json::json; use crate::{services, Error, PduEvent, Result}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn threads_until<'a>( + pub(crate) fn threads_until<'a>( &'a self, user_id: &'a UserId, room_id: &'a RoomId, @@ -26,7 +26,7 @@ impl Service { self.db.threads_until(user_id, room_id, until, include) } - pub fn add_to_thread(&self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> { + pub(crate) fn add_to_thread(&self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> { let root_id = &services() .rooms .timeline diff --git a/src/service/rooms/threads/data.rs b/src/service/rooms/threads/data.rs index e7159de0..42596e9b 100644 --- a/src/service/rooms/threads/data.rs +++ b/src/service/rooms/threads/data.rs @@ -1,7 +1,7 @@ use crate::{PduEvent, Result}; use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { #[allow(clippy::type_complexity)] fn threads_until<'a>( &'a self, diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 6c15b751..e8a815b7 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -6,7 +6,7 @@ use std::{ sync::Arc, }; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ api::{client::error::ErrorKind, federation}, @@ -42,20 +42,20 @@ use crate::{ use super::state_compressor::CompressedStateEvent; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] -pub enum PduCount { +pub(crate) enum PduCount { Backfilled(u64), Normal(u64), } impl PduCount { - pub fn min() -> Self { + pub(crate) fn min() -> Self { Self::Backfilled(u64::MAX) } - pub fn max() -> Self { + pub(crate) fn max() -> Self { Self::Normal(u64::MAX) } - pub fn try_from_string(token: &str) -> Result { + pub(crate) fn try_from_string(token: &str) -> Result { if let Some(stripped) = token.strip_prefix('-') { stripped.parse().map(PduCount::Backfilled) } else { @@ -64,7 +64,7 @@ impl PduCount { .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid pagination token.")) } - pub fn stringify(&self) -> String { + pub(crate) fn stringify(&self) -> String { match self { PduCount::Backfilled(x) => format!("-{x}"), PduCount::Normal(x) => x.to_string(), @@ -89,15 +89,15 @@ impl Ord for PduCount { } } -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, - pub lasttimelinecount_cache: Mutex>, + pub(crate) lasttimelinecount_cache: Mutex>, } impl Service { #[tracing::instrument(skip(self))] - pub fn first_pdu_in_room(&self, room_id: &RoomId) -> Result>> { + pub(crate) fn first_pdu_in_room(&self, room_id: &RoomId) -> Result>> { self.all_pdus(user_id!("@doesntmatter:grapevine"), room_id)? .next() .map(|o| o.map(|(_, p)| Arc::new(p))) @@ -105,19 +105,23 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn last_timeline_count( + &self, + sender_user: &UserId, + room_id: &RoomId, + ) -> Result { self.db.last_timeline_count(sender_user, room_id) } /// Returns the `count` of this pdu's id. - pub fn get_pdu_count(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_pdu_count(&self, event_id: &EventId) -> Result> { self.db.get_pdu_count(event_id) } // TODO Is this the same as the function above? /* #[tracing::instrument(skip(self))] - pub fn latest_pdu_count(&self, room_id: &RoomId) -> Result { + pub(crate) fn latest_pdu_count(&self, room_id: &RoomId) -> Result { let prefix = self .get_shortroomid(room_id)? .expect("room exists") @@ -138,12 +142,12 @@ impl Service { */ /// Returns the json of a pdu. - pub fn get_pdu_json(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_pdu_json(&self, event_id: &EventId) -> Result> { self.db.get_pdu_json(event_id) } /// Returns the json of a pdu. - pub fn get_non_outlier_pdu_json( + pub(crate) fn get_non_outlier_pdu_json( &self, event_id: &EventId, ) -> Result> { @@ -151,39 +155,42 @@ impl Service { } /// Returns the pdu's id. - pub fn get_pdu_id(&self, event_id: &EventId) -> Result>> { + pub(crate) fn get_pdu_id(&self, event_id: &EventId) -> Result>> { self.db.get_pdu_id(event_id) } /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. - pub fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result> { self.db.get_non_outlier_pdu(event_id) } /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. - pub fn get_pdu(&self, event_id: &EventId) -> Result>> { + pub(crate) fn get_pdu(&self, event_id: &EventId) -> Result>> { self.db.get_pdu(event_id) } /// Returns the pdu. /// /// This does __NOT__ check the outliers `Tree`. - pub fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result> { + pub(crate) fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result> { self.db.get_pdu_from_id(pdu_id) } /// Returns the pdu as a `BTreeMap`. - pub fn get_pdu_json_from_id(&self, pdu_id: &[u8]) -> Result> { + pub(crate) fn get_pdu_json_from_id( + &self, + pdu_id: &[u8], + ) -> Result> { self.db.get_pdu_json_from_id(pdu_id) } /// Removes a pdu and creates a new one with the same id. #[tracing::instrument(skip(self))] - pub fn replace_pdu( + pub(crate) fn replace_pdu( &self, pdu_id: &[u8], pdu_json: &CanonicalJsonObject, @@ -199,7 +206,7 @@ impl Service { /// /// Returns pdu id #[tracing::instrument(skip(self, pdu, pdu_json, leaves))] - pub async fn append_pdu<'a>( + pub(crate) async fn append_pdu<'a>( &self, pdu: &PduEvent, mut pdu_json: CanonicalJsonObject, @@ -624,7 +631,7 @@ impl Service { Ok(pdu_id) } - pub fn create_hash_and_sign_event( + pub(crate) fn create_hash_and_sign_event( &self, pdu_builder: PduBuilder, sender: &UserId, @@ -807,7 +814,7 @@ impl Service { /// Creates a new persisted data unit and adds it to a room. This function takes a /// roomid_mutex_state, meaning that only this function is able to mutate the room state. #[tracing::instrument(skip(self, state_lock))] - pub async fn build_and_append_pdu( + pub(crate) async fn build_and_append_pdu( &self, pdu_builder: PduBuilder, sender: &UserId, @@ -1007,7 +1014,7 @@ impl Service { /// Append the incoming event setting the state snapshot to the state from the /// server that sent the event. #[tracing::instrument(skip_all)] - pub async fn append_incoming_pdu<'a>( + pub(crate) async fn append_incoming_pdu<'a>( &self, pdu: &PduEvent, pdu_json: CanonicalJsonObject, @@ -1047,7 +1054,7 @@ impl Service { } /// Returns an iterator over all PDUs in a room. - pub fn all_pdus<'a>( + pub(crate) fn all_pdus<'a>( &'a self, user_id: &UserId, room_id: &RoomId, @@ -1058,7 +1065,7 @@ impl Service { /// Returns an iterator over all events and their tokens in a room that happened before the /// event with id `until` in reverse-chronological order. #[tracing::instrument(skip(self))] - pub fn pdus_until<'a>( + pub(crate) fn pdus_until<'a>( &'a self, user_id: &UserId, room_id: &RoomId, @@ -1070,7 +1077,7 @@ impl Service { /// Returns an iterator over all events and their token in a room that happened after the event /// with id `from` in chronological order. #[tracing::instrument(skip(self))] - pub fn pdus_after<'a>( + pub(crate) fn pdus_after<'a>( &'a self, user_id: &UserId, room_id: &RoomId, @@ -1081,7 +1088,7 @@ impl Service { /// Replace a PDU with the redacted form. #[tracing::instrument(skip(self, reason))] - pub fn redact_pdu(&self, event_id: &EventId, reason: &PduEvent) -> Result<()> { + pub(crate) fn redact_pdu(&self, event_id: &EventId, reason: &PduEvent) -> Result<()> { // TODO: Don't reserialize, keep original json if let Some(pdu_id) = self.get_pdu_id(event_id)? { let mut pdu = self @@ -1100,7 +1107,11 @@ impl Service { } #[tracing::instrument(skip(self, room_id))] - pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result<()> { + pub(crate) async fn backfill_if_required( + &self, + room_id: &RoomId, + from: PduCount, + ) -> Result<()> { let first_pdu = self .all_pdus(user_id!("@doesntmatter:grapevine"), room_id)? .next() @@ -1165,7 +1176,7 @@ impl Service { } #[tracing::instrument(skip(self, pdu))] - pub async fn backfill_pdu( + pub(crate) async fn backfill_pdu( &self, origin: &ServerName, pdu: Box, diff --git a/src/service/rooms/timeline/data.rs b/src/service/rooms/timeline/data.rs index 6290b8cc..025e3ef0 100644 --- a/src/service/rooms/timeline/data.rs +++ b/src/service/rooms/timeline/data.rs @@ -6,7 +6,7 @@ use crate::{PduEvent, Result}; use super::PduCount; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result; /// Returns the `count` of this pdu's id. diff --git a/src/service/rooms/user.rs b/src/service/rooms/user.rs index 672e502d..f50754f8 100644 --- a/src/service/rooms/user.rs +++ b/src/service/rooms/user.rs @@ -1,32 +1,36 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; use crate::Result; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn reset_notification_counts(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { + pub(crate) fn reset_notification_counts( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result<()> { self.db.reset_notification_counts(user_id, room_id) } - pub fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.notification_count(user_id, room_id) } - pub fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.highlight_count(user_id, room_id) } - pub fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result { self.db.last_notification_read(user_id, room_id) } - pub fn associate_token_shortstatehash( + pub(crate) fn associate_token_shortstatehash( &self, room_id: &RoomId, token: u64, @@ -36,11 +40,15 @@ impl Service { .associate_token_shortstatehash(room_id, token, shortstatehash) } - pub fn get_token_shortstatehash(&self, room_id: &RoomId, token: u64) -> Result> { + pub(crate) fn get_token_shortstatehash( + &self, + room_id: &RoomId, + token: u64, + ) -> Result> { self.db.get_token_shortstatehash(room_id, token) } - pub fn get_shared_rooms( + pub(crate) fn get_shared_rooms( &self, users: Vec, ) -> Result>> { diff --git a/src/service/rooms/user/data.rs b/src/service/rooms/user/data.rs index 4b8a4eca..6b5f1cde 100644 --- a/src/service/rooms/user/data.rs +++ b/src/service/rooms/user/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn reset_notification_counts(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result; diff --git a/src/service/sending.rs b/src/service/sending.rs index 7e54e8b4..f873f5ab 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -1,6 +1,6 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use std::{ collections::{BTreeMap, HashMap, HashSet}, @@ -45,7 +45,7 @@ use tokio::{ use tracing::{debug, error, warn}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum OutgoingKind { +pub(crate) enum OutgoingKind { Appservice(String), Push(OwnedUserId, String), // user and pushkey Normal(OwnedServerName), @@ -53,7 +53,7 @@ pub enum OutgoingKind { impl OutgoingKind { #[tracing::instrument(skip(self))] - pub fn get_prefix(&self) -> Vec { + pub(crate) fn get_prefix(&self) -> Vec { let mut prefix = match self { OutgoingKind::Appservice(server) => { let mut p = b"+".to_vec(); @@ -80,17 +80,17 @@ impl OutgoingKind { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum SendingEventType { +pub(crate) enum SendingEventType { Pdu(Vec), // pduid Edu(Vec), // pdu json } -pub struct Service { +pub(crate) struct Service { db: &'static dyn Data, /// The state for a given state hash. pub(super) maximum_requests: Arc, - pub sender: mpsc::UnboundedSender<(OutgoingKind, SendingEventType, Vec)>, + pub(crate) sender: mpsc::UnboundedSender<(OutgoingKind, SendingEventType, Vec)>, receiver: Mutex)>>, } @@ -101,7 +101,7 @@ enum TransactionStatus { } impl Service { - pub fn build(db: &'static dyn Data, config: &Config) -> Arc { + pub(crate) fn build(db: &'static dyn Data, config: &Config) -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); Arc::new(Self { db, @@ -111,7 +111,7 @@ impl Service { }) } - pub fn start_handler(self: &Arc) { + pub(crate) fn start_handler(self: &Arc) { let self2 = Arc::clone(self); tokio::spawn(async move { self2.handler().await.unwrap(); @@ -267,7 +267,7 @@ impl Service { } #[tracing::instrument(skip(self, server_name))] - pub fn select_edus(&self, server_name: &ServerName) -> Result<(Vec>, u64)> { + pub(crate) fn select_edus(&self, server_name: &ServerName) -> Result<(Vec>, u64)> { // u64: count of last edu let since = self.db.get_latest_educount(server_name)?; let mut events = Vec::new(); @@ -370,7 +370,12 @@ impl Service { } #[tracing::instrument(skip(self, pdu_id, user, pushkey))] - pub fn send_push_pdu(&self, pdu_id: &[u8], user: &UserId, pushkey: String) -> Result<()> { + pub(crate) fn send_push_pdu( + &self, + pdu_id: &[u8], + user: &UserId, + pushkey: String, + ) -> Result<()> { let outgoing_kind = OutgoingKind::Push(user.to_owned(), pushkey); let event = SendingEventType::Pdu(pdu_id.to_owned()); let keys = self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; @@ -382,7 +387,7 @@ impl Service { } #[tracing::instrument(skip(self, servers, pdu_id))] - pub fn send_pdu>( + pub(crate) fn send_pdu>( &self, servers: I, pdu_id: &[u8], @@ -412,7 +417,7 @@ impl Service { } #[tracing::instrument(skip(self, server, serialized))] - pub fn send_reliable_edu( + pub(crate) fn send_reliable_edu( &self, server: &ServerName, serialized: Vec, @@ -429,7 +434,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub fn send_pdu_appservice(&self, appservice_id: String, pdu_id: Vec) -> Result<()> { + pub(crate) fn send_pdu_appservice(&self, appservice_id: String, pdu_id: Vec) -> Result<()> { let outgoing_kind = OutgoingKind::Appservice(appservice_id); let event = SendingEventType::Pdu(pdu_id); let keys = self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; @@ -444,7 +449,7 @@ impl Service { /// Used for instance after we remove an appservice registration /// #[tracing::instrument(skip(self))] - pub fn cleanup_events(&self, appservice_id: String) -> Result<()> { + pub(crate) fn cleanup_events(&self, appservice_id: String) -> Result<()> { self.db .delete_all_requests_for(&OutgoingKind::Appservice(appservice_id))?; @@ -675,7 +680,7 @@ impl Service { } #[tracing::instrument(skip(self, destination, request))] - pub async fn send_federation_request( + pub(crate) async fn send_federation_request( &self, destination: &ServerName, request: T, @@ -704,7 +709,7 @@ impl Service { /// /// Only returns None if there is no url specified in the appservice registration file #[tracing::instrument(skip(self, registration, request))] - pub async fn send_appservice_request( + pub(crate) async fn send_appservice_request( &self, registration: Registration, request: T, diff --git a/src/service/sending/data.rs b/src/service/sending/data.rs index 8b4d236f..f2351534 100644 --- a/src/service/sending/data.rs +++ b/src/service/sending/data.rs @@ -4,7 +4,7 @@ use crate::Result; use super::{OutgoingKind, SendingEventType}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { #[allow(clippy::type_complexity)] fn active_requests<'a>( &'a self, diff --git a/src/service/transaction_ids.rs b/src/service/transaction_ids.rs index 2fa3b02e..b28919c5 100644 --- a/src/service/transaction_ids.rs +++ b/src/service/transaction_ids.rs @@ -1,16 +1,16 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use crate::Result; use ruma::{DeviceId, TransactionId, UserId}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { - pub fn add_txnid( + pub(crate) fn add_txnid( &self, user_id: &UserId, device_id: Option<&DeviceId>, @@ -20,7 +20,7 @@ impl Service { self.db.add_txnid(user_id, device_id, txn_id, data) } - pub fn existing_txnid( + pub(crate) fn existing_txnid( &self, user_id: &UserId, device_id: Option<&DeviceId>, diff --git a/src/service/transaction_ids/data.rs b/src/service/transaction_ids/data.rs index 74855318..6964abcd 100644 --- a/src/service/transaction_ids/data.rs +++ b/src/service/transaction_ids/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{DeviceId, TransactionId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn add_txnid( &self, user_id: &UserId, diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index ed39af99..190fb195 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -1,6 +1,6 @@ mod data; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ api::client::{ @@ -13,13 +13,13 @@ use tracing::error; use crate::{api::client_server::SESSION_ID_LENGTH, services, utils, Error, Result}; -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, } impl Service { /// Creates a new Uiaa session. Make sure the session token is unique. - pub fn create( + pub(crate) fn create( &self, user_id: &UserId, device_id: &DeviceId, @@ -40,7 +40,7 @@ impl Service { ) } - pub fn try_auth( + pub(crate) fn try_auth( &self, user_id: &UserId, device_id: &DeviceId, @@ -145,7 +145,7 @@ impl Service { Ok((true, uiaainfo)) } - pub fn get_uiaa_request( + pub(crate) fn get_uiaa_request( &self, user_id: &UserId, device_id: &DeviceId, diff --git a/src/service/uiaa/data.rs b/src/service/uiaa/data.rs index c64deb90..9a63fbb8 100644 --- a/src/service/uiaa/data.rs +++ b/src/service/uiaa/data.rs @@ -1,7 +1,7 @@ use crate::Result; use ruma::{api::client::uiaa::UiaaInfo, CanonicalJsonValue, DeviceId, UserId}; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { fn set_uiaa_request( &self, user_id: &UserId, diff --git a/src/service/users.rs b/src/service/users.rs index fb983a41..6520446d 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -5,7 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; -pub use data::Data; +pub(crate) use data::Data; use ruma::{ api::client::{ device::Device, @@ -25,27 +25,27 @@ use ruma::{ use crate::{services, Error, Result}; -pub struct SlidingSyncCache { +pub(crate) struct SlidingSyncCache { lists: BTreeMap, subscriptions: BTreeMap, known_rooms: BTreeMap>, // For every room, the roomsince number extensions: ExtensionsConfig, } -pub struct Service { - pub db: &'static dyn Data, +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub connections: + pub(crate) connections: Mutex>>>, } impl Service { /// Check if a user has an account on this homeserver. - pub fn exists(&self, user_id: &UserId) -> Result { + pub(crate) fn exists(&self, user_id: &UserId) -> Result { self.db.exists(user_id) } - pub fn forget_sync_request_connection( + pub(crate) fn forget_sync_request_connection( &self, user_id: OwnedUserId, device_id: OwnedDeviceId, @@ -57,7 +57,7 @@ impl Service { .remove(&(user_id, device_id, conn_id)); } - pub fn update_sync_request_with_cache( + pub(crate) fn update_sync_request_with_cache( &self, user_id: OwnedUserId, device_id: OwnedDeviceId, @@ -186,7 +186,7 @@ impl Service { cached.known_rooms.clone() } - pub fn update_sync_subscriptions( + pub(crate) fn update_sync_subscriptions( &self, user_id: OwnedUserId, device_id: OwnedDeviceId, @@ -212,7 +212,7 @@ impl Service { cached.subscriptions = subscriptions; } - pub fn update_sync_known_rooms( + pub(crate) fn update_sync_known_rooms( &self, user_id: OwnedUserId, device_id: OwnedDeviceId, @@ -254,12 +254,12 @@ impl Service { } /// Check if account is deactivated - pub fn is_deactivated(&self, user_id: &UserId) -> Result { + pub(crate) fn is_deactivated(&self, user_id: &UserId) -> Result { self.db.is_deactivated(user_id) } /// Check if a user is an admin - pub fn is_admin(&self, user_id: &UserId) -> Result { + pub(crate) fn is_admin(&self, user_id: &UserId) -> Result { let admin_room_alias_id = RoomAliasId::parse(format!("#admins:{}", services().globals.server_name())) .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias."))?; @@ -276,75 +276,83 @@ impl Service { } /// Create a new user account on this homeserver. - pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { + pub(crate) fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { self.db.set_password(user_id, password)?; Ok(()) } /// Returns the number of users registered on this server. - pub fn count(&self) -> Result { + pub(crate) fn count(&self) -> Result { self.db.count() } /// Find out which user an access token belongs to. - pub fn find_from_token(&self, token: &str) -> Result> { + pub(crate) fn find_from_token(&self, token: &str) -> Result> { self.db.find_from_token(token) } /// Returns an iterator over all users on this homeserver. - pub fn iter(&self) -> impl Iterator> + '_ { + pub(crate) fn iter(&self) -> impl Iterator> + '_ { self.db.iter() } /// Returns a list of local users as list of usernames. /// /// A user account is considered `local` if the length of it's password is greater then zero. - pub fn list_local_users(&self) -> Result> { + pub(crate) fn list_local_users(&self) -> Result> { self.db.list_local_users() } /// Returns the password hash for the given user. - pub fn password_hash(&self, user_id: &UserId) -> Result> { + pub(crate) fn password_hash(&self, user_id: &UserId) -> Result> { self.db.password_hash(user_id) } /// Hash and set the user's password to the Argon2 hash - pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { + pub(crate) fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { self.db.set_password(user_id, password) } /// Returns the displayname of a user on this homeserver. - pub fn displayname(&self, user_id: &UserId) -> Result> { + pub(crate) fn displayname(&self, user_id: &UserId) -> Result> { self.db.displayname(user_id) } /// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change. - pub fn set_displayname(&self, user_id: &UserId, displayname: Option) -> Result<()> { + pub(crate) fn set_displayname( + &self, + user_id: &UserId, + displayname: Option, + ) -> Result<()> { self.db.set_displayname(user_id, displayname) } /// Get the avatar_url of a user. - pub fn avatar_url(&self, user_id: &UserId) -> Result> { + pub(crate) fn avatar_url(&self, user_id: &UserId) -> Result> { self.db.avatar_url(user_id) } /// Sets a new avatar_url or removes it if avatar_url is None. - pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()> { + pub(crate) fn set_avatar_url( + &self, + user_id: &UserId, + avatar_url: Option, + ) -> Result<()> { self.db.set_avatar_url(user_id, avatar_url) } /// Get the blurhash of a user. - pub fn blurhash(&self, user_id: &UserId) -> Result> { + pub(crate) fn blurhash(&self, user_id: &UserId) -> Result> { self.db.blurhash(user_id) } /// Sets a new avatar_url or removes it if avatar_url is None. - pub fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { + pub(crate) fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { self.db.set_blurhash(user_id, blurhash) } /// Adds a new device to a user. - pub fn create_device( + pub(crate) fn create_device( &self, user_id: &UserId, device_id: &DeviceId, @@ -356,12 +364,12 @@ impl Service { } /// Removes a device from a user. - pub fn remove_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { + pub(crate) fn remove_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { self.db.remove_device(user_id, device_id) } /// Returns an iterator over all device ids of this user. - pub fn all_device_ids<'a>( + pub(crate) fn all_device_ids<'a>( &'a self, user_id: &UserId, ) -> impl Iterator> + 'a { @@ -369,11 +377,16 @@ impl Service { } /// Replaces the access token of one device. - pub fn set_token(&self, user_id: &UserId, device_id: &DeviceId, token: &str) -> Result<()> { + pub(crate) fn set_token( + &self, + user_id: &UserId, + device_id: &DeviceId, + token: &str, + ) -> Result<()> { self.db.set_token(user_id, device_id, token) } - pub fn add_one_time_key( + pub(crate) fn add_one_time_key( &self, user_id: &UserId, device_id: &DeviceId, @@ -384,11 +397,11 @@ impl Service { .add_one_time_key(user_id, device_id, one_time_key_key, one_time_key_value) } - pub fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { + pub(crate) fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { self.db.last_one_time_keys_update(user_id) } - pub fn take_one_time_key( + pub(crate) fn take_one_time_key( &self, user_id: &UserId, device_id: &DeviceId, @@ -397,7 +410,7 @@ impl Service { self.db.take_one_time_key(user_id, device_id, key_algorithm) } - pub fn count_one_time_keys( + pub(crate) fn count_one_time_keys( &self, user_id: &UserId, device_id: &DeviceId, @@ -405,7 +418,7 @@ impl Service { self.db.count_one_time_keys(user_id, device_id) } - pub fn add_device_keys( + pub(crate) fn add_device_keys( &self, user_id: &UserId, device_id: &DeviceId, @@ -414,7 +427,7 @@ impl Service { self.db.add_device_keys(user_id, device_id, device_keys) } - pub fn add_cross_signing_keys( + pub(crate) fn add_cross_signing_keys( &self, user_id: &UserId, master_key: &Raw, @@ -431,7 +444,7 @@ impl Service { ) } - pub fn sign_key( + pub(crate) fn sign_key( &self, target_id: &UserId, key_id: &str, @@ -441,7 +454,7 @@ impl Service { self.db.sign_key(target_id, key_id, signature, sender_id) } - pub fn keys_changed<'a>( + pub(crate) fn keys_changed<'a>( &'a self, user_or_room_id: &str, from: u64, @@ -450,11 +463,11 @@ impl Service { self.db.keys_changed(user_or_room_id, from, to) } - pub fn mark_device_key_update(&self, user_id: &UserId) -> Result<()> { + pub(crate) fn mark_device_key_update(&self, user_id: &UserId) -> Result<()> { self.db.mark_device_key_update(user_id) } - pub fn get_device_keys( + pub(crate) fn get_device_keys( &self, user_id: &UserId, device_id: &DeviceId, @@ -462,7 +475,7 @@ impl Service { self.db.get_device_keys(user_id, device_id) } - pub fn parse_master_key( + pub(crate) fn parse_master_key( &self, user_id: &UserId, master_key: &Raw, @@ -470,7 +483,7 @@ impl Service { self.db.parse_master_key(user_id, master_key) } - pub fn get_key( + pub(crate) fn get_key( &self, key: &[u8], sender_user: Option<&UserId>, @@ -481,7 +494,7 @@ impl Service { .get_key(key, sender_user, user_id, allowed_signatures) } - pub fn get_master_key( + pub(crate) fn get_master_key( &self, sender_user: Option<&UserId>, user_id: &UserId, @@ -491,7 +504,7 @@ impl Service { .get_master_key(sender_user, user_id, allowed_signatures) } - pub fn get_self_signing_key( + pub(crate) fn get_self_signing_key( &self, sender_user: Option<&UserId>, user_id: &UserId, @@ -501,11 +514,14 @@ impl Service { .get_self_signing_key(sender_user, user_id, allowed_signatures) } - pub fn get_user_signing_key(&self, user_id: &UserId) -> Result>> { + pub(crate) fn get_user_signing_key( + &self, + user_id: &UserId, + ) -> Result>> { self.db.get_user_signing_key(user_id) } - pub fn add_to_device_event( + pub(crate) fn add_to_device_event( &self, sender: &UserId, target_user_id: &UserId, @@ -522,7 +538,7 @@ impl Service { ) } - pub fn get_to_device_events( + pub(crate) fn get_to_device_events( &self, user_id: &UserId, device_id: &DeviceId, @@ -530,7 +546,7 @@ impl Service { self.db.get_to_device_events(user_id, device_id) } - pub fn remove_to_device_events( + pub(crate) fn remove_to_device_events( &self, user_id: &UserId, device_id: &DeviceId, @@ -539,7 +555,7 @@ impl Service { self.db.remove_to_device_events(user_id, device_id, until) } - pub fn update_device_metadata( + pub(crate) fn update_device_metadata( &self, user_id: &UserId, device_id: &DeviceId, @@ -549,7 +565,7 @@ impl Service { } /// Get device metadata. - pub fn get_device_metadata( + pub(crate) fn get_device_metadata( &self, user_id: &UserId, device_id: &DeviceId, @@ -557,11 +573,11 @@ impl Service { self.db.get_device_metadata(user_id, device_id) } - pub fn get_devicelist_version(&self, user_id: &UserId) -> Result> { + pub(crate) fn get_devicelist_version(&self, user_id: &UserId) -> Result> { self.db.get_devicelist_version(user_id) } - pub fn all_devices_metadata<'a>( + pub(crate) fn all_devices_metadata<'a>( &'a self, user_id: &UserId, ) -> impl Iterator> + 'a { @@ -569,7 +585,7 @@ impl Service { } /// Deactivate account - pub fn deactivate_account(&self, user_id: &UserId) -> Result<()> { + pub(crate) fn deactivate_account(&self, user_id: &UserId) -> Result<()> { // Remove all associated devices for device_id in self.all_device_ids(user_id) { self.remove_device(user_id, &device_id?)?; @@ -585,11 +601,15 @@ impl Service { } /// Creates a new sync filter. Returns the filter id. - pub fn create_filter(&self, user_id: &UserId, filter: &FilterDefinition) -> Result { + pub(crate) fn create_filter( + &self, + user_id: &UserId, + filter: &FilterDefinition, + ) -> Result { self.db.create_filter(user_id, filter) } - pub fn get_filter( + pub(crate) fn get_filter( &self, user_id: &UserId, filter_id: &str, @@ -599,7 +619,7 @@ impl Service { } /// Ensure that a user only sees signatures from themselves and the target user -pub fn clean_signatures bool>( +pub(crate) fn clean_signatures bool>( cross_signing_key: &mut serde_json::Value, sender_user: Option<&UserId>, user_id: &UserId, diff --git a/src/service/users/data.rs b/src/service/users/data.rs index ddf941e3..7a9b2610 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -9,7 +9,7 @@ use ruma::{ }; use std::collections::BTreeMap; -pub trait Data: Send + Sync { +pub(crate) trait Data: Send + Sync { /// Check if a user has an account on this homeserver. fn exists(&self, user_id: &UserId) -> Result; diff --git a/src/utils.rs b/src/utils.rs index 0ac81b62..a38a3fc6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -pub mod error; +pub(crate) mod error; use argon2::{Config, Variant}; use cmp::Ordering; @@ -11,7 +11,7 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -pub fn millis_since_unix_epoch() -> u64 { +pub(crate) fn millis_since_unix_epoch() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .expect("time is valid") @@ -19,7 +19,7 @@ pub fn millis_since_unix_epoch() -> u64 { } #[cfg(any(feature = "rocksdb", feature = "sqlite"))] -pub fn increment(old: Option<&[u8]>) -> Option> { +pub(crate) fn increment(old: Option<&[u8]>) -> Option> { let number = match old.map(|bytes| bytes.try_into()) { Some(Ok(bytes)) => { let number = u64::from_be_bytes(bytes); @@ -31,7 +31,7 @@ pub fn increment(old: Option<&[u8]>) -> Option> { Some(number.to_be_bytes().to_vec()) } -pub fn generate_keypair() -> Vec { +pub(crate) fn generate_keypair() -> Vec { let mut value = random_string(8).as_bytes().to_vec(); value.push(0xff); value.extend_from_slice( @@ -42,17 +42,17 @@ pub fn generate_keypair() -> Vec { } /// Parses the bytes into an u64. -pub fn u64_from_bytes(bytes: &[u8]) -> Result { +pub(crate) fn u64_from_bytes(bytes: &[u8]) -> Result { let array: [u8; 8] = bytes.try_into()?; Ok(u64::from_be_bytes(array)) } /// Parses the bytes into a string. -pub fn string_from_bytes(bytes: &[u8]) -> Result { +pub(crate) fn string_from_bytes(bytes: &[u8]) -> Result { String::from_utf8(bytes.to_vec()) } -pub fn random_string(length: usize) -> String { +pub(crate) fn random_string(length: usize) -> String { thread_rng() .sample_iter(&rand::distributions::Alphanumeric) .take(length) @@ -61,7 +61,7 @@ pub fn random_string(length: usize) -> String { } /// Calculate a new hash for the given password -pub fn calculate_password_hash(password: &str) -> Result { +pub(crate) fn calculate_password_hash(password: &str) -> Result { let hashing_config = Config { variant: Variant::Argon2id, ..Default::default() @@ -72,14 +72,14 @@ pub fn calculate_password_hash(password: &str) -> Result } #[tracing::instrument(skip(keys))] -pub fn calculate_hash(keys: &[&[u8]]) -> Vec { +pub(crate) fn calculate_hash(keys: &[&[u8]]) -> Vec { // We only hash the pdu's event ids, not the whole pdu let bytes = keys.join(&0xff); let hash = digest::digest(&digest::SHA256, &bytes); hash.as_ref().to_owned() } -pub fn common_elements( +pub(crate) fn common_elements( mut iterators: impl Iterator>>, check_order: impl Fn(&[u8], &[u8]) -> Ordering, ) -> Option>> { @@ -106,7 +106,7 @@ pub fn common_elements( /// Fallible conversion from any value that implements `Serialize` to a `CanonicalJsonObject`. /// /// `value` must serialize to an `serde_json::Value::Object`. -pub fn to_canonical_object( +pub(crate) fn to_canonical_object( value: T, ) -> Result { use serde::ser::Error; @@ -119,7 +119,7 @@ pub fn to_canonical_object( } } -pub fn deserialize_from_str< +pub(crate) fn deserialize_from_str< 'de, D: serde::de::Deserializer<'de>, T: FromStr, @@ -150,7 +150,7 @@ pub fn deserialize_from_str< /// Wrapper struct which will emit the HTML-escaped version of the contained /// string when passed to a format string. -pub struct HtmlEscape<'a>(pub &'a str); +pub(crate) struct HtmlEscape<'a>(pub(crate) &'a str); impl<'a> fmt::Display for HtmlEscape<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/utils/error.rs b/src/utils/error.rs index 6fa7f7ec..f9c5b4ea 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -13,10 +13,10 @@ use tracing::{error, info}; use crate::RumaResponse; -pub type Result = std::result::Result; +pub(crate) type Result = std::result::Result; #[derive(Error, Debug)] -pub enum Error { +pub(crate) enum Error { #[cfg(feature = "sqlite")] #[error("There was a problem with the connection to the sqlite database: {source}")] SqliteError { @@ -77,19 +77,19 @@ pub enum Error { } impl Error { - pub fn bad_database(message: &'static str) -> Self { + pub(crate) fn bad_database(message: &'static str) -> Self { error!("BadDatabase: {}", message); Self::BadDatabase(message) } - pub fn bad_config(message: &'static str) -> Self { + pub(crate) fn bad_config(message: &'static str) -> Self { error!("BadConfig: {}", message); Self::BadConfig(message) } } impl Error { - pub fn to_response(&self) -> RumaResponse { + pub(crate) fn to_response(&self) -> RumaResponse { if let Self::Uiaa(uiaainfo) = self { return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone())); } @@ -136,7 +136,7 @@ impl Error { } /// Sanitizes public-facing errors that can leak sensitive information. - pub fn sanitized_error(&self) -> String { + pub(crate) fn sanitized_error(&self) -> String { let db_error = String::from("Database or I/O error occurred."); match self { From 2ff08c9fc4356ab438098a2ce4fc164d796667e8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 22:32:36 -0700 Subject: [PATCH 022/617] enable `dead_code` lint And delete all the dead code. And add some cfgs for feature-specific items. --- Cargo.toml | 3 --- src/api/client_server/unversioned.rs | 20 ++------------------ src/config.rs | 3 ++- src/service/appservice.rs | 10 +--------- src/service/globals.rs | 4 ---- src/service/rooms/outlier.rs | 7 +------ src/service/rooms/timeline.rs | 7 ------- src/service/users.rs | 4 ---- 8 files changed, 6 insertions(+), 52 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b185ccc2..624c9157 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,6 @@ unused_import_braces = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" -# TODO: Remove these -dead_code = "allow" - # Keep alphabetically sorted [workspace.lints.clippy] assertions_on_result_states = "warn" diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 81899b68..27840af4 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -1,9 +1,8 @@ use std::{collections::BTreeMap, iter::FromIterator}; -use axum::{response::IntoResponse, Json}; -use ruma::api::client::{discovery::get_supported_versions, error::ErrorKind}; +use ruma::api::client::discovery::get_supported_versions; -use crate::{services, Error, Result, Ruma}; +use crate::{Result, Ruma}; /// # `GET /_matrix/client/versions` /// @@ -33,18 +32,3 @@ pub(crate) async fn get_supported_versions_route( Ok(resp) } - -/// # `GET /.well-known/matrix/client` -pub(crate) 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/config.rs b/src/config.rs index 449e91e4..f10819b5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,6 +27,7 @@ pub(crate) struct Config { pub(crate) db_cache_capacity_mb: f64, #[serde(default = "default_cache_capacity_modifier")] pub(crate) cache_capacity_modifier: f64, + #[cfg(feature = "rocksdb")] #[serde(default = "default_rocksdb_max_open_files")] pub(crate) rocksdb_max_open_files: i32, #[serde(default = "default_pdu_cache_capacity")] @@ -52,7 +53,6 @@ pub(crate) struct Config { pub(crate) allow_unstable_room_versions: bool, #[serde(default = "default_default_room_version")] pub(crate) default_room_version: RoomVersionId, - pub(crate) well_known_client: Option, #[serde(default = "false_fn")] pub(crate) allow_jaeger: bool, #[serde(default = "false_fn")] @@ -222,6 +222,7 @@ fn default_cache_capacity_modifier() -> f64 { 1.0 } +#[cfg(feature = "rocksdb")] fn default_rocksdb_max_open_files() -> i32 { 1000 } diff --git a/src/service/appservice.rs b/src/service/appservice.rs index 1d5346aa..ebfc82be 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -8,7 +8,7 @@ use futures_util::Future; use regex::RegexSet; use ruma::{ api::appservice::{Namespace, Registration}, - RoomAliasId, RoomId, UserId, + RoomAliasId, UserId, }; use tokio::sync::RwLock; @@ -207,14 +207,6 @@ impl Service { .any(|info| info.aliases.is_exclusive_match(alias.as_str())) } - // Checks if a given room id matches any exclusive appservice regex - pub(crate) async fn is_exclusive_room_id(&self, room_id: &RoomId) -> bool { - self.read() - .await - .values() - .any(|info| info.rooms.is_exclusive_match(room_id.as_str())) - } - pub(crate) fn read( &self, ) -> impl Future>> diff --git a/src/service/globals.rs b/src/service/globals.rs index d88dda3d..a55cdda5 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -399,10 +399,6 @@ impl Service { r } - pub(crate) fn well_known_client(&self) -> &Option { - &self.config.well_known_client - } - pub(crate) fn shutdown(&self) { self.shutdown.store(true, atomic::Ordering::Relaxed); // On shutdown diff --git a/src/service/rooms/outlier.rs b/src/service/rooms/outlier.rs index 64665fa5..c0e9dc28 100644 --- a/src/service/rooms/outlier.rs +++ b/src/service/rooms/outlier.rs @@ -3,7 +3,7 @@ mod data; pub(crate) use data::Data; use ruma::{CanonicalJsonObject, EventId}; -use crate::{PduEvent, Result}; +use crate::Result; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -18,11 +18,6 @@ impl Service { self.db.get_outlier_pdu_json(event_id) } - /// Returns the pdu from the outlier tree. - pub(crate) fn get_pdu_outlier(&self, event_id: &EventId) -> Result> { - self.db.get_outlier_pdu(event_id) - } - /// Append the PDU as an outlier. #[tracing::instrument(skip(self, pdu))] pub(crate) fn add_pdu_outlier( diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index e8a815b7..74f48935 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -159,13 +159,6 @@ impl Service { self.db.get_pdu_id(event_id) } - /// Returns the pdu. - /// - /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. - pub(crate) fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result> { - self.db.get_non_outlier_pdu(event_id) - } - /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. diff --git a/src/service/users.rs b/src/service/users.rs index 6520446d..282663ea 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -397,10 +397,6 @@ impl Service { .add_one_time_key(user_id, device_id, one_time_key_key, one_time_key_value) } - pub(crate) fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { - self.db.last_one_time_keys_update(user_id) - } - pub(crate) fn take_one_time_key( &self, user_id: &UserId, From bc2f7c6826ee25d4f728471e55747972143a8ff5 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 22:36:15 -0700 Subject: [PATCH 023/617] enable `enum_variant_names` lint --- Cargo.toml | 3 --- src/api/server_server.rs | 2 +- src/service/pdu.rs | 2 +- src/utils/error.rs | 28 ++++++++++++++-------------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 624c9157..c4b3d64d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,9 +48,6 @@ unseparated_literal_suffix = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" -# TODO: Remove these -enum_variant_names = "allow" - [package] name = "grapevine" description = "A Matrix homeserver written in Rust" diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 32a9b7f4..cc1ebbd3 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -306,7 +306,7 @@ where }) } else { debug!("Returning error from {destination}"); - Err(Error::FederationError( + Err(Error::Federation( destination.to_owned(), RumaError::from_http_response(http_response), )) diff --git a/src/service/pdu.rs b/src/service/pdu.rs index d535fc3f..ec192c78 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -61,7 +61,7 @@ impl PduEvent { let mut content = serde_json::from_str(self.content.get()) .map_err(|_| Error::bad_database("PDU in db has invalid content."))?; redact_content_in_place(&mut content, &room_version_id, self.kind.to_string()) - .map_err(|e| Error::RedactionError(self.sender.server_name().to_owned(), e))?; + .map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?; self.unsigned = Some(to_raw_value(&json!({ "redacted_because": serde_json::to_value(reason).expect("to_value(PduEvent) always works") diff --git a/src/utils/error.rs b/src/utils/error.rs index f9c5b4ea..d05a9a28 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -19,35 +19,35 @@ pub(crate) type Result = std::result::Result; pub(crate) enum Error { #[cfg(feature = "sqlite")] #[error("There was a problem with the connection to the sqlite database: {source}")] - SqliteError { + Sqlite { #[from] source: rusqlite::Error, }, #[cfg(feature = "rocksdb")] #[error("There was a problem with the connection to the rocksdb database: {source}")] - RocksDbError { + RocksDb { #[from] source: rocksdb::Error, }, #[error("Could not generate an image.")] - ImageError { + Image { #[from] source: image::error::ImageError, }, #[error("Could not connect to server: {source}")] - ReqwestError { + Reqwest { #[from] source: reqwest::Error, }, #[error("Could build regular expression: {source}")] - RegexError { + Regex { #[from] source: regex::Error, }, #[error("{0}")] - FederationError(OwnedServerName, RumaError), + Federation(OwnedServerName, RumaError), #[error("Could not do this io: {source}")] - IoError { + Io { #[from] source: std::io::Error, }, @@ -65,13 +65,13 @@ pub(crate) enum Error { #[error("{0}")] Conflict(&'static str), // This is only needed for when a room alias already exists #[error("{0}")] - ExtensionError(#[from] axum::extract::rejection::ExtensionRejection), + Extension(#[from] axum::extract::rejection::ExtensionRejection), #[error("{0}")] - PathError(#[from] axum::extract::rejection::PathRejection), + Path(#[from] axum::extract::rejection::PathRejection), #[error("{0}")] AdminCommand(&'static str), #[error("from {0}: {1}")] - RedactionError(OwnedServerName, ruma::canonical_json::RedactionError), + Redaction(OwnedServerName, ruma::canonical_json::RedactionError), #[error("{0} in {1}")] InconsistentRoomState(&'static str, ruma::OwnedRoomId), } @@ -94,7 +94,7 @@ impl Error { return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone())); } - if let Self::FederationError(origin, error) = self { + if let Self::Federation(origin, error) = self { let mut error = error.clone(); error.body = ErrorBody::Standard { kind: Unknown, @@ -141,10 +141,10 @@ impl Error { match self { #[cfg(feature = "sqlite")] - Self::SqliteError { .. } => db_error, + Self::Sqlite { .. } => db_error, #[cfg(feature = "rocksdb")] - Self::RocksDbError { .. } => db_error, - Self::IoError { .. } => db_error, + Self::RocksDb { .. } => db_error, + Self::Io { .. } => db_error, Self::BadConfig { .. } => db_error, Self::BadDatabase { .. } => db_error, _ => self.to_string(), From 5d5e28770b8ad906cd5120f0aef8c72429341972 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 14:49:24 -0700 Subject: [PATCH 024/617] enable `single_use_lifetimes` lint --- Cargo.toml | 1 + src/database/key_value/rooms/state_cache.rs | 2 +- src/service/rooms/state_cache.rs | 6 +----- src/utils.rs | 6 ++---- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c4b3d64d..37d4d7ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ macro_use_extern_crate = "warn" missing_abi = "warn" noop_method_call = "warn" pointer_structural_match = "warn" +single_use_lifetimes = "warn" unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_extern_crates = "warn" diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 49e3842b..6cb7a8c6 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -264,7 +264,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { } #[tracing::instrument(skip(self))] - fn server_in_room<'a>(&'a self, server: &ServerName, room_id: &RoomId) -> Result { + fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result { let mut key = server.as_bytes().to_vec(); key.push(0xff); key.extend_from_slice(room_id.as_bytes()); diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 8b844f1e..78aba47b 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -226,11 +226,7 @@ impl Service { } #[tracing::instrument(skip(self))] - pub(crate) fn server_in_room<'a>( - &'a self, - server: &ServerName, - room_id: &RoomId, - ) -> Result { + pub(crate) fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result { self.db.server_in_room(server, room_id) } diff --git a/src/utils.rs b/src/utils.rs index a38a3fc6..b679e6b6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -128,9 +128,7 @@ pub(crate) fn deserialize_from_str< deserializer: D, ) -> Result { struct Visitor, E>(std::marker::PhantomData); - impl<'de, T: FromStr, Err: std::fmt::Display> serde::de::Visitor<'de> - for Visitor - { + impl, Err: std::fmt::Display> serde::de::Visitor<'_> for Visitor { type Value = T; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "a parsable string") @@ -152,7 +150,7 @@ pub(crate) fn deserialize_from_str< /// string when passed to a format string. pub(crate) struct HtmlEscape<'a>(pub(crate) &'a str); -impl<'a> fmt::Display for HtmlEscape<'a> { +impl fmt::Display for HtmlEscape<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { // Because the internet is always right, turns out there's not that many // characters to escape: http://stackoverflow.com/questions/7381974 From a78bf8f50be7440919c2ea6f196d74e807d60bdc Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 14:54:48 -0700 Subject: [PATCH 025/617] enable `unused_lifetimes` lint --- Cargo.toml | 1 + src/api/client_server/membership.rs | 2 +- src/database/abstraction/rocksdb.rs | 4 ++-- src/database/abstraction/sqlite.rs | 4 ++-- src/database/key_value/rooms/search.rs | 2 +- src/database/key_value/rooms/state.rs | 2 +- src/service/rooms/search.rs | 2 +- src/service/rooms/timeline.rs | 4 ++-- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 37d4d7ff..e25bc864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_extern_crates = "warn" unused_import_braces = "warn" +unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index bc98c00b..dbaa69a2 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1206,7 +1206,7 @@ async fn validate_and_add_event_id( Ok((event_id, value)) } -pub(crate) async fn invite_helper<'a>( +pub(crate) async fn invite_helper( sender_user: &UserId, user_id: &UserId, room_id: &RoomId, diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 78e76524..9fd24e1e 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -156,7 +156,7 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(()) } - fn insert_batch<'a>(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { + fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { let writeoptions = rocksdb::WriteOptions::default(); for (key, value) in iter { self.db @@ -230,7 +230,7 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(new) } - fn increment_batch<'a>(&self, iter: &mut dyn Iterator>) -> Result<()> { + fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()> { let readoptions = rocksdb::ReadOptions::default(); let writeoptions = rocksdb::WriteOptions::default(); diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index a5bfe540..8e7d3f56 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -197,7 +197,7 @@ impl KvTree for SqliteTable { Ok(()) } - fn insert_batch<'a>(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { + fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { let guard = self.engine.write_lock(); guard.execute("BEGIN", [])?; @@ -211,7 +211,7 @@ impl KvTree for SqliteTable { Ok(()) } - fn increment_batch<'a>(&self, iter: &mut dyn Iterator>) -> Result<()> { + fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()> { let guard = self.engine.write_lock(); guard.execute("BEGIN", [])?; diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index ad573f06..652c508c 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -3,7 +3,7 @@ use ruma::RoomId; use crate::{database::KeyValueDatabase, service, services, utils, Result}; impl service::rooms::search::Data for KeyValueDatabase { - fn index_pdu<'a>(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { + fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { let mut batch = message_body .split_terminator(|c: char| !c.is_alphanumeric()) .filter(|s| !s.is_empty()) diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index f17d37bb..6865d7b9 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -49,7 +49,7 @@ impl service::rooms::state::Data for KeyValueDatabase { .collect() } - fn set_forward_extremities<'a>( + fn set_forward_extremities( &self, room_id: &RoomId, event_ids: Vec, diff --git a/src/service/rooms/search.rs b/src/service/rooms/search.rs index 48dacca2..8f2ae8aa 100644 --- a/src/service/rooms/search.rs +++ b/src/service/rooms/search.rs @@ -11,7 +11,7 @@ pub(crate) struct Service { impl Service { #[tracing::instrument(skip(self))] - pub(crate) fn index_pdu<'a>( + pub(crate) fn index_pdu( &self, shortroomid: u64, pdu_id: &[u8], diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 74f48935..307b5f97 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -199,7 +199,7 @@ impl Service { /// /// Returns pdu id #[tracing::instrument(skip(self, pdu, pdu_json, leaves))] - pub(crate) async fn append_pdu<'a>( + pub(crate) async fn append_pdu( &self, pdu: &PduEvent, mut pdu_json: CanonicalJsonObject, @@ -1007,7 +1007,7 @@ impl Service { /// Append the incoming event setting the state snapshot to the state from the /// server that sent the event. #[tracing::instrument(skip_all)] - pub(crate) async fn append_incoming_pdu<'a>( + pub(crate) async fn append_incoming_pdu( &self, pdu: &PduEvent, pdu_json: CanonicalJsonObject, From 71c48f66c4922813c2dc30b7b875200e06ce4b75 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 15:32:23 -0700 Subject: [PATCH 026/617] enable `as_conversions` lint There were some very, uh, creative (and inconsistent) ways to convert between numeric types in here... --- Cargo.toml | 1 + src/api/client_server/backup.rs | 55 +++++++++++++++---------- src/api/client_server/context.rs | 8 ++-- src/api/client_server/directory.rs | 16 +++---- src/api/client_server/message.rs | 7 +++- src/api/client_server/relations.rs | 30 ++++++++------ src/api/client_server/search.rs | 24 +++++++---- src/api/client_server/space.rs | 14 +++++-- src/api/client_server/sync.rs | 36 +++++++++------- src/api/client_server/typing.rs | 3 +- src/api/client_server/user_directory.rs | 2 +- src/api/ruma_wrapper/axum.rs | 4 +- src/api/server_server.rs | 2 +- src/database.rs | 27 +++++++++++- src/database/abstraction/rocksdb.rs | 8 +++- src/database/abstraction/sqlite.rs | 11 +++-- src/service.rs | 15 +++++++ src/service/media.rs | 16 +++---- src/service/rooms/auth_chain.rs | 3 ++ src/service/sending.rs | 2 +- src/utils.rs | 2 + 21 files changed, 195 insertions(+), 91 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e25bc864..26ffd0d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ unused_qualifications = "warn" # Keep alphabetically sorted [workspace.lints.clippy] +as_conversions = "warn" assertions_on_result_states = "warn" cloned_instead_of_copied = "warn" dbg_macro = "warn" diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index 0f9c8082..709ed73a 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -56,7 +56,11 @@ pub(crate) async fn get_latest_backup_info_route( Ok(get_latest_backup_info::v3::Response { algorithm, - count: (services().key_backups.count_keys(sender_user, &version)? as u32).into(), + count: services() + .key_backups + .count_keys(sender_user, &version)? + .try_into() + .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &version)?, version, }) @@ -79,10 +83,11 @@ pub(crate) async fn get_backup_info_route( Ok(get_backup_info::v3::Response { algorithm, - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, @@ -144,10 +149,11 @@ pub(crate) async fn add_backup_keys_route( } Ok(add_backup_keys::v3::Response { - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, @@ -189,10 +195,11 @@ pub(crate) async fn add_backup_keys_for_room_route( } Ok(add_backup_keys_for_room::v3::Response { - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, @@ -232,10 +239,11 @@ pub(crate) async fn add_backup_keys_for_session_route( )?; Ok(add_backup_keys_for_session::v3::Response { - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, @@ -302,10 +310,11 @@ pub(crate) async fn delete_backup_keys_route( .delete_all_keys(sender_user, &body.version)?; Ok(delete_backup_keys::v3::Response { - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, @@ -325,10 +334,11 @@ pub(crate) async fn delete_backup_keys_for_room_route( .delete_room_keys(sender_user, &body.version, &body.room_id)?; Ok(delete_backup_keys_for_room::v3::Response { - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, @@ -351,10 +361,11 @@ pub(crate) async fn delete_backup_keys_for_session_route( )?; Ok(delete_backup_keys_for_session::v3::Response { - count: (services() + count: services() .key_backups - .count_keys(sender_user, &body.version)? as u32) - .into(), + .count_keys(sender_user, &body.version)? + .try_into() + .expect("count should fit in UInt"), etag: services() .key_backups .get_etag(sender_user, &body.version)?, diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 205ecc3a..db83fc8f 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -2,6 +2,7 @@ use crate::{services, Error, Result, Ruma}; use ruma::{ api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions}, events::StateEventType, + uint, }; use std::collections::HashSet; use tracing::error; @@ -70,7 +71,8 @@ pub(crate) async fn get_context_route( } // Use limit with maximum 100 - let limit = u64::from(body.limit).min(100) as usize; + let half_limit = + usize::try_from(body.limit.min(uint!(100)) / uint!(2)).expect("0-50 should fit in usize"); let base_event = base_event.to_room_event(); @@ -78,7 +80,7 @@ pub(crate) async fn get_context_route( .rooms .timeline .pdus_until(sender_user, &room_id, base_token)? - .take(limit / 2) + .take(half_limit) .filter_map(|r| r.ok()) // Remove buggy events .filter(|(_, pdu)| { services() @@ -115,7 +117,7 @@ pub(crate) async fn get_context_route( .rooms .timeline .pdus_after(sender_user, &room_id, base_token)? - .take(limit / 2) + .take(half_limit) .filter_map(|r| r.ok()) // Remove buggy events .filter(|(_, pdu)| { services() diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index feba5ff3..4d926442 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -24,7 +24,7 @@ use ruma::{ }, StateEventType, }, - ServerName, UInt, + uint, ServerName, UInt, }; use tracing::{error, info, warn}; @@ -157,8 +157,8 @@ pub(crate) async fn get_public_rooms_filtered_helper( }); } - let limit = limit.map_or(10, u64::from); - let mut num_since = 0_u64; + let limit = limit.unwrap_or(uint!(10)); + let mut num_since = UInt::MIN; if let Some(s) = &since { let mut characters = s.chars(); @@ -340,21 +340,21 @@ pub(crate) async fn get_public_rooms_filtered_helper( all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members)); - let total_room_count_estimate = (all_rooms.len() as u32).into(); + let total_room_count_estimate = all_rooms.len().try_into().unwrap_or(UInt::MAX); let chunk: Vec<_> = all_rooms .into_iter() - .skip(num_since as usize) - .take(limit as usize) + .skip(num_since.try_into().expect("UInt should fit in usize")) + .take(limit.try_into().expect("UInt should fit in usize")) .collect(); - let prev_batch = if num_since == 0 { + let prev_batch = if num_since == uint!(0) { None } else { Some(format!("p{num_since}")) }; - let next_batch = if chunk.len() < limit as usize { + let next_batch = if chunk.len() < limit.try_into().expect("UInt should fit in usize") { None } else { Some(format!("n{}", num_since + limit)) diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 36890cab..77a1d81d 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -8,6 +8,7 @@ use ruma::{ message::{get_message_events, send_message_event}, }, events::{StateEventType, TimelineEventType}, + uint, }; use std::{ collections::{BTreeMap, HashSet}, @@ -136,7 +137,11 @@ pub(crate) async fn get_message_events_route( .lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from) .await?; - let limit = u64::from(body.limit).min(100) as usize; + let limit = body + .limit + .min(uint!(100)) + .try_into() + .expect("0-100 should fit in usize"); let next_token; diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index abf3ea70..2bdad29c 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -1,6 +1,9 @@ -use ruma::api::client::relations::{ - get_relating_events, get_relating_events_with_rel_type, - get_relating_events_with_rel_type_and_event_type, +use ruma::{ + api::client::relations::{ + get_relating_events, get_relating_events_with_rel_type, + get_relating_events_with_rel_type_and_event_type, + }, + uint, }; use crate::{service::rooms::timeline::PduCount, services, Result, Ruma}; @@ -28,9 +31,10 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( // Use limit or else 10, with maximum 100 let limit = body .limit - .and_then(|u| u32::try_from(u).ok()) - .map_or(10_usize, |u| u as usize) - .min(100); + .map(|x| x.min(uint!(100))) + .unwrap_or(uint!(10)) + .try_into() + .expect("0-100 should fit in usize"); let res = services() .rooms @@ -78,9 +82,10 @@ pub(crate) async fn get_relating_events_with_rel_type_route( // Use limit or else 10, with maximum 100 let limit = body .limit - .and_then(|u| u32::try_from(u).ok()) - .map_or(10_usize, |u| u as usize) - .min(100); + .map(|x| x.min(uint!(100))) + .unwrap_or(uint!(10)) + .try_into() + .expect("0-100 should fit in usize"); let res = services() .rooms @@ -126,9 +131,10 @@ pub(crate) async fn get_relating_events_route( // Use limit or else 10, with maximum 100 let limit = body .limit - .and_then(|u| u32::try_from(u).ok()) - .map_or(10_usize, |u| u as usize) - .min(100); + .map(|x| x.min(uint!(100))) + .unwrap_or(uint!(10)) + .try_into() + .expect("0-100 should fit in usize"); services() .rooms diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 1fb70253..eb6902b9 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -1,10 +1,13 @@ use crate::{services, Error, Result, Ruma}; -use ruma::api::client::{ - error::ErrorKind, - search::search_events::{ - self, - v3::{EventContextResult, ResultCategories, ResultRoomEvents, SearchResult}, +use ruma::{ + api::client::{ + error::ErrorKind, + search::search_events::{ + self, + v3::{EventContextResult, ResultCategories, ResultRoomEvents, SearchResult}, + }, }, + uint, }; use std::collections::BTreeMap; @@ -32,7 +35,12 @@ pub(crate) async fn search_events_route( }); // Use limit or else 10, with maximum 100 - let limit = filter.limit.map_or(10, u64::from).min(100) as usize; + let limit = filter + .limit + .map(|x| x.min(uint!(100))) + .unwrap_or(uint!(10)) + .try_into() + .expect("0-100 should fit in usize"); let mut searches = Vec::new(); @@ -123,8 +131,8 @@ pub(crate) async fn search_events_route( Ok(search_events::v3::Response::new(ResultCategories { room_events: ResultRoomEvents { - count: Some((results.len() as u32).into()), // TODO: set this to none. Element shouldn't depend on it - groups: BTreeMap::new(), // TODO + count: None, + groups: BTreeMap::new(), // TODO next_batch, results, state: BTreeMap::new(), // TODO diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index ffbd5b8d..a05d5c36 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,5 +1,5 @@ use crate::{services, Result, Ruma}; -use ruma::api::client::space::get_hierarchy; +use ruma::{api::client::space::get_hierarchy, uint}; /// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`` /// @@ -15,9 +15,17 @@ pub(crate) async fn get_hierarchy_route( .and_then(|s| s.parse::().ok()) .unwrap_or(0); - let limit = body.limit.map_or(10, u64::from).min(100) as usize; + let limit = body + .limit + .map(|x| x.min(uint!(100))) + .unwrap_or(uint!(10)) + .try_into() + .expect("0-100 should fit in usize"); - let max_depth = body.max_depth.map_or(3, u64::from).min(10) as usize + 1; // +1 to skip the space room itself + let max_depth = usize::try_from(body.max_depth.map(|x| x.min(uint!(10))).unwrap_or(uint!(3))) + .expect("0-10 should fit in usize") + // Skip the space room itself + + 1; services() .rooms diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index e10563f4..60f54e4f 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1087,8 +1087,8 @@ async fn load_joined_room( }, summary: RoomSummary { heroes, - joined_member_count: joined_member_count.map(|n| (n as u32).into()), - invited_member_count: invited_member_count.map(|n| (n as u32).into()), + joined_member_count: joined_member_count.map(UInt::new_saturating), + invited_member_count: invited_member_count.map(UInt::new_saturating), }, unread_notifications: UnreadNotificationsCount { highlight_count, @@ -1140,7 +1140,7 @@ fn load_timeline( // Take the last events for the timeline timeline_pdus = non_timeline_pdus .by_ref() - .take(limit as usize) + .take(limit.try_into().expect("limit should fit in usize")) .collect::>() .into_iter() .rev() @@ -1427,12 +1427,16 @@ pub(crate) async fn sync_events_v4_route( .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)] + r.0 = r.0.clamp( + uint!(0), + UInt::try_from(all_joined_rooms.len() - 1).unwrap_or(UInt::MAX), + ); + r.1 = r.1.clamp( + r.0, + UInt::try_from(all_joined_rooms.len() - 1).unwrap_or(UInt::MAX), + ); + let room_ids = all_joined_rooms[r.0.try_into().unwrap_or(usize::MAX) + ..=r.1.try_into().unwrap_or(usize::MAX)] .to_vec(); new_known_rooms.extend(room_ids.iter().cloned()); for room_id in &room_ids { @@ -1468,7 +1472,7 @@ pub(crate) async fn sync_events_v4_route( } }) .collect(), - count: UInt::from(all_joined_rooms.len() as u32), + count: UInt::try_from(all_joined_rooms.len()).unwrap_or(UInt::MAX), }, ); @@ -1663,20 +1667,20 @@ pub(crate) async fn sync_events_v4_route( prev_batch, limited, joined_count: Some( - (services() + services() .rooms .state_cache .room_joined_count(room_id)? - .unwrap_or(0) as u32) - .into(), + .map(UInt::new_saturating) + .unwrap_or(uint!(0)), ), invited_count: Some( - (services() + services() .rooms .state_cache .room_invited_count(room_id)? - .unwrap_or(0) as u32) - .into(), + .map(UInt::new_saturating) + .unwrap_or(uint!(0)), ), num_live: None, // Count events in timeline greater than global sync counter timestamp: None, diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index 7a3dd5c9..d9a24530 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -30,7 +30,8 @@ pub(crate) async fn create_typing_event_route( .typing_add( sender_user, &body.room_id, - duration.as_millis() as u64 + utils::millis_since_unix_epoch(), + duration.as_millis().try_into().unwrap_or(u64::MAX) + + utils::millis_since_unix_epoch(), ) .await?; } else { diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 30f7686e..fe8ad179 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -17,7 +17,7 @@ pub(crate) async fn search_users_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let limit = u64::from(body.limit) as usize; + let limit = body.limit.try_into().unwrap_or(usize::MAX); let mut users = services().users.iter().filter_map(|user_id| { // Filter out buggy users (they should not exist, but you never know...) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 7f5c9b8a..c1ebaa7a 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -437,7 +437,9 @@ where }; // With more than 1 buf, we gotta flatten into a Vec first. - let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize; + let cap = first.remaining() + + second.remaining() + + body.size_hint().lower().try_into().unwrap_or(usize::MAX); let mut vec = Vec::with_capacity(cap); vec.put(first); vec.put(second); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index cc1ebbd3..5bf8101b 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1100,7 +1100,7 @@ pub(crate) async fn get_missing_events_route( let mut events = Vec::new(); let mut i = 0; - while i < queued_events.len() && events.len() < u64::from(body.limit) as usize { + while i < queued_events.len() && events.len() < body.limit.try_into().unwrap_or(usize::MAX) { if let Some(pdu) = services().rooms.timeline.get_pdu_json(&queued_events[i])? { let room_id_str = pdu .get("room_id") diff --git a/src/database.rs b/src/database.rs index 0de879d4..8e684079 100644 --- a/src/database.rs +++ b/src/database.rs @@ -345,18 +345,43 @@ impl KeyValueDatabase { .try_into() .expect("pdu cache capacity fits into usize"), )), + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] auth_chain_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] shorteventid_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] eventidshort_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] shortstatekey_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] statekeyshort_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), @@ -979,7 +1004,7 @@ impl KeyValueDatabase { use std::time::{Duration, Instant}; let timer_interval = - Duration::from_secs(services().globals.config.cleanup_second_interval as u64); + Duration::from_secs(u64::from(services().globals.config.cleanup_second_interval)); tokio::spawn(async move { let mut i = interval(timer_interval); diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 9fd24e1e..da1763a8 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -32,7 +32,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O let mut db_opts = rocksdb::Options::default(); db_opts.set_block_based_table_factory(&block_based_options); db_opts.create_if_missing(true); - db_opts.increase_parallelism(num_cpus::get() as i32); + db_opts.increase_parallelism(num_cpus::get().try_into().unwrap_or(i32::MAX)); db_opts.set_max_open_files(max_open_files); db_opts.set_compression_type(rocksdb::DBCompressionType::Lz4); db_opts.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd); @@ -58,6 +58,11 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O impl KeyValueDatabaseEngine for Arc { fn open(config: &Config) -> Result { + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize; let rocksdb_cache = rocksdb::Cache::new_lru_cache(cache_capacity_bytes); @@ -109,6 +114,7 @@ impl KeyValueDatabaseEngine for Arc { Ok(()) } + #[allow(clippy::as_conversions, clippy::cast_precision_loss)] fn memory_usage(&self) -> Result { let stats = rocksdb::perf::get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 8e7d3f56..1ec56c59 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -88,9 +88,14 @@ impl KeyValueDatabaseEngine for Arc { // 1. convert MB to KiB // 2. divide by permanent connections + permanent iter connections + write connection // 3. round down to nearest integer - let cache_size_per_thread: u32 = ((config.db_cache_capacity_mb * 1024.0) - / ((num_cpus::get().max(1) * 2) + 1) as f64) - as u32; + #[allow( + clippy::as_conversions, + clippy::cast_possible_truncation, + clippy::cast_precision_loss, + clippy::cast_sign_loss + )] + let cache_size_per_thread = ((config.db_cache_capacity_mb * 1024.0) + / ((num_cpus::get() as f64 * 2.0) + 1.0)) as u32; let writer = Mutex::new(Engine::prepare_conn(&path, cache_size_per_thread)?); diff --git a/src/service.rs b/src/service.rs index 4ab89c68..fe06c40d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -84,9 +84,19 @@ impl Services { state: rooms::state::Service { db }, state_accessor: rooms::state_accessor::Service { db, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] server_visibility_cache: StdMutex::new(LruCache::new( (100.0 * config.cache_capacity_modifier) as usize, )), + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] user_visibility_cache: StdMutex::new(LruCache::new( (100.0 * config.cache_capacity_modifier) as usize, )), @@ -94,6 +104,11 @@ impl Services { state_cache: rooms::state_cache::Service { db }, state_compressor: rooms::state_compressor::Service { db, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] stateinfo_cache: StdMutex::new(LruCache::new( (100.0 * config.cache_capacity_modifier) as usize, )), diff --git a/src/service/media.rs b/src/service/media.rs index 9ecd2668..3dc764e1 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -154,10 +154,8 @@ impl Service { } else { let (exact_width, exact_height) = { // Copied from image::dynimage::resize_dimensions - let ratio = u64::from(original_width) * u64::from(height); - let nratio = u64::from(width) * u64::from(original_height); - - let use_width = nratio <= ratio; + let use_width = (u64::from(width) * u64::from(original_height)) + <= (u64::from(original_width) * u64::from(height)); let intermediate = if use_width { u64::from(original_height) * u64::from(width) / u64::from(original_width) @@ -167,21 +165,23 @@ impl Service { }; if use_width { if intermediate <= u64::from(::std::u32::MAX) { - (width, intermediate as u32) + (width, intermediate.try_into().unwrap_or(u32::MAX)) } else { ( (u64::from(width) * u64::from(::std::u32::MAX) / intermediate) - as u32, + .try_into() + .unwrap_or(u32::MAX), ::std::u32::MAX, ) } } else if intermediate <= u64::from(::std::u32::MAX) { - (intermediate as u32, height) + (intermediate.try_into().unwrap_or(u32::MAX), height) } else { ( ::std::u32::MAX, (u64::from(height) * u64::from(::std::u32::MAX) / intermediate) - as u32, + .try_into() + .unwrap_or(u32::MAX), ) } }; diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index e1dc6527..40f49713 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -44,6 +44,9 @@ impl Service { let mut i = 0; for id in starting_events { let short = services().rooms.short.get_or_create_shorteventid(&id)?; + // I'm afraid to change this in case there is accidental reliance on + // the truncation + #[allow(clippy::as_conversions, clippy::cast_possible_truncation)] let bucket_id = (short % NUM_BUCKETS as u64) as usize; buckets[bucket_id].insert((short, id.clone())); i += 1; diff --git a/src/service/sending.rs b/src/service/sending.rs index f873f5ab..1e6e233e 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -107,7 +107,7 @@ impl Service { db, sender, receiver: Mutex::new(receiver), - maximum_requests: Arc::new(Semaphore::new(config.max_concurrent_requests as usize)), + maximum_requests: Arc::new(Semaphore::new(config.max_concurrent_requests.into())), }) } diff --git a/src/utils.rs b/src/utils.rs index b679e6b6..530f1886 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -11,6 +11,8 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +// Hopefully we have a better chat protocol in 530 years +#[allow(clippy::as_conversions, clippy::cast_possible_truncation)] pub(crate) fn millis_since_unix_epoch() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) From 885fc8428c3c6532f368e3b6bfbc8235413531ab Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 16:23:04 -0700 Subject: [PATCH 027/617] enable `deref_by_slicing` lint --- Cargo.toml | 1 + src/api/client_server/sync.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 26ffd0d5..bdf40ddb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ assertions_on_result_states = "warn" cloned_instead_of_copied = "warn" dbg_macro = "warn" default_union_representation = "warn" +deref_by_slicing = "warn" empty_drop = "warn" filetype_is_file = "warn" float_cmp_const = "warn" diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 60f54e4f..0e3cc38c 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1608,7 +1608,7 @@ pub(crate) async fn sync_events_v4_route( }) .take(5) .collect::>(); - let name = match &heroes[..] { + let name = match &*heroes { [] => None, [only] => Some(only.0.clone()), [firsts @ .., last] => Some( @@ -1622,7 +1622,7 @@ pub(crate) async fn sync_events_v4_route( ), }; - let avatar = if let [only] = &heroes[..] { + let avatar = if let [only] = &*heroes { only.1.clone() } else { None From a127253daa116e540bbfc3d8520d03ba678c28c1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 16:25:22 -0700 Subject: [PATCH 028/617] enable `empty_struct_with_brackets` lint --- Cargo.toml | 1 + src/clap.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bdf40ddb..bc8aceef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ dbg_macro = "warn" default_union_representation = "warn" deref_by_slicing = "warn" empty_drop = "warn" +empty_structs_with_brackets = "warn" filetype_is_file = "warn" float_cmp_const = "warn" get_unwrap = "warn" diff --git a/src/clap.rs b/src/clap.rs index 7b7a420a..e17c5106 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -19,7 +19,7 @@ fn version() -> String { /// Command line arguments #[derive(Parser)] #[clap(about, version = version())] -pub(crate) struct Args {} +pub(crate) struct Args; /// Parse command line arguments into structured data pub(crate) fn parse() -> Args { From 5fd156a6bbe6a9ee9a2e75cead51ed8051a7c175 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 16:27:31 -0700 Subject: [PATCH 029/617] enable `error_impl_error` lint Except the 1 violation would have to be renamed in, like, every single file in this project. So we're just enabling it so that we don't make the same mistake in the future. --- Cargo.toml | 1 + src/utils/error.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index bc8aceef..c93a5705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ default_union_representation = "warn" deref_by_slicing = "warn" empty_drop = "warn" empty_structs_with_brackets = "warn" +error_impl_error = "warn" filetype_is_file = "warn" float_cmp_const = "warn" get_unwrap = "warn" diff --git a/src/utils/error.rs b/src/utils/error.rs index d05a9a28..d08a8dde 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -16,6 +16,7 @@ use crate::RumaResponse; pub(crate) type Result = std::result::Result; #[derive(Error, Debug)] +#[allow(clippy::error_impl_error)] pub(crate) enum Error { #[cfg(feature = "sqlite")] #[error("There was a problem with the connection to the sqlite database: {source}")] From bd6ea4e3587292a53a1f9783c821c0f37af3d436 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 16:36:51 -0700 Subject: [PATCH 030/617] enable `format_push_string` lint --- Cargo.toml | 1 + src/config.rs | 4 +++- src/service/admin.rs | 29 ++++++++++++++++++----------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c93a5705..4d10883b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ empty_structs_with_brackets = "warn" error_impl_error = "warn" filetype_is_file = "warn" float_cmp_const = "warn" +format_push_string = "warn" get_unwrap = "warn" lossy_float_literal = "warn" mem_forget = "warn" diff --git a/src/config.rs b/src/config.rs index f10819b5..539a26f9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use std::{ collections::BTreeMap, fmt, + fmt::Write, net::{IpAddr, Ipv4Addr}, }; @@ -191,7 +192,8 @@ impl fmt::Display for Config { let mut msg: String = "Active config values:\n\n".to_owned(); for line in lines.into_iter().enumerate() { - msg += &format!("{}: {}\n", line.1 .0, line.1 .1); + writeln!(msg, "{}: {}", line.1 .0, line.1 .1) + .expect("write to in-memory buffer should succeed"); } write!(f, "{msg}") diff --git a/src/service/admin.rs b/src/service/admin.rs index f61938a1..f3cd40fc 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1,6 +1,7 @@ use std::{ collections::BTreeMap, convert::{TryFrom, TryInto}, + fmt::Write, sync::Arc, time::Instant, }; @@ -413,13 +414,13 @@ impl Service { for (r, (e, i)) in map.iter() { let elapsed = i.elapsed(); - msg += &format!( - "{} {}: {}m{}s\n", - r, - e, + writeln!( + msg, + "{r} {e}: {}m{}s", elapsed.as_secs() / 60, elapsed.as_secs() % 60 - ); + ) + .expect("write to in-memory buffer should succeed"); } RoomMessageEventContent::text_plain(&msg) } @@ -718,8 +719,10 @@ impl Service { markdown_message.push_str("The following user ids are not valid:\n```\n"); html_message.push_str("The following user ids are not valid:\n
\n");
                         for invalid_user in invalid_users {
-                            markdown_message.push_str(&format!("{invalid_user}\n"));
-                            html_message.push_str(&format!("{invalid_user}\n"));
+                            writeln!(markdown_message, "{invalid_user}")
+                                .expect("write to in-memory buffer should succeed");
+                            writeln!(html_message, "{invalid_user}")
+                                .expect("write to in-memory buffer should succeed");
                         }
                         markdown_message.push_str("```\n\n");
                         html_message.push_str("
\n\n"); @@ -730,8 +733,10 @@ impl Service { html_message .push_str("The following users are not from this server:\n
\n");
                         for remote_id in remote_ids {
-                            markdown_message.push_str(&format!("{remote_id}\n"));
-                            html_message.push_str(&format!("{remote_id}\n"));
+                            writeln!(markdown_message, "{remote_id}")
+                                .expect("write to in-memory buffer should succeed");
+                            writeln!(html_message, "{remote_id}")
+                                .expect("write to in-memory buffer should succeed");
                         }
                         markdown_message.push_str("```\n\n");
                         html_message.push_str("
\n\n"); @@ -740,8 +745,10 @@ impl Service { markdown_message.push_str("The following users do not exist:\n```\n"); html_message.push_str("The following users do not exist:\n
\n");
                         for non_existant_id in non_existant_ids {
-                            markdown_message.push_str(&format!("{non_existant_id}\n"));
-                            html_message.push_str(&format!("{non_existant_id}\n"));
+                            writeln!(markdown_message, "{non_existant_id}")
+                                .expect("write to in-memory buffer should succeed");
+                            writeln!(html_message, "{non_existant_id}")
+                                .expect("write to in-memory buffer should succeed");
                         }
                         markdown_message.push_str("```\n\n");
                         html_message.push_str("
\n\n"); From 52c2893073d5f07ee0c9afc59dcfc330fd230077 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 16:41:54 -0700 Subject: [PATCH 031/617] enable `if_then_some_else_none` lint --- Cargo.toml | 1 + src/api/client_server/sync.rs | 60 ++++++++----------- src/database.rs | 8 +-- .../key_value/rooms/state_compressor.rs | 2 +- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d10883b..f7c57201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ filetype_is_file = "warn" float_cmp_const = "warn" format_push_string = "warn" get_unwrap = "warn" +if_then_some_else_none = "warn" lossy_float_literal = "warn" mem_forget = "warn" mod_module_files = "warn" diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 0e3cc38c..d1bcda0e 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -996,31 +996,20 @@ async fn load_joined_room( .filter_map(|r| r.ok()), ); - let notification_count = if send_notification_counts { - Some( + let notification_count = send_notification_counts + .then(|| { services() .rooms .user - .notification_count(sender_user, room_id)? - .try_into() - .expect("notification count can't go that high"), - ) - } else { - None - }; + .notification_count(sender_user, room_id) + }) + .transpose()? + .map(|x| x.try_into().expect("notification count can't go that high")); - let highlight_count = if send_notification_counts { - Some( - services() - .rooms - .user - .highlight_count(sender_user, room_id)? - .try_into() - .expect("highlight count can't go that high"), - ) - } else { - None - }; + let highlight_count = send_notification_counts + .then(|| services().rooms.user.highlight_count(sender_user, room_id)) + .transpose()? + .map(|x| x.try_into().expect("highlight count can't go that high")); let prev_batch = timeline_pdus .first() @@ -1557,13 +1546,7 @@ pub(crate) async fn sync_events_v4_route( PduCount::Normal(c) => c.to_string(), })) })? - .or_else(|| { - if roomsince != &0 { - Some(roomsince.to_string()) - } else { - None - } - }); + .or_else(|| (roomsince != &0).then(|| roomsince.to_string())); let room_events: Vec<_> = timeline_pdus .iter() @@ -1708,16 +1691,21 @@ pub(crate) async fn sync_events_v4_route( lists, rooms, extensions: sync_events::v4::Extensions { - to_device: if body.extensions.to_device.enabled.unwrap_or(false) { - Some(sync_events::v4::ToDevice { - events: services() + to_device: body + .extensions + .to_device + .enabled + .unwrap_or(false) + .then(|| { + services() .users - .get_to_device_events(&sender_user, &sender_device)?, - next_batch: next_batch.to_string(), + .get_to_device_events(&sender_user, &sender_device) + .map(|events| sync_events::v4::ToDevice { + events, + next_batch: next_batch.to_string(), + }) }) - } else { - None - }, + .transpose()?, e2ee: sync_events::v4::E2EE { device_lists: DeviceLists { changed: device_list_changes.into_iter().collect(), diff --git a/src/database.rs b/src/database.rs index 8e684079..7bc56703 100644 --- a/src/database.rs +++ b/src/database.rs @@ -763,13 +763,7 @@ impl KeyValueDatabase { let batch2: Vec<_> = db .tokenids .iter() - .filter_map(|(key, _)| { - if key.starts_with(b"!") { - Some(key) - } else { - None - } - }) + .filter_map(|(key, _)| key.starts_with(b"!").then_some(key)) .collect(); for key in batch2 { diff --git a/src/database/key_value/rooms/state_compressor.rs b/src/database/key_value/rooms/state_compressor.rs index 65ea603e..ab06d8f3 100644 --- a/src/database/key_value/rooms/state_compressor.rs +++ b/src/database/key_value/rooms/state_compressor.rs @@ -14,7 +14,7 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase { .ok_or_else(|| Error::bad_database("State hash does not exist"))?; let parent = utils::u64_from_bytes(&value[0..size_of::()]).expect("bytes have right length"); - let parent = if parent != 0 { Some(parent) } else { None }; + let parent = (parent != 0).then_some(parent); let mut add_mode = true; let mut added = HashSet::new(); From 2ded335adb866e746243bbd468f45ad9f028fcb9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 17:18:29 -0700 Subject: [PATCH 032/617] enable `impl_trait_in_params` lint See also . --- Cargo.toml | 1 + src/utils.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7c57201..c719dbd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ float_cmp_const = "warn" format_push_string = "warn" get_unwrap = "warn" if_then_some_else_none = "warn" +impl_trait_in_params = "warn" lossy_float_literal = "warn" mem_forget = "warn" mod_module_files = "warn" diff --git a/src/utils.rs b/src/utils.rs index 530f1886..4b2d7c3f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -81,10 +81,15 @@ pub(crate) fn calculate_hash(keys: &[&[u8]]) -> Vec { hash.as_ref().to_owned() } -pub(crate) fn common_elements( - mut iterators: impl Iterator>>, - check_order: impl Fn(&[u8], &[u8]) -> Ordering, -) -> Option>> { +pub(crate) fn common_elements( + mut iterators: I, + check_order: F, +) -> Option>> +where + I: Iterator, + I::Item: Iterator>, + F: Fn(&[u8], &[u8]) -> Ordering, +{ let first_iterator = iterators.next()?; let mut other_iterators = iterators.map(|i| i.peekable()).collect::>(); From 052f3088e95464dd7986d5f1264d3893018f904f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 17:02:08 -0700 Subject: [PATCH 033/617] enable `let_underscore_must_use` lint --- Cargo.toml | 1 + src/api/client_server/membership.rs | 4 +++- src/api/client_server/profile.rs | 15 +++++++++++---- src/api/client_server/room.rs | 6 +++++- src/api/client_server/sync.rs | 15 +++++++++++---- src/database/abstraction/rocksdb.rs | 6 +++--- src/database/abstraction/watchers.rs | 2 +- src/main.rs | 9 ++++++--- src/service/admin.rs | 5 ++++- src/service/globals.rs | 4 ++-- src/service/rooms/edus/typing.rs | 13 ++++++++++--- src/service/rooms/event_handler.rs | 21 ++++++++++++--------- 12 files changed, 69 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c719dbd6..5764c9a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ format_push_string = "warn" get_unwrap = "warn" if_then_some_else_none = "warn" impl_trait_in_params = "warn" +let_underscore_must_use = "warn" lossy_float_literal = "warn" mem_forget = "warn" mod_module_files = "warn" diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index dbaa69a2..ac7b3bc9 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1400,7 +1400,9 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> { Err(_) => continue, }; - let _ = leave_room(user_id, &room_id, None).await; + if let Err(error) = leave_room(user_id, &room_id, None).await { + warn!(%user_id, %room_id, %error, "failed to leave room"); + } } Ok(()) diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index eafa726f..3a86d81d 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -13,6 +13,7 @@ use ruma::{ }; use serde_json::value::to_raw_value; use std::sync::Arc; +use tracing::warn; /// # `PUT /_matrix/client/r0/profile/{userId}/displayname` /// @@ -84,11 +85,14 @@ pub(crate) async fn set_displayname_route( ); let state_lock = mutex_state.lock().await; - let _ = services() + if let Err(error) = services() .rooms .timeline .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) - .await; + .await + { + warn!(%error, "failed to add PDU"); + } } Ok(set_display_name::v3::Response {}) @@ -198,11 +202,14 @@ pub(crate) async fn set_avatar_url_route( ); let state_lock = mutex_state.lock().await; - let _ = services() + if let Err(error) = services() .rooms .timeline .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) - .await; + .await + { + warn!(%error, "failed to add PDU"); + }; } Ok(set_avatar_url::v3::Response {}) diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index f1f67076..f51696d1 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -480,7 +480,11 @@ pub(crate) async fn create_room_route( // 8. Events implied by invite (and TODO: invite_3pid) drop(state_lock); for user_id in &body.invite { - let _ = invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await; + if let Err(error) = + invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await + { + warn!(%error, "invite helper failed"); + }; } // Homeserver specific stuff diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index d1bcda0e..b430379f 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -29,7 +29,7 @@ use std::{ time::Duration, }; use tokio::sync::watch::Sender; -use tracing::{error, info}; +use tracing::{debug, error, info}; /// # `GET /_matrix/client/r0/sync` /// @@ -164,7 +164,8 @@ async fn sync_helper_wrapper( } } - let _ = tx.send(Some(r.map(|(r, _)| r))); + tx.send(Some(r.map(|(r, _)| r))) + .expect("receiver should not be dropped"); } async fn sync_helper( @@ -543,7 +544,10 @@ async fn sync_helper( if duration.as_secs() > 30 { duration = Duration::from_secs(30); } - let _ = tokio::time::timeout(duration, watcher).await; + match tokio::time::timeout(duration, watcher).await { + Ok(x) => x.expect("watcher should succeed"), + Err(error) => debug!(%error, "timed out"), + }; Ok((response, false)) } else { Ok((response, since != next_batch)) // Only cache if we made progress @@ -1681,7 +1685,10 @@ pub(crate) async fn sync_events_v4_route( if duration.as_secs() > 30 { duration = Duration::from_secs(30); } - let _ = tokio::time::timeout(duration, watcher).await; + match tokio::time::timeout(duration, watcher).await { + Ok(x) => x.expect("watcher should succeed"), + Err(error) => debug!(%error, "timed out"), + }; } Ok(sync_events::v4::Response { diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index da1763a8..dbfa49b2 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -96,9 +96,9 @@ impl KeyValueDatabaseEngine for Arc { fn open_tree(&self, name: &'static str) -> Result> { if !self.old_cfs.contains(&name.to_owned()) { // Create if it didn't exist - let _ = self - .rocks - .create_cf(name, &db_options(self.max_open_files, &self.cache)); + self.rocks + .create_cf(name, &db_options(self.max_open_files, &self.cache)) + .expect("should be able to create column family"); } Ok(Arc::new(RocksDbEngineTree { diff --git a/src/database/abstraction/watchers.rs b/src/database/abstraction/watchers.rs index 01156abd..c8737583 100644 --- a/src/database/abstraction/watchers.rs +++ b/src/database/abstraction/watchers.rs @@ -47,7 +47,7 @@ impl Watchers { let mut watchers = self.watchers.write().unwrap(); for prefix in triggered { if let Some(tx) = watchers.remove(prefix) { - let _ = tx.0.send(()); + tx.0.send(()).expect("channel should still be open"); } } }; diff --git a/src/main.rs b/src/main.rs index 4b86b111..91588c88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -227,7 +227,8 @@ async fn run_server() -> io::Result<()> { let server = bind_rustls(addr, conf).handle(handle).serve(app); #[cfg(feature = "systemd")] - let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); + sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) + .expect("should be able to notify systemd"); server.await? } @@ -235,7 +236,8 @@ async fn run_server() -> io::Result<()> { let server = bind(addr).handle(handle).serve(app); #[cfg(feature = "systemd")] - let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); + sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) + .expect("should be able to notify systemd"); server.await? } @@ -494,7 +496,8 @@ async fn shutdown_signal(handle: ServerHandle) { services().globals.shutdown(); #[cfg(feature = "systemd")] - let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Stopping]); + sd_notify::notify(true, &[sd_notify::NotifyState::Stopping]) + .expect("should be able to notify systemd"); } async fn federation_disabled(_: Uri) -> impl IntoResponse { diff --git a/src/service/admin.rs b/src/service/admin.rs index f3cd40fc..d8c3a29b 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -29,6 +29,7 @@ use ruma::{ }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; +use tracing::warn; use crate::{ api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH}, @@ -784,7 +785,9 @@ impl Service { if leave_rooms { for &user_id in &user_ids { - let _ = leave_all_rooms(user_id).await; + if let Err(error) = leave_all_rooms(user_id).await { + warn!(%user_id, %error, "failed to leave one or more rooms"); + } } } diff --git a/src/service/globals.rs b/src/service/globals.rs index a55cdda5..98b543c8 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -92,12 +92,12 @@ impl RotationHandler { let mut r = self.0.subscribe(); async move { - let _ = r.recv().await; + r.recv().await.expect("should receive a message"); } } pub(crate) fn fire(&self) { - let _ = self.0.send(()); + self.0.send(()).expect("should be able to send message"); } } diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 76be3791..873736da 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -1,6 +1,7 @@ use ruma::{events::SyncEphemeralRoomEvent, OwnedRoomId, OwnedUserId, RoomId, UserId}; use std::collections::BTreeMap; use tokio::sync::{broadcast, RwLock}; +use tracing::trace; use crate::{services, utils, Result}; @@ -29,7 +30,9 @@ impl Service { .write() .await .insert(room_id.to_owned(), services().globals.next_count()?); - let _ = self.typing_update_sender.send(room_id.to_owned()); + if self.typing_update_sender.send(room_id.to_owned()).is_err() { + trace!("receiver found what it was looking for and is no longer interested"); + } Ok(()) } @@ -45,7 +48,9 @@ impl Service { .write() .await .insert(room_id.to_owned(), services().globals.next_count()?); - let _ = self.typing_update_sender.send(room_id.to_owned()); + if self.typing_update_sender.send(room_id.to_owned()).is_err() { + trace!("receiver found what it was looking for and is no longer interested"); + } Ok(()) } @@ -86,7 +91,9 @@ impl Service { .write() .await .insert(room_id.to_owned(), services().globals.next_count()?); - let _ = self.typing_update_sender.send(room_id.to_owned()); + if self.typing_update_sender.send(room_id.to_owned()).is_err() { + trace!("receiver found what it was looking for and is no longer interested"); + } } Ok(()) } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 84b210e1..261811d4 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1533,19 +1533,22 @@ impl Service { // Try to fetch keys, failure is okay // Servers we couldn't find in the cache will be added to `servers` - for pdu in &event.room_state.state { - let _ = self + for pdu in event + .room_state + .state + .iter() + .chain(&event.room_state.auth_chain) + { + if let Err(error) = self .get_server_keys_from_cache(pdu, &mut servers, room_version, &mut pkm) - .await; - } - for pdu in &event.room_state.auth_chain { - let _ = self - .get_server_keys_from_cache(pdu, &mut servers, room_version, &mut pkm) - .await; + .await + { + debug!(%error, "failed to get server keys from cache"); + }; } drop(pkm); - } + }; if servers.is_empty() { info!("We had all keys locally"); From 4a7b9c16cc391bc5d87333ab2aa5efe67825fdf1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 18:28:41 -0700 Subject: [PATCH 034/617] enable `missing_assert_message` lint --- Cargo.toml | 1 + src/database.rs | 3 ++- src/database/key_value/users.rs | 25 ++++++++++++++++--------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5764c9a6..0d567172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ impl_trait_in_params = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" mem_forget = "warn" +missing_assert_message = "warn" mod_module_files = "warn" mutex_atomic = "warn" negative_feature_names = "warn" diff --git a/src/database.rs b/src/database.rs index 7bc56703..94509c79 100644 --- a/src/database.rs +++ b/src/database.rs @@ -929,7 +929,8 @@ impl KeyValueDatabase { assert_eq!( services().globals.database_version().unwrap(), - latest_database_version + latest_database_version, + "database should be migrated to the current version", ); info!( diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 0e6db83a..91691b01 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -198,8 +198,10 @@ impl service::users::Data for KeyValueDatabase { token: &str, initial_device_display_name: Option, ) -> Result<()> { - // This method should never be called for nonexistent users. - assert!(self.exists(user_id)?); + assert!( + self.exists(user_id)?, + "user must exist before calling this method" + ); let mut userdeviceid = user_id.as_bytes().to_vec(); userdeviceid.push(0xff); @@ -285,8 +287,10 @@ impl service::users::Data for KeyValueDatabase { userdeviceid.push(0xff); userdeviceid.extend_from_slice(device_id.as_bytes()); - // All devices have metadata - assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some()); + assert!( + self.userdeviceid_metadata.get(&userdeviceid)?.is_some(), + "devices should have metadata" + ); // Remove old token if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? { @@ -314,9 +318,10 @@ impl service::users::Data for KeyValueDatabase { key.push(0xff); key.extend_from_slice(device_id.as_bytes()); - // All devices have metadata - // Only existing devices should be able to call this. - assert!(self.userdeviceid_metadata.get(&key)?.is_some()); + assert!( + self.userdeviceid_metadata.get(&key)?.is_some(), + "devices should have metadata and this method should only be called with existing devices" + ); key.push(0xff); // TODO: Use DeviceKeyId::to_string when it's available (and update everything, @@ -852,8 +857,10 @@ impl service::users::Data for KeyValueDatabase { userdeviceid.push(0xff); userdeviceid.extend_from_slice(device_id.as_bytes()); - // Only existing devices should be able to call this. - assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some()); + assert!( + self.userdeviceid_metadata.get(&userdeviceid)?.is_some(), + "this method should only be called with existing devices" + ); self.userid_devicelistversion .increment(user_id.as_bytes())?; From 6bdaeab1afcabe2bbeb00b1d967f030c9689e76a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 18:31:09 -0700 Subject: [PATCH 035/617] enable `multiple_inherent_impl` lint --- Cargo.toml | 1 + src/database/key_value/users.rs | 2 -- src/utils/error.rs | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0d567172..26086f8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ lossy_float_literal = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" +multiple_inherent_impl = "warn" mutex_atomic = "warn" negative_feature_names = "warn" pub_without_shorthand = "warn" diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 91691b01..37b70e01 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -952,8 +952,6 @@ impl service::users::Data for KeyValueDatabase { } } -impl KeyValueDatabase {} - /// Will only return with Some(username) if the password was not empty and the /// username could be successfully parsed. /// If utils::string_from_bytes(...) returns an error that username will be skipped diff --git a/src/utils/error.rs b/src/utils/error.rs index d08a8dde..7d5f9357 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -87,9 +87,7 @@ impl Error { error!("BadConfig: {}", message); Self::BadConfig(message) } -} -impl Error { pub(crate) fn to_response(&self) -> RumaResponse { if let Self::Uiaa(uiaainfo) = self { return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone())); From d144db8688c4e30a4dba9d14ff6bcc1a0bc7ce4a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 18:38:12 -0700 Subject: [PATCH 036/617] enable `ref_patterns` lint --- Cargo.toml | 1 + src/api/client_server/account.rs | 2 +- src/api/client_server/alias.rs | 4 ++-- src/api/client_server/room.rs | 4 ++-- src/api/client_server/session.rs | 6 +++--- src/api/server_server.rs | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 26086f8d..e00eb260 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ rc_buffer = "warn" rc_mutex = "warn" redundant_feature_names = "warn" redundant_type_annotations = "warn" +ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" semicolon_inside_block = "warn" str_to_string = "warn" diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 9c3d2083..2368ae92 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -122,7 +122,7 @@ pub(crate) async fn register_route( }; if body.body.login_type == Some(LoginType::ApplicationService) { - if let Some(ref info) = body.appservice_info { + if let Some(info) = &body.appservice_info { if !info.is_user_match(&user_id) { return Err(Error::BadRequest( ErrorKind::Exclusive, diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 6e198e26..45eff66c 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -25,7 +25,7 @@ pub(crate) async fn create_alias_route( )); } - if let Some(ref info) = body.appservice_info { + if let Some(info) = &body.appservice_info { if !info.aliases.is_match(body.room_alias.as_str()) { return Err(Error::BadRequest( ErrorKind::Exclusive, @@ -76,7 +76,7 @@ pub(crate) async fn delete_alias_route( )); } - if let Some(ref info) = body.appservice_info { + if let Some(info) = &body.appservice_info { if !info.aliases.is_match(body.room_alias.as_str()) { return Err(Error::BadRequest( ErrorKind::Exclusive, diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index f51696d1..f3edf62d 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -104,8 +104,8 @@ pub(crate) async fn create_room_route( } })?; - if let Some(ref alias) = alias { - if let Some(ref info) = body.appservice_info { + if let Some(alias) = &alias { + if let Some(info) = &body.appservice_info { if !info.aliases.is_match(alias.as_str()) { return Err(Error::BadRequest( ErrorKind::Exclusive, diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index d18d384a..0b2c2c42 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -147,7 +147,7 @@ pub(crate) async fn login_route(body: Ruma) -> Result) -> Result Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Some(ref info) = body.appservice_info { + if let Some(info) = &body.appservice_info { if !info.is_user_match(sender_user) { return Err(Error::BadRequest( ErrorKind::Exclusive, diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 5bf8101b..6f46a232 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -93,7 +93,7 @@ impl FedDest { fn into_uri_string(self) -> String { match self { Self::Literal(addr) => addr.to_string(), - Self::Named(host, ref port) => host + port, + Self::Named(host, port) => format!("{host}{port}"), } } From cc5977b4e4d1ae3ab2b6c72072c78e3106964cd7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 18:43:00 -0700 Subject: [PATCH 037/617] enable `same_name_method` lint --- Cargo.toml | 1 + src/api/client_server/message.rs | 4 ++-- src/api/client_server/relations.rs | 12 ++++++------ src/api/client_server/sync.rs | 2 +- src/database/key_value/rooms/timeline.rs | 2 +- src/service/rooms/timeline.rs | 10 +++------- 6 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e00eb260..692fd60f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ redundant_feature_names = "warn" redundant_type_annotations = "warn" ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" +same_name_method = "warn" semicolon_inside_block = "warn" str_to_string = "warn" string_lit_chars_any = "warn" diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 77a1d81d..1d86cf2b 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -121,8 +121,8 @@ pub(crate) async fn get_message_events_route( let from = match body.from.clone() { Some(from) => PduCount::try_from_string(&from)?, None => match body.dir { - ruma::api::Direction::Forward => PduCount::min(), - ruma::api::Direction::Backward => PduCount::max(), + ruma::api::Direction::Forward => PduCount::MIN, + ruma::api::Direction::Backward => PduCount::MAX, }, }; diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 2bdad29c..bb85641b 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -18,8 +18,8 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( Some(from) => PduCount::try_from_string(&from)?, None => match ruma::api::Direction::Backward { // TODO: fix ruma so `body.dir` exists - ruma::api::Direction::Forward => PduCount::min(), - ruma::api::Direction::Backward => PduCount::max(), + ruma::api::Direction::Forward => PduCount::MIN, + ruma::api::Direction::Backward => PduCount::MAX, }, }; @@ -69,8 +69,8 @@ pub(crate) async fn get_relating_events_with_rel_type_route( Some(from) => PduCount::try_from_string(&from)?, None => match ruma::api::Direction::Backward { // TODO: fix ruma so `body.dir` exists - ruma::api::Direction::Forward => PduCount::min(), - ruma::api::Direction::Backward => PduCount::max(), + ruma::api::Direction::Forward => PduCount::MIN, + ruma::api::Direction::Backward => PduCount::MAX, }, }; @@ -118,8 +118,8 @@ pub(crate) async fn get_relating_events_route( Some(from) => PduCount::try_from_string(&from)?, None => match ruma::api::Direction::Backward { // TODO: fix ruma so `body.dir` exists - ruma::api::Direction::Forward => PduCount::min(), - ruma::api::Direction::Backward => PduCount::max(), + ruma::api::Direction::Forward => PduCount::MIN, + ruma::api::Direction::Backward => PduCount::MAX, }, }; diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index b430379f..f174e98b 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1120,7 +1120,7 @@ fn load_timeline( let mut non_timeline_pdus = services() .rooms .timeline - .pdus_until(sender_user, room_id, PduCount::max())? + .pdus_until(sender_user, room_id, PduCount::MAX)? .filter_map(|r| { // Filter out buggy events if r.is_err() { diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 0331a624..438e57e6 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -19,7 +19,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase { { hash_map::Entry::Vacant(v) => { if let Some(last_count) = self - .pdus_until(sender_user, room_id, PduCount::max())? + .pdus_until(sender_user, room_id, PduCount::MAX)? .find_map(|r| { // Filter out buggy events if r.is_err() { diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 307b5f97..bafcec29 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -48,12 +48,8 @@ pub(crate) enum PduCount { } impl PduCount { - pub(crate) fn min() -> Self { - Self::Backfilled(u64::MAX) - } - pub(crate) fn max() -> Self { - Self::Normal(u64::MAX) - } + pub(crate) const MIN: Self = Self::Backfilled(u64::MAX); + pub(crate) const MAX: Self = Self::Normal(u64::MAX); pub(crate) fn try_from_string(token: &str) -> Result { if let Some(stripped) = token.strip_prefix('-') { @@ -1052,7 +1048,7 @@ impl Service { user_id: &UserId, room_id: &RoomId, ) -> Result> + 'a> { - self.pdus_after(user_id, room_id, PduCount::min()) + self.pdus_after(user_id, room_id, PduCount::MIN) } /// Returns an iterator over all events and their tokens in a room that happened before the From 9abe4799db50dee7ae4809f9b6511b38f3f1e91f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 19:01:29 -0700 Subject: [PATCH 038/617] enable `string_add` lint --- Cargo.toml | 1 + src/api/appservice_server.rs | 2 +- src/api/client_server/media.rs | 2 +- src/api/client_server/sync.rs | 12 ++++++------ src/service/admin.rs | 10 +++++----- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 692fd60f..9b6e6f0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ rest_pat_in_fully_bound_structs = "warn" same_name_method = "warn" semicolon_inside_block = "warn" str_to_string = "warn" +string_add = "warn" string_lit_chars_any = "warn" string_to_string = "warn" suspicious_xor_used_as_pow = "warn" diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index 3ec7a66e..0c29472d 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -44,7 +44,7 @@ where }; parts.path_and_query = Some( - (old_path_and_query + symbol + "access_token=" + hs_token) + format!("{old_path_and_query}{symbol}access_token={hs_token}") .parse() .unwrap(), ); diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 62fe4c09..493c31da 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -43,7 +43,7 @@ pub(crate) async fn create_content_route( mxc.clone(), body.filename .as_ref() - .map(|filename| "inline; filename=".to_owned() + filename) + .map(|filename| format!("inline; filename={filename}")) .as_deref(), body.content_type.as_deref(), &body.file, diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index f174e98b..a7167d26 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1598,15 +1598,15 @@ pub(crate) async fn sync_events_v4_route( let name = match &*heroes { [] => None, [only] => Some(only.0.clone()), - [firsts @ .., last] => Some( - firsts + [firsts @ .., last] => Some({ + let firsts = firsts .iter() .map(|h| h.0.clone()) .collect::>() - .join(", ") - + " and " - + &last.0, - ), + .join(", "); + + format!("{firsts} and {}", last.0) + }), }; let avatar = if let [only] = &*heroes { diff --git a/src/service/admin.rs b/src/service/admin.rs index d8c3a29b..fd954bb3 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -385,17 +385,17 @@ impl Service { let output = format!( "Rooms:\n{}", room_ids - .filter_map(|r| r.ok()) - .map(|id| id.to_string() - + "\tMembers: " - + &services() + .filter_map(std::result::Result::ok) + .map(|id| format!( + "{id}\tMembers: {}", + &services() .rooms .state_cache .room_joined_count(&id) .ok() .flatten() .unwrap_or(0) - .to_string()) + )) .collect::>() .join("\n") ); From 844b32f0971a5ba3859fc830fe858982186e0076 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 19:08:12 -0700 Subject: [PATCH 039/617] enable `string_slice` lint Also swaps out vendored HTML-escaping code for a dependency that I imagine has decent testing considering all of its reverse depedencies. --- Cargo.lock | 16 ++++++++++++++ Cargo.toml | 2 ++ src/api/client_server/report.rs | 4 ++-- src/api/server_server.rs | 2 +- src/config/proxy.rs | 11 ++++------ src/service/admin.rs | 34 ++++++++-------------------- src/utils.rs | 39 +-------------------------------- 7 files changed, 35 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 885f2486..0f01f20b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,6 +800,7 @@ dependencies = [ "figment", "futures-util", "hmac", + "html-escape", "http", "hyper", "image", @@ -937,6 +938,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "http" version = "0.2.12" @@ -3045,6 +3055,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + [[package]] name = "uuid" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 9b6e6f0a..46c5c8f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ semicolon_inside_block = "warn" str_to_string = "warn" string_add = "warn" string_lit_chars_any = "warn" +string_slice = "warn" string_to_string = "warn" suspicious_xor_used_as_pow = "warn" tests_outside_test_module = "warn" @@ -87,6 +88,7 @@ clap = { version = "4.3.0", default-features = false, features = ["std", "derive figment = { version = "0.10.8", features = ["env", "toml"] } futures-util = { version = "0.3.28", default-features = false } hmac = "0.12.1" +html-escape = "0.2.13" http = "0.2.9" hyper = "0.14.26" image = { version = "0.24.6", default-features = false, features = ["jpeg", "png", "gif"] } diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index 8e7dbed2..af07760d 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -1,4 +1,4 @@ -use crate::{services, utils::HtmlEscape, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma}; use ruma::{ api::client::{error::ErrorKind, room::report_content}, events::room::message, @@ -61,7 +61,7 @@ pub(crate) async fn report_event_route( pdu.room_id, pdu.sender, body.score, - HtmlEscape(body.reason.as_deref().unwrap_or("")) + html_escape::encode_safe(body.reason.as_deref().unwrap_or("")) ), )); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 6f46a232..eae0ed7a 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -107,7 +107,7 @@ impl FedDest { fn port(&self) -> Option { match &self { Self::Literal(addr) => Some(addr.port()), - Self::Named(_, port) => port[1..].parse().ok(), + Self::Named(_, port) => port.strip_prefix(':').and_then(|x| x.parse().ok()), } } } diff --git a/src/config/proxy.rs b/src/config/proxy.rs index bf725190..7672e02b 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -124,13 +124,10 @@ impl std::str::FromStr for WildCardedDomain { type Err = std::convert::Infallible; fn from_str(s: &str) -> Result { // maybe do some domain validation? - Ok(if s.starts_with("*.") { - WildCardedDomain::WildCarded(s[1..].to_owned()) - } else if s == "*" { - WildCardedDomain::WildCarded("".to_owned()) - } else { - WildCardedDomain::Exact(s.to_owned()) - }) + Ok(s.strip_prefix("*.") + .map(|x| WildCardedDomain::WildCarded(x.to_owned())) + .or_else(|| (s == "*").then(|| WildCardedDomain::WildCarded(String::new()))) + .unwrap_or_else(|| WildCardedDomain::Exact(s.to_owned()))) } } impl<'de> Deserialize<'de> for WildCardedDomain { diff --git a/src/service/admin.rs b/src/service/admin.rs index fd954bb3..cad796c0 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -33,9 +33,7 @@ use tracing::warn; use crate::{ api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH}, - services, - utils::{self, HtmlEscape}, - Error, PduEvent, Result, + services, utils, Error, PduEvent, Result, }; use super::pdu::PduBuilder; @@ -516,7 +514,7 @@ impl Service { } else { "PDU was accepted" }, - HtmlEscape(&json_text) + html_escape::encode_safe(&json_text) ), ) } @@ -897,29 +895,15 @@ impl Service { // Look for a `[commandbody]()` tag. If it exists, use all lines below it that // start with a `#` in the USAGE section. let mut text_lines: Vec<&str> = text.lines().collect(); - let mut command_body = String::new(); - - if let Some(line_index) = text_lines + let command_body = text_lines .iter() - .position(|line| *line == "[commandbody]()") - { - text_lines.remove(line_index); - - while text_lines - .get(line_index) - .map(|line| line.starts_with('#')) - .unwrap_or(false) - { - command_body += if text_lines[line_index].starts_with("# ") { - &text_lines[line_index][2..] - } else { - &text_lines[line_index][1..] - }; - command_body += "[nobr]\n"; - text_lines.remove(line_index); - } - } + .skip_while(|x| x != &&"[commandbody]()") + .skip(1) + .map_while(|&x| x.strip_prefix('#')) + .map(|x| x.strip_prefix(' ').unwrap_or(x)) + .collect::(); + text_lines.retain(|x| x != &"[commandbody]()"); let text = text_lines.join("\n"); // Improve the usage section diff --git a/src/utils.rs b/src/utils.rs index 4b2d7c3f..d3d0c0a8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,7 +6,7 @@ use rand::prelude::*; use ring::digest; use ruma::{canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject}; use std::{ - cmp, fmt, + cmp, str::FromStr, time::{SystemTime, UNIX_EPOCH}, }; @@ -149,40 +149,3 @@ pub(crate) fn deserialize_from_str< } deserializer.deserialize_str(Visitor(std::marker::PhantomData)) } - -// Copied from librustdoc: -// https://github.com/rust-lang/rust/blob/cbaeec14f90b59a91a6b0f17fc046c66fa811892/src/librustdoc/html/escape.rs - -/// Wrapper struct which will emit the HTML-escaped version of the contained -/// string when passed to a format string. -pub(crate) struct HtmlEscape<'a>(pub(crate) &'a str); - -impl fmt::Display for HtmlEscape<'_> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - // Because the internet is always right, turns out there's not that many - // characters to escape: http://stackoverflow.com/questions/7381974 - let HtmlEscape(s) = *self; - let pile_o_bits = s; - let mut last = 0; - for (i, ch) in s.char_indices() { - let s = match ch { - '>' => ">", - '<' => "<", - '&' => "&", - '\'' => "'", - '"' => """, - _ => continue, - }; - fmt.write_str(&pile_o_bits[last..i])?; - fmt.write_str(s)?; - // NOTE: we only expect single byte characters here - which is fine as long as we - // only match single byte characters - last = i + 1; - } - - if last < s.len() { - fmt.write_str(&pile_o_bits[last..])?; - } - Ok(()) - } -} From 2b6a933538621ced69273d478b30f184b1605a06 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 19:10:31 -0700 Subject: [PATCH 040/617] enable `undocumented_unsafe_blocks` lint There was only one unsafe block (thankfully) but it also had no docs. I did some reading and found out this in fact safe, but only for cursed reasons, and documented them. Also, the name of the type was misleading, as the entire point is the aliasing, and `Box` is already non-aliasing. --- Cargo.toml | 1 + src/database/abstraction/sqlite.rs | 29 ++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 46c5c8f5..29b73a30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ string_to_string = "warn" suspicious_xor_used_as_pow = "warn" tests_outside_test_module = "warn" try_err = "warn" +undocumented_unsafe_blocks = "warn" unnecessary_safety_comment = "warn" unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 1ec56c59..51490442 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -19,7 +19,7 @@ thread_local! { struct PreparedStatementIterator<'a> { pub(crate) iterator: Box + 'a>, - pub(crate) _statement_ref: NonAliasingBox>, + pub(crate) _statement_ref: AliasableBox>, } impl Iterator for PreparedStatementIterator<'_> { @@ -30,10 +30,25 @@ impl Iterator for PreparedStatementIterator<'_> { } } -struct NonAliasingBox(*mut T); -impl Drop for NonAliasingBox { +struct AliasableBox(*mut T); +impl Drop for AliasableBox { fn drop(&mut self) { - drop(unsafe { Box::from_raw(self.0) }); + // SAFETY: This is cursed and relies on non-local reasoning. + // + // In order for this to be safe: + // + // * All aliased references to this value must have been dropped first, + // for example by coming after its referrers in struct fields, because + // struct fields are automatically dropped in order from top to bottom + // in the absence of an explicit Drop impl. Otherwise, the referrers + // may read into deallocated memory. + // * This type must not be copyable or cloneable. Otherwise, double-free + // can occur. + // + // These conditions are met, but again, note that changing safe code in + // this module can result in unsoundness if any of these constraints are + // violated. + unsafe { drop(Box::from_raw(self.0)) } } } @@ -171,7 +186,7 @@ impl SqliteTable { .unwrap(), )); - let statement_ref = NonAliasingBox(statement); + let statement_ref = AliasableBox(statement); //let name = self.name.clone(); @@ -270,7 +285,7 @@ impl KvTree for SqliteTable { .unwrap(), )); - let statement_ref = NonAliasingBox(statement); + let statement_ref = AliasableBox(statement); let iterator = Box::new( statement @@ -292,7 +307,7 @@ impl KvTree for SqliteTable { .unwrap(), )); - let statement_ref = NonAliasingBox(statement); + let statement_ref = AliasableBox(statement); let iterator = Box::new( statement From da842c649946b5f7a6065583913e319411f5ead8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 12 May 2024 19:14:59 -0700 Subject: [PATCH 041/617] enable `unneeded_field_pattern` lint --- Cargo.toml | 1 + src/api/client_server/media.rs | 4 +--- src/api/client_server/session.rs | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 29b73a30..1de818ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ undocumented_unsafe_blocks = "warn" unnecessary_safety_comment = "warn" unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" +unneeded_field_pattern = "warn" unseparated_literal_suffix = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 493c31da..2a0f05e7 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -130,9 +130,7 @@ pub(crate) async fn get_content_as_filename_route( let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { - content_disposition: _, - content_type, - file, + content_type, file, .. }) = services().media.get(mxc.clone()).await? { Ok(get_content_as_filename::v3::Response { diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 0b2c2c42..f0cf9b6b 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -51,8 +51,7 @@ pub(crate) async fn login_route(body: Ruma) -> Result { let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier { UserId::parse_with_server_name( From 0f2cf26a3630777fe35b5a1b8f8f640bb2e78468 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 16:08:26 -0700 Subject: [PATCH 042/617] enable `default_trait_access` lint --- Cargo.toml | 1 + src/api/client_server/account.rs | 8 ++++---- src/api/client_server/device.rs | 4 ++-- src/api/client_server/keys.rs | 2 +- src/api/client_server/session.rs | 12 +++++++++--- src/service/rooms/threads.rs | 4 ++-- src/service/rooms/timeline.rs | 2 +- 7 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1de818ac..02c1cfe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ as_conversions = "warn" assertions_on_result_states = "warn" cloned_instead_of_copied = "warn" dbg_macro = "warn" +default_trait_access = "warn" default_union_representation = "warn" deref_by_slicing = "warn" empty_drop = "warn" diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 2368ae92..784a00bb 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -151,7 +151,7 @@ pub(crate) async fn register_route( stages: vec![AuthType::RegistrationToken], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -163,7 +163,7 @@ pub(crate) async fn register_route( stages: vec![AuthType::Dummy], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -320,7 +320,7 @@ pub(crate) async fn change_password_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -407,7 +407,7 @@ pub(crate) async fn deactivate_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 03f55d77..d63dd636 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -83,7 +83,7 @@ pub(crate) async fn delete_device_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -137,7 +137,7 @@ pub(crate) async fn delete_devices_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 3ea66d7d..97ba84ff 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -108,7 +108,7 @@ pub(crate) async fn upload_signing_keys_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index f0cf9b6b..9d1cc552 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -3,7 +3,13 @@ use crate::{services, utils, Error, Result, Ruma}; use ruma::{ api::client::{ error::ErrorKind, - session::{get_login_types, login, logout, logout_all}, + session::{ + get_login_types::{ + self, + v3::{ApplicationServiceLoginType, PasswordLoginType}, + }, + login, logout, logout_all, + }, uiaa::UserIdentifier, }, UserId, @@ -25,8 +31,8 @@ pub(crate) async fn get_login_types_route( _body: Ruma, ) -> Result { Ok(get_login_types::v3::Response::new(vec![ - get_login_types::v3::LoginType::Password(Default::default()), - get_login_types::v3::LoginType::ApplicationService(Default::default()), + get_login_types::v3::LoginType::Password(PasswordLoginType::default()), + get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()), ])) } diff --git a/src/service/rooms/threads.rs b/src/service/rooms/threads.rs index d7690c08..99f28c8d 100644 --- a/src/service/rooms/threads.rs +++ b/src/service/rooms/threads.rs @@ -4,7 +4,7 @@ pub(crate) use data::Data; use ruma::{ api::client::{error::ErrorKind, threads::get_threads::v1::IncludeThreads}, events::relation::BundledThread, - uint, CanonicalJsonValue, EventId, RoomId, UserId, + uint, CanonicalJsonObject, CanonicalJsonValue, EventId, RoomId, UserId, }; use serde_json::json; @@ -56,7 +56,7 @@ impl Service { if let CanonicalJsonValue::Object(unsigned) = root_pdu_json .entry("unsigned".to_owned()) - .or_insert_with(|| CanonicalJsonValue::Object(Default::default())) + .or_insert_with(|| CanonicalJsonValue::Object(CanonicalJsonObject::default())) { if let Some(mut relations) = unsigned .get("m.relations") diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index bafcec29..a8577f5a 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -214,7 +214,7 @@ impl Service { if let Some(state_key) = &pdu.state_key { if let CanonicalJsonValue::Object(unsigned) = pdu_json .entry("unsigned".to_owned()) - .or_insert_with(|| CanonicalJsonValue::Object(Default::default())) + .or_insert_with(|| CanonicalJsonValue::Object(CanonicalJsonObject::default())) { if let Some(shortstatehash) = services() .rooms From da440934bda39f0609e3accfd0b728a53555f520 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 16:34:10 -0700 Subject: [PATCH 043/617] enable `doc_markdown` lint --- Cargo.toml | 1 + clippy.toml | 4 ++++ src/api/client_server/account.rs | 10 +++++----- src/api/client_server/context.rs | 2 +- src/api/client_server/message.rs | 2 +- src/api/client_server/profile.rs | 8 ++++---- src/api/client_server/room.rs | 4 ++-- src/api/client_server/space.rs | 2 +- src/api/client_server/state.rs | 4 ++-- src/api/client_server/sync.rs | 6 +++--- src/api/server_server.rs | 6 +++--- src/database/key_value/rooms/short.rs | 2 +- src/database/key_value/users.rs | 14 +++++++------- src/service/media/data.rs | 2 +- src/service/rooms/edus/read_receipt/data.rs | 2 +- src/service/rooms/edus/typing.rs | 2 +- src/service/rooms/short.rs | 2 +- src/service/rooms/short/data.rs | 2 +- src/service/rooms/state/data.rs | 4 ++-- src/service/users.rs | 8 ++++---- src/service/users/data.rs | 10 +++++----- 21 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 clippy.toml diff --git a/Cargo.toml b/Cargo.toml index 02c1cfe2..f7cb3022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ dbg_macro = "warn" default_trait_access = "warn" default_union_representation = "warn" deref_by_slicing = "warn" +doc_markdown = "warn" empty_drop = "warn" empty_structs_with_brackets = "warn" error_impl_error = "warn" diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..a6330574 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +doc-valid-idents = [ + "SemVer", + "..", +] diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 784a00bb..e10d83a5 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -69,11 +69,11 @@ pub(crate) async fn get_register_available_route( /// to check if the user id is valid and available. /// /// - Only works if registration is enabled -/// - If type is guest: ignores all parameters except initial_device_display_name +/// - If type is guest: ignores all parameters except `initial_device_display_name` /// - If sender is not appservice: Requires UIAA (but we only use a dummy stage) /// - If type is not guest and no username is given: Always fails after UIAA check /// - Creates a new account and populates it with default account data -/// - If `inhibit_login` is false: Creates a device and returns device id and access_token +/// - If `inhibit_login` is false: Creates a device and returns `device_id` and `access_token` pub(crate) async fn register_route( body: Ruma, ) -> Result { @@ -304,9 +304,9 @@ pub(crate) async fn register_route( /// - The password hash is calculated using argon2 with 32 character salt, the plain password is /// not saved /// -/// If logout_devices is true it does the following for each device except the sender device: +/// If `logout_devices` is true it does the following for each device except the sender device: /// - Invalidates access token -/// - Deletes device metadata (device id, device display name, last seen ip, last seen ts) +/// - Deletes device metadata (device ID, device display name, last seen IP, last seen timestamp) /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn change_password_route( @@ -372,7 +372,7 @@ pub(crate) async fn change_password_route( /// # `GET _matrix/client/r0/account/whoami` /// -/// Get user_id of the sender user. +/// Get `user_id` of the sender user. /// /// Note: Also works for Application Services pub(crate) async fn whoami_route(body: Ruma) -> Result { diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index db83fc8f..a91f536a 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -12,7 +12,7 @@ use tracing::error; /// Allows loading room history around an event. /// /// - Only works if the user is joined (TODO: always allow, but only show events if the user was -/// joined, depending on history_visibility) +/// joined, depending on `history_visibility`) pub(crate) async fn get_context_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 1d86cf2b..dec75e7a 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -111,7 +111,7 @@ pub(crate) async fn send_message_event_route( /// Allows paginating through room history. /// /// - Only works if the user is joined (TODO: always allow, but only show events where the user was -/// joined, depending on history_visibility) +/// joined, depending on `history_visibility`) pub(crate) async fn get_message_events_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index 3a86d81d..3e9806cd 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -130,7 +130,7 @@ pub(crate) async fn get_displayname_route( /// # `PUT /_matrix/client/r0/profile/{userId}/avatar_url` /// -/// Updates the avatar_url and blurhash. +/// Updates the `avatar_url` and `blurhash`. /// /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_avatar_url_route( @@ -217,9 +217,9 @@ pub(crate) async fn set_avatar_url_route( /// # `GET /_matrix/client/r0/profile/{userId}/avatar_url` /// -/// Returns the avatar_url and blurhash of the user. +/// Returns the `avatar_url` and `blurhash` of the user. /// -/// - If user is on another server: Fetches avatar_url and blurhash over federation +/// - If user is on another server: Fetches `avatar_url` and `blurhash` over federation pub(crate) async fn get_avatar_url_route( body: Ruma, ) -> Result { @@ -249,7 +249,7 @@ pub(crate) async fn get_avatar_url_route( /// # `GET /_matrix/client/r0/profile/{userId}` /// -/// Returns the displayname, avatar_url and blurhash of the user. +/// Returns the `displayname`, `avatar_url` and `blurhash` of the user. /// /// - If user is on another server: Fetches profile over federation pub(crate) async fn get_profile_route( diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index f3edf62d..420e7110 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -34,7 +34,7 @@ use tracing::{info, warn}; /// Creates a new room. /// /// - Room ID is randomly generated -/// - Create alias if room_alias_name is set +/// - Create alias if `room_alias_name` is set /// - Send create event /// - Join sender user /// - Send power levels event @@ -543,7 +543,7 @@ pub(crate) async fn get_room_event_route( /// /// Lists all aliases of the room. /// -/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable +/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if `history_visibility` is world readable pub(crate) async fn get_room_aliases_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index a05d5c36..43463184 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,7 +1,7 @@ use crate::{services, Result, Ruma}; use ruma::{api::client::space::get_hierarchy, uint}; -/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`` +/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy` /// /// Paginates over the space tree in a depth-first manner to locate child rooms of a given space. pub(crate) async fn get_hierarchy_route( diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 97d9821e..35d78dc3 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -20,7 +20,7 @@ use tracing::log::warn; /// /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed -/// - If event is new canonical_alias: Rejects if alias is incorrect +/// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_key_route( body: Ruma, ) -> Result { @@ -45,7 +45,7 @@ pub(crate) async fn send_state_event_for_key_route( /// /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed -/// - If event is new canonical_alias: Rejects if alias is incorrect +/// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_empty_key_route( body: Ruma, ) -> Result> { diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index a7167d26..4004bc42 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -46,8 +46,8 @@ use tracing::{debug, error, info}; /// /// Calling this endpoint with a `since` parameter from a previous `next_batch` returns: /// For joined rooms: -/// - Some of the most recent events of each timeline that happened after since -/// - If user joined the room after since: All state events (unless lazy loading is activated) and +/// - Some of the most recent events of each timeline that happened after `since` +/// - If user joined the room after `since`: All state events (unless lazy loading is activated) and /// all device list updates in that room /// - If the user was already in the room: A list of all events that are in the state now, but were /// not in the state at `since` @@ -61,7 +61,7 @@ use tracing::{debug, error, info}; /// - If the user was invited after `since`: A subset of the state of the room at the point of the invite /// /// For left rooms: -/// - If the user left after `since`: prev_batch token, empty state (TODO: subset of the state at the point of the leave) +/// - If the user left after `since`: `prev_batch` token, empty state (TODO: subset of the state at the point of the leave) /// /// - Sync is handled in an async task, multiple requests from the same device with the same /// `since` will be cached diff --git a/src/api/server_server.rs b/src/api/server_server.rs index eae0ed7a..781ee74c 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -61,8 +61,8 @@ use tracing::{debug, error, warn}; /// Wraps either an literal IP address plus port, or a hostname plus complement /// (colon-plus-port if it was specified). /// -/// Note: A `FedDest::Named` might contain an IP address in string form if there -/// was no port specified to construct a SocketAddr with. +/// Note: A [`FedDest::Named`] might contain an IP address in string form if there +/// was no port specified to construct a [`SocketAddr`] with. /// /// # Examples: /// ```rust @@ -340,7 +340,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { FedDest::Named(host.to_owned(), port.to_owned()) } -/// Returns: actual_destination, host header +/// Returns: `actual_destination`, `Host` header /// Implemented according to the specification at /// Numbers in comments below refer to bullet points in linked section of specification async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) { diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 98cfa48a..6ec4dd5f 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -175,7 +175,7 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(result) } - /// Returns (shortstatehash, already_existed) + /// Returns `(shortstatehash, already_existed)` fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { Ok(match self.statehash_shortstatehash.get(state_hash)? { Some(shortstatehash) => ( diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 37b70e01..d2e1ef14 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -118,7 +118,7 @@ impl service::users::Data for KeyValueDatabase { } } - /// Returns the displayname of a user on this homeserver. + /// Returns the `displayname` of a user on this homeserver. fn displayname(&self, user_id: &UserId) -> Result> { self.userid_displayname .get(user_id.as_bytes())? @@ -129,7 +129,7 @@ impl service::users::Data for KeyValueDatabase { }) } - /// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change. + /// Sets a new `displayname` or removes it if `displayname` is `None`. You still need to nofify all rooms of this change. fn set_displayname(&self, user_id: &UserId, displayname: Option) -> Result<()> { if let Some(displayname) = displayname { self.userid_displayname @@ -141,7 +141,7 @@ impl service::users::Data for KeyValueDatabase { Ok(()) } - /// Get the avatar_url of a user. + /// Get the `avatar_url` of a user. fn avatar_url(&self, user_id: &UserId) -> Result> { self.userid_avatarurl .get(user_id.as_bytes())? @@ -153,7 +153,7 @@ impl service::users::Data for KeyValueDatabase { .transpose() } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()> { if let Some(avatar_url) = avatar_url { self.userid_avatarurl @@ -165,7 +165,7 @@ impl service::users::Data for KeyValueDatabase { Ok(()) } - /// Get the blurhash of a user. + /// Get the `blurhash` of a user. fn blurhash(&self, user_id: &UserId) -> Result> { self.userid_blurhash .get(user_id.as_bytes())? @@ -178,7 +178,7 @@ impl service::users::Data for KeyValueDatabase { .transpose() } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { if let Some(blurhash) = blurhash { self.userid_blurhash @@ -954,7 +954,7 @@ impl service::users::Data for KeyValueDatabase { /// Will only return with Some(username) if the password was not empty and the /// username could be successfully parsed. -/// If utils::string_from_bytes(...) returns an error that username will be skipped +/// If [`utils::string_from_bytes`] returns an error that username will be skipped /// and the error will be logged. fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option { // A valid password is not empty diff --git a/src/service/media/data.rs b/src/service/media/data.rs index ce2fc76c..0aea3f9d 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -10,7 +10,7 @@ pub(crate) trait Data: Send + Sync { content_type: Option<&str>, ) -> Result>; - /// Returns content_disposition, content_type and the metadata key. + /// Returns `content_disposition`, `content_type` and the `metadata` key. fn search_file_metadata( &self, mxc: String, diff --git a/src/service/rooms/edus/read_receipt/data.rs b/src/service/rooms/edus/read_receipt/data.rs index c25af24e..366e946c 100644 --- a/src/service/rooms/edus/read_receipt/data.rs +++ b/src/service/rooms/edus/read_receipt/data.rs @@ -10,7 +10,7 @@ pub(crate) trait Data: Send + Sync { event: ReceiptEvent, ) -> Result<()>; - /// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`. + /// Returns an iterator over the most recent read receipts in a room that happened after the event with id `since`. #[allow(clippy::type_complexity)] fn readreceipts_since<'a>( &'a self, diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 873736da..44d3e6af 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -12,7 +12,7 @@ pub(crate) struct Service { } impl Service { - /// Sets a user as typing until the timeout timestamp is reached or roomtyping_remove is + /// Sets a user as typing until the timeout timestamp is reached or `roomtyping_remove` is /// called. pub(crate) async fn typing_add( &self, diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index b2ad4fc5..f8d5be16 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -42,7 +42,7 @@ impl Service { self.db.get_statekey_from_short(shortstatekey) } - /// Returns (shortstatehash, already_existed) + /// Returns `(shortstatehash, already_existed)` pub(crate) fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { self.db.get_or_create_shortstatehash(state_hash) } diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index 2b7465f1..295f11a2 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -22,7 +22,7 @@ pub(crate) trait Data: Send + Sync { fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)>; - /// Returns (shortstatehash, already_existed) + /// Returns `(shortstatehash, already_existed)` fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)>; fn get_shortroomid(&self, room_id: &RoomId) -> Result>; diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index b8dda66b..1074a5dd 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -7,7 +7,7 @@ pub(crate) trait Data: Send + Sync { /// Returns the last state hash key added to the db for the given room. fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result>; - /// Set the state hash to a new version, but does not update state_cache. + /// Set the state hash to a new version, but does not update `state_cache`. fn set_room_state( &self, room_id: &RoomId, @@ -18,7 +18,7 @@ pub(crate) trait Data: Send + Sync { /// Associates a state with an event. fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()>; - /// Returns all events we would send as the prev_events of the next event. + /// Returns all events we would send as the `prev_events` of the next event. fn get_forward_extremities(&self, room_id: &RoomId) -> Result>>; /// Replace the forward extremities of the room. diff --git a/src/service/users.rs b/src/service/users.rs index 282663ea..b63e90bb 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -327,12 +327,12 @@ impl Service { self.db.set_displayname(user_id, displayname) } - /// Get the avatar_url of a user. + /// Get the `avatar_url` of a user. pub(crate) fn avatar_url(&self, user_id: &UserId) -> Result> { self.db.avatar_url(user_id) } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. pub(crate) fn set_avatar_url( &self, user_id: &UserId, @@ -341,12 +341,12 @@ impl Service { self.db.set_avatar_url(user_id, avatar_url) } - /// Get the blurhash of a user. + /// Get the `blurhash` of a user. pub(crate) fn blurhash(&self, user_id: &UserId) -> Result> { self.db.blurhash(user_id) } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. pub(crate) fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { self.db.set_blurhash(user_id, blurhash) } diff --git a/src/service/users/data.rs b/src/service/users/data.rs index 7a9b2610..3a7af883 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -39,19 +39,19 @@ pub(crate) trait Data: Send + Sync { /// Returns the displayname of a user on this homeserver. fn displayname(&self, user_id: &UserId) -> Result>; - /// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change. + /// Sets a new `displayname` or removes it if `displayname` is `None`. You still need to nofify all rooms of this change. fn set_displayname(&self, user_id: &UserId, displayname: Option) -> Result<()>; - /// Get the avatar_url of a user. + /// Get the `avatar_url` of a user. fn avatar_url(&self, user_id: &UserId) -> Result>; - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()>; - /// Get the blurhash of a user. + /// Get the `blurhash` of a user. fn blurhash(&self, user_id: &UserId) -> Result>; - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()>; /// Adds a new device to a user. From cb2cd6e253d351c88b89a6931f205acd5ce915d6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 16:36:45 -0700 Subject: [PATCH 044/617] enable `enum_glob_use` lint --- Cargo.toml | 1 + src/utils/error.rs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f7cb3022..1e00743c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ deref_by_slicing = "warn" doc_markdown = "warn" empty_drop = "warn" empty_structs_with_brackets = "warn" +enum_glob_use = "warn" error_impl_error = "warn" filetype_is_file = "warn" float_cmp_const = "warn" diff --git a/src/utils/error.rs b/src/utils/error.rs index 7d5f9357..ab534c18 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -104,7 +104,11 @@ impl Error { let message = format!("{self}"); - use ErrorKind::*; + use ErrorKind::{ + Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, + ThreepidAuthFailed, ThreepidDenied, TooLarge, Unauthorized, Unknown, UnknownToken, + Unrecognized, UserDeactivated, WrongRoomKeysVersion, + }; let (kind, status_code) = match self { Self::BadRequest(kind, _) => ( kind.clone(), From 703e08947f2a116627f2865a421a696edee029b8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 16:38:21 -0700 Subject: [PATCH 045/617] enable `explicit_into_iter_loop` lint --- Cargo.toml | 1 + src/api/client_server/keys.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1e00743c..cfbef630 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ empty_drop = "warn" empty_structs_with_brackets = "warn" enum_glob_use = "warn" error_impl_error = "warn" +explicit_into_iter_loop = "warn" filetype_is_file = "warn" float_cmp_const = "warn" format_push_string = "warn" diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 97ba84ff..5aeaa879 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -175,7 +175,6 @@ pub(crate) async fn upload_signatures_route( "Invalid signature.", ))? .clone() - .into_iter() { // Signature validation? let signature = ( From a32b7c1ac18c4839a5829dd9fbdf73cb921ade5e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 16:41:04 -0700 Subject: [PATCH 046/617] enable `flat_map_option` lint --- Cargo.toml | 1 + src/api/client_server/sync.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cfbef630..154cbbb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ enum_glob_use = "warn" error_impl_error = "warn" explicit_into_iter_loop = "warn" filetype_is_file = "warn" +flat_map_option = "warn" float_cmp_const = "warn" format_push_string = "warn" get_unwrap = "warn" diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 4004bc42..d1536a81 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1559,7 +1559,7 @@ pub(crate) async fn sync_events_v4_route( let required_state = required_state_request .iter() - .flat_map(|state| { + .filter_map(|state| { services() .rooms .state_accessor @@ -1577,7 +1577,7 @@ pub(crate) async fn sync_events_v4_route( .room_members(room_id) .filter_map(|r| r.ok()) .filter(|member| member != &sender_user) - .flat_map(|member| { + .filter_map(|member| { services() .rooms .state_accessor From 623824dc0c3989d28f4123ca8f552a0f52c7814a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 16:50:15 -0700 Subject: [PATCH 047/617] enable `if_not_else` lint --- Cargo.toml | 1 + src/api/client_server/membership.rs | 544 ++++++++++++++-------------- src/api/client_server/sync.rs | 6 +- src/api/client_server/voip.rs | 12 +- src/service/rooms/spaces.rs | 6 +- src/service/sending.rs | 6 +- 6 files changed, 288 insertions(+), 287 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 154cbbb4..f172bc54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ flat_map_option = "warn" float_cmp_const = "warn" format_push_string = "warn" get_unwrap = "warn" +if_not_else = "warn" if_then_some_else_none = "warn" impl_trait_in_params = "warn" let_underscore_must_use = "warn" diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index ac7b3bc9..44aa25c3 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -545,11 +545,257 @@ async fn join_room_by_id_helper( let state_lock = mutex_state.lock().await; // Ask a remote server if we are not participating in this room - if !services() + if services() .rooms .state_cache .server_in_room(services().globals.server_name(), room_id)? { + info!("We can join locally"); + + let join_rules_event = services().rooms.state_accessor.room_state_get( + room_id, + &StateEventType::RoomJoinRules, + "", + )?; + + let join_rules_event_content: Option = join_rules_event + .as_ref() + .map(|join_rules_event| { + serde_json::from_str(join_rules_event.content.get()).map_err(|e| { + warn!("Invalid join rules event: {}", e); + Error::bad_database("Invalid join rules event in db.") + }) + }) + .transpose()?; + + let restriction_rooms = match join_rules_event_content { + Some(RoomJoinRulesEventContent { + join_rule: JoinRule::Restricted(restricted), + }) + | Some(RoomJoinRulesEventContent { + join_rule: JoinRule::KnockRestricted(restricted), + }) => restricted + .allow + .into_iter() + .filter_map(|a| match a { + AllowRule::RoomMembership(r) => Some(r.room_id), + _ => None, + }) + .collect(), + _ => Vec::new(), + }; + + let authorized_user = if restriction_rooms.iter().any(|restriction_room_id| { + services() + .rooms + .state_cache + .is_joined(sender_user, restriction_room_id) + .unwrap_or(false) + }) { + let mut auth_user = None; + for user in services() + .rooms + .state_cache + .room_members(room_id) + .filter_map(Result::ok) + .collect::>() + { + if user.server_name() == services().globals.server_name() + && services() + .rooms + .state_accessor + .user_can_invite(room_id, &user, sender_user, &state_lock) + .await + .unwrap_or(false) + { + auth_user = Some(user); + break; + } + } + auth_user + } else { + None + }; + + let event = RoomMemberEventContent { + membership: MembershipState::Join, + displayname: services().users.displayname(sender_user)?, + avatar_url: services().users.avatar_url(sender_user)?, + is_direct: None, + third_party_invite: None, + blurhash: services().users.blurhash(sender_user)?, + reason: reason.clone(), + join_authorized_via_users_server: authorized_user, + }; + + // Try normal join first + let error = match services() + .rooms + .timeline + .build_and_append_pdu( + PduBuilder { + event_type: TimelineEventType::RoomMember, + content: to_raw_value(&event).expect("event is valid, we just created it"), + unsigned: None, + state_key: Some(sender_user.to_string()), + redacts: None, + }, + sender_user, + room_id, + &state_lock, + ) + .await + { + Ok(_event_id) => return Ok(join_room_by_id::v3::Response::new(room_id.to_owned())), + Err(e) => e, + }; + + if restriction_rooms.is_empty() + && servers + .iter() + .any(|s| *s != services().globals.server_name()) + { + return Err(error); + } + + info!( + "We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements" + ); + let (make_join_response, remote_server) = + make_join_request(sender_user, room_id, servers).await?; + + let room_version_id = match make_join_response.room_version { + Some(room_version_id) + if services() + .globals + .supported_room_versions() + .contains(&room_version_id) => + { + room_version_id + } + _ => return Err(Error::BadServerResponse("Room version is not supported")), + }; + let mut join_event_stub: CanonicalJsonObject = + serde_json::from_str(make_join_response.event.get()).map_err(|_| { + Error::BadServerResponse("Invalid make_join event json received from server.") + })?; + let join_authorized_via_users_server = join_event_stub + .get("content") + .map(|s| { + s.as_object()? + .get("join_authorised_via_users_server")? + .as_str() + }) + .and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok()); + // TODO: Is origin needed? + join_event_stub.insert( + "origin".to_owned(), + CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), + ); + join_event_stub.insert( + "origin_server_ts".to_owned(), + CanonicalJsonValue::Integer( + utils::millis_since_unix_epoch() + .try_into() + .expect("Timestamp is valid js_int value"), + ), + ); + join_event_stub.insert( + "content".to_owned(), + to_canonical_value(RoomMemberEventContent { + membership: MembershipState::Join, + displayname: services().users.displayname(sender_user)?, + avatar_url: services().users.avatar_url(sender_user)?, + is_direct: None, + third_party_invite: None, + blurhash: services().users.blurhash(sender_user)?, + reason, + join_authorized_via_users_server, + }) + .expect("event is valid, we just created it"), + ); + + // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms + join_event_stub.remove("event_id"); + + // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present + ruma::signatures::hash_and_sign_event( + services().globals.server_name().as_str(), + services().globals.keypair(), + &mut join_event_stub, + &room_version_id, + ) + .expect("event is valid, we just created it"); + + // Generate event id + let event_id = format!( + "${}", + ruma::signatures::reference_hash(&join_event_stub, &room_version_id) + .expect("ruma can calculate reference hashes") + ); + let event_id = <&EventId>::try_from(event_id.as_str()) + .expect("ruma's reference hashes are valid event ids"); + + // Add event_id back + join_event_stub.insert( + "event_id".to_owned(), + CanonicalJsonValue::String(event_id.as_str().to_owned()), + ); + + // It has enough fields to be called a proper event now + let join_event = join_event_stub; + + let send_join_response = services() + .sending + .send_federation_request( + &remote_server, + federation::membership::create_join_event::v2::Request { + room_id: room_id.to_owned(), + event_id: event_id.to_owned(), + pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()), + omit_members: false, + }, + ) + .await?; + + let Some(signed_raw) = send_join_response.room_state.event else { + return Err(error); + }; + + let (signed_event_id, signed_value) = + match gen_event_id_canonical_json(&signed_raw, &room_version_id) { + Ok(t) => t, + Err(_) => { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); + } + }; + + if signed_event_id != event_id { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Server sent event with wrong event id", + )); + } + + drop(state_lock); + let pub_key_map = RwLock::new(BTreeMap::new()); + services() + .rooms + .event_handler + .handle_incoming_pdu( + &remote_server, + &signed_event_id, + room_id, + signed_value, + true, + &pub_key_map, + ) + .await?; + } else { info!("Joining {room_id} over federation."); let (make_join_response, remote_server) = @@ -851,252 +1097,6 @@ async fn join_room_by_id_helper( .rooms .state .set_room_state(room_id, statehash_after_join, &state_lock)?; - } else { - info!("We can join locally"); - - let join_rules_event = services().rooms.state_accessor.room_state_get( - room_id, - &StateEventType::RoomJoinRules, - "", - )?; - - let join_rules_event_content: Option = join_rules_event - .as_ref() - .map(|join_rules_event| { - serde_json::from_str(join_rules_event.content.get()).map_err(|e| { - warn!("Invalid join rules event: {}", e); - Error::bad_database("Invalid join rules event in db.") - }) - }) - .transpose()?; - - let restriction_rooms = match join_rules_event_content { - Some(RoomJoinRulesEventContent { - join_rule: JoinRule::Restricted(restricted), - }) - | Some(RoomJoinRulesEventContent { - join_rule: JoinRule::KnockRestricted(restricted), - }) => restricted - .allow - .into_iter() - .filter_map(|a| match a { - AllowRule::RoomMembership(r) => Some(r.room_id), - _ => None, - }) - .collect(), - _ => Vec::new(), - }; - - let authorized_user = if restriction_rooms.iter().any(|restriction_room_id| { - services() - .rooms - .state_cache - .is_joined(sender_user, restriction_room_id) - .unwrap_or(false) - }) { - let mut auth_user = None; - for user in services() - .rooms - .state_cache - .room_members(room_id) - .filter_map(Result::ok) - .collect::>() - { - if user.server_name() == services().globals.server_name() - && services() - .rooms - .state_accessor - .user_can_invite(room_id, &user, sender_user, &state_lock) - .await - .unwrap_or(false) - { - auth_user = Some(user); - break; - } - } - auth_user - } else { - None - }; - - let event = RoomMemberEventContent { - membership: MembershipState::Join, - displayname: services().users.displayname(sender_user)?, - avatar_url: services().users.avatar_url(sender_user)?, - is_direct: None, - third_party_invite: None, - blurhash: services().users.blurhash(sender_user)?, - reason: reason.clone(), - join_authorized_via_users_server: authorized_user, - }; - - // Try normal join first - let error = match services() - .rooms - .timeline - .build_and_append_pdu( - PduBuilder { - event_type: TimelineEventType::RoomMember, - content: to_raw_value(&event).expect("event is valid, we just created it"), - unsigned: None, - state_key: Some(sender_user.to_string()), - redacts: None, - }, - sender_user, - room_id, - &state_lock, - ) - .await - { - Ok(_event_id) => return Ok(join_room_by_id::v3::Response::new(room_id.to_owned())), - Err(e) => e, - }; - - if !restriction_rooms.is_empty() - && servers - .iter() - .any(|s| *s != services().globals.server_name()) - { - info!( - "We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements" - ); - let (make_join_response, remote_server) = - make_join_request(sender_user, room_id, servers).await?; - - let room_version_id = match make_join_response.room_version { - Some(room_version_id) - if services() - .globals - .supported_room_versions() - .contains(&room_version_id) => - { - room_version_id - } - _ => return Err(Error::BadServerResponse("Room version is not supported")), - }; - let mut join_event_stub: CanonicalJsonObject = - serde_json::from_str(make_join_response.event.get()).map_err(|_| { - Error::BadServerResponse("Invalid make_join event json received from server.") - })?; - let join_authorized_via_users_server = join_event_stub - .get("content") - .map(|s| { - s.as_object()? - .get("join_authorised_via_users_server")? - .as_str() - }) - .and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok()); - // TODO: Is origin needed? - join_event_stub.insert( - "origin".to_owned(), - CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), - ); - join_event_stub.insert( - "origin_server_ts".to_owned(), - CanonicalJsonValue::Integer( - utils::millis_since_unix_epoch() - .try_into() - .expect("Timestamp is valid js_int value"), - ), - ); - join_event_stub.insert( - "content".to_owned(), - to_canonical_value(RoomMemberEventContent { - membership: MembershipState::Join, - displayname: services().users.displayname(sender_user)?, - avatar_url: services().users.avatar_url(sender_user)?, - is_direct: None, - third_party_invite: None, - blurhash: services().users.blurhash(sender_user)?, - reason, - join_authorized_via_users_server, - }) - .expect("event is valid, we just created it"), - ); - - // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms - join_event_stub.remove("event_id"); - - // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present - ruma::signatures::hash_and_sign_event( - services().globals.server_name().as_str(), - services().globals.keypair(), - &mut join_event_stub, - &room_version_id, - ) - .expect("event is valid, we just created it"); - - // Generate event id - let event_id = format!( - "${}", - ruma::signatures::reference_hash(&join_event_stub, &room_version_id) - .expect("ruma can calculate reference hashes") - ); - let event_id = <&EventId>::try_from(event_id.as_str()) - .expect("ruma's reference hashes are valid event ids"); - - // Add event_id back - join_event_stub.insert( - "event_id".to_owned(), - CanonicalJsonValue::String(event_id.as_str().to_owned()), - ); - - // It has enough fields to be called a proper event now - let join_event = join_event_stub; - - let send_join_response = services() - .sending - .send_federation_request( - &remote_server, - federation::membership::create_join_event::v2::Request { - room_id: room_id.to_owned(), - event_id: event_id.to_owned(), - pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()), - omit_members: false, - }, - ) - .await?; - - if let Some(signed_raw) = send_join_response.room_state.event { - let (signed_event_id, signed_value) = - match gen_event_id_canonical_json(&signed_raw, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } - }; - - if signed_event_id != event_id { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Server sent event with wrong event id", - )); - } - - drop(state_lock); - let pub_key_map = RwLock::new(BTreeMap::new()); - services() - .rooms - .event_handler - .handle_incoming_pdu( - &remote_server, - &signed_event_id, - room_id, - signed_value, - true, - &pub_key_map, - ) - .await?; - } else { - return Err(error); - } - } else { - return Err(error); - } } Ok(join_room_by_id::v3::Response::new(room_id.to_owned())) @@ -1414,35 +1414,11 @@ pub(crate) async fn leave_room( reason: Option, ) -> Result<()> { // Ask a remote server if we don't have this room - if !services() + if services() .rooms .state_cache .server_in_room(services().globals.server_name(), room_id)? { - if let Err(e) = remote_leave_room(user_id, room_id).await { - warn!("Failed to leave room {} remotely: {}", user_id, e); - // Don't tell the client about this error - } - - let last_state = services() - .rooms - .state_cache - .invite_state(user_id, room_id)? - .map_or_else( - || services().rooms.state_cache.left_state(user_id, room_id), - |s| Ok(Some(s)), - )?; - - // We always drop the invite, we can't rely on other servers - services().rooms.state_cache.update_membership( - room_id, - user_id, - MembershipState::Leave, - user_id, - last_state, - true, - )?; - } else { let mutex_state = Arc::clone( services() .globals @@ -1501,6 +1477,30 @@ pub(crate) async fn leave_room( &state_lock, ) .await?; + } else { + if let Err(e) = remote_leave_room(user_id, room_id).await { + warn!("Failed to leave room {} remotely: {}", user_id, e); + // Don't tell the client about this error + } + + let last_state = services() + .rooms + .state_cache + .invite_state(user_id, room_id)? + .map_or_else( + || services().rooms.state_cache.left_state(user_id, room_id), + |s| Ok(Some(s)), + )?; + + // We always drop the invite, we can't rely on other servers + services().rooms.state_cache.update_membership( + room_id, + user_id, + MembershipState::Leave, + user_id, + last_state, + true, + )?; } Ok(()) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index d1536a81..08bf754e 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -94,7 +94,9 @@ pub(crate) async fn sync_events_route( rx } Entry::Occupied(mut o) => { - if o.get().0 != body.since { + if o.get().0 == body.since { + o.get().1.clone() + } else { let (tx, rx) = tokio::sync::watch::channel(None); o.insert((body.since.clone(), rx.clone())); @@ -109,8 +111,6 @@ pub(crate) async fn sync_events_route( )); rx - } else { - o.get().1.clone() } } }; diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index f7bce1a6..657a6d20 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -17,7 +17,12 @@ pub(crate) async fn turn_server_route( let turn_secret = services().globals.turn_secret().clone(); - let (username, password) = if !turn_secret.is_empty() { + let (username, password) = if turn_secret.is_empty() { + ( + services().globals.turn_username().clone(), + services().globals.turn_password().clone(), + ) + } else { let expiry = SecondsSinceUnixEpoch::from_system_time( SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()), ) @@ -32,11 +37,6 @@ pub(crate) async fn turn_server_route( let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes()); (username, password) - } else { - ( - services().globals.turn_username().clone(), - services().globals.turn_password().clone(), - ) }; Ok(get_turn_server_info::v3::Response { diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 27d00c30..10fba1c2 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -66,10 +66,10 @@ impl Service { while stack.last().map_or(false, |s| s.is_empty()) { stack.pop(); } - if !stack.is_empty() { - stack.last_mut().and_then(|s| s.pop()) - } else { + if stack.is_empty() { None + } else { + stack.last_mut().and_then(|s| s.pop()) } } { rooms_in_path.push(current_room.clone()); diff --git a/src/service/sending.rs b/src/service/sending.rs index 1e6e233e..30c04b1b 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -160,7 +160,9 @@ impl Service { // Find events that have been added since starting the last request let new_events = self.db.queued_requests(&outgoing_kind).filter_map(|r| r.ok()).take(30).collect::>(); - if !new_events.is_empty() { + if new_events.is_empty() { + current_transaction_status.remove(&outgoing_kind); + } else { // Insert pdus we found self.db.mark_as_active(&new_events)?; @@ -170,8 +172,6 @@ impl Service { new_events.into_iter().map(|(event, _)| event).collect(), ) ); - } else { - current_transaction_status.remove(&outgoing_kind); } } Err((outgoing_kind, _)) => { From b0f65913f3b688b1d670345bcba12553e0e49384 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:03:46 -0700 Subject: [PATCH 048/617] enable `ignored_unit_patterns` lint --- Cargo.toml | 1 + src/database/key_value/pusher.rs | 5 +---- src/main.rs | 4 ++-- src/service/admin.rs | 2 +- src/service/rooms/timeline.rs | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f172bc54..c04bb52c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ format_push_string = "warn" get_unwrap = "warn" if_not_else = "warn" if_then_some_else_none = "warn" +ignored_unit_patterns = "warn" impl_trait_in_params = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" diff --git a/src/database/key_value/pusher.rs b/src/database/key_value/pusher.rs index 50a6faca..cf61a4a0 100644 --- a/src/database/key_value/pusher.rs +++ b/src/database/key_value/pusher.rs @@ -22,10 +22,7 @@ impl service::pusher::Data for KeyValueDatabase { let mut key = sender.as_bytes().to_vec(); key.push(0xff); key.extend_from_slice(ids.pushkey.as_bytes()); - self.senderkey_pusher - .remove(&key) - .map(|_| ()) - .map_err(Into::into) + self.senderkey_pusher.remove(&key).map_err(Into::into) } } } diff --git a/src/main.rs b/src/main.rs index 91588c88..90248e11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -486,8 +486,8 @@ async fn shutdown_signal(handle: ServerHandle) { let sig: &str; tokio::select! { - _ = ctrl_c => { sig = "Ctrl+C"; }, - _ = terminate => { sig = "SIGTERM"; }, + () = ctrl_c => { sig = "Ctrl+C"; }, + () = terminate => { sig = "SIGTERM"; }, } warn!("Received {}, shutting down...", sig); diff --git a/src/service/admin.rs b/src/service/admin.rs index cad796c0..d692c809 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -842,7 +842,7 @@ impl Service { let pub_key_map = pub_key_map.read().await; match ruma::signatures::verify_json(&pub_key_map, &value) { - Ok(_) => RoomMessageEventContent::text_plain("Signature correct"), + Ok(()) => RoomMessageEventContent::text_plain("Signature correct"), Err(e) => RoomMessageEventContent::text_plain(format!( "Signature verification failed: {e}" )), diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index a8577f5a..7be88ecb 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -763,7 +763,7 @@ impl Service { &mut pdu_json, &room_version_id, ) { - Ok(_) => {} + Ok(()) => {} Err(e) => { return match e { ruma::signatures::Error::PduSize => Err(Error::BadRequest( From ebae8ceeb0220071e950ada987bb3500ae7d8545 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:08:36 -0700 Subject: [PATCH 049/617] enable `implicit_clone` lint --- Cargo.toml | 1 + src/api/client_server/alias.rs | 2 +- src/api/client_server/backup.rs | 2 +- src/api/client_server/read_marker.rs | 2 +- src/api/client_server/state.rs | 4 ++-- src/api/client_server/sync.rs | 2 +- src/api/ruma_wrapper/axum.rs | 2 +- src/api/server_server.rs | 4 ++-- src/database/key_value/globals.rs | 2 +- src/database/key_value/rooms/alias.rs | 2 +- src/database/key_value/rooms/state.rs | 2 +- src/service/admin.rs | 4 ++-- src/service/media.rs | 8 ++++---- src/service/rooms/event_handler.rs | 2 +- src/service/rooms/spaces.rs | 4 ++-- src/service/rooms/timeline.rs | 2 +- src/service/sending.rs | 2 +- 17 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c04bb52c..ef81655c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ if_not_else = "warn" if_then_some_else_none = "warn" ignored_unit_patterns = "warn" impl_trait_in_params = "warn" +implicit_clone = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" mem_forget = "warn" diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 45eff66c..6bba5f6b 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -121,7 +121,7 @@ pub(crate) async fn get_alias_helper( .send_federation_request( room_alias.server_name(), federation::query::get_room_information::v1::Request { - room_alias: room_alias.to_owned(), + room_alias: room_alias.clone(), }, ) .await?; diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index 709ed73a..cf7f5468 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -91,7 +91,7 @@ pub(crate) async fn get_backup_info_route( etag: services() .key_backups .get_etag(sender_user, &body.version)?, - version: body.version.to_owned(), + version: body.version.clone(), }) } diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index fb614bc9..a1a7bbbf 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -140,7 +140,7 @@ pub(crate) async fn create_receipt_route( receipts.insert(ReceiptType::Read, user_receipts); let mut receipt_content = BTreeMap::new(); - receipt_content.insert(body.event_id.to_owned(), receipts); + receipt_content.insert(body.event_id.clone(), receipts); services().rooms.edus.read_receipt.readreceipt_update( sender_user, diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 35d78dc3..8ac33241 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -31,7 +31,7 @@ pub(crate) async fn send_state_event_for_key_route( &body.room_id, &body.event_type, &body.body.body, // Yes, I hate it too - body.state_key.to_owned(), + body.state_key.clone(), ) .await?; @@ -64,7 +64,7 @@ pub(crate) async fn send_state_event_for_empty_key_route( &body.room_id, &body.event_type.to_string().into(), &body.body.body, - body.state_key.to_owned(), + body.state_key.clone(), ) .await?; diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 08bf754e..4d11f272 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -82,7 +82,7 @@ pub(crate) async fn sync_events_route( Entry::Vacant(v) => { let (tx, rx) = tokio::sync::watch::channel(None); - v.insert((body.since.to_owned(), rx.clone())); + v.insert((body.since.clone(), rx.clone())); tokio::spawn(sync_helper_wrapper( sender_user.clone(), diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index c1ebaa7a..30a4caf0 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -217,7 +217,7 @@ where let keys_result = services() .rooms .event_handler - .fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.to_owned()]) + .fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.clone()]) .await; let keys = match keys_result { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 781ee74c..fdb35374 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -727,7 +727,7 @@ pub(crate) async fn send_transaction_message_route( .roomid_mutex_federation .write() .await - .entry(room_id.to_owned()) + .entry(room_id.clone()) .or_default(), ); let mutex_lock = mutex.lock().await; @@ -1374,7 +1374,7 @@ pub(crate) async fn create_join_event_template_route( .roomid_mutex_state .write() .await - .entry(body.room_id.to_owned()) + .entry(body.room_id.clone()) .or_default(), ); let state_lock = mutex_state.lock().await; diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 1f47a445..10748ccd 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -191,7 +191,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" self.global.insert(b"keypair", &keypair)?; Ok::<_, Error>(keypair) }, - |s| Ok(s.to_vec()), + |s| Ok(s.clone()), )?; let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff); diff --git a/src/database/key_value/rooms/alias.rs b/src/database/key_value/rooms/alias.rs index 6f230323..a4666834 100644 --- a/src/database/key_value/rooms/alias.rs +++ b/src/database/key_value/rooms/alias.rs @@ -15,7 +15,7 @@ impl service::rooms::alias::Data for KeyValueDatabase { fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { if let Some(room_id) = self.alias_roomid.get(alias.alias().as_bytes())? { - let mut prefix = room_id.to_vec(); + let mut prefix = room_id.clone(); prefix.push(0xff); for (key, _) in self.aliasid_alias.scan_prefix(prefix) { diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index 6865d7b9..fd0c81e6 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -63,7 +63,7 @@ impl service::rooms::state::Data for KeyValueDatabase { } for event_id in event_ids { - let mut key = prefix.to_owned(); + let mut key = prefix.clone(); key.extend_from_slice(event_id.as_bytes()); self.roomid_pduleaves.insert(&key, event_id.as_bytes())?; } diff --git a/src/service/admin.rs b/src/service/admin.rs index d692c809..58ef401a 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -230,7 +230,7 @@ impl Service { .roomid_mutex_state .write() .await - .entry(grapevine_room.to_owned()) + .entry(grapevine_room.clone()) .or_default(), ); @@ -1273,7 +1273,7 @@ impl Service { // Set power level let mut users = BTreeMap::new(); - users.insert(grapevine_user.to_owned(), 100.into()); + users.insert(grapevine_user.clone(), 100.into()); users.insert(user_id.to_owned(), 100.into()); services() diff --git a/src/service/media.rs b/src/service/media.rs index 3dc764e1..c19497cf 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -128,7 +128,7 @@ impl Service { Ok(Some(FileMeta { content_disposition, content_type, - file: file.to_vec(), + file: file.clone(), })) } else if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc.clone(), 0, 0) @@ -145,7 +145,7 @@ impl Service { return Ok(Some(FileMeta { content_disposition, content_type, - file: file.to_vec(), + file: file.clone(), })); } @@ -211,14 +211,14 @@ impl Service { Ok(Some(FileMeta { content_disposition, content_type, - file: thumbnail_bytes.to_vec(), + file: thumbnail_bytes.clone(), })) } else { // Couldn't parse file to generate thumbnail, send original Ok(Some(FileMeta { content_disposition, content_type, - file: file.to_vec(), + file: file.clone(), })) } } else { diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 261811d4..92538227 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -99,7 +99,7 @@ impl Service { // 1. Skip the PDU if we already have it as a timeline event if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? { - return Ok(Some(pdu_id.to_vec())); + return Ok(Some(pdu_id.clone())); } let create_event = services() diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 10fba1c2..74891e30 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -81,7 +81,7 @@ impl Service { .roomid_spacechunk_cache .lock() .await - .get_mut(¤t_room.to_owned()) + .get_mut(¤t_room.clone()) .as_ref() { if let Some(cached) = cached { @@ -202,7 +202,7 @@ impl Service { .send_federation_request( server, federation::space::get_hierarchy::v1::Request { - room_id: current_room.to_owned(), + room_id: current_room.clone(), suggested_only, }, ) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 7be88ecb..6947eabb 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -1180,7 +1180,7 @@ impl Service { .roomid_mutex_federation .write() .await - .entry(room_id.to_owned()) + .entry(room_id.clone()) .or_default(), ); let mutex_lock = mutex.lock().await; diff --git a/src/service/sending.rs b/src/service/sending.rs index 30c04b1b..9f82a716 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -409,7 +409,7 @@ impl Service { )?; for ((outgoing_kind, event), key) in requests.into_iter().zip(keys) { self.sender - .send((outgoing_kind.to_owned(), event, key)) + .send((outgoing_kind.clone(), event, key)) .unwrap(); } From 5c9967f89c334be897f035abf9ed6c3945374093 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:11:37 -0700 Subject: [PATCH 050/617] enable `inconsistent_struct_constructor` lint --- Cargo.toml | 1 + src/api/client_server/keys.rs | 4 ++-- src/api/ruma_wrapper/axum.rs | 2 +- src/api/server_server.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef81655c..3ee99811 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ if_then_some_else_none = "warn" ignored_unit_patterns = "warn" impl_trait_in_params = "warn" implicit_clone = "warn" +inconsistent_struct_constructor = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" mem_forget = "warn" diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 5aeaa879..10ef99bc 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -439,11 +439,11 @@ pub(crate) async fn get_keys_helper bool>( } Ok(get_keys::v3::Response { + failures, + device_keys, master_keys, self_signing_keys, user_signing_keys, - device_keys, - failures, }) } diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 30a4caf0..84209ca7 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -325,8 +325,8 @@ where sender_user, sender_device, sender_servername, - appservice_info, json_body, + appservice_info, }) } } diff --git a/src/api/server_server.rs b/src/api/server_server.rs index fdb35374..c28e8bea 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1853,9 +1853,9 @@ pub(crate) async fn get_profile_information_route( } Ok(get_profile_information::v1::Response { - blurhash, displayname, avatar_url, + blurhash, }) } From c51e87ec9ad3a13a5c287e79ccba2b0ab2755cd1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:14:43 -0700 Subject: [PATCH 051/617] enable `items_after_statements` lint --- Cargo.toml | 1 + src/service/rooms/timeline.rs | 31 ++++++++++++++++--------------- src/utils/error.rs | 11 ++++++----- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3ee99811..d206ef29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ ignored_unit_patterns = "warn" impl_trait_in_params = "warn" implicit_clone = "warn" inconsistent_struct_constructor = "warn" +items_after_statements = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" mem_forget = "warn" diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 6947eabb..e03dec8e 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -504,21 +504,6 @@ impl Service { } // Update Relationships - #[derive(Deserialize)] - struct ExtractRelatesTo { - #[serde(rename = "m.relates_to")] - relates_to: Relation, - } - - #[derive(Clone, Debug, Deserialize)] - struct ExtractEventId { - event_id: OwnedEventId, - } - #[derive(Clone, Debug, Deserialize)] - struct ExtractRelatesToEventId { - #[serde(rename = "m.relates_to")] - relates_to: ExtractEventId, - } if let Ok(content) = serde_json::from_str::(pdu.content.get()) { if let Some(related_pducount) = services() @@ -1250,6 +1235,22 @@ impl Service { } } +#[derive(Deserialize)] +struct ExtractRelatesTo { + #[serde(rename = "m.relates_to")] + relates_to: Relation, +} + +#[derive(Clone, Debug, Deserialize)] +struct ExtractEventId { + event_id: OwnedEventId, +} +#[derive(Clone, Debug, Deserialize)] +struct ExtractRelatesToEventId { + #[serde(rename = "m.relates_to")] + relates_to: ExtractEventId, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/utils/error.rs b/src/utils/error.rs index ab534c18..4554bd53 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -89,6 +89,12 @@ impl Error { } pub(crate) fn to_response(&self) -> RumaResponse { + use ErrorKind::{ + Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, + ThreepidAuthFailed, ThreepidDenied, TooLarge, Unauthorized, Unknown, UnknownToken, + Unrecognized, UserDeactivated, WrongRoomKeysVersion, + }; + if let Self::Uiaa(uiaainfo) = self { return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone())); } @@ -104,11 +110,6 @@ impl Error { let message = format!("{self}"); - use ErrorKind::{ - Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, - ThreepidAuthFailed, ThreepidDenied, TooLarge, Unauthorized, Unknown, UnknownToken, - Unrecognized, UserDeactivated, WrongRoomKeysVersion, - }; let (kind, status_code) = match self { Self::BadRequest(kind, _) => ( kind.clone(), From 9606f59141c0114679d77b6357c60bfb6d7f8445 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:34:43 -0700 Subject: [PATCH 052/617] enable `manual_let_else` lint --- Cargo.toml | 1 + src/api/appservice_server.rs | 7 +- src/api/client_server/alias.rs | 13 +-- src/api/client_server/context.rs | 18 ++-- src/api/client_server/filter.rs | 5 +- src/api/client_server/membership.rs | 72 ++++++------- src/api/client_server/report.rs | 13 +-- src/api/client_server/sync.rs | 93 ++++++---------- src/api/server_server.rs | 30 +++--- src/database.rs | 9 +- src/database/key_value/rooms/search.rs | 9 +- .../key_value/rooms/state_accessor.rs | 7 +- src/service/rooms/event_handler.rs | 100 +++++++----------- src/service/rooms/state.rs | 21 ++-- src/service/rooms/state_accessor.rs | 10 +- src/service/sending.rs | 7 +- src/service/uiaa.rs | 13 +-- 17 files changed, 165 insertions(+), 263 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d206ef29..9a60da04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ inconsistent_struct_constructor = "warn" items_after_statements = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" +manual_let_else = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index 0c29472d..8591abb3 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -17,11 +17,8 @@ pub(crate) async fn send_request( where T: Debug, { - let destination = match registration.url { - Some(url) => url, - None => { - return Ok(None); - } + let Some(destination) = registration.url else { + return Ok(None); }; let hs_token = registration.hs_token.as_str(); diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 6bba5f6b..38bbe85b 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -166,14 +166,11 @@ pub(crate) async fn get_alias_helper( } }; - let room_id = match room_id { - Some(room_id) => room_id, - None => { - return Err(Error::BadRequest( - ErrorKind::NotFound, - "Room with alias not found.", - )) - } + let Some(room_id) = room_id else { + return Err(Error::BadRequest( + ErrorKind::NotFound, + "Room with alias not found.", + )); }; Ok(get_alias::v3::Response::new( diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index a91f536a..3957e83a 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -178,21 +178,15 @@ pub(crate) async fn get_context_route( .get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; state.push(pdu.to_state_event()); } else if !lazy_load_enabled || lazy_loaded.contains(&state_key) { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; state.push(pdu.to_state_event()); } diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index 5293a95e..0ceefe40 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -13,9 +13,8 @@ pub(crate) async fn get_filter_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let filter = match services().users.get_filter(sender_user, &body.filter_id)? { - Some(filter) => filter, - None => return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found.")), + let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? else { + return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found.")); }; Ok(get_filter::v3::Response::new(filter)) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 44aa25c3..632b9ff2 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -762,17 +762,15 @@ async fn join_room_by_id_helper( return Err(error); }; - let (signed_event_id, signed_value) = - match gen_event_id_canonical_json(&signed_raw, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } - }; + let Ok((signed_event_id, signed_value)) = + gen_event_id_canonical_json(&signed_raw, &room_version_id) + else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); + }; if signed_event_id != event_id { return Err(Error::BadRequest( @@ -905,17 +903,15 @@ async fn join_room_by_id_helper( if let Some(signed_raw) = &send_join_response.room_state.event { info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event"); - let (signed_event_id, signed_value) = - match gen_event_id_canonical_json(signed_raw, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } - }; + let Ok((signed_event_id, signed_value)) = + gen_event_id_canonical_json(signed_raw, &room_version_id) + else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); + }; if signed_event_id != event_id { return Err(Error::BadRequest( @@ -975,9 +971,8 @@ async fn join_room_by_id_helper( .iter() .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) { - let (event_id, value) = match result.await { - Ok(t) => t, - Err(_) => continue, + let Ok((event_id, value)) = result.await else { + continue; }; let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| { @@ -1005,9 +1000,8 @@ async fn join_room_by_id_helper( .iter() .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) { - let (event_id, value) = match result.await { - Ok(t) => t, - Err(_) => continue, + let Ok((event_id, value)) = result.await else { + continue; }; services() @@ -1277,16 +1271,13 @@ pub(crate) async fn invite_helper( let pub_key_map = RwLock::new(BTreeMap::new()); // We do not add the event_id field to the pdu here because of signature and hashes checks - let (event_id, value) = match gen_event_id_canonical_json(&response.event, &room_version_id) - { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } + let Ok((event_id, value)) = gen_event_id_canonical_json(&response.event, &room_version_id) + else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); }; if *pdu.event_id != *event_id { @@ -1395,10 +1386,7 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> { .collect::>(); for room_id in all_rooms { - let room_id = match room_id { - Ok(room_id) => room_id, - Err(_) => continue, - }; + let Ok(room_id) = room_id else { continue }; if let Err(error) = leave_room(user_id, &room_id, None).await { warn!(%user_id, %room_id, %error, "failed to leave room"); diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index af07760d..1f118622 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -14,14 +14,11 @@ pub(crate) async fn report_event_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let pdu = match services().rooms.timeline.get_pdu(&body.event_id)? { - Some(pdu) => pdu, - _ => { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Invalid Event ID", - )) - } + let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid Event ID", + )); }; if let Some(true) = body.score.map(|s| s > int!(0) || s < int!(-100)) { diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 4d11f272..9a7e5440 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -337,28 +337,23 @@ async fn sync_helper( None => HashMap::new(), }; - let left_event_id = match services().rooms.state_accessor.room_state_get_id( + let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id( &room_id, &StateEventType::RoomMember, sender_user.as_str(), - )? { - Some(e) => e, - None => { - error!("Left room but no left state event"); - continue; - } + )? + else { + error!("Left room but no left state event"); + continue; }; - let left_shortstatehash = match services() + let Some(left_shortstatehash) = services() .rooms .state_accessor .pdu_shortstatehash(&left_event_id)? - { - Some(s) => s, - None => { - error!("Leave event has no state"); - continue; - } + else { + error!("Leave event has no state"); + continue; }; let mut left_state_ids = services() @@ -386,12 +381,9 @@ async fn sync_helper( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; left_state_events.push(pdu.to_sync_state_event()); @@ -607,13 +599,11 @@ async fn load_joined_room( // Database queries: - let current_shortstatehash = - if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? { - s - } else { - error!("Room {} has no state", room_id); - return Err(Error::BadDatabase("Room has no state")); - }; + let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? + else { + error!("Room {} has no state", room_id); + return Err(Error::BadDatabase("Room has no state")); + }; let since_shortstatehash = services() .rooms @@ -747,12 +737,9 @@ async fn load_joined_room( .get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; state_events.push(pdu); @@ -766,12 +753,9 @@ async fn load_joined_room( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; // This check is in case a bad user ID made it into the database @@ -836,12 +820,9 @@ async fn load_joined_room( for (key, id) in current_state_ids { if full_state || since_state_ids.get(&key) != Some(&id) { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; if pdu.kind == TimelineEventType::RoomMember { @@ -1234,13 +1215,12 @@ pub(crate) async fn sync_events_v4_route( ); for room_id in &all_joined_rooms { - let current_shortstatehash = - if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? { - s - } else { - error!("Room {} has no state", room_id); - continue; - }; + let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + else { + error!("Room {} has no state", room_id); + continue; + }; let since_shortstatehash = services() .rooms @@ -1302,12 +1282,9 @@ pub(crate) async fn sync_events_v4_route( for (key, id) in current_state_ids { if since_state_ids.get(&key) != Some(&id) { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; if pdu.kind == TimelineEventType::RoomMember { if let Some(state_key) = &pdu.state_key { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index c28e8bea..92bd028d 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -656,15 +656,12 @@ pub(crate) fn parse_incoming_pdu( let room_version_id = services().rooms.state.get_room_version(&room_id)?; - let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } + let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); }; Ok((event_id, value, room_id)) } @@ -1514,15 +1511,12 @@ async fn create_join_event( // We do not add the event_id field to the pdu here because of signature and hashes checks let room_version_id = services().rooms.state.get_room_version(room_id)?; - let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } + let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); }; let origin: OwnedServerName = serde_json::from_value( diff --git a/src/database.rs b/src/database.rs index 94509c79..ff8f47ad 100644 --- a/src/database.rs +++ b/src/database.rs @@ -424,12 +424,9 @@ impl KeyValueDatabase { for (roomserverid, _) in db.roomserverids.iter() { let mut parts = roomserverid.split(|&b| b == 0xff); let room_id = parts.next().expect("split always returns one element"); - let servername = match parts.next() { - Some(s) => s, - None => { - error!("Migration: Invalid roomserverid in db."); - continue; - } + let Some(servername) = parts.next() else { + error!("Migration: Invalid roomserverid in db."); + continue; }; let mut serverroomid = servername.to_vec(); serverroomid.push(0xff); diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index 652c508c..6a52b348 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -54,12 +54,9 @@ impl service::rooms::search::Data for KeyValueDatabase { .map(move |(key, _)| key[prefix3.len()..].to_vec()) }); - let common_elements = match utils::common_elements(iterators, |a, b| { - // We compare b with a because we reversed the iterator earlier - b.cmp(a) - }) { - Some(it) => it, - None => return Ok(None), + // We compare b with a because we reversed the iterator earlier + let Some(common_elements) = utils::common_elements(iterators, |a, b| b.cmp(a)) else { + return Ok(None); }; Ok(Some((Box::new(common_elements), words))) diff --git a/src/database/key_value/rooms/state_accessor.rs b/src/database/key_value/rooms/state_accessor.rs index fe40b937..dc5a112d 100644 --- a/src/database/key_value/rooms/state_accessor.rs +++ b/src/database/key_value/rooms/state_accessor.rs @@ -79,13 +79,12 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { event_type: &StateEventType, state_key: &str, ) -> Result>> { - let shortstatekey = match services() + let Some(shortstatekey) = services() .rooms .short .get_shortstatekey(event_type, state_key)? - { - Some(s) => s, - None => return Ok(None), + else { + return Ok(None); }; let full_state = services() .rooms diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 92538227..5587a303 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -342,14 +342,11 @@ impl Service { Ok(ruma::signatures::Verified::Signatures) => { // Redact warn!("Calculated hash does not match: {}", event_id); - let obj = match ruma::canonical_json::redact(value, room_version_id, None) { - Ok(obj) => obj, - Err(_) => { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Redaction failed", - )) - } + let Ok(obj) = ruma::canonical_json::redact(value, room_version_id, None) else { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Redaction failed", + )); }; // Skip the PDU if it is redacted and we already have it as an outlier event @@ -409,12 +406,9 @@ impl Service { // Build map of auth events let mut auth_events = HashMap::new(); for id in &incoming_pdu.auth_events { - let auth_event = match services().rooms.timeline.get_pdu(id)? { - Some(e) => e, - None => { - warn!("Could not find auth event {}", id); - continue; - } + let Some(auth_event) = services().rooms.timeline.get_pdu(id)? else { + warn!("Could not find auth event {}", id); + continue; }; self.check_room_id(room_id, &auth_event)?; @@ -574,21 +568,16 @@ impl Service { let mut okay = true; for prev_eventid in &incoming_pdu.prev_events { - let prev_event = - if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(prev_eventid) { - pdu - } else { - okay = false; - break; - }; + let Ok(Some(prev_event)) = services().rooms.timeline.get_pdu(prev_eventid) else { + okay = false; + break; + }; - let sstatehash = if let Ok(Some(s)) = services() + let Ok(Some(sstatehash)) = services() .rooms .state_accessor .pdu_shortstatehash(prev_eventid) - { - s - } else { + else { okay = false; break; }; @@ -1046,16 +1035,12 @@ impl Service { }; let lock = services().globals.stateres_mutex.lock(); - let state = match state_res::resolve( - room_version_id, - &fork_states, - auth_chain_sets, - fetch_event, - ) { - Ok(new_state) => new_state, - Err(_) => { - return Err(Error::bad_database("State resolution failed, either an event could not be found or deserialization")); - } + let Ok(state) = + state_res::resolve(room_version_id, &fork_states, auth_chain_sets, fetch_event) + else { + return Err(Error::bad_database( + "State resolution failed, either an event could not be found or deserialization", + )); }; drop(lock); @@ -1184,14 +1169,12 @@ impl Service { { Ok(res) => { info!("Got {} over federation", next_id); - let (calculated_event_id, value) = - match pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) { - Ok(t) => t, - Err(_) => { - back_off((*next_id).to_owned()).await; - continue; - } - }; + let Ok((calculated_event_id, value)) = + pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) + else { + back_off((*next_id).to_owned()).await; + continue; + }; if calculated_event_id != *next_id { warn!("Server didn't return event id we requested: requested: {}, we got {}. Event: {:?}", @@ -1410,12 +1393,9 @@ impl Service { ) .await; - let keys = match fetch_res { - Ok(keys) => keys, - Err(_) => { - warn!("Signature verification failed: Could not fetch signing key.",); - continue; - } + let Ok(keys) = fetch_res else { + warn!("Signature verification failed: Could not fetch signing key.",); + continue; }; pub_key_map @@ -1640,23 +1620,21 @@ impl Service { /// Returns Ok if the acl allows the server pub(crate) fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> { - let acl_event = match services().rooms.state_accessor.room_state_get( + let Some(acl_event) = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomServerAcl, "", - )? { - Some(acl) => acl, - None => return Ok(()), + )? + else { + return Ok(()); }; - let acl_event_content: RoomServerAclEventContent = - match serde_json::from_str(acl_event.content.get()) { - Ok(content) => content, - Err(_) => { - warn!("Invalid ACL event"); - return Ok(()); - } - }; + let Ok(acl_event_content) = + serde_json::from_str::(acl_event.content.get()) + else { + warn!("Invalid ACL event"); + return Ok(()); + }; if acl_event_content.allow.is_empty() { // Ignore broken acl events diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 2b3da501..daa17a58 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -45,9 +45,8 @@ impl Service { .ok() .map(|(_, id)| id) }) { - let pdu = match services().rooms.timeline.get_pdu_json(&event_id)? { - Some(pdu) => pdu, - None => continue, + let Some(pdu) = services().rooms.timeline.get_pdu_json(&event_id)? else { + continue; }; let pdu: PduEvent = match serde_json::from_str( @@ -70,14 +69,12 @@ impl Service { Err(_) => continue, }; - let state_key = match pdu.state_key { - Some(k) => k, - None => continue, + let Some(state_key) = pdu.state_key else { + continue; }; - let user_id = match UserId::parse(state_key) { - Ok(id) => id, - Err(_) => continue, + let Ok(user_id) = UserId::parse(state_key) else { + continue; }; services().rooms.state_cache.update_membership( @@ -377,11 +374,7 @@ impl Service { state_key: Option<&str>, content: &serde_json::value::RawValue, ) -> Result>> { - let shortstatehash = if let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - { - current_shortstatehash - } else { + let Some(shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else { return Ok(HashMap::new()); }; diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 6026677e..d6b9213a 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -109,9 +109,8 @@ impl Service { room_id: &RoomId, event_id: &EventId, ) -> Result { - let shortstatehash = match self.pdu_shortstatehash(event_id)? { - Some(shortstatehash) => shortstatehash, - None => return Ok(true), + let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else { + return Ok(true); }; if let Some(visibility) = self @@ -173,9 +172,8 @@ impl Service { room_id: &RoomId, event_id: &EventId, ) -> Result { - let shortstatehash = match self.pdu_shortstatehash(event_id)? { - Some(shortstatehash) => shortstatehash, - None => return Ok(true), + let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else { + return Ok(true); }; if let Some(visibility) = self diff --git a/src/service/sending.rs b/src/service/sending.rs index 9f82a716..6ca6e462 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -564,13 +564,12 @@ impl Service { } } - let pusher = match services() + let Some(pusher) = services() .pusher .get_pusher(userid, pushkey) .map_err(|e| (OutgoingKind::Push(userid.clone(), pushkey.clone()), e))? - { - Some(pusher) => pusher, - None => continue, + else { + continue; }; let rules_for_user = services() diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 190fb195..8d812e41 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -63,14 +63,11 @@ impl Service { password, .. }) => { - let username = match identifier { - UserIdentifier::UserIdOrLocalpart(username) => username, - _ => { - return Err(Error::BadRequest( - ErrorKind::Unrecognized, - "Identifier type not recognized.", - )) - } + let UserIdentifier::UserIdOrLocalpart(username) = identifier else { + return Err(Error::BadRequest( + ErrorKind::Unrecognized, + "Identifier type not recognized.", + )); }; let user_id = UserId::parse_with_server_name( From 645d88177a45edf35be206e363f3c2f5cc95478b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:37:57 -0700 Subject: [PATCH 053/617] enable `manual_string_new` lint --- Cargo.toml | 1 + src/api/client_server/room.rs | 26 +++++++++++++------------- src/service/admin.rs | 18 +++++++++--------- src/service/rooms/event_handler.rs | 2 +- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a60da04..f55a82e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ items_after_statements = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" manual_let_else = "warn" +manual_string_new = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 420e7110..ad43b957 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -228,7 +228,7 @@ pub(crate) async fn create_room_route( event_type: TimelineEventType::RoomCreate, content: to_raw_value(&content).expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -309,7 +309,7 @@ pub(crate) async fn create_room_route( content: to_raw_value(&power_levels_content) .expect("to_raw_value always works on serde_json::Value"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -332,7 +332,7 @@ pub(crate) async fn create_room_route( }) .expect("We checked that alias earlier, it must be fine"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -358,7 +358,7 @@ pub(crate) async fn create_room_route( })) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -379,7 +379,7 @@ pub(crate) async fn create_room_route( )) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -401,7 +401,7 @@ pub(crate) async fn create_room_route( })) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -418,7 +418,7 @@ pub(crate) async fn create_room_route( })?; // Implicit state key defaults to "" - pdu_builder.state_key.get_or_insert_with(|| "".to_owned()); + pdu_builder.state_key.get_or_insert_with(String::new); // Silently skip encryption events if they are not allowed if pdu_builder.event_type == TimelineEventType::RoomEncryption @@ -445,7 +445,7 @@ pub(crate) async fn create_room_route( content: to_raw_value(&RoomNameEventContent::new(name.clone())) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -467,7 +467,7 @@ pub(crate) async fn create_room_route( }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -628,7 +628,7 @@ pub(crate) async fn upgrade_room_route( }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -729,7 +729,7 @@ pub(crate) async fn upgrade_room_route( content: to_raw_value(&create_event_content) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -799,7 +799,7 @@ pub(crate) async fn upgrade_room_route( event_type: event_type.to_string().into(), content: event_content, unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -849,7 +849,7 @@ pub(crate) async fn upgrade_room_route( content: to_raw_value(&power_levels_event_content) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, diff --git a/src/service/admin.rs b/src/service/admin.rs index 58ef401a..2da95e13 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -983,7 +983,7 @@ impl Service { event_type: TimelineEventType::RoomCreate, content: to_raw_value(&content).expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1036,7 +1036,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1055,7 +1055,7 @@ impl Service { content: to_raw_value(&RoomJoinRulesEventContent::new(JoinRule::Invite)) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1076,7 +1076,7 @@ impl Service { )) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1097,7 +1097,7 @@ impl Service { )) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1117,7 +1117,7 @@ impl Service { content: to_raw_value(&RoomNameEventContent::new(room_name)) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1137,7 +1137,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1163,7 +1163,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, @@ -1288,7 +1288,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &grapevine_user, diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 5587a303..22edf677 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -435,7 +435,7 @@ impl Service { // The original create event must be in the auth events if !matches!( auth_events - .get(&(StateEventType::RoomCreate, "".to_owned())) + .get(&(StateEventType::RoomCreate, String::new())) .map(|a| a.as_ref()), Some(_) | None ) { From 224ba65d067a0725dfa697e58b26b5e34a4a0740 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:44:06 -0700 Subject: [PATCH 054/617] enable `map_unwrap_or` lint --- Cargo.toml | 1 + src/api/client_server/context.rs | 6 +-- src/api/client_server/tag.rs | 45 ++++++++++-------- src/database/key_value/globals.rs | 2 +- src/database/key_value/rooms/user.rs | 6 +-- src/database/key_value/users.rs | 3 +- src/service/rooms/state_accessor.rs | 71 +++++++++++++++------------- src/service/rooms/timeline.rs | 6 ++- src/service/sending.rs | 6 ++- src/service/uiaa.rs | 8 ++-- 10 files changed, 81 insertions(+), 73 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f55a82e5..965f66cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ let_underscore_must_use = "warn" lossy_float_literal = "warn" manual_let_else = "warn" manual_string_new = "warn" +map_unwrap_or = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 3957e83a..44c6bd40 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -105,8 +105,7 @@ pub(crate) async fn get_context_route( let start_token = events_before .last() - .map(|(count, _)| count.stringify()) - .unwrap_or_else(|| base_token.stringify()); + .map_or_else(|| base_token.stringify(), |(count, _)| count.stringify()); let events_before: Vec<_> = events_before .into_iter() @@ -161,8 +160,7 @@ pub(crate) async fn get_context_route( let end_token = events_after .last() - .map(|(count, _)| count.stringify()) - .unwrap_or_else(|| base_token.stringify()); + .map_or_else(|| base_token.stringify(), |(count, _)| count.stringify()); let events_after: Vec<_> = events_after .into_iter() diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 4c333821..616fc618 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -24,18 +24,19 @@ pub(crate) async fn update_tag_route( RoomAccountDataEventType::Tag, )?; - let mut tags_event = event - .map(|e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) - }) - .unwrap_or_else(|| { + let mut tags_event = event.map_or_else( + || { Ok(TagEvent { content: TagEventContent { tags: BTreeMap::new(), }, }) - })?; + }, + |e| { + serde_json::from_str(e.get()) + .map_err(|_| Error::bad_database("Invalid account data event in db.")) + }, + )?; tags_event .content @@ -68,18 +69,19 @@ pub(crate) async fn delete_tag_route( RoomAccountDataEventType::Tag, )?; - let mut tags_event = event - .map(|e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) - }) - .unwrap_or_else(|| { + let mut tags_event = event.map_or_else( + || { Ok(TagEvent { content: TagEventContent { tags: BTreeMap::new(), }, }) - })?; + }, + |e| { + serde_json::from_str(e.get()) + .map_err(|_| Error::bad_database("Invalid account data event in db.")) + }, + )?; tags_event.content.tags.remove(&body.tag.clone().into()); @@ -109,18 +111,19 @@ pub(crate) async fn get_tags_route( RoomAccountDataEventType::Tag, )?; - let tags_event = event - .map(|e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) - }) - .unwrap_or_else(|| { + let tags_event = event.map_or_else( + || { Ok(TagEvent { content: TagEventContent { tags: BTreeMap::new(), }, }) - })?; + }, + |e| { + serde_json::from_str(e.get()) + .map_err(|_| Error::bad_database("Invalid account data event in db.")) + }, + )?; Ok(get_tags::v3::Response { tags: tags_event.content.tags, diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 10748ccd..1d899301 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -276,7 +276,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" ); tree }) - .unwrap_or_else(BTreeMap::new); + .unwrap_or_default(); Ok(signingkeys) } diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index 4c435720..58b07eee 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -31,11 +31,10 @@ impl service::rooms::user::Data for KeyValueDatabase { self.userroomid_notificationcount .get(&userroom_id)? - .map(|bytes| { + .map_or(Ok(0), |bytes| { utils::u64_from_bytes(&bytes) .map_err(|_| Error::bad_database("Invalid notification count in db.")) }) - .unwrap_or(Ok(0)) } fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { @@ -45,11 +44,10 @@ impl service::rooms::user::Data for KeyValueDatabase { self.userroomid_highlightcount .get(&userroom_id)? - .map(|bytes| { + .map_or(Ok(0), |bytes| { utils::u64_from_bytes(&bytes) .map_err(|_| Error::bad_database("Invalid highlight count in db.")) }) - .unwrap_or(Ok(0)) } fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result { diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index d2e1ef14..6e12d284 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -348,12 +348,11 @@ impl service::users::Data for KeyValueDatabase { fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { self.userid_lastonetimekeyupdate .get(user_id.as_bytes())? - .map(|bytes| { + .map_or(Ok(0), |bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.") }) }) - .unwrap_or(Ok(0)) } fn take_one_time_key( diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index d6b9213a..ca2d8fb3 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -363,41 +363,46 @@ impl Service { federation: bool, ) -> Result { self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")? - .map(|e| { - serde_json::from_str(e.content.get()) - .map(|c: RoomPowerLevelsEventContent| c.into()) - .map(|e: RoomPowerLevels| { - e.user_can_redact_event_of_other(sender) - || e.user_can_redact_own_event(sender) - && if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) - { - if federation { - pdu.sender().server_name() == sender.server_name() + .map_or_else( + // Falling back on m.room.create to judge power levels + || { + if let Some(pdu) = + self.room_state_get(room_id, &StateEventType::RoomCreate, "")? + { + Ok(pdu.sender == sender + || if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) { + pdu.sender == sender + } else { + false + }) + } else { + Err(Error::bad_database( + "No m.room.power_levels or m.room.create events in database for room", + )) + } + }, + |e| { + serde_json::from_str(e.content.get()) + .map(|c: RoomPowerLevelsEventContent| c.into()) + .map(|e: RoomPowerLevels| { + e.user_can_redact_event_of_other(sender) + || e.user_can_redact_own_event(sender) + && if let Ok(Some(pdu)) = + services().rooms.timeline.get_pdu(redacts) + { + if federation { + pdu.sender().server_name() == sender.server_name() + } else { + pdu.sender == sender + } } else { - pdu.sender == sender + false } - } else { - false - } - }) - .map_err(|_| { - Error::bad_database("Invalid m.room.power_levels event in database") - }) - }) - // Falling back on m.room.create to judge power levels - .unwrap_or_else(|| { - if let Some(pdu) = self.room_state_get(room_id, &StateEventType::RoomCreate, "")? { - Ok(pdu.sender == sender - || if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) { - pdu.sender == sender - } else { - false }) - } else { - Err(Error::bad_database( - "No m.room.power_levels or m.room.create events in database for room", - )) - } - }) + .map_err(|_| { + Error::bad_database("Invalid m.room.power_levels event in database") + }) + }, + ) } } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index e03dec8e..132b079d 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -338,8 +338,10 @@ impl Service { .map_err(|_| Error::bad_database("Invalid push rules event in db.")) }) .transpose()? - .map(|ev: PushRulesEvent| ev.content.global) - .unwrap_or_else(|| Ruleset::server_default(user)); + .map_or_else( + || Ruleset::server_default(user), + |ev: PushRulesEvent| ev.content.global, + ); let mut highlight = false; let mut notify = false; diff --git a/src/service/sending.rs b/src/service/sending.rs index 6ca6e462..02324e7e 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -581,8 +581,10 @@ impl Service { ) .unwrap_or_default() .and_then(|event| serde_json::from_str::(event.get()).ok()) - .map(|ev: PushRulesEvent| ev.content.global) - .unwrap_or_else(|| push::Ruleset::server_default(userid)); + .map_or_else( + || push::Ruleset::server_default(userid), + |ev: PushRulesEvent| ev.content.global, + ); let unread: UInt = services() .rooms diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 8d812e41..1efd1d85 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -47,10 +47,10 @@ impl Service { auth: &AuthData, uiaainfo: &UiaaInfo, ) -> Result<(bool, UiaaInfo)> { - let mut uiaainfo = auth - .session() - .map(|session| self.db.get_uiaa_session(user_id, device_id, session)) - .unwrap_or_else(|| Ok(uiaainfo.clone()))?; + let mut uiaainfo = auth.session().map_or_else( + || Ok(uiaainfo.clone()), + |session| self.db.get_uiaa_session(user_id, device_id, session), + )?; if uiaainfo.session.is_none() { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); From 4e6c8451caf24e2b000e921567d2382e879a87a8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:45:58 -0700 Subject: [PATCH 055/617] enable `match_bool` lint --- Cargo.toml | 1 + src/service/admin.rs | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 965f66cd..341e434a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ lossy_float_literal = "warn" manual_let_else = "warn" manual_string_new = "warn" map_unwrap_or = "warn" +match_bool = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" diff --git a/src/service/admin.rs b/src/service/admin.rs index 2da95e13..8e33bdbb 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -764,13 +764,14 @@ impl Service { if !force { user_ids.retain(|&user_id| match services().users.is_admin(user_id) { - Ok(is_admin) => match is_admin { - true => { + Ok(is_admin) => { + if is_admin { admins.push(user_id.localpart()); false + } else { + true } - false => true, - }, + } Err(_) => false, }) } From 2b8b5ccb1abf9b033bd9478f7393653fe802ba36 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 17:54:03 -0700 Subject: [PATCH 056/617] enable `match_same_arms` lint --- Cargo.toml | 1 + src/api/client_server/room.rs | 2 +- src/api/server_server.rs | 3 +-- src/service/pusher.rs | 1 - src/service/rooms/spaces.rs | 3 +-- src/utils/error.rs | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 341e434a..4c36d1ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ manual_let_else = "warn" manual_string_new = "warn" map_unwrap_or = "warn" match_bool = "warn" +match_same_arms = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index ad43b957..d3f97218 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -271,7 +271,7 @@ pub(crate) async fn create_room_route( let preset = body.preset.clone().unwrap_or(match &body.visibility { room::Visibility::Private => RoomPreset::PrivateChat, room::Visibility::Public => RoomPreset::PublicChat, - _ => RoomPreset::PrivateChat, // Room visibility should not be custom + _ => unimplemented!("unknown room visibility"), }); let mut users = BTreeMap::new(); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 92bd028d..4e2b3b1d 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -770,7 +770,6 @@ pub(crate) async fn send_transaction_message_route( .filter_map(|edu| serde_json::from_str::(edu.json().get()).ok()) { match edu { - Edu::Presence(_) => {} Edu::Receipt(receipt) => { for (room_id, room_updates) in receipt.receipts { for (user_id, user_updates) in room_updates.read { @@ -922,7 +921,7 @@ pub(crate) async fn send_transaction_message_route( )?; } } - Edu::_Custom(_) => {} + Edu::_Custom(_) | Edu::Presence(_) => {} } } diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 1f7bf0a0..2c3862e0 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -283,7 +283,6 @@ impl Service { Ok(()) } // TODO: Handle email - PusherKind::Email(_) => Ok(()), _ => Ok(()), } } diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 74891e30..e154c41a 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -453,8 +453,7 @@ impl Service { room_id: &RoomId, ) -> Result { let allowed = match join_rule { - SpaceRoomJoinRule::Public => true, - SpaceRoomJoinRule::Knock => true, + SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::Public => true, SpaceRoomJoinRule::Invite => services() .rooms .state_cache diff --git a/src/utils/error.rs b/src/utils/error.rs index 4554bd53..ae8a7aff 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -118,11 +118,11 @@ impl Error { | Forbidden | GuestAccessForbidden | ThreepidAuthFailed + | UserDeactivated | ThreepidDenied => StatusCode::FORBIDDEN, Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED, NotFound | Unrecognized => StatusCode::NOT_FOUND, LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS, - UserDeactivated => StatusCode::FORBIDDEN, TooLarge => StatusCode::PAYLOAD_TOO_LARGE, _ => StatusCode::BAD_REQUEST, }, From c4a9bca16f363cee66a93b0cdc8e17404f6fd3b3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 18:35:18 -0700 Subject: [PATCH 057/617] enable `match_wildcard_for_single_variants` lint --- Cargo.toml | 1 + src/api/client_server/context.rs | 2 +- src/api/client_server/sync.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4c36d1ca..7c267517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ manual_string_new = "warn" map_unwrap_or = "warn" match_bool = "warn" match_same_arms = "warn" +match_wildcard_for_single_variants = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 44c6bd40..8b47da2f 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -23,7 +23,7 @@ pub(crate) async fn get_context_route( LazyLoadOptions::Enabled { include_redundant_members, } => (true, *include_redundant_members), - _ => (false, false), + LazyLoadOptions::Disabled => (false, false), }; let mut lazy_loaded = HashSet::new(); diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 9a7e5440..eaa3a210 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -195,7 +195,7 @@ async fn sync_helper( LazyLoadOptions::Enabled { include_redundant_members: redundant, } => (true, redundant), - _ => (false, false), + LazyLoadOptions::Disabled => (false, false), }; let full_state = body.full_state; From a636405bed334b1eed270c8e8289011ad7f57554 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 18:41:37 -0700 Subject: [PATCH 058/617] enable `needless_pass_by_value` lint --- Cargo.toml | 1 + src/api/client_server/relations.rs | 6 +++--- src/service/rooms/pdu_metadata.rs | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c267517..18d3a397 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ missing_assert_message = "warn" mod_module_files = "warn" multiple_inherent_impl = "warn" mutex_atomic = "warn" +needless_pass_by_value = "warn" negative_feature_names = "warn" pub_without_shorthand = "warn" rc_buffer = "warn" diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index bb85641b..353e4cc7 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -43,8 +43,8 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( sender_user, &body.room_id, &body.event_id, - Some(body.event_type.clone()), - Some(body.rel_type.clone()), + Some(&body.event_type), + Some(&body.rel_type), from, to, limit, @@ -95,7 +95,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route( &body.room_id, &body.event_id, None, - Some(body.rel_type.clone()), + Some(&body.rel_type), from, to, limit, diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index ee904e07..69049ad6 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -46,8 +46,8 @@ impl Service { sender_user: &UserId, room_id: &RoomId, target: &EventId, - filter_event_type: Option, - filter_rel_type: Option, + filter_event_type: Option<&TimelineEventType>, + filter_rel_type: Option<&RelationType>, from: PduCount, to: Option, limit: usize, @@ -63,7 +63,7 @@ impl Service { .relations_until(sender_user, room_id, target, from)? // TODO: should be relations_after .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { - filter_event_type.as_ref().map_or(true, |t| &pdu.kind == t) + filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t) && if let Ok(content) = serde_json::from_str::( pdu.content.get(), @@ -71,7 +71,7 @@ impl Service { { filter_rel_type .as_ref() - .map_or(true, |r| &content.relates_to.rel_type == r) + .map_or(true, |r| &&content.relates_to.rel_type == r) } else { false } @@ -110,7 +110,7 @@ impl Service { .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { - filter_event_type.as_ref().map_or(true, |t| &pdu.kind == t) + filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t) && if let Ok(content) = serde_json::from_str::( pdu.content.get(), @@ -118,7 +118,7 @@ impl Service { { filter_rel_type .as_ref() - .map_or(true, |r| &content.relates_to.rel_type == r) + .map_or(true, |r| &&content.relates_to.rel_type == r) } else { false } From 96e1877639d771a00f4d558f977fd82ea63eb38a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 18:59:24 -0700 Subject: [PATCH 059/617] enable `redundant_closure_for_method_calls` lint --- Cargo.toml | 1 + src/api/appservice_server.rs | 2 +- src/api/client_server/account.rs | 2 +- src/api/client_server/context.rs | 4 ++-- src/api/client_server/device.rs | 2 +- src/api/client_server/keys.rs | 6 +++--- src/api/client_server/membership.rs | 12 +++++------ src/api/client_server/message.rs | 4 ++-- src/api/client_server/profile.rs | 8 +++---- src/api/client_server/push.rs | 4 ++-- src/api/client_server/room.rs | 4 ++-- src/api/client_server/search.rs | 4 ++-- src/api/client_server/sync.rs | 24 ++++++++++----------- src/api/client_server/threads.rs | 2 +- src/api/client_server/user_directory.rs | 2 +- src/api/server_server.rs | 6 +++--- src/database.rs | 2 +- src/database/abstraction/rocksdb.rs | 6 +++--- src/database/abstraction/sqlite.rs | 6 +++--- src/database/key_value/account_data.rs | 6 +++--- src/database/key_value/appservice.rs | 2 +- src/database/key_value/globals.rs | 2 +- src/database/key_value/key_backups.rs | 2 +- src/database/key_value/rooms/state_cache.rs | 6 +++--- src/database/key_value/rooms/threads.rs | 2 +- src/database/key_value/rooms/user.rs | 2 +- src/database/key_value/transaction_ids.rs | 4 ++-- src/database/key_value/uiaa.rs | 2 +- src/database/key_value/users.rs | 4 ++-- src/service/pusher.rs | 2 +- src/service/rooms/event_handler.rs | 8 ++----- src/service/rooms/pdu_metadata.rs | 4 ++-- src/service/rooms/spaces.rs | 4 ++-- src/service/rooms/state_accessor.rs | 2 +- src/service/rooms/timeline.rs | 8 +++---- src/service/sending.rs | 8 +++---- src/utils.rs | 4 ++-- 37 files changed, 85 insertions(+), 88 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 18d3a397..00087365 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ negative_feature_names = "warn" pub_without_shorthand = "warn" rc_buffer = "warn" rc_mutex = "warn" +redundant_closure_for_method_calls = "warn" redundant_feature_names = "warn" redundant_type_annotations = "warn" ref_patterns = "warn" diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index 8591abb3..276a1c57 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -30,7 +30,7 @@ where &[MatrixVersion::V1_0], ) .unwrap() - .map(|body| body.freeze()); + .map(BytesMut::freeze); let mut parts = http_request.uri().clone().into_parts(); let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned(); diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index e10d83a5..54b735b3 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -353,7 +353,7 @@ pub(crate) async fn change_password_route( for id in services() .users .all_device_ids(sender_user) - .filter_map(|id| id.ok()) + .filter_map(Result::ok) .filter(|id| id != sender_device) { services().users.remove_device(sender_user, &id)?; diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 8b47da2f..14f5eb0d 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -81,7 +81,7 @@ pub(crate) async fn get_context_route( .timeline .pdus_until(sender_user, &room_id, base_token)? .take(half_limit) - .filter_map(|r| r.ok()) // Remove buggy events + .filter_map(Result::ok) // Remove buggy events .filter(|(_, pdu)| { services() .rooms @@ -117,7 +117,7 @@ pub(crate) async fn get_context_route( .timeline .pdus_after(sender_user, &room_id, base_token)? .take(half_limit) - .filter_map(|r| r.ok()) // Remove buggy events + .filter_map(Result::ok) // Remove buggy events .filter(|(_, pdu)| { services() .rooms diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index d63dd636..0bda0484 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -18,7 +18,7 @@ pub(crate) async fn get_devices_route( let devices: Vec = services() .users .all_devices_metadata(sender_user) - .filter_map(|r| r.ok()) // Filter out buggy devices + .filter_map(Result::ok) // Filter out buggy devices .collect(); Ok(get_devices::v3::Response { devices }) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 10ef99bc..97277f5f 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -226,14 +226,14 @@ pub(crate) async fn get_key_changes_route( .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`."))?, ), ) - .filter_map(|r| r.ok()), + .filter_map(Result::ok), ); for room_id in services() .rooms .state_cache .rooms_joined(sender_user) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) { device_list_updates.extend( services() @@ -247,7 +247,7 @@ pub(crate) async fn get_key_changes_route( Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`.") })?), ) - .filter_map(|r| r.ok()), + .filter_map(Result::ok), ); } Ok(get_key_changes::v3::Response { diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 632b9ff2..21332ed9 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -59,7 +59,7 @@ pub(crate) async fn join_room_by_id_route( .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| sender.as_str().map(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()), ); @@ -105,7 +105,7 @@ pub(crate) async fn join_room_by_id_or_alias_route( .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| sender.as_str().map(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()), ); @@ -435,7 +435,7 @@ pub(crate) async fn joined_rooms_route( .rooms .state_cache .rooms_joined(sender_user) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect(), }) } @@ -501,7 +501,7 @@ pub(crate) async fn joined_members_route( .rooms .state_cache .room_members(&body.room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) { let display_name = services().users.displayname(&user_id)?; let avatar_url = services().users.avatar_url(&user_id)?; @@ -1308,7 +1308,7 @@ pub(crate) async fn invite_helper( .rooms .state_cache .room_servers(room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|server| &**server != services().globals.server_name()); services().sending.send_pdu(servers, &pdu_id)?; @@ -1512,7 +1512,7 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { .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| sender.as_str().map(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()) .collect(); diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index dec75e7a..db8c8bf8 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -156,7 +156,7 @@ pub(crate) async fn get_message_events_route( .timeline .pdus_after(sender_user, &body.room_id, from)? .take(limit) - .filter_map(|r| r.ok()) // Filter out buggy events + .filter_map(Result::ok) // Filter out buggy events .filter(|(_, pdu)| { services() .rooms @@ -205,7 +205,7 @@ pub(crate) async fn get_message_events_route( .timeline .pdus_until(sender_user, &body.room_id, from)? .take(limit) - .filter_map(|r| r.ok()) // Filter out buggy events + .filter_map(Result::ok) // Filter out buggy events .filter(|(_, pdu)| { services() .rooms diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index 3e9806cd..f9a96904 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -34,7 +34,7 @@ pub(crate) async fn set_displayname_route( .rooms .state_cache .rooms_joined(sender_user) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .map(|room_id| { Ok::<_, Error>(( PduBuilder { @@ -70,7 +70,7 @@ pub(crate) async fn set_displayname_route( room_id, )) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect(); for (pdu_builder, room_id) in all_rooms_joined { @@ -151,7 +151,7 @@ pub(crate) async fn set_avatar_url_route( .rooms .state_cache .rooms_joined(sender_user) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .map(|room_id| { Ok::<_, Error>(( PduBuilder { @@ -187,7 +187,7 @@ pub(crate) async fn set_avatar_url_route( room_id, )) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect(); for (pdu_builder, room_id) in all_joined_rooms { diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index cdb8cb82..63a12715 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -9,7 +9,7 @@ use ruma::{ }, }, events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, - push::{InsertPushRuleError, RemovePushRuleError}, + push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, }; /// # `GET /_matrix/client/r0/pushrules` @@ -281,7 +281,7 @@ pub(crate) async fn get_pushrule_enabled_route( let global = account_data.content.global; let enabled = global .get(body.kind.clone(), &body.rule_id) - .map(|r| r.enabled()) + .map(AnyPushRuleRef::enabled) .ok_or(Error::BadRequest( ErrorKind::NotFound, "Push rule not found.", diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index d3f97218..8167ed25 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -565,7 +565,7 @@ pub(crate) async fn get_room_aliases_route( .rooms .alias .local_aliases_for_room(&body.room_id) - .filter_map(|a| a.ok()) + .filter_map(Result::ok) .collect(), }) } @@ -814,7 +814,7 @@ pub(crate) async fn upgrade_room_route( .rooms .alias .local_aliases_for_room(&body.room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) { services() .rooms diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index eb6902b9..3743b877 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -30,7 +30,7 @@ pub(crate) async fn search_events_route( .rooms .state_cache .rooms_joined(sender_user) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect() }); @@ -118,7 +118,7 @@ pub(crate) async fn search_events_route( result: Some(result), }) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .skip(skip) .take(limit) .collect(); diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index eaa3a210..246b8ced 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -217,7 +217,7 @@ async fn sync_helper( services() .users .keys_changed(sender_user.as_ref(), since, None) - .filter_map(|r| r.ok()), + .filter_map(Result::ok), ); let all_joined_rooms = services() @@ -461,7 +461,7 @@ async fn sync_helper( .rooms .user .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter_map(|other_room_id| { Some( services() @@ -639,7 +639,7 @@ async fn load_joined_room( .rooms .timeline .all_pdus(sender_user, room_id)? - .filter_map(|pdu| pdu.ok()) // Ignore all broken pdus + .filter_map(Result::ok) // Ignore all broken pdus .filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember) .map(|(_, pdu)| { let content: RoomMemberEventContent = @@ -674,7 +674,7 @@ async fn load_joined_room( } }) // Filter out buggy users - .filter_map(|u| u.ok()) + .filter_map(Result::ok) // Filter for possible heroes .flatten() { @@ -978,7 +978,7 @@ async fn load_joined_room( services() .users .keys_changed(room_id.as_ref(), since, None) - .filter_map(|r| r.ok()), + .filter_map(Result::ok), ); let notification_count = send_notification_counts @@ -1018,7 +1018,7 @@ async fn load_joined_room( .edus .read_receipt .readreceipts_since(room_id, since) - .filter_map(|r| r.ok()) // Filter out buggy events + .filter_map(Result::ok) // Filter out buggy events .map(|(_, _, v)| v) .collect(); @@ -1139,7 +1139,7 @@ fn share_encrypted_room( .rooms .user .get_shared_rooms(vec![sender_user.to_owned(), user_id.to_owned()])? - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|room_id| room_id != ignore_room) .filter_map(|other_room_id| { Some( @@ -1192,7 +1192,7 @@ pub(crate) async fn sync_events_v4_route( .rooms .state_cache .rooms_joined(&sender_user) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect::>(); if body.extensions.to_device.enabled.unwrap_or(false) { @@ -1211,7 +1211,7 @@ pub(crate) async fn sync_events_v4_route( services() .users .keys_changed(sender_user.as_ref(), globalsince, None) - .filter_map(|r| r.ok()), + .filter_map(Result::ok), ); for room_id in &all_joined_rooms { @@ -1352,7 +1352,7 @@ pub(crate) async fn sync_events_v4_route( services() .users .keys_changed(room_id.as_ref(), globalsince, None) - .filter_map(|r| r.ok()), + .filter_map(Result::ok), ); } for user_id in left_encrypted_users { @@ -1360,7 +1360,7 @@ pub(crate) async fn sync_events_v4_route( .rooms .user .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter_map(|other_room_id| { Some( services() @@ -1552,7 +1552,7 @@ pub(crate) async fn sync_events_v4_route( .rooms .state_cache .room_members(room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|member| member != &sender_user) .filter_map(|member| { services() diff --git a/src/api/client_server/threads.rs b/src/api/client_server/threads.rs index 6c93644a..c9142610 100644 --- a/src/api/client_server/threads.rs +++ b/src/api/client_server/threads.rs @@ -27,7 +27,7 @@ pub(crate) async fn get_threads_route( .threads .threads_until(sender_user, &body.room_id, from, &body.include)? .take(limit) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index fe8ad179..560b0441 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -55,7 +55,7 @@ pub(crate) async fn search_users_route( .rooms .state_cache .rooms_joined(&user_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .any(|room| { services() .rooms diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 4e2b3b1d..c4d6d60a 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1042,7 +1042,7 @@ pub(crate) async fn get_backfill_route( .take(limit.try_into().unwrap()); let events = all_events - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|(_, e)| { matches!( services().rooms.state_accessor.server_can_see_event( @@ -1563,7 +1563,7 @@ async fn create_join_event( .rooms .state_cache .room_servers(room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|server| &**server != services().globals.server_name()); services().sending.send_pdu(servers, &pdu_id)?; @@ -1767,7 +1767,7 @@ pub(crate) async fn get_devices_route( devices: services() .users .all_devices_metadata(&body.user_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter_map(|metadata| { Some(UserDevice { keys: services() diff --git a/src/database.rs b/src/database.rs index ff8f47ad..2c61b0f7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -780,7 +780,7 @@ impl KeyValueDatabase { } // Force E2EE device list updates so we can send them over federation - for user_id in services().users.iter().filter_map(|r| r.ok()) { + for user_id in services().users.iter().filter_map(Result::ok) { services().users.mark_device_key_update(&user_id)?; } diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index dbfa49b2..831e9bcd 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -188,7 +188,7 @@ impl KvTree for RocksDbEngineTree<'_> { self.db .rocks .iterator_cf_opt(&self.cf(), readoptions, rocksdb::IteratorMode::Start) - .map(|r| r.unwrap()) + .map(Result::unwrap) .map(|(k, v)| (Vec::from(k), Vec::from(v))), ) } @@ -215,7 +215,7 @@ impl KvTree for RocksDbEngineTree<'_> { }, ), ) - .map(|r| r.unwrap()) + .map(Result::unwrap) .map(|(k, v)| (Vec::from(k), Vec::from(v))), ) } @@ -269,7 +269,7 @@ impl KvTree for RocksDbEngineTree<'_> { readoptions, rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward), ) - .map(|r| r.unwrap()) + .map(Result::unwrap) .map(|(k, v)| (Vec::from(k), Vec::from(v))) .take_while(move |(k, _)| k.starts_with(&prefix)), ) diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 51490442..3105da22 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -194,7 +194,7 @@ impl SqliteTable { statement .query_map([], |row| Ok((row.get_unwrap(0), row.get_unwrap(1)))) .unwrap() - .map(move |r| r.unwrap()), + .map(Result::unwrap), ); Box::new(PreparedStatementIterator { @@ -291,7 +291,7 @@ impl KvTree for SqliteTable { statement .query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1)))) .unwrap() - .map(move |r| r.unwrap()), + .map(Result::unwrap), ); Box::new(PreparedStatementIterator { iterator, @@ -313,7 +313,7 @@ impl KvTree for SqliteTable { statement .query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1)))) .unwrap() - .map(move |r| r.unwrap()), + .map(Result::unwrap), ); Box::new(PreparedStatementIterator { diff --git a/src/database/key_value/account_data.rs b/src/database/key_value/account_data.rs index 970b36b5..f08ade13 100644 --- a/src/database/key_value/account_data.rs +++ b/src/database/key_value/account_data.rs @@ -20,7 +20,7 @@ impl service::account_data::Data for KeyValueDatabase { data: &serde_json::Value, ) -> Result<()> { let mut prefix = room_id - .map(|r| r.to_string()) + .map(ToString::to_string) .unwrap_or_default() .as_bytes() .to_vec(); @@ -70,7 +70,7 @@ impl service::account_data::Data for KeyValueDatabase { kind: RoomAccountDataEventType, ) -> Result>> { let mut key = room_id - .map(|r| r.to_string()) + .map(ToString::to_string) .unwrap_or_default() .as_bytes() .to_vec(); @@ -105,7 +105,7 @@ impl service::account_data::Data for KeyValueDatabase { let mut userdata = HashMap::new(); let mut prefix = room_id - .map(|r| r.to_string()) + .map(ToString::to_string) .unwrap_or_default() .as_bytes() .to_vec(); diff --git a/src/database/key_value/appservice.rs b/src/database/key_value/appservice.rs index b547e66a..57907f48 100644 --- a/src/database/key_value/appservice.rs +++ b/src/database/key_value/appservice.rs @@ -48,7 +48,7 @@ impl service::appservice::Data for KeyValueDatabase { fn all(&self) -> Result> { self.iter_ids()? - .filter_map(|id| id.ok()) + .filter_map(Result::ok) .map(move |id| { Ok(( id.clone(), diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 1d899301..0d58b241 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -56,7 +56,7 @@ impl service::globals::Data for KeyValueDatabase { .rooms .state_cache .rooms_joined(user_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) { let short_roomid = services() .rooms diff --git a/src/database/key_value/key_backups.rs b/src/database/key_value/key_backups.rs index 900b700b..c2b9a57f 100644 --- a/src/database/key_value/key_backups.rs +++ b/src/database/key_value/key_backups.rs @@ -283,7 +283,7 @@ impl service::key_backups::Data for KeyValueDatabase { Ok::<_, Error>((session_id, key_data)) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect()) } diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 6cb7a8c6..be085b5c 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -101,7 +101,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { let mut joined_servers = HashSet::new(); let mut real_users = HashSet::new(); - for joined in self.room_members(room_id).filter_map(|r| r.ok()) { + for joined in self.room_members(room_id).filter_map(Result::ok) { joined_servers.insert(joined.server_name().to_owned()); if joined.server_name() == services().globals.server_name() && !services().users.is_deactivated(&joined).unwrap_or(true) @@ -111,7 +111,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { joinedcount += 1; } - for _invited in self.room_members_invited(room_id).filter_map(|r| r.ok()) { + for _invited in self.room_members_invited(room_id).filter_map(Result::ok) { invitedcount += 1; } @@ -126,7 +126,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .unwrap() .insert(room_id.to_owned(), Arc::new(real_users)); - for old_joined_server in self.room_servers(room_id).filter_map(|r| r.ok()) { + for old_joined_server in self.room_servers(room_id).filter_map(Result::ok) { if !joined_servers.remove(&old_joined_server) { // Server not in room anymore let mut roomserver_id = room_id.as_bytes().to_vec(); diff --git a/src/database/key_value/rooms/threads.rs b/src/database/key_value/rooms/threads.rs index 5e3dc970..257828bb 100644 --- a/src/database/key_value/rooms/threads.rs +++ b/src/database/key_value/rooms/threads.rs @@ -68,7 +68,7 @@ impl service::rooms::threads::Data for KeyValueDatabase { })?) .map_err(|_| Error::bad_database("Invalid UserId in threadid_userids.")) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect(), )) } else { diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index 58b07eee..6d5977dd 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -129,7 +129,7 @@ impl service::rooms::user::Data for KeyValueDatabase { Ok::<_, Error>(room_id) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) }); // We use the default compare function because keys are sorted correctly (not reversed) diff --git a/src/database/key_value/transaction_ids.rs b/src/database/key_value/transaction_ids.rs index 2ea6ad4a..b3bd05f4 100644 --- a/src/database/key_value/transaction_ids.rs +++ b/src/database/key_value/transaction_ids.rs @@ -12,7 +12,7 @@ impl service::transaction_ids::Data for KeyValueDatabase { ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); key.push(0xff); - key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default()); + key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default()); key.push(0xff); key.extend_from_slice(txn_id.as_bytes()); @@ -29,7 +29,7 @@ impl service::transaction_ids::Data for KeyValueDatabase { ) -> Result>> { let mut key = user_id.as_bytes().to_vec(); key.push(0xff); - key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default()); + key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default()); key.push(0xff); key.extend_from_slice(txn_id.as_bytes()); diff --git a/src/database/key_value/uiaa.rs b/src/database/key_value/uiaa.rs index 5fd91b07..20a0357d 100644 --- a/src/database/key_value/uiaa.rs +++ b/src/database/key_value/uiaa.rs @@ -34,7 +34,7 @@ impl service::uiaa::Data for KeyValueDatabase { .read() .unwrap() .get(&(user_id.to_owned(), device_id.to_owned(), session.to_owned())) - .map(|j| j.to_owned()) + .map(ToOwned::to_owned) } fn update_uiaa_session( diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 6e12d284..0122d936 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -632,7 +632,7 @@ impl service::users::Data for KeyValueDatabase { .rooms .state_cache .rooms_joined(user_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) { // Don't send key updates to unencrypted rooms if services() @@ -837,7 +837,7 @@ impl service::users::Data for KeyValueDatabase { .map_err(|_| Error::bad_database("ToDeviceId has invalid count bytes."))?, )) }) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .take_while(|&(_, count)| count <= until) { self.todeviceid_events.remove(&key)?; diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 2c3862e0..b35eac0a 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -68,7 +68,7 @@ impl Service { warn!("Failed to find destination {}: {}", destination, e); Error::BadServerResponse("Invalid destination") })? - .map(|body| body.freeze()); + .map(BytesMut::freeze); let reqwest_request = reqwest::Request::try_from(http_request)?; diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 22edf677..e4db6bb8 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -434,9 +434,7 @@ impl Service { // The original create event must be in the auth events if !matches!( - auth_events - .get(&(StateEventType::RoomCreate, String::new())) - .map(|a| a.as_ref()), + auth_events.get(&(StateEventType::RoomCreate, String::new())), Some(_) | None ) { return Err(Error::BadRequest( @@ -727,9 +725,7 @@ impl Service { .get_shortstatekey(&StateEventType::RoomCreate, "")? .expect("Room exists"); - if state.get(&create_shortstatekey).map(|id| id.as_ref()) - != Some(&create_event.event_id) - { + if state.get(&create_shortstatekey) != Some(&create_event.event_id) { return Err(Error::bad_database( "Incoming event refers to wrong create event.", )); diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 69049ad6..e9060ae7 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -78,7 +78,7 @@ impl Service { }) }) .take(limit) - .filter_map(|r| r.ok()) // Filter out buggy events + .filter_map(Result::ok) // Filter out buggy events .filter(|(_, pdu)| { services() .rooms @@ -125,7 +125,7 @@ impl Service { }) }) .take(limit) - .filter_map(|r| r.ok()) // Filter out buggy events + .filter_map(Result::ok) // Filter out buggy events .filter(|(_, pdu)| { services() .rooms diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index e154c41a..af019e3d 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -63,13 +63,13 @@ impl Service { let mut results = Vec::new(); while let Some(current_room) = { - while stack.last().map_or(false, |s| s.is_empty()) { + while stack.last().map_or(false, Vec::is_empty) { stack.pop(); } if stack.is_empty() { None } else { - stack.last_mut().and_then(|s| s.pop()) + stack.last_mut().and_then(Vec::pop) } } { rooms_in_path.push(current_room.clone()); diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index ca2d8fb3..0c0b8ac5 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -136,7 +136,7 @@ impl Service { .rooms .state_cache .room_members(room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|member| member.server_name() == origin); let visibility = match history_visibility { diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 132b079d..33cd1889 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -590,7 +590,7 @@ impl Service { .rooms .alias .local_aliases_for_room(&pdu.room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .any(|room_alias| aliases.is_match(room_alias.as_str())) }; @@ -838,7 +838,7 @@ impl Service { .rooms .state_cache .room_members(room_id) - .filter_map(|m| m.ok()) + .filter_map(Result::ok) .filter(|m| m.server_name() == server_name) .filter(|m| m != target) .count(); @@ -864,7 +864,7 @@ impl Service { .rooms .state_cache .room_members(room_id) - .filter_map(|m| m.ok()) + .filter_map(Result::ok) .filter(|m| m.server_name() == server_name) .filter(|m| m != target) .count(); @@ -965,7 +965,7 @@ impl Service { .rooms .state_cache .room_servers(room_id) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .collect(); // In case we are kicking or banning a user, we need to inform their server of the change diff --git a/src/service/sending.rs b/src/service/sending.rs index 02324e7e..3128781a 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -128,7 +128,7 @@ impl Service { // Retry requests we could not finish yet let mut initial_transactions = HashMap::>::new(); - for (key, outgoing_kind, event) in self.db.active_requests().filter_map(|r| r.ok()) { + for (key, outgoing_kind, event) in self.db.active_requests().filter_map(Result::ok) { let entry = initial_transactions .entry(outgoing_kind.clone()) .or_default(); @@ -158,7 +158,7 @@ impl Service { self.db.delete_all_active_requests_for(&outgoing_kind)?; // Find events that have been added since starting the last request - let new_events = self.db.queued_requests(&outgoing_kind).filter_map(|r| r.ok()).take(30).collect::>(); + let new_events = self.db.queued_requests(&outgoing_kind).filter_map(Result::ok).take(30).collect::>(); if new_events.is_empty() { current_transaction_status.remove(&outgoing_kind); @@ -244,7 +244,7 @@ impl Service { for (_, e) in self .db .active_requests_for(outgoing_kind) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) { events.push(e); } @@ -281,7 +281,7 @@ impl Service { services() .users .keys_changed(room_id.as_ref(), since, None) - .filter_map(|r| r.ok()) + .filter_map(Result::ok) .filter(|user_id| user_id.server_name() == services().globals.server_name()), ); diff --git a/src/utils.rs b/src/utils.rs index d3d0c0a8..1ff95576 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -22,7 +22,7 @@ pub(crate) fn millis_since_unix_epoch() -> u64 { #[cfg(any(feature = "rocksdb", feature = "sqlite"))] pub(crate) fn increment(old: Option<&[u8]>) -> Option> { - let number = match old.map(|bytes| bytes.try_into()) { + let number = match old.map(TryInto::try_into) { Some(Ok(bytes)) => { let number = u64::from_be_bytes(bytes); number + 1 @@ -91,7 +91,7 @@ where F: Fn(&[u8], &[u8]) -> Ordering, { let first_iterator = iterators.next()?; - let mut other_iterators = iterators.map(|i| i.peekable()).collect::>(); + let mut other_iterators = iterators.map(Iterator::peekable).collect::>(); Some(first_iterator.filter(move |target| { other_iterators.iter_mut().all(|it| { From db4951c5fde81adc82662808cc37089a720a3d7d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:01:46 -0700 Subject: [PATCH 060/617] enable `semicolon_if_nothing_returned` lint --- Cargo.toml | 1 + src/api/client_server/backup.rs | 4 ++-- src/api/client_server/device.rs | 2 +- src/api/client_server/sync.rs | 2 +- src/api/client_server/to_device.rs | 2 +- src/api/server_server.rs | 10 +++++----- src/config/proxy.rs | 2 +- src/database.rs | 2 +- src/database/key_value/sending.rs | 4 ++-- src/main.rs | 4 ++-- src/service/admin.rs | 10 +++++----- src/service/pusher.rs | 2 +- src/service/rooms/event_handler.rs | 6 +++--- src/service/sending.rs | 2 +- 14 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00087365..34fe4709 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ redundant_type_annotations = "warn" ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" same_name_method = "warn" +semicolon_if_nothing_returned = "warn" semicolon_inside_block = "warn" str_to_string = "warn" string_add = "warn" diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index cf7f5468..daab262d 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -144,7 +144,7 @@ pub(crate) async fn add_backup_keys_route( room_id, session_id, key_data, - )? + )?; } } @@ -191,7 +191,7 @@ pub(crate) async fn add_backup_keys_for_room_route( &body.room_id, session_id, key_data, - )? + )?; } Ok(add_backup_keys_for_room::v3::Response { diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 0bda0484..50ad47c1 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -162,7 +162,7 @@ pub(crate) async fn delete_devices_route( } for device_id in &body.devices { - services().users.remove_device(sender_user, device_id)? + services().users.remove_device(sender_user, device_id)?; } Ok(delete_devices::v3::Response {}) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 246b8ced..b5799bf5 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1177,7 +1177,7 @@ pub(crate) async fn sync_events_v4_route( sender_user.clone(), sender_device.clone(), conn_id.clone(), - ) + ); } } diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index bfa79631..fbaad4d9 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -63,7 +63,7 @@ pub(crate) async fn send_event_to_device_route( event.deserialize_as().map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid") })?, - )? + )?; } DeviceIdOrAllDevices::AllDevices => { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index c4d6d60a..1799f71d 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -873,7 +873,7 @@ pub(crate) async fn send_transaction_message_route( "Event is invalid", ) })?, - )? + )?; } DeviceIdOrAllDevices::AllDevices => { @@ -1830,11 +1830,11 @@ pub(crate) async fn get_profile_information_route( match &body.field { Some(ProfileField::DisplayName) => { - displayname = services().users.displayname(&body.user_id)? + displayname = services().users.displayname(&body.user_id)?; } Some(ProfileField::AvatarUrl) => { avatar_url = services().users.avatar_url(&body.user_id)?; - blurhash = services().users.blurhash(&body.user_id)? + blurhash = services().users.blurhash(&body.user_id)?; } // TODO: what to do with custom Some(_) => {} @@ -1938,7 +1938,7 @@ mod tests { assert_eq!( add_port_to_hostname("example.com"), FedDest::Named(String::from("example.com"), String::from(":8448")) - ) + ); } #[test] @@ -1946,6 +1946,6 @@ mod tests { assert_eq!( add_port_to_hostname("example.com:1337"), FedDest::Named(String::from("example.com"), String::from(":1337")) - ) + ); } } diff --git a/src/config/proxy.rs b/src/config/proxy.rs index 7672e02b..2adab9f6 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -67,7 +67,7 @@ impl PartialProxyConfig { let mut excluded_because = None; // most specific reason it was excluded if self.include.is_empty() { // treat empty include list as `*` - included_because = Some(&WildCardedDomain::WildCard) + included_because = Some(&WildCardedDomain::WildCard); } for wc_domain in &self.include { if wc_domain.matches(domain) { diff --git a/src/database.rs b/src/database.rs index 2c61b0f7..d3fda641 100644 --- a/src/database.rs +++ b/src/database.rs @@ -964,7 +964,7 @@ impl KeyValueDatabase { error!( "Could not set the configured emergency password for the grapevine user: {}", e - ) + ); } }; diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 3fc3e042..6c8e939b 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -67,9 +67,9 @@ impl service::sending::Data for KeyValueDatabase { for (outgoing_kind, event) in requests { let mut key = outgoing_kind.get_prefix(); if let SendingEventType::Pdu(value) = &event { - key.extend_from_slice(value) + key.extend_from_slice(value); } else { - key.extend_from_slice(&services().globals.next_count()?.to_be_bytes()) + key.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); } let value = if let SendingEventType::Edu(value) = &event { &**value diff --git a/src/main.rs b/src/main.rs index 90248e11..ab06163f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -230,7 +230,7 @@ async fn run_server() -> io::Result<()> { sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) .expect("should be able to notify systemd"); - server.await? + server.await?; } None => { let server = bind(addr).handle(handle).serve(app); @@ -239,7 +239,7 @@ async fn run_server() -> io::Result<()> { sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) .expect("should be able to notify systemd"); - server.await? + server.await?; } } diff --git a/src/service/admin.rs b/src/service/admin.rs index 8e33bdbb..6545c062 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -699,11 +699,11 @@ impl Service { match <&UserId>::try_from(user) { Ok(user_id) => { if user_id.server_name() != services().globals.server_name() { - remote_ids.push(user_id) + remote_ids.push(user_id); } else if !services().users.exists(user_id)? { - non_existant_ids.push(user_id) + non_existant_ids.push(user_id); } else { - user_ids.push(user_id) + user_ids.push(user_id); } } Err(_) => { @@ -773,12 +773,12 @@ impl Service { } } Err(_) => false, - }) + }); } for &user_id in &user_ids { if services().users.deactivate_account(user_id).is_ok() { - deactivation_count += 1 + deactivation_count += 1; } } diff --git a/src/service/pusher.rs b/src/service/pusher.rs index b35eac0a..2e54f867 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -256,7 +256,7 @@ impl Service { .iter() .any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_))) { - notifi.prio = NotificationPriority::High + notifi.prio = NotificationPriority::High; } if event_id_only { diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index e4db6bb8..7751a172 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -199,7 +199,7 @@ impl Service { e.insert((Instant::now(), 1)); } hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1) + *e.get_mut() = (Instant::now(), e.get().1 + 1); } } continue; @@ -243,7 +243,7 @@ impl Service { e.insert((Instant::now(), 1)); } hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1) + *e.get_mut() = (Instant::now(), e.get().1 + 1); } } } @@ -1094,7 +1094,7 @@ impl Service { e.insert((Instant::now(), 1)); } hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1) + *e.get_mut() = (Instant::now(), e.get().1 + 1); } } }; diff --git a/src/service/sending.rs b/src/service/sending.rs index 3128781a..6ad34076 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -479,7 +479,7 @@ impl Service { ), ) })? - .to_room_event()) + .to_room_event()); } SendingEventType::Edu(_) => { // Appservices don't need EDUs (?) From a62fa7b6eec8c423e1c606fdb84e79a550d5d226 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:05:02 -0700 Subject: [PATCH 061/617] enable `similar_names` lint --- Cargo.toml | 1 + src/database/key_value/rooms/short.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34fe4709..5ee8f54a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ rest_pat_in_fully_bound_structs = "warn" same_name_method = "warn" semicolon_if_nothing_returned = "warn" semicolon_inside_block = "warn" +similar_names = "warn" str_to_string = "warn" string_add = "warn" string_lit_chars_any = "warn" diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 6ec4dd5f..66f7eb74 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -45,13 +45,13 @@ impl service::rooms::short::Data for KeyValueDatabase { return Ok(Some(*short)); } - let mut statekey = event_type.to_string().as_bytes().to_vec(); - statekey.push(0xff); - statekey.extend_from_slice(state_key.as_bytes()); + let mut db_key = event_type.to_string().as_bytes().to_vec(); + db_key.push(0xff); + db_key.extend_from_slice(state_key.as_bytes()); let short = self .statekey_shortstatekey - .get(&statekey)? + .get(&db_key)? .map(|shortstatekey| { utils::u64_from_bytes(&shortstatekey) .map_err(|_| Error::bad_database("Invalid shortstatekey in db.")) @@ -82,19 +82,19 @@ impl service::rooms::short::Data for KeyValueDatabase { return Ok(*short); } - let mut statekey = event_type.to_string().as_bytes().to_vec(); - statekey.push(0xff); - statekey.extend_from_slice(state_key.as_bytes()); + let mut db_key = event_type.to_string().as_bytes().to_vec(); + db_key.push(0xff); + db_key.extend_from_slice(state_key.as_bytes()); - let short = match self.statekey_shortstatekey.get(&statekey)? { + let short = match self.statekey_shortstatekey.get(&db_key)? { Some(shortstatekey) => utils::u64_from_bytes(&shortstatekey) .map_err(|_| Error::bad_database("Invalid shortstatekey in db."))?, None => { let shortstatekey = services().globals.next_count()?; self.statekey_shortstatekey - .insert(&statekey, &shortstatekey.to_be_bytes())?; + .insert(&db_key, &shortstatekey.to_be_bytes())?; self.shortstatekey_statekey - .insert(&shortstatekey.to_be_bytes(), &statekey)?; + .insert(&shortstatekey.to_be_bytes(), &db_key)?; shortstatekey } }; From 75358340bb22178e08ece5dad562357e14c29e2d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:09:29 -0700 Subject: [PATCH 062/617] enable `single_match_else` lint Also collapses some if statements in the federation name resolution code --- Cargo.toml | 1 + src/api/client_server/keys.rs | 55 ++++---- src/api/server_server.rs | 177 ++++++++++++-------------- src/database/key_value/rooms/short.rs | 65 +++++----- src/service/rooms/event_handler.rs | 92 +++++++------ src/service/sending.rs | 57 ++++----- 6 files changed, 211 insertions(+), 236 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ee8f54a..52412cfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ same_name_method = "warn" semicolon_if_nothing_returned = "warn" semicolon_inside_block = "warn" similar_names = "warn" +single_match_else = "warn" str_to_string = "warn" string_add = "warn" string_lit_chars_any = "warn" diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 97277f5f..4c104a6b 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -402,39 +402,36 @@ pub(crate) async fn get_keys_helper bool>( .collect(); while let Some((server, response)) = futures.next().await { - match response { - Ok(Ok(response)) => { - for (user, masterkey) in response.master_keys { - let (master_key_id, mut master_key) = - services().users.parse_master_key(&user, &masterkey)?; + if let Ok(Ok(response)) = response { + for (user, masterkey) in response.master_keys { + let (master_key_id, mut master_key) = + services().users.parse_master_key(&user, &masterkey)?; - if let Some(our_master_key) = services().users.get_key( - &master_key_id, - sender_user, - &user, - &allowed_signatures, - )? { - let (_, our_master_key) = - services().users.parse_master_key(&user, &our_master_key)?; - master_key.signatures.extend(our_master_key.signatures); - } - let json = serde_json::to_value(master_key).expect("to_value always works"); - let raw = serde_json::from_value(json).expect("Raw::from_value always works"); - services().users.add_cross_signing_keys( - &user, &raw, &None, &None, - false, // Dont notify. A notification would trigger another key request resulting in an endless loop - )?; - master_keys.insert(user, raw); + if let Some(our_master_key) = services().users.get_key( + &master_key_id, + sender_user, + &user, + &allowed_signatures, + )? { + let (_, our_master_key) = + services().users.parse_master_key(&user, &our_master_key)?; + master_key.signatures.extend(our_master_key.signatures); } - - self_signing_keys.extend(response.self_signing_keys); - device_keys.extend(response.device_keys); + let json = serde_json::to_value(master_key).expect("to_value always works"); + let raw = serde_json::from_value(json).expect("Raw::from_value always works"); + services().users.add_cross_signing_keys( + &user, &raw, &None, &None, + false, // Dont notify. A notification would trigger another key request resulting in an endless loop + )?; + master_keys.insert(user, raw); } - _ => { - back_off(server.to_owned()).await; - failures.insert(server.to_string(), json!({})); - } + self_signing_keys.extend(response.self_signing_keys); + device_keys.extend(response.device_keys); + } else { + back_off(server.to_owned()).await; + + failures.insert(server.to_string(), json!({})); } } diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 1799f71d..41b0db57 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -359,100 +359,84 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe FedDest::Named(host.to_owned(), port.to_owned()) } else { debug!("Requesting well known for {destination}"); - match request_well_known(destination.as_str()).await { - Some(delegated_hostname) => { - debug!("3: A .well-known file is available"); - hostname = add_port_to_hostname(&delegated_hostname).into_uri_string(); - match get_ip_with_port(&delegated_hostname) { - Some(host_and_port) => host_and_port, // 3.1: IP literal in .well-known file - None => { - if let Some(pos) = delegated_hostname.find(':') { - debug!("3.2: Hostname with port in .well-known file"); - let (host, port) = delegated_hostname.split_at(pos); - FedDest::Named(host.to_owned(), port.to_owned()) - } else { - debug!("Delegated hostname has no port in this branch"); - if let Some(hostname_override) = - query_srv_record(&delegated_hostname).await - { - debug!("3.3: SRV lookup successful"); - let force_port = hostname_override.port(); + if let Some(delegated_hostname) = request_well_known(destination.as_str()).await { + debug!("3: A .well-known file is available"); + hostname = add_port_to_hostname(&delegated_hostname).into_uri_string(); + if let Some(host_and_port) = get_ip_with_port(&delegated_hostname) { + host_and_port + } else if let Some(pos) = delegated_hostname.find(':') { + debug!("3.2: Hostname with port in .well-known file"); + let (host, port) = delegated_hostname.split_at(pos); + FedDest::Named(host.to_owned(), port.to_owned()) + } else { + debug!("Delegated hostname has no port in this branch"); + if let Some(hostname_override) = query_srv_record(&delegated_hostname).await + { + debug!("3.3: SRV lookup successful"); + let force_port = hostname_override.port(); - if let Ok(override_ip) = services() - .globals - .dns_resolver() - .lookup_ip(hostname_override.hostname()) - .await - { - services() - .globals - .tls_name_override - .write() - .unwrap() - .insert( - delegated_hostname.clone(), - ( - override_ip.iter().collect(), - force_port.unwrap_or(8448), - ), - ); - } else { - warn!("Using SRV record, but could not resolve to IP"); - } - - if let Some(port) = force_port { - FedDest::Named(delegated_hostname, format!(":{port}")) - } else { - add_port_to_hostname(&delegated_hostname) - } - } else { - debug!("3.4: No SRV records, just use the hostname from .well-known"); - add_port_to_hostname(&delegated_hostname) - } - } + if let Ok(override_ip) = services() + .globals + .dns_resolver() + .lookup_ip(hostname_override.hostname()) + .await + { + services() + .globals + .tls_name_override + .write() + .unwrap() + .insert( + delegated_hostname.clone(), + (override_ip.iter().collect(), force_port.unwrap_or(8448)), + ); + } else { + warn!("Using SRV record, but could not resolve to IP"); } + + if let Some(port) = force_port { + FedDest::Named(delegated_hostname, format!(":{port}")) + } else { + add_port_to_hostname(&delegated_hostname) + } + } else { + debug!("3.4: No SRV records, just use the hostname from .well-known"); + add_port_to_hostname(&delegated_hostname) } } - None => { - debug!("4: No .well-known or an error occured"); - match query_srv_record(&destination_str).await { - Some(hostname_override) => { - debug!("4: SRV record found"); - let force_port = hostname_override.port(); + } else { + debug!("4: No .well-known or an error occured"); + if let Some(hostname_override) = query_srv_record(&destination_str).await { + debug!("4: SRV record found"); + let force_port = hostname_override.port(); - if let Ok(override_ip) = services() - .globals - .dns_resolver() - .lookup_ip(hostname_override.hostname()) - .await - { - services() - .globals - .tls_name_override - .write() - .unwrap() - .insert( - hostname.clone(), - ( - override_ip.iter().collect(), - force_port.unwrap_or(8448), - ), - ); - } else { - warn!("Using SRV record, but could not resolve to IP"); - } - - if let Some(port) = force_port { - FedDest::Named(hostname.clone(), format!(":{port}")) - } else { - add_port_to_hostname(&hostname) - } - } - None => { - debug!("5: No SRV record found"); - add_port_to_hostname(&destination_str) - } + if let Ok(override_ip) = services() + .globals + .dns_resolver() + .lookup_ip(hostname_override.hostname()) + .await + { + services() + .globals + .tls_name_override + .write() + .unwrap() + .insert( + hostname.clone(), + (override_ip.iter().collect(), force_port.unwrap_or(8448)), + ); + } else { + warn!("Using SRV record, but could not resolve to IP"); } + + if let Some(port) = force_port { + FedDest::Named(hostname.clone(), format!(":{port}")) + } else { + add_port_to_hostname(&hostname) + } + } else { + debug!("5: No SRV record found"); + add_port_to_hostname(&destination_str) } } } @@ -1270,15 +1254,14 @@ pub(crate) async fn get_room_state_route( Ok(get_room_state::v1::Response { auth_chain: auth_chain_ids - .filter_map( - |id| match services().rooms.timeline.get_pdu_json(&id).ok()? { - Some(json) => Some(PduEvent::convert_to_outgoing_federation_event(json)), - None => { - error!("Could not find event json for {id} in db."); - None - } - }, - ) + .filter_map(|id| { + if let Some(json) = services().rooms.timeline.get_pdu_json(&id).ok()? { + Some(PduEvent::convert_to_outgoing_federation_event(json)) + } else { + error!("Could not find event json for {id} in db."); + None + } + }) .collect(), pdus, }) diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 66f7eb74..b8186561 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -10,18 +10,18 @@ impl service::rooms::short::Data for KeyValueDatabase { return Ok(*short); } - let short = match self.eventid_shorteventid.get(event_id.as_bytes())? { - Some(shorteventid) => utils::u64_from_bytes(&shorteventid) - .map_err(|_| Error::bad_database("Invalid shorteventid in db."))?, - None => { + let short = + if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? { + utils::u64_from_bytes(&shorteventid) + .map_err(|_| Error::bad_database("Invalid shorteventid in db."))? + } else { let shorteventid = services().globals.next_count()?; self.eventid_shorteventid .insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?; self.shorteventid_eventid .insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?; shorteventid - } - }; + }; self.eventidshort_cache .lock() @@ -86,17 +86,16 @@ impl service::rooms::short::Data for KeyValueDatabase { db_key.push(0xff); db_key.extend_from_slice(state_key.as_bytes()); - let short = match self.statekey_shortstatekey.get(&db_key)? { - Some(shortstatekey) => utils::u64_from_bytes(&shortstatekey) - .map_err(|_| Error::bad_database("Invalid shortstatekey in db."))?, - None => { - let shortstatekey = services().globals.next_count()?; - self.statekey_shortstatekey - .insert(&db_key, &shortstatekey.to_be_bytes())?; - self.shortstatekey_statekey - .insert(&shortstatekey.to_be_bytes(), &db_key)?; - shortstatekey - } + let short = if let Some(shortstatekey) = self.statekey_shortstatekey.get(&db_key)? { + utils::u64_from_bytes(&shortstatekey) + .map_err(|_| Error::bad_database("Invalid shortstatekey in db."))? + } else { + let shortstatekey = services().globals.next_count()?; + self.statekey_shortstatekey + .insert(&db_key, &shortstatekey.to_be_bytes())?; + self.shortstatekey_statekey + .insert(&shortstatekey.to_be_bytes(), &db_key)?; + shortstatekey }; self.statekeyshort_cache @@ -177,19 +176,20 @@ impl service::rooms::short::Data for KeyValueDatabase { /// Returns `(shortstatehash, already_existed)` fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { - Ok(match self.statehash_shortstatehash.get(state_hash)? { - Some(shortstatehash) => ( - utils::u64_from_bytes(&shortstatehash) - .map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?, - true, - ), - None => { + Ok( + if let Some(shortstatehash) = self.statehash_shortstatehash.get(state_hash)? { + ( + utils::u64_from_bytes(&shortstatehash) + .map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?, + true, + ) + } else { let shortstatehash = services().globals.next_count()?; self.statehash_shortstatehash .insert(state_hash, &shortstatehash.to_be_bytes())?; (shortstatehash, false) - } - }) + }, + ) } fn get_shortroomid(&self, room_id: &RoomId) -> Result> { @@ -203,15 +203,16 @@ impl service::rooms::short::Data for KeyValueDatabase { } fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { - Ok(match self.roomid_shortroomid.get(room_id.as_bytes())? { - Some(short) => utils::u64_from_bytes(&short) - .map_err(|_| Error::bad_database("Invalid shortroomid in db."))?, - None => { + Ok( + if let Some(short) = self.roomid_shortroomid.get(room_id.as_bytes())? { + utils::u64_from_bytes(&short) + .map_err(|_| Error::bad_database("Invalid shortroomid in db."))? + } else { let short = services().globals.next_count()?; self.roomid_shortroomid .insert(room_id.as_bytes(), &short.to_be_bytes())?; short - } - }) + }, + ) } } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 7751a172..f86465ed 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1153,7 +1153,7 @@ impl Service { } info!("Fetching {} over federation.", next_id); - match services() + if let Ok(res) = services() .sending .send_federation_request( origin, @@ -1163,44 +1163,41 @@ impl Service { ) .await { - Ok(res) => { - info!("Got {} over federation", next_id); - let Ok((calculated_event_id, value)) = - pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) - else { - back_off((*next_id).to_owned()).await; - continue; - }; - - if calculated_event_id != *next_id { - warn!("Server didn't return event id we requested: requested: {}, we got {}. Event: {:?}", - next_id, calculated_event_id, &res.pdu); - } - - if let Some(auth_events) = - value.get("auth_events").and_then(|c| c.as_array()) - { - for auth_event in auth_events { - if let Ok(auth_event) = - serde_json::from_value(auth_event.clone().into()) - { - let a: Arc = auth_event; - todo_auth_events.push(a); - } else { - warn!("Auth event id is not valid"); - } - } - } else { - warn!("Auth event list invalid"); - } - - events_in_reverse_order.push((next_id.clone(), value)); - events_all.insert(next_id); - } - Err(_) => { - warn!("Failed to fetch event: {}", next_id); + info!("Got {} over federation", next_id); + let Ok((calculated_event_id, value)) = + pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) + else { back_off((*next_id).to_owned()).await; + continue; + }; + + if calculated_event_id != *next_id { + warn!("Server didn't return event id we requested: requested: {}, we got {}. Event: {:?}", + next_id, calculated_event_id, &res.pdu); } + + if let Some(auth_events) = + value.get("auth_events").and_then(|c| c.as_array()) + { + for auth_event in auth_events { + if let Ok(auth_event) = + serde_json::from_value(auth_event.clone().into()) + { + let a: Arc = auth_event; + todo_auth_events.push(a); + } else { + warn!("Auth event id is not valid"); + } + } + } else { + warn!("Auth event list invalid"); + } + + events_in_reverse_order.push((next_id.clone(), value)); + events_all.insert(next_id); + } else { + warn!("Failed to fetch event: {}", next_id); + back_off((*next_id).to_owned()).await; } } @@ -1670,18 +1667,17 @@ impl Service { .get(origin) .map(|s| Arc::clone(s).acquire_owned()); - let permit = match permit { - Some(p) => p, - None => { - let mut write = services().globals.servername_ratelimiter.write().await; - let s = Arc::clone( - write - .entry(origin.to_owned()) - .or_insert_with(|| Arc::new(Semaphore::new(1))), - ); + let permit = if let Some(p) = permit { + p + } else { + let mut write = services().globals.servername_ratelimiter.write().await; + let s = Arc::clone( + write + .entry(origin.to_owned()) + .or_insert_with(|| Arc::new(Semaphore::new(1))), + ); - s.acquire_owned() - } + s.acquire_owned() } .await; diff --git a/src/service/sending.rs b/src/service/sending.rs index 6ad34076..9cfb8cfe 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -305,41 +305,38 @@ impl Service { let event: AnySyncEphemeralRoomEvent = serde_json::from_str(read_receipt.json().get()) .map_err(|_| Error::bad_database("Invalid edu event in read_receipts."))?; - let federation_event = match event { - AnySyncEphemeralRoomEvent::Receipt(r) => { - let mut read = BTreeMap::new(); + let federation_event = if let AnySyncEphemeralRoomEvent::Receipt(r) = event { + let mut read = BTreeMap::new(); - let (event_id, mut receipt) = r - .content - .0 - .into_iter() - .next() - .expect("we only use one event per read receipt"); - let receipt = receipt - .remove(&ReceiptType::Read) - .expect("our read receipts always set this") - .remove(&user_id) - .expect("our read receipts always have the user here"); + let (event_id, mut receipt) = r + .content + .0 + .into_iter() + .next() + .expect("we only use one event per read receipt"); + let receipt = receipt + .remove(&ReceiptType::Read) + .expect("our read receipts always set this") + .remove(&user_id) + .expect("our read receipts always have the user here"); - read.insert( - user_id, - ReceiptData { - data: receipt.clone(), - event_ids: vec![event_id.clone()], - }, - ); + read.insert( + user_id, + ReceiptData { + data: receipt.clone(), + event_ids: vec![event_id.clone()], + }, + ); - let receipt_map = ReceiptMap { read }; + let receipt_map = ReceiptMap { read }; - let mut receipts = BTreeMap::new(); - receipts.insert(room_id.clone(), receipt_map); + let mut receipts = BTreeMap::new(); + receipts.insert(room_id.clone(), receipt_map); - Edu::Receipt(ReceiptContent { receipts }) - } - _ => { - Error::bad_database("Invalid event type in read_receipts"); - continue; - } + Edu::Receipt(ReceiptContent { receipts }) + } else { + Error::bad_database("Invalid event type in read_receipts"); + continue; }; events.push(serde_json::to_vec(&federation_event).expect("json can be serialized")); From 8ef278d3588a90fed81af143f5942e33db023af3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:11:22 -0700 Subject: [PATCH 063/617] enable `struct_excessive_bools` lint And ignore it in the one place it fires, I know. --- Cargo.toml | 1 + src/config.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 52412cfd..fd2da7b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ string_add = "warn" string_lit_chars_any = "warn" string_slice = "warn" string_to_string = "warn" +struct_excessive_bools = "warn" suspicious_xor_used_as_pow = "warn" tests_outside_test_module = "warn" try_err = "warn" diff --git a/src/config.rs b/src/config.rs index 539a26f9..8e512a49 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,7 @@ mod proxy; use self::proxy::ProxyConfig; +#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Debug, Deserialize)] pub(crate) struct Config { #[serde(default = "default_address")] From baab9282818794c344fa22994b5721f9dedd38e6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:17:47 -0700 Subject: [PATCH 064/617] enable `too_many_lines` lint And just disable it everywhere it fires, I know. --- Cargo.toml | 1 + src/api/client_server/account.rs | 1 + src/api/client_server/context.rs | 1 + src/api/client_server/directory.rs | 1 + src/api/client_server/keys.rs | 1 + src/api/client_server/membership.rs | 2 ++ src/api/client_server/message.rs | 1 + src/api/client_server/room.rs | 2 ++ src/api/client_server/search.rs | 1 + src/api/client_server/session.rs | 1 + src/api/client_server/sync.rs | 4 +++- src/api/ruma_wrapper/axum.rs | 1 + src/api/server_server.rs | 3 +++ src/database.rs | 1 + src/main.rs | 1 + src/service/admin.rs | 2 ++ src/service/rooms/spaces.rs | 2 ++ src/service/rooms/timeline.rs | 1 + src/service/users.rs | 1 + 19 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fd2da7b1..22e57bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ string_to_string = "warn" struct_excessive_bools = "warn" suspicious_xor_used_as_pow = "warn" tests_outside_test_module = "warn" +too_many_lines = "warn" try_err = "warn" undocumented_unsafe_blocks = "warn" unnecessary_safety_comment = "warn" diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 54b735b3..6d254a9c 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -74,6 +74,7 @@ pub(crate) async fn get_register_available_route( /// - If type is not guest and no username is given: Always fails after UIAA check /// - Creates a new account and populates it with default account data /// - If `inhibit_login` is false: Creates a device and returns `device_id` and `access_token` +#[allow(clippy::too_many_lines)] pub(crate) async fn register_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 14f5eb0d..a063a2ec 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -13,6 +13,7 @@ use tracing::error; /// /// - Only works if the user is joined (TODO: always allow, but only show events if the user was /// joined, depending on `history_visibility`) +#[allow(clippy::too_many_lines)] pub(crate) async fn get_context_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 4d926442..f4141fa9 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -123,6 +123,7 @@ pub(crate) async fn get_room_visibility_route( }) } +#[allow(clippy::too_many_lines)] pub(crate) async fn get_public_rooms_filtered_helper( server: Option<&ServerName>, limit: Option, diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 4c104a6b..8a9191d1 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -256,6 +256,7 @@ pub(crate) async fn get_key_changes_route( }) } +#[allow(clippy::too_many_lines)] pub(crate) async fn get_keys_helper bool>( sender_user: Option<&UserId>, device_keys_input: &BTreeMap>, diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 21332ed9..84b92f4f 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -518,6 +518,7 @@ pub(crate) async fn joined_members_route( Ok(joined_members::v3::Response { joined }) } +#[allow(clippy::too_many_lines)] async fn join_room_by_id_helper( sender_user: Option<&UserId>, room_id: &RoomId, @@ -1200,6 +1201,7 @@ async fn validate_and_add_event_id( Ok((event_id, value)) } +#[allow(clippy::too_many_lines)] pub(crate) async fn invite_helper( sender_user: &UserId, user_id: &UserId, diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index db8c8bf8..76b367c8 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -112,6 +112,7 @@ pub(crate) async fn send_message_event_route( /// /// - Only works if the user is joined (TODO: always allow, but only show events where the user was /// joined, depending on `history_visibility`) +#[allow(clippy::too_many_lines)] pub(crate) async fn get_message_events_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 8167ed25..1b266e25 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -45,6 +45,7 @@ use tracing::{info, warn}; /// - Send events listed in initial state /// - Send events implied by `name` and `topic` /// - Send invite events +#[allow(clippy::too_many_lines)] pub(crate) async fn create_room_route( body: Ruma, ) -> Result { @@ -580,6 +581,7 @@ pub(crate) async fn get_room_aliases_route( /// - Transfers some state events /// - Moves local aliases /// - Modifies old room power levels to prevent users from speaking +#[allow(clippy::too_many_lines)] pub(crate) async fn upgrade_room_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 3743b877..9d69d3b8 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -17,6 +17,7 @@ use std::collections::BTreeMap; /// Searches rooms for messages. /// /// - Only works if the user is currently joined to the room (TODO: Respect history visibility) +#[allow(clippy::too_many_lines)] pub(crate) async fn search_events_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 9d1cc552..7a58a992 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -47,6 +47,7 @@ pub(crate) async fn get_login_types_route( /// /// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see /// supported login types. +#[allow(clippy::too_many_lines)] pub(crate) async fn login_route(body: Ruma) -> Result { // To allow deprecated login methods #![allow(deprecated)] diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index b5799bf5..7655c66d 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -168,6 +168,7 @@ async fn sync_helper_wrapper( .expect("receiver should not be dropped"); } +#[allow(clippy::too_many_lines)] async fn sync_helper( sender_user: OwnedUserId, sender_device: OwnedDeviceId, @@ -546,7 +547,7 @@ async fn sync_helper( } } -#[allow(clippy::too_many_arguments)] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] async fn load_joined_room( sender_user: &UserId, sender_device: &DeviceId, @@ -1154,6 +1155,7 @@ fn share_encrypted_room( .any(|encrypted| encrypted)) } +#[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_v4_route( body: Ruma, ) -> Result> { diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 84209ca7..22931cf2 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -40,6 +40,7 @@ where { type Rejection = Error; + #[allow(clippy::too_many_lines)] async fn from_request(req: Request, _state: &S) -> Result { #[derive(Deserialize)] struct QueryParams { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 41b0db57..daba63ff 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -343,6 +343,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { /// Returns: `actual_destination`, `Host` header /// Implemented according to the specification at /// Numbers in comments below refer to bullet points in linked section of specification +#[allow(clippy::too_many_lines)] async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) { debug!("Finding actual destination for {destination}"); let destination_str = destination.as_str().to_owned(); @@ -653,6 +654,7 @@ pub(crate) fn parse_incoming_pdu( /// # `PUT /_matrix/federation/v1/send/{txnId}` /// /// Push EDUs and PDUs to this server. +#[allow(clippy::too_many_lines)] pub(crate) async fn send_transaction_message_route( body: Ruma, ) -> Result { @@ -1432,6 +1434,7 @@ pub(crate) async fn create_join_event_template_route( }) } +#[allow(clippy::too_many_lines)] async fn create_join_event( sender_servername: &ServerName, room_id: &RoomId, diff --git a/src/database.rs b/src/database.rs index d3fda641..3e105120 100644 --- a/src/database.rs +++ b/src/database.rs @@ -214,6 +214,7 @@ impl KeyValueDatabase { not(any(feature = "rocksdb", feature = "sqlite")), allow(unreachable_code) )] + #[allow(clippy::too_many_lines)] pub(crate) async fn load_or_create(config: Config) -> Result<()> { Self::check_db_setup(&config)?; diff --git a/src/main.rs b/src/main.rs index ab06163f..def5a85a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -279,6 +279,7 @@ async fn unrecognized_method( Ok(inner) } +#[allow(clippy::too_many_lines)] fn routes(config: &Config) -> Router { let router = Router::new() .ruma_route(client_server::get_supported_versions_route) diff --git a/src/service/admin.rs b/src/service/admin.rs index 6545c062..1a7cd8aa 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -327,6 +327,7 @@ impl Service { AdminCommand::try_parse_from(argv).map_err(|error| error.to_string()) } + #[allow(clippy::too_many_lines)] async fn process_admin_command( &self, command: AdminCommand, @@ -933,6 +934,7 @@ impl Service { /// /// Users in this room are considered admins by grapevine, and the room can be /// used to issue admin commands by talking to the server user inside it. + #[allow(clippy::too_many_lines)] pub(crate) async fn create_admin_room(&self) -> Result<()> { let room_id = RoomId::new(services().globals.server_name()); diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index af019e3d..0ffbbf9a 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -47,6 +47,7 @@ pub(crate) struct Service { } impl Service { + #[allow(clippy::too_many_lines)] pub(crate) async fn get_hierarchy( &self, sender_user: &UserId, @@ -306,6 +307,7 @@ impl Service { }) } + #[allow(clippy::too_many_lines)] fn get_room_chunk( &self, sender_user: &UserId, diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 33cd1889..4dce9420 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -607,6 +607,7 @@ impl Service { Ok(pdu_id) } + #[allow(clippy::too_many_lines)] pub(crate) fn create_hash_and_sign_event( &self, pdu_builder: PduBuilder, diff --git a/src/service/users.rs b/src/service/users.rs index b63e90bb..2e52f478 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -57,6 +57,7 @@ impl Service { .remove(&(user_id, device_id, conn_id)); } + #[allow(clippy::too_many_lines)] pub(crate) fn update_sync_request_with_cache( &self, user_id: OwnedUserId, From 86218f47719f6104a7941f5d0f582fbe296186ff Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:19:54 -0700 Subject: [PATCH 065/617] enable `uninlined_format_args` lint --- Cargo.toml | 1 + src/clap.rs | 2 +- src/service/rooms/timeline.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22e57bb5..b24397b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ tests_outside_test_module = "warn" too_many_lines = "warn" try_err = "warn" undocumented_unsafe_blocks = "warn" +uninlined_format_args = "warn" unnecessary_safety_comment = "warn" unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" diff --git a/src/clap.rs b/src/clap.rs index e17c5106..f38d162a 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -11,7 +11,7 @@ fn version() -> String { let cargo_pkg_version = env!("CARGO_PKG_VERSION"); match option_env!("GRAPEVINE_VERSION_EXTRA") { - Some(x) => format!("{} ({})", cargo_pkg_version, x), + Some(x) => format!("{cargo_pkg_version} ({x})"), None => cargo_pkg_version.to_owned(), } } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 4dce9420..cb5f3332 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -822,7 +822,7 @@ impl Service { .filter(|v| v.starts_with('@')) .unwrap_or(sender.as_str()); let server_name = services().globals.server_name(); - let server_user = format!("@grapevine:{}", server_name); + let server_user = format!("@grapevine:{server_name}"); let content = serde_json::from_str::(pdu.content.get()) .map_err(|_| Error::bad_database("Invalid content in pdu."))?; From 4e80dc028ef2eaa06df5994a37e10a60f091e6f0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:22:21 -0700 Subject: [PATCH 066/617] enable `unnecessary_wraps` lint --- Cargo.toml | 1 + src/database/abstraction/rocksdb.rs | 4 ++-- src/database/abstraction/sqlite.rs | 6 ++---- src/utils.rs | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b24397b9..6b8e03d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ uninlined_format_args = "warn" unnecessary_safety_comment = "warn" unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" +unnecessary_wraps = "warn" unneeded_field_pattern = "warn" unseparated_literal_suffix = "warn" verbose_file_reads = "warn" diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 831e9bcd..e34c6c32 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -227,7 +227,7 @@ impl KvTree for RocksDbEngineTree<'_> { let lock = self.write_lock.write().unwrap(); let old = self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?; - let new = utils::increment(old.as_deref()).unwrap(); + let new = utils::increment(old.as_deref()); self.db .rocks .put_cf_opt(&self.cf(), key, &new, &writeoptions)?; @@ -244,7 +244,7 @@ impl KvTree for RocksDbEngineTree<'_> { for key in iter { let old = self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?; - let new = utils::increment(old.as_deref()).unwrap(); + let new = utils::increment(old.as_deref()); self.db .rocks .put_cf_opt(&self.cf(), key, new, &writeoptions)?; diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 3105da22..a463cb61 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -237,8 +237,7 @@ impl KvTree for SqliteTable { guard.execute("BEGIN", [])?; for key in iter { let old = self.get_with_guard(&guard, &key)?; - let new = crate::utils::increment(old.as_deref()) - .expect("utils::increment always returns Some"); + let new = crate::utils::increment(old.as_deref()); self.insert_with_guard(&guard, &key, &new)?; } guard.execute("COMMIT", [])?; @@ -328,8 +327,7 @@ impl KvTree for SqliteTable { let old = self.get_with_guard(&guard, key)?; - let new = - crate::utils::increment(old.as_deref()).expect("utils::increment always returns Some"); + let new = crate::utils::increment(old.as_deref()); self.insert_with_guard(&guard, key, &new)?; diff --git a/src/utils.rs b/src/utils.rs index 1ff95576..a335e29c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,7 +21,7 @@ pub(crate) fn millis_since_unix_epoch() -> u64 { } #[cfg(any(feature = "rocksdb", feature = "sqlite"))] -pub(crate) fn increment(old: Option<&[u8]>) -> Option> { +pub(crate) fn increment(old: Option<&[u8]>) -> Vec { let number = match old.map(TryInto::try_into) { Some(Ok(bytes)) => { let number = u64::from_be_bytes(bytes); @@ -30,7 +30,7 @@ pub(crate) fn increment(old: Option<&[u8]>) -> Option> { _ => 1, // Start at one. since 0 should return the first event in the db }; - Some(number.to_be_bytes().to_vec()) + number.to_be_bytes().to_vec() } pub(crate) fn generate_keypair() -> Vec { From a4ff32667e1036d6e5129940942182fdc08307be Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:24:40 -0700 Subject: [PATCH 067/617] enable `unnested_or_patterns` lint --- Cargo.toml | 1 + src/api/client_server/membership.rs | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6b8e03d7..566525bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" unnecessary_wraps = "warn" unneeded_field_pattern = "warn" +unnested_or_patterns = "warn" unseparated_literal_suffix = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 84b92f4f..b8663ce1 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -571,10 +571,7 @@ async fn join_room_by_id_helper( let restriction_rooms = match join_rules_event_content { Some(RoomJoinRulesEventContent { - join_rule: JoinRule::Restricted(restricted), - }) - | Some(RoomJoinRulesEventContent { - join_rule: JoinRule::KnockRestricted(restricted), + join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted), }) => restricted .allow .into_iter() From 4419e855ae1a906d608eead353cb93804426170e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:25:48 -0700 Subject: [PATCH 068/617] enable `unreadable_literal` lint --- Cargo.toml | 1 + src/database/abstraction/rocksdb.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 566525bd..80eb00bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,7 @@ unnecessary_self_imports = "warn" unnecessary_wraps = "warn" unneeded_field_pattern = "warn" unnested_or_patterns = "warn" +unreadable_literal = "warn" unseparated_literal_suffix = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index e34c6c32..64444142 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -41,7 +41,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning db_opts.set_level_compaction_dynamic_level_bytes(true); db_opts.set_max_background_jobs(6); - db_opts.set_bytes_per_sync(1048576); + db_opts.set_bytes_per_sync(1_048_576); // https://github.com/facebook/rocksdb/issues/849 db_opts.set_keep_log_file_num(100); From f855bd09d1cac4159da85614c8cf3fff5b1c03e7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:28:36 -0700 Subject: [PATCH 069/617] enable `unused_async` lint This also caused a "new" `unnecessary_wraps` lint to fire too, so that got fixed too. --- Cargo.toml | 1 + src/api/client_server/membership.rs | 12 ++++++------ src/service/rooms/state_accessor.rs | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 80eb00bd..dd4cf60c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ unneeded_field_pattern = "warn" unnested_or_patterns = "warn" unreadable_literal = "warn" unseparated_literal_suffix = "warn" +unused_async = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index b8663ce1..005ce01d 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -599,12 +599,12 @@ async fn join_room_by_id_helper( .collect::>() { if user.server_name() == services().globals.server_name() - && services() - .rooms - .state_accessor - .user_can_invite(room_id, &user, sender_user, &state_lock) - .await - .unwrap_or(false) + && services().rooms.state_accessor.user_can_invite( + room_id, + &user, + sender_user, + &state_lock, + ) { auth_user = Some(user); break; diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 0c0b8ac5..4ed4d43f 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -310,13 +310,13 @@ impl Service { }) } - pub(crate) async fn user_can_invite( + pub(crate) fn user_can_invite( &self, room_id: &RoomId, sender: &UserId, target_user: &UserId, state_lock: &MutexGuard<'_, ()>, - ) -> Result { + ) -> bool { let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite)) .expect("Event content always serializes"); @@ -328,11 +328,11 @@ impl Service { redacts: None, }; - Ok(services() + services() .rooms .timeline .create_hash_and_sign_event(new_event, sender, room_id, state_lock) - .is_ok()) + .is_ok() } pub(crate) fn get_member( From e3672eb4e0135f75ec279727bee26f316e6a3ef1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:40:35 -0700 Subject: [PATCH 070/617] enable `unused_self` lint Functions using `services()` are allowed to pointlessly take `self` because the existence of `services()` is a crime and the solution is making the types store references to their dependencies and then going through `self`, so just allowing the lint saves us from modifying some code only to switch it back later. Much later. Getting rid of `services()` will probably be an ordeal. --- Cargo.toml | 1 + src/service/admin.rs | 10 ++++++---- src/service/media.rs | 7 +++---- src/service/rooms/event_handler.rs | 12 +++++++----- src/service/rooms/pdu_metadata.rs | 6 +++++- src/service/rooms/spaces.rs | 8 +++++--- src/service/rooms/state_accessor.rs | 17 +++++------------ src/service/rooms/state_compressor.rs | 4 ++++ src/service/rooms/timeline.rs | 2 +- src/service/users.rs | 2 ++ 10 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd4cf60c..8e816675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,7 @@ unnested_or_patterns = "warn" unreadable_literal = "warn" unseparated_literal_suffix = "warn" unused_async = "warn" +unused_self = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" diff --git a/src/service/admin.rs b/src/service/admin.rs index 1a7cd8aa..3d10be63 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -277,12 +277,12 @@ impl Service { let command_line = lines.next().expect("each string has at least one line"); let body: Vec<_> = lines.collect(); - let admin_command = match self.parse_admin_command(command_line) { + let admin_command = match Self::parse_admin_command(command_line) { Ok(command) => command, Err(error) => { let server_name = services().globals.server_name(); let message = error.replace("server.name", server_name.as_str()); - let html_message = self.usage_to_html(&message, server_name); + let html_message = Self::usage_to_html(&message, server_name); return RoomMessageEventContent::text_html(message, html_message); } @@ -306,7 +306,7 @@ impl Service { } // Parse chat messages from the admin room into an AdminCommand object - fn parse_admin_command(&self, command_line: &str) -> std::result::Result { + fn parse_admin_command(command_line: &str) -> std::result::Result { // Note: argv[0] is `@grapevine:servername:`, which is treated as the main command let mut argv: Vec<_> = command_line.split_whitespace().collect(); @@ -864,7 +864,7 @@ impl Service { } // Utility to turn clap's `--help` text to HTML. - fn usage_to_html(&self, text: &str, server_name: &ServerName) -> String { + fn usage_to_html(text: &str, server_name: &ServerName) -> String { // Replace `@grapevine:servername:-subcmdname` with `@grapevine:servername: subcmdname` let text = text.replace( &format!("@grapevine:{server_name}:-"), @@ -1183,6 +1183,8 @@ impl Service { /// Gets the room ID of the admin room /// /// Errors are propagated from the database, and will have None if there is no admin room + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] pub(crate) fn get_admin_room(&self) -> Result> { let admin_room_alias: Box = format!("#admins:{}", services().globals.server_name()) diff --git a/src/service/media.rs b/src/service/media.rs index c19497cf..7b12ab1f 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -86,7 +86,7 @@ impl Service { /// Returns width, height of the thumbnail and whether it should be cropped. Returns None when /// the server should send the original file. - pub(crate) fn thumbnail_properties(&self, width: u32, height: u32) -> Option<(u32, u32, bool)> { + fn thumbnail_properties(width: u32, height: u32) -> Option<(u32, u32, bool)> { match (width, height) { (0..=32, 0..=32) => Some((32, 32, true)), (0..=96, 0..=96) => Some((96, 96, true)), @@ -113,9 +113,8 @@ impl Service { width: u32, height: u32, ) -> Result> { - let (width, height, crop) = self - .thumbnail_properties(width, height) - .unwrap_or((0, 0, false)); // 0, 0 because that's the original file + let (width, height, crop) = + Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); // 0, 0 because that's the original file if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc.clone(), width, height) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index f86465ed..9e6b4802 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -132,7 +132,7 @@ impl Service { pub_key_map, ) .await?; - self.check_room_id(room_id, &incoming_pdu)?; + Self::check_room_id(room_id, &incoming_pdu)?; // 8. if not timeline event: stop if !is_timeline_event { @@ -375,7 +375,7 @@ impl Service { ) .map_err(|_| Error::bad_database("Event is not a valid PDU."))?; - self.check_room_id(room_id, &incoming_pdu)?; + Self::check_room_id(room_id, &incoming_pdu)?; if !auth_events_known { // 4. fetch any missing auth events doing all checks listed here starting at 1. These are not timeline events @@ -411,7 +411,7 @@ impl Service { continue; }; - self.check_room_id(room_id, &auth_event)?; + Self::check_room_id(room_id, &auth_event)?; match auth_events.entry(( auth_event.kind.to_string().into(), @@ -1287,7 +1287,7 @@ impl Service { .await .pop() { - self.check_room_id(room_id, &pdu)?; + Self::check_room_id(room_id, &pdu)?; if amount > services().globals.max_fetch_prev_events() { // Max limit reached @@ -1612,6 +1612,8 @@ impl Service { } /// Returns Ok if the acl allows the server + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] pub(crate) fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> { let Some(acl_event) = services().rooms.state_accessor.room_state_get( room_id, @@ -1815,7 +1817,7 @@ impl Service { )) } - fn check_room_id(&self, room_id: &RoomId, pdu: &PduEvent) -> Result<()> { + fn check_room_id(room_id: &RoomId, pdu: &PduEvent) -> Result<()> { if pdu.room_id != room_id { warn!("Found event from room {} in room {}", pdu.room_id, room_id); return Err(Error::BadRequest( diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index e9060ae7..13f29663 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -40,7 +40,11 @@ impl Service { } } - #[allow(clippy::too_many_arguments)] + #[allow( + clippy::too_many_arguments, + // Allowed because this function uses `services()` + clippy::unused_self, + )] pub(crate) fn paginate_relations_with_filter( &self, sender_user: &UserId, diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 0ffbbf9a..bd1f5702 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -415,7 +415,7 @@ impl Service { )); } - self.translate_joinrule(&join_rule)? + Self::translate_joinrule(&join_rule)? }, room_type: services() .rooms @@ -436,7 +436,7 @@ impl Service { }) } - fn translate_joinrule(&self, join_rule: &JoinRule) -> Result { + fn translate_joinrule(join_rule: &JoinRule) -> Result { match join_rule { JoinRule::Invite => Ok(SpaceRoomJoinRule::Invite), JoinRule::Knock => Ok(SpaceRoomJoinRule::Knock), @@ -448,6 +448,8 @@ impl Service { } } + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] fn handle_simplified_join_rule( &self, join_rule: &SpaceRoomJoinRule, @@ -473,7 +475,7 @@ impl Service { room_id: &RoomId, ) -> Result { if self.handle_simplified_join_rule( - &self.translate_joinrule(join_rule)?, + &Self::translate_joinrule(join_rule)?, sender_user, room_id, )? { diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 4ed4d43f..6c1dd527 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -282,10 +282,7 @@ impl Service { } pub(crate) fn get_name(&self, room_id: &RoomId) -> Result> { - services() - .rooms - .state_accessor - .room_state_get(room_id, &StateEventType::RoomName, "")? + self.room_state_get(room_id, &StateEventType::RoomName, "")? .map_or(Ok(None), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomNameEventContent| Some(c.name)) @@ -300,16 +297,15 @@ impl Service { } pub(crate) fn get_avatar(&self, room_id: &RoomId) -> Result> { - services() - .rooms - .state_accessor - .room_state_get(room_id, &StateEventType::RoomAvatar, "")? + self.room_state_get(room_id, &StateEventType::RoomAvatar, "")? .map_or(Ok(JsOption::Undefined), |s| { serde_json::from_str(s.content.get()) .map_err(|_| Error::bad_database("Invalid room avatar event in database.")) }) } + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] pub(crate) fn user_can_invite( &self, room_id: &RoomId, @@ -340,10 +336,7 @@ impl Service { room_id: &RoomId, user_id: &UserId, ) -> Result> { - services() - .rooms - .state_accessor - .room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())? + self.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())? .map_or(Ok(None), |s| { serde_json::from_str(s.content.get()) .map_err(|_| Error::bad_database("Invalid room member event in database.")) diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 8ba07634..8881c652 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -89,6 +89,8 @@ impl Service { } } + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] pub(crate) fn compress_state_event( &self, shortstatekey: u64, @@ -106,6 +108,8 @@ impl Service { } /// Returns shortstatekey, event id + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] pub(crate) fn parse_compressed_state_event( &self, compressed_event: &CompressedStateEvent, diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index cb5f3332..1c57369d 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -662,7 +662,7 @@ impl Service { // Our depth is the maximum depth of prev_events + 1 let depth = prev_events .iter() - .filter_map(|event_id| Some(services().rooms.timeline.get_pdu(event_id).ok()??.depth)) + .filter_map(|event_id| Some(self.get_pdu(event_id).ok()??.depth)) .max() .unwrap_or_else(|| uint!(0)) + uint!(1); diff --git a/src/service/users.rs b/src/service/users.rs index 2e52f478..53882040 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -260,6 +260,8 @@ impl Service { } /// Check if a user is an admin + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] pub(crate) fn is_admin(&self, user_id: &UserId) -> Result { let admin_room_alias_id = RoomAliasId::parse(format!("#admins:{}", services().globals.server_name())) From 3978b9c580da572d531bde2f7fb8878aeddd362e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:44:26 -0700 Subject: [PATCH 071/617] enable `used_underscore_binding` lint --- Cargo.toml | 1 + src/database.rs | 10 +++++----- src/database/key_value/globals.rs | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e816675..578772b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,7 @@ unreadable_literal = "warn" unseparated_literal_suffix = "warn" unused_async = "warn" unused_self = "warn" +used_underscore_binding = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" diff --git a/src/database.rs b/src/database.rs index 3e105120..1410ca27 100644 --- a/src/database.rs +++ b/src/database.rs @@ -30,7 +30,7 @@ use std::{ use tracing::{debug, error, info, warn}; pub(crate) struct KeyValueDatabase { - _db: Arc, + db: Arc, //pub(crate) globals: globals::Globals, pub(super) global: Arc, @@ -246,7 +246,7 @@ impl KeyValueDatabase { } let db_raw = Box::new(Self { - _db: builder.clone(), + db: builder.clone(), userid_password: builder.open_tree("userid_password")?, userid_displayname: builder.open_tree("userid_displayname")?, userid_avatarurl: builder.open_tree("userid_avatarurl")?, @@ -614,7 +614,7 @@ impl KeyValueDatabase { Ok::<_, Error>(()) }; - for (k, seventid) in db._db.open_tree("stateid_shorteventid")?.iter() { + for (k, seventid) in db.db.open_tree("stateid_shorteventid")?.iter() { let sstatehash = utils::u64_from_bytes(&k[0..size_of::()]) .expect("number of bytes is correct"); let sstatekey = k[size_of::()..].to_vec(); @@ -791,7 +791,7 @@ impl KeyValueDatabase { } if services().globals.database_version()? < 11 { - db._db + db.db .open_tree("userdevicesessionid_uiaarequest")? .clear()?; services().globals.bump_database_version(11)?; @@ -980,7 +980,7 @@ impl KeyValueDatabase { pub(crate) fn flush(&self) -> Result<()> { let start = std::time::Instant::now(); - let res = self._db.flush(); + let res = self.db.flush(); debug!("flush: took {:?}", start.elapsed()); diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 0d58b241..59aaf575 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -118,7 +118,7 @@ impl service::globals::Data for KeyValueDatabase { } fn cleanup(&self) -> Result<()> { - self._db.cleanup() + self.db.cleanup() } fn memory_usage(&self) -> String { @@ -142,7 +142,7 @@ our_real_users_cache: {our_real_users_cache} appservice_in_room_cache: {appservice_in_room_cache} lasttimelinecount_cache: {lasttimelinecount_cache}\n" ); - if let Ok(db_stats) = self._db.memory_usage() { + if let Ok(db_stats) = self.db.memory_usage() { response += &db_stats; } From 44b15dcb083d0b62caccda2eca4bdf307a5ee2e3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:46:20 -0700 Subject: [PATCH 072/617] enable `wildcard_imports` lint --- Cargo.toml | 1 + src/service/rooms/event_handler.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 578772b8..4e0d51d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,6 +95,7 @@ unused_self = "warn" used_underscore_binding = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" +wildcard_imports = "warn" [package] name = "grapevine" diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 9e6b4802..c334f6d8 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -39,7 +39,7 @@ use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; -use crate::{service::*, services, Error, PduEvent, Result}; +use crate::{service::pdu, services, Error, PduEvent, Result}; use super::state_compressor::CompressedStateEvent; From a569bf8d99cb53f47584a67401f5a785ff1f7d53 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:47:55 -0700 Subject: [PATCH 073/617] enable `zero_sized_map_values` lint --- Cargo.toml | 1 + src/config.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 4e0d51d4..9509250c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ used_underscore_binding = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" wildcard_imports = "warn" +zero_sized_map_values = "warn" [package] name = "grapevine" diff --git a/src/config.rs b/src/config.rs index 8e512a49..ee861693 100644 --- a/src/config.rs +++ b/src/config.rs @@ -80,6 +80,8 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, #[serde(flatten)] + // This has special meaning to `serde` + #[allow(clippy::zero_sized_map_values)] pub(crate) catchall: BTreeMap, } From e3c57fa83d9734e91a67e6505bdb4405acecaca1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 14 May 2024 19:50:07 -0700 Subject: [PATCH 074/617] enable most pedantic lints as a group I'm turning off the documentation related ones because they generate way too many warnings, this kind of thing will need to be improved over a longer timespan. --- Cargo.toml | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9509250c..671a4e6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,88 +15,62 @@ unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" -# Keep alphabetically sorted [workspace.lints.clippy] +# Groups. Keep alphabetically sorted +pedantic = "warn" + +# Lints. Keep alphabetically sorted as_conversions = "warn" assertions_on_result_states = "warn" -cloned_instead_of_copied = "warn" dbg_macro = "warn" -default_trait_access = "warn" default_union_representation = "warn" deref_by_slicing = "warn" -doc_markdown = "warn" empty_drop = "warn" empty_structs_with_brackets = "warn" -enum_glob_use = "warn" error_impl_error = "warn" -explicit_into_iter_loop = "warn" filetype_is_file = "warn" -flat_map_option = "warn" float_cmp_const = "warn" format_push_string = "warn" get_unwrap = "warn" -if_not_else = "warn" if_then_some_else_none = "warn" -ignored_unit_patterns = "warn" impl_trait_in_params = "warn" -implicit_clone = "warn" -inconsistent_struct_constructor = "warn" -items_after_statements = "warn" let_underscore_must_use = "warn" lossy_float_literal = "warn" -manual_let_else = "warn" -manual_string_new = "warn" -map_unwrap_or = "warn" -match_bool = "warn" -match_same_arms = "warn" -match_wildcard_for_single_variants = "warn" mem_forget = "warn" missing_assert_message = "warn" mod_module_files = "warn" multiple_inherent_impl = "warn" mutex_atomic = "warn" -needless_pass_by_value = "warn" negative_feature_names = "warn" pub_without_shorthand = "warn" rc_buffer = "warn" rc_mutex = "warn" -redundant_closure_for_method_calls = "warn" redundant_feature_names = "warn" redundant_type_annotations = "warn" ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" same_name_method = "warn" -semicolon_if_nothing_returned = "warn" semicolon_inside_block = "warn" -similar_names = "warn" -single_match_else = "warn" str_to_string = "warn" string_add = "warn" string_lit_chars_any = "warn" string_slice = "warn" string_to_string = "warn" -struct_excessive_bools = "warn" suspicious_xor_used_as_pow = "warn" tests_outside_test_module = "warn" -too_many_lines = "warn" try_err = "warn" undocumented_unsafe_blocks = "warn" -uninlined_format_args = "warn" unnecessary_safety_comment = "warn" unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" -unnecessary_wraps = "warn" unneeded_field_pattern = "warn" -unnested_or_patterns = "warn" -unreadable_literal = "warn" unseparated_literal_suffix = "warn" -unused_async = "warn" -unused_self = "warn" -used_underscore_binding = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" -wildcard_imports = "warn" -zero_sized_map_values = "warn" + +# TODO: Remove these: +missing_errors_doc = "allow" +missing_panics_doc = "allow" [package] name = "grapevine" From f2052805201f0685d850592b1c96f4861c58fb22 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 23:02:45 -0700 Subject: [PATCH 075/617] use the intended default port by default Why was it not like this before? All the documentation and examples used this as the default port. --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index ee861693..b42f5f57 100644 --- a/src/config.rs +++ b/src/config.rs @@ -216,7 +216,7 @@ fn default_address() -> IpAddr { } fn default_port() -> u16 { - 8000 + 6167 } fn default_db_cache_capacity_mb() -> f64 { From ce5ce60dd90ae81f0d675de73460d6958d57c062 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 00:55:57 -0700 Subject: [PATCH 076/617] use nix-output-monitor if available --- bin/nix-build-and-cache | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 7d21428f..b206da66 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -5,8 +5,13 @@ set -euo pipefail # The first argument must be the desired installable INSTALLABLE="$1" -# Build the installable and forward any other arguments too -nix build "$@" +# Build the installable and forward any other arguments too. Also, use +# nix-output-monitor instead if it's available. +if command -v nom &> /dev/null; then + nom build "$@" +else + nix build "$@" +fi if [ ! -z ${ATTIC_TOKEN+x} ]; then nix run --inputs-from . attic -- \ From ca037220729fc2e3ed632221c0e9857dbf04d3b6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 01:48:24 -0700 Subject: [PATCH 077/617] build and cache all packages and CI dependencies This fixes the problem where some artifacts were not being cached when they should have been. The secret sauce is the `nix-store` command. Also stops emitting artifacts to GitLab. Automatic build scheduling via Nix is too convenient. Maybe I'll figure out a way to do both later on. Also pins the remaining unpinned dependencies, namely direnv and nix-direnv. --- .gitlab-ci.yml | 32 ++-------------- bin/nix-build-and-cache | 84 +++++++++++++++++++++++++++++++---------- 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fef354ce..9f7a1273 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ before_script: - if command -v nix > /dev/null; then echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf; fi # Install direnv and nix-direnv - - if command -v nix > /dev/null; then nix-env -iA nixpkgs.direnv nixpkgs.nix-direnv; fi + - if command -v nix > /dev/null; then nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv; fi # Allow .envrc - if command -v nix > /dev/null; then direnv allow; fi @@ -35,8 +35,7 @@ ci: stage: ci image: nixos/nix:2.20.4 script: - # Cache the inputs required for the devShell - - ./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation + - ./bin/nix-build-and-cache ci - direnv exec . engage cache: @@ -48,29 +47,4 @@ artifacts: stage: artifacts image: nixos/nix:2.20.4 script: - - ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl - - cp result/bin/grapevine x86_64-unknown-linux-musl - - # Since the OCI image package is based on the binary package, this has the - # deploying with Nix can leverage this fact by adding our binary cache to - # fun side effect of uploading the normal binary too. Grapevine users who are - # their systems. - # - # Note that although we have an `oci-image-x86_64-unknown-linux-musl` - # output, we don't build it because it would be largely redundant to this - # one since it's all containerized anyway. - - ./bin/nix-build-and-cache .#oci-image - - cp result oci-image-amd64.tar.gz - - - ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl - - cp result/bin/grapevine aarch64-unknown-linux-musl - - - ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl - - cp result oci-image-arm64v8.tar.gz - artifacts: - paths: - - x86_64-unknown-linux-musl - - aarch64-unknown-linux-musl - - x86_64-unknown-linux-musl.deb - - oci-image-amd64.tar.gz - - oci-image-arm64v8.tar.gz + - ./bin/nix-build-and-cache packages diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index b206da66..4c2a8d71 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -2,30 +2,74 @@ set -euo pipefail -# The first argument must be the desired installable -INSTALLABLE="$1" +toplevel="$(git rev-parse --show-toplevel)" -# Build the installable and forward any other arguments too. Also, use -# nix-output-monitor instead if it's available. -if command -v nom &> /dev/null; then - nom build "$@" -else - nix build "$@" -fi +# Build and cache the specified arguments +just() { + if command -v nom &> /dev/null; then + nom build "$@" + else + nix build "$@" + fi -if [ ! -z ${ATTIC_TOKEN+x} ]; then - nix run --inputs-from . attic -- \ + if [ -z ${ATTIC_TOKEN+x} ]; then + echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache" + return + fi + + nix run --inputs-from "$toplevel" attic -- \ login \ "$ATTIC_SERVER" \ "$ATTIC_ENDPOINT" \ "$ATTIC_TOKEN" - # Push the target installable and its build dependencies - nix run --inputs-from . attic -- \ - push \ - "$ATTIC_SERVER:$ATTIC_CACHE" \ - "$(nix path-info "$INSTALLABLE" --derivation)" \ - "$(nix path-info "$INSTALLABLE")" -else - echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache" -fi + # Find all output paths of the installables and their build dependencies + readarray -t derivations < <(nix path-info --derivation "$@") + cache=() + for derivation in "${derivations[@]}"; do + cache+=( + "$(nix-store --query --requisites --include-outputs "$derivation")" + ) + done + + # Upload them to Attic + # + # Use `xargs` and a here-string because something would probably explode if + # several thousand arguments got passed to a command at once. Hopefully no + # store paths include a newline in them. + ( + IFS=$'\n' + nix shell --inputs-from "$toplevel" attic -c xargs \ + attic push "$ATTIC_SERVER:$ATTIC_CACHE" <<< "${cache[*]}" + ) +} + +# Build and cache things needed for CI +ci() { + cache=( + --inputs-from "$toplevel" + + # Keep sorted + "$toplevel#devShells.x86_64-linux.default" + attic#default + nixpkgs#direnv + nixpkgs#jq + nixpkgs#nix-direnv + ) + + just "${cache[@]}" +} + +# Build and cache all the package outputs +packages() { + declare -a cache="($( + nix flake show --json 2> /dev/null | + nix run --inputs-from "$toplevel" nixpkgs#jq -- \ + -r \ + '.packages."x86_64-linux" | keys | map("'"$toplevel"'#" + .) | @sh' + ))" + + just "${cache[@]}" +} + +eval "$@" From b7df5a7da62da841f56008ff4b28098d1e30bddd Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 01:59:03 -0700 Subject: [PATCH 078/617] allow loading env vars from `.env` if it exists This is primarily useful for replicating the environment from CI so that the `nix-build-and-cache` script is easier to invoke. --- .envrc | 2 ++ .gitignore | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.envrc b/.envrc index 403a9bdf..e080e228 100644 --- a/.envrc +++ b/.envrc @@ -3,3 +3,5 @@ use flake PATH_add bin + +dotenv_if_exists diff --git a/.gitignore b/.gitignore index 5d4e599f..a17d5fe5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Local environment overrides +/.env + # Cargo artifacts target From 81d85153e9815d6da241ae6e16d217919c914040 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 14:42:54 -0700 Subject: [PATCH 079/617] specify ref for all flake inputs --- flake.lock | 4 ++++ flake.nix | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 1983d800..e89e09ca 100644 --- a/flake.lock +++ b/flake.lock @@ -82,6 +82,7 @@ }, "original": { "owner": "nix-community", + "ref": "main", "repo": "fenix", "type": "github" } @@ -114,6 +115,7 @@ }, "original": { "owner": "edolstra", + "ref": "master", "repo": "flake-compat", "type": "github" } @@ -147,6 +149,7 @@ }, "original": { "owner": "numtide", + "ref": "main", "repo": "flake-utils", "type": "github" } @@ -162,6 +165,7 @@ }, "original": { "owner": "numtide", + "ref": "main", "repo": "nix-filter", "type": "github" } diff --git a/flake.nix b/flake.nix index 568e75ab..f2549f35 100644 --- a/flake.nix +++ b/flake.nix @@ -1,15 +1,15 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - nix-filter.url = "github:numtide/nix-filter"; + flake-utils.url = "github:numtide/flake-utils?ref=main"; + nix-filter.url = "github:numtide/nix-filter?ref=main"; flake-compat = { - url = "github:edolstra/flake-compat"; + url = "github:edolstra/flake-compat?ref=master"; flake = false; }; fenix = { - url = "github:nix-community/fenix"; + url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; crane = { From 17cb7732bd595cac2a33c956b2365b7c8bcbae04 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 14:45:45 -0700 Subject: [PATCH 080/617] flatten flake inputs --- flake.nix | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/flake.nix b/flake.nix index f2549f35..3acbf6fb 100644 --- a/flake.nix +++ b/flake.nix @@ -3,19 +3,9 @@ nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; - flake-compat = { - url = "github:edolstra/flake-compat?ref=master"; - flake = false; - }; - - fenix = { - url = "github:nix-community/fenix?ref=main"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - crane = { - url = "github:ipetkov/crane?ref=master"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + flake-compat = { url = "github:edolstra/flake-compat?ref=master"; flake = false; }; + fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; + crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; }; attic.url = "github:zhaofengli/attic?ref=main"; }; From 238bc85b1dff2316acbac67cdf30eceaf2b3f304 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 14:46:04 -0700 Subject: [PATCH 081/617] sort flake inputs --- flake.nix | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index 3acbf6fb..94d7a656 100644 --- a/flake.nix +++ b/flake.nix @@ -1,12 +1,13 @@ { + # Keep sorted inputs = { - nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + attic.url = "github:zhaofengli/attic?ref=main"; + crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; }; + fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; + flake-compat = { url = "github:edolstra/flake-compat?ref=master"; flake = false; }; flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; - flake-compat = { url = "github:edolstra/flake-compat?ref=master"; flake = false; }; - fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; - crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; }; - attic.url = "github:zhaofengli/attic?ref=main"; + nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; }; outputs = From 8d8d4425f3d68bedce7551ef729c9c4eb41d13a1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 14:47:04 -0700 Subject: [PATCH 082/617] always go through `inputs` This way we don't have to modify the destructuring of `outputs`' argument when adding or removing inputs. --- flake.nix | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/flake.nix b/flake.nix index 94d7a656..eec81d6e 100644 --- a/flake.nix +++ b/flake.nix @@ -10,24 +10,15 @@ nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; }; - outputs = - { self - , nixpkgs - , flake-utils - , nix-filter - - , fenix - , crane - , ... - }: flake-utils.lib.eachDefaultSystem (system: + outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system: let - pkgsHost = nixpkgs.legacyPackages.${system}; + pkgsHost = inputs.nixpkgs.legacyPackages.${system}; # Nix-accessible `Cargo.toml` cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); # The Rust toolchain to use - toolchain = fenix.packages.${system}.fromToolchainFile { + toolchain = inputs.fenix.packages.${system}.fromToolchainFile { file = ./rust-toolchain.toml; # See also `rust-toolchain.toml` @@ -35,7 +26,7 @@ }; builder = pkgs: - ((crane.mkLib pkgs).overrideToolchain toolchain).buildPackage; + ((inputs.crane.mkLib pkgs).overrideToolchain toolchain).buildPackage; nativeBuildInputs = pkgs: [ # bindgen needs the build platform's libclang. Apparently due to @@ -59,7 +50,8 @@ }); env = pkgs: { - GRAPEVINE_VERSION_EXTRA = self.shortRev or self.dirtyShortRev; + GRAPEVINE_VERSION_EXTRA = + inputs.self.shortRev or inputs.self.dirtyShortRev; ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include"; ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib"; } @@ -152,7 +144,7 @@ )); package = pkgs: builder pkgs { - src = nix-filter { + src = inputs.nix-filter { root = ./.; include = [ "src" @@ -193,7 +185,7 @@ { packages = { default = package pkgsHost; - oci-image = mkOciImage pkgsHost self.packages.${system}.default; + oci-image = mkOciImage pkgsHost inputs.self.packages.${system}.default; } // builtins.listToAttrs @@ -203,7 +195,7 @@ let binaryName = "static-${crossSystem}"; pkgsCrossStatic = - (import nixpkgs { + (import inputs.nixpkgs { inherit system; crossSystem = { config = crossSystem; @@ -222,7 +214,7 @@ name = "oci-image-${crossSystem}"; value = mkOciImage pkgsCrossStatic - self.packages.${system}.${binaryName}; + inputs.self.packages.${system}.${binaryName}; } ] ) @@ -247,7 +239,7 @@ # # This needs to come before `toolchain` in this list, otherwise # `$PATH` will have stable rustfmt instead. - fenix.packages.${system}.latest.rustfmt + inputs.fenix.packages.${system}.latest.rustfmt toolchain ] ++ (with pkgsHost; [ From 0f8d1a5ed75de15fdc1077ed45b50c80bf4e0906 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 16:00:19 -0700 Subject: [PATCH 083/617] factor out nix code into new files via `makeScope` This makes the Nix code a lot easier to reason about. --- flake.nix | 299 ++++++--------------- nix/pkgs/default/cross-compilation-env.nix | 100 +++++++ nix/pkgs/default/default.nix | 64 +++++ nix/pkgs/oci-image/default.nix | 25 ++ nix/shell.nix | 34 +++ 5 files changed, 299 insertions(+), 223 deletions(-) create mode 100644 nix/pkgs/default/cross-compilation-env.nix create mode 100644 nix/pkgs/default/default.nix create mode 100644 nix/pkgs/oci-image/default.nix create mode 100644 nix/shell.nix diff --git a/flake.nix b/flake.nix index eec81d6e..2a268b18 100644 --- a/flake.nix +++ b/flake.nix @@ -10,241 +10,94 @@ nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; }; - outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system: + outputs = inputs: let - pkgsHost = inputs.nixpkgs.legacyPackages.${system}; + # Keep sorted + mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: { + craneLib = + (inputs.crane.mkLib pkgs).overrideToolchain self.toolchain; - # Nix-accessible `Cargo.toml` - cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); + default = self.callPackage ./nix/pkgs/default {}; - # The Rust toolchain to use - toolchain = inputs.fenix.packages.${system}.fromToolchainFile { - file = ./rust-toolchain.toml; + inherit inputs; - # See also `rust-toolchain.toml` - sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8="; - }; + oci-image = self.callPackage ./nix/pkgs/oci-image {}; - builder = pkgs: - ((inputs.crane.mkLib pkgs).overrideToolchain toolchain).buildPackage; + rocksdb = + let + version = "9.1.0"; + in + pkgs.rocksdb.overrideAttrs (old: { + inherit version; + src = pkgs.fetchFromGitHub { + owner = "facebook"; + repo = "rocksdb"; + rev = "v${version}"; + hash = "sha256-vRPyrXkXVVhP56n5FVYef8zbIsnnanQSpElmQLZ7mh8="; + }; + }); - nativeBuildInputs = pkgs: [ - # bindgen needs the build platform's libclang. Apparently due to - # "splicing weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't - # quite do the right thing here. - pkgs.pkgsBuildHost.rustPlatform.bindgenHook - ]; + shell = self.callPackage ./nix/shell.nix {}; - rocksdb' = pkgs: - let - version = "9.1.0"; - in - pkgs.rocksdb.overrideAttrs (old: { - inherit version; - src = pkgs.fetchFromGitHub { - owner = "facebook"; - repo = "rocksdb"; - rev = "v${version}"; - hash = "sha256-vRPyrXkXVVhP56n5FVYef8zbIsnnanQSpElmQLZ7mh8="; - }; + # The Rust toolchain to use + toolchain = inputs + .fenix + .packages + .${pkgs.pkgsBuildHost.system} + .fromToolchainFile { + file = ./rust-toolchain.toml; + + # See also `rust-toolchain.toml` + sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8="; + }; }); + in + inputs.flake-utils.lib.eachDefaultSystem (system: + let + pkgs = inputs.nixpkgs.legacyPackages.${system}; + in + { + packages = { + default = (mkScope pkgs).default; + oci-image = (mkScope pkgs).oci-image; + } + // + builtins.listToAttrs + (builtins.concatLists + (builtins.map + (crossSystem: + let + binaryName = "static-${crossSystem}"; + pkgsCrossStatic = + (import inputs.nixpkgs { + inherit system; + crossSystem = { + config = crossSystem; + }; + }).pkgsStatic; + in + [ + # An output for a statically-linked binary + { + name = binaryName; + value = (mkScope pkgsCrossStatic).default; + } - env = pkgs: { - GRAPEVINE_VERSION_EXTRA = - inputs.self.shortRev or inputs.self.dirtyShortRev; - ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include"; - ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib"; - } - // pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isStatic { - ROCKSDB_STATIC = ""; - } - // { - CARGO_BUILD_RUSTFLAGS = let inherit (pkgs) lib stdenv; in - lib.concatStringsSep " " ([] - ++ lib.optionals - # This disables PIE for static builds, which isn't great in terms - # of security. Unfortunately, my hand is forced because nixpkgs' - # `libstdc++.a` is built without `-fPIE`, which precludes us from - # leaving PIE enabled. - stdenv.hostPlatform.isStatic - ["-C" "relocation-model=static"] - ++ lib.optionals - (stdenv.buildPlatform.config != stdenv.hostPlatform.config) - ["-l" "c"] - ++ lib.optionals - # This check has to match the one [here][0]. We only need to set - # these flags when using a different linker. Don't ask me why, - # though, because I don't know. All I know is it breaks otherwise. - # - # [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L37-L40 - ( - # Nixpkgs doesn't check for x86_64 here but we do, because I - # observed a failure building statically for x86_64 without - # including it here. Linkers are weird. - (stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64) - && stdenv.hostPlatform.isStatic - && !stdenv.isDarwin - && !stdenv.cc.bintools.isLLVM + # An output for an OCI image based on that binary + { + name = "oci-image-${crossSystem}"; + value = (mkScope pkgsCrossStatic).oci-image; + } + ] ) [ - "-l" - "stdc++" - "-L" - "${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib" - ] - ); - } - - # What follows is stolen from [here][0]. Its purpose is to properly - # configure compilers and linkers for various stages of the build, and - # even covers the case of build scripts that need native code compiled and - # run on the build platform (I think). - # - # [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L57-L80 - // ( - let - inherit (pkgs.rust.lib) envVars; - in - pkgs.lib.optionalAttrs - (pkgs.stdenv.targetPlatform.rust.rustcTarget - != pkgs.stdenv.hostPlatform.rust.rustcTarget) - ( - let - inherit (pkgs.stdenv.targetPlatform.rust) cargoEnvVarTarget; - in - { - "CC_${cargoEnvVarTarget}" = envVars.ccForTarget; - "CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget; - "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = - envVars.linkerForTarget; - } - ) - // ( - let - inherit (pkgs.stdenv.hostPlatform.rust) cargoEnvVarTarget rustcTarget; - in - { - "CC_${cargoEnvVarTarget}" = envVars.ccForHost; - "CXX_${cargoEnvVarTarget}" = envVars.cxxForHost; - "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost; - CARGO_BUILD_TARGET = rustcTarget; - } - ) - // ( - let - inherit (pkgs.stdenv.buildPlatform.rust) cargoEnvVarTarget; - in - { - "CC_${cargoEnvVarTarget}" = envVars.ccForBuild; - "CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild; - "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild; - HOST_CC = "${pkgs.pkgsBuildHost.stdenv.cc}/bin/cc"; - HOST_CXX = "${pkgs.pkgsBuildHost.stdenv.cc}/bin/c++"; - } - )); - - package = pkgs: builder pkgs { - src = inputs.nix-filter { - root = ./.; - include = [ - "src" - "Cargo.toml" - "Cargo.lock" - ]; - }; - - # This is redundant with CI - doCheck = false; - - env = env pkgs; - nativeBuildInputs = nativeBuildInputs pkgs; - - meta.mainProgram = cargoToml.package.name; - }; - - mkOciImage = pkgs: package: - pkgs.dockerTools.buildImage { - name = package.pname; - tag = "next"; - copyToRoot = [ - pkgs.dockerTools.caCertificates - ]; - config = { - # Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT) - # are handled as expected - Entrypoint = [ - "${pkgs.lib.getExe' pkgs.tini "tini"}" - "--" - ]; - Cmd = [ - "${pkgs.lib.getExe package}" - ]; - }; - }; - in - { - packages = { - default = package pkgsHost; - oci-image = mkOciImage pkgsHost inputs.self.packages.${system}.default; - } - // - builtins.listToAttrs - (builtins.concatLists - (builtins.map - (crossSystem: - let - binaryName = "static-${crossSystem}"; - pkgsCrossStatic = - (import inputs.nixpkgs { - inherit system; - crossSystem = { - config = crossSystem; - }; - }).pkgsStatic; - in - [ - # An output for a statically-linked binary - { - name = binaryName; - value = package pkgsCrossStatic; - } - - # An output for an OCI image based on that binary - { - name = "oci-image-${crossSystem}"; - value = mkOciImage - pkgsCrossStatic - inputs.self.packages.${system}.${binaryName}; - } + "x86_64-unknown-linux-musl" + "aarch64-unknown-linux-musl" ] ) - [ - "x86_64-unknown-linux-musl" - "aarch64-unknown-linux-musl" - ] - ) - ); + ); - devShells.default = pkgsHost.mkShell { - env = env pkgsHost // { - # Rust Analyzer needs to be able to find the path to default crate - # sources, and it can read this environment variable to do so. The - # `rust-src` component is required in order for this to work. - RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library"; - }; - - # Development tools - nativeBuildInputs = nativeBuildInputs pkgsHost ++ [ - # Always use nightly rustfmt because most of its options are unstable - # - # This needs to come before `toolchain` in this list, otherwise - # `$PATH` will have stable rustfmt instead. - inputs.fenix.packages.${system}.latest.rustfmt - - toolchain - ] ++ (with pkgsHost; [ - engage - ]); - }; - }); + devShells.default = (mkScope pkgs).shell; + } + ); } diff --git a/nix/pkgs/default/cross-compilation-env.nix b/nix/pkgs/default/cross-compilation-env.nix new file mode 100644 index 00000000..fac85e02 --- /dev/null +++ b/nix/pkgs/default/cross-compilation-env.nix @@ -0,0 +1,100 @@ +{ lib +, pkgsBuildHost +, rust +, stdenv +}: + +lib.optionalAttrs stdenv.hostPlatform.isStatic { + ROCKSDB_STATIC = ""; +} +// +{ + CARGO_BUILD_RUSTFLAGS = + lib.concatStringsSep + " " + ([] + # This disables PIE for static builds, which isn't great in terms of + # security. Unfortunately, my hand is forced because nixpkgs' + # `libstdc++.a` is built without `-fPIE`, which precludes us from + # leaving PIE enabled. + ++ lib.optionals + stdenv.hostPlatform.isStatic + [ "-C" "relocation-model=static" ] + ++ lib.optionals + (stdenv.buildPlatform.config != stdenv.hostPlatform.config) + [ "-l" "c" ] + ++ lib.optionals + # This check has to match the one [here][0]. We only need to set + # these flags when using a different linker. Don't ask me why, though, + # because I don't know. All I know is it breaks otherwise. + # + # [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L37-L40 + ( + # Nixpkgs doesn't check for x86_64 here but we do, because I + # observed a failure building statically for x86_64 without + # including it here. Linkers are weird. + (stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64) + && stdenv.hostPlatform.isStatic + && !stdenv.isDarwin + && !stdenv.cc.bintools.isLLVM + ) + [ + "-l" + "stdc++" + "-L" + "${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib" + ] + ); +} + +# What follows is stolen from [here][0]. Its purpose is to properly configure +# compilers and linkers for various stages of the build, and even covers the +# case of build scripts that need native code compiled and run on the build +# platform (I think). +# +# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L57-L80 +// +( + let + inherit (rust.lib) envVars; + in + lib.optionalAttrs + (stdenv.targetPlatform.rust.rustcTarget + != stdenv.hostPlatform.rust.rustcTarget) + ( + let + inherit (stdenv.targetPlatform.rust) cargoEnvVarTarget; + in + { + "CC_${cargoEnvVarTarget}" = envVars.ccForTarget; + "CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget; + "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = + envVars.linkerForTarget; + } + ) + // + ( + let + inherit (stdenv.hostPlatform.rust) cargoEnvVarTarget rustcTarget; + in + { + "CC_${cargoEnvVarTarget}" = envVars.ccForHost; + "CXX_${cargoEnvVarTarget}" = envVars.cxxForHost; + "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost; + CARGO_BUILD_TARGET = rustcTarget; + } + ) + // + ( + let + inherit (stdenv.buildPlatform.rust) cargoEnvVarTarget; + in + { + "CC_${cargoEnvVarTarget}" = envVars.ccForBuild; + "CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild; + "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild; + HOST_CC = "${pkgsBuildHost.stdenv.cc}/bin/cc"; + HOST_CXX = "${pkgsBuildHost.stdenv.cc}/bin/c++"; + } + ) +) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix new file mode 100644 index 00000000..c15d5dfd --- /dev/null +++ b/nix/pkgs/default/default.nix @@ -0,0 +1,64 @@ +# Keep sorted +{ craneLib +, inputs +, lib +, pkgsBuildHost +, rocksdb +, rust +, stdenv +}: + +let + env = { + GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev; + ROCKSDB_INCLUDE_DIR = "${rocksdb}/include"; + ROCKSDB_LIB_DIR = "${rocksdb}/lib"; + } + // + (import ./cross-compilation-env.nix { + # Keep sorted + inherit + lib + pkgsBuildHost + rust + stdenv; + }); +in + +craneLib.buildPackage rec { + inherit + (craneLib.crateNameFromCargoToml { + cargoToml = "${inputs.self}/Cargo.toml"; + }) + pname + version; + + src = let filter = inputs.nix-filter.lib; in filter { + root = inputs.self; + + # Keep sorted + include = [ + "Cargo.lock" + "Cargo.toml" + "src" + ]; + }; + + # This is redundant with CI + doCheck = false; + + nativeBuildInputs = [ + # bindgen needs the build platform's libclang. Apparently due to "splicing + # weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the + # right thing here. + pkgsBuildHost.rustPlatform.bindgenHook + ]; + + inherit env; + + passthru = { + inherit env; + }; + + meta.mainProgram = pname; +} diff --git a/nix/pkgs/oci-image/default.nix b/nix/pkgs/oci-image/default.nix new file mode 100644 index 00000000..8b359ce4 --- /dev/null +++ b/nix/pkgs/oci-image/default.nix @@ -0,0 +1,25 @@ +# Keep sorted +{ default +, dockerTools +, lib +, tini +}: + +dockerTools.buildImage { + name = default.pname; + tag = "next"; + copyToRoot = [ + dockerTools.caCertificates + ]; + config = { + # Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT) + # are handled as expected + Entrypoint = [ + "${lib.getExe' tini "tini"}" + "--" + ]; + Cmd = [ + "${lib.getExe default}" + ]; + }; +} diff --git a/nix/shell.nix b/nix/shell.nix new file mode 100644 index 00000000..e9d7f493 --- /dev/null +++ b/nix/shell.nix @@ -0,0 +1,34 @@ +# Keep sorted +{ default +, engage +, inputs +, jq +, mkShell +, system +, toolchain +}: + +mkShell { + env = default.env // { + # Rust Analyzer needs to be able to find the path to default crate + # sources, and it can read this environment variable to do so. The + # `rust-src` component is required in order for this to work. + RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library"; + }; + + # Development tools + nativeBuildInputs = [ + # Always use nightly rustfmt because most of its options are unstable + # + # This needs to come before `toolchain` in this list, otherwise + # `$PATH` will have stable rustfmt instead. + inputs.fenix.packages.${system}.latest.rustfmt + + # Keep sorted + engage + jq + toolchain + ] + ++ + default.nativeBuildInputs; +} From 09751227eaba15b67d7f06d8b799a762bced0a01 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 16:13:34 -0700 Subject: [PATCH 084/617] get rocksdb via flake inputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Added input 'rocksdb': 'github:facebook/rocksdb/bcf88d48ce8aa8b536aee4dd305533b3b83cf435' (2024-04-16) --- flake.lock | 20 +++++++++++++++++++- flake.nix | 19 +++++++------------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/flake.lock b/flake.lock index e89e09ca..396bd2f2 100644 --- a/flake.lock +++ b/flake.lock @@ -218,6 +218,23 @@ "type": "github" } }, + "rocksdb": { + "flake": false, + "locked": { + "lastModified": 1713310517, + "narHash": "sha256-vRPyrXkXVVhP56n5FVYef8zbIsnnanQSpElmQLZ7mh8=", + "owner": "facebook", + "repo": "rocksdb", + "rev": "bcf88d48ce8aa8b536aee4dd305533b3b83cf435", + "type": "github" + }, + "original": { + "owner": "facebook", + "ref": "v9.1.0", + "repo": "rocksdb", + "type": "github" + } + }, "root": { "inputs": { "attic": "attic", @@ -226,7 +243,8 @@ "flake-compat": "flake-compat_2", "flake-utils": "flake-utils_2", "nix-filter": "nix-filter", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_2", + "rocksdb": "rocksdb" } }, "rust-analyzer-src": { diff --git a/flake.nix b/flake.nix index 2a268b18..5ed0022d 100644 --- a/flake.nix +++ b/flake.nix @@ -8,6 +8,7 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.0"; flake = false; }; }; outputs = inputs: @@ -23,18 +24,12 @@ oci-image = self.callPackage ./nix/pkgs/oci-image {}; - rocksdb = - let - version = "9.1.0"; - in - pkgs.rocksdb.overrideAttrs (old: { - inherit version; - src = pkgs.fetchFromGitHub { - owner = "facebook"; - repo = "rocksdb"; - rev = "v${version}"; - hash = "sha256-vRPyrXkXVVhP56n5FVYef8zbIsnnanQSpElmQLZ7mh8="; - }; + rocksdb = pkgs.rocksdb.overrideAttrs (old: { + src = inputs.rocksdb; + version = pkgs.lib.removePrefix + "v" + (builtins.fromJSON (builtins.readFile ./flake.lock)) + .nodes.rocksdb.original.ref; }); shell = self.callPackage ./nix/shell.nix {}; From 51f9650ca7bc9378690d331192c85fea3c151b58 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 00:38:55 -0700 Subject: [PATCH 085/617] make it easy to configure cargo features from nix Users of the nix package can now just use `.override` to choose what features they want. This also makes RocksDB automatically use jemalloc when Grapevine is configured to use jemalloc. --- nix/pkgs/default/default.nix | 49 +++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index c15d5dfd..5f906054 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -1,4 +1,4 @@ -# Keep sorted +# Dependencies (keep sorted) { craneLib , inputs , lib @@ -6,23 +6,34 @@ , rocksdb , rust , stdenv + +# Options (keep sorted) +, default-features ? true +, features ? [] }: let - env = { - GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev; - ROCKSDB_INCLUDE_DIR = "${rocksdb}/include"; - ROCKSDB_LIB_DIR = "${rocksdb}/lib"; - } - // - (import ./cross-compilation-env.nix { - # Keep sorted - inherit - lib - pkgsBuildHost - rust - stdenv; - }); + env = + let + rocksdb' = rocksdb.override { + enableJemalloc = builtins.elem "jemalloc" features; + }; + in + { + GRAPEVINE_VERSION_EXTRA = + inputs.self.shortRev or inputs.self.dirtyShortRev; + ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; + ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; + } + // + (import ./cross-compilation-env.nix { + # Keep sorted + inherit + lib + pkgsBuildHost + rust + stdenv; + }); in craneLib.buildPackage rec { @@ -44,6 +55,14 @@ craneLib.buildPackage rec { ]; }; + cargoExtraArgs = "--locked " + + lib.optionalString + (!default-features) + "--no-default-features " + + lib.optionalString + (features != []) + "--features " + (builtins.concatStringsSep "," features); + # This is redundant with CI doCheck = false; From bbb1a6fea45b16e8d4f94c1afbf7fa22c9281f37 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 12:08:42 -0700 Subject: [PATCH 086/617] make it easy to configure cargo profiles from nix This way you can easily build in debug mode with Nix. --- nix/pkgs/default/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 5f906054..298dd6cb 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -10,6 +10,7 @@ # Options (keep sorted) , default-features ? true , features ? [] +, profile ? "release" }: let @@ -22,6 +23,7 @@ let { GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev; + CARGO_PROFILE = profile; ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; } From 21a4b9e5a1ed3296431ed4e143a82d15ab704f76 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 01:05:24 -0700 Subject: [PATCH 087/617] only set `GRAPEVINE_VERSION_EXTRA` for final build This prevents us from needing to recompile the dependencies when that environment variable changes, which generally changes on every commit, which is far more frequently than the dependencies are actually changed. --- nix/pkgs/default/default.nix | 66 ++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 298dd6cb..8cfeac8e 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -14,15 +14,13 @@ }: let - env = + buildDepsOnlyEnv = let rocksdb' = rocksdb.override { enableJemalloc = builtins.elem "jemalloc" features; }; in { - GRAPEVINE_VERSION_EXTRA = - inputs.self.shortRev or inputs.self.dirtyShortRev; CARGO_PROFILE = profile; ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; @@ -36,26 +34,43 @@ let rust stdenv; }); -in -craneLib.buildPackage rec { - inherit - (craneLib.crateNameFromCargoToml { - cargoToml = "${inputs.self}/Cargo.toml"; - }) - pname - version; + buildPackageEnv = { + GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev; + } // buildDepsOnlyEnv; - src = let filter = inputs.nix-filter.lib; in filter { - root = inputs.self; + commonAttrs = { + inherit + (craneLib.crateNameFromCargoToml { + cargoToml = "${inputs.self}/Cargo.toml"; + }) + pname + version; - # Keep sorted - include = [ - "Cargo.lock" - "Cargo.toml" - "src" + src = let filter = inputs.nix-filter.lib; in filter { + root = inputs.self; + + # Keep sorted + include = [ + "Cargo.lock" + "Cargo.toml" + "src" + ]; + }; + + nativeBuildInputs = [ + # bindgen needs the build platform's libclang. Apparently due to "splicing + # weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the + # right thing here. + pkgsBuildHost.rustPlatform.bindgenHook ]; }; +in + +craneLib.buildPackage ( commonAttrs // { + cargoArtifacts = craneLib.buildDepsOnly (commonAttrs // { + env = buildDepsOnlyEnv; + }); cargoExtraArgs = "--locked " + lib.optionalString @@ -68,18 +83,11 @@ craneLib.buildPackage rec { # This is redundant with CI doCheck = false; - nativeBuildInputs = [ - # bindgen needs the build platform's libclang. Apparently due to "splicing - # weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the - # right thing here. - pkgsBuildHost.rustPlatform.bindgenHook - ]; - - inherit env; + env = buildPackageEnv; passthru = { - inherit env; + env = buildPackageEnv; }; - meta.mainProgram = pname; -} + meta.mainProgram = commonAttrs.pname; +}) From 17eb3545906d21f2ed18f0f0f917a4638f12ef6c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 5 May 2024 00:23:36 -0700 Subject: [PATCH 088/617] prevent bindgen and dependents from building twice See also . --- nix/pkgs/default/default.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 8cfeac8e..d5accf32 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -1,6 +1,7 @@ # Dependencies (keep sorted) { craneLib , inputs +, jq , lib , pkgsBuildHost , rocksdb @@ -21,6 +22,7 @@ let }; in { + NIX_OUTPATH_USED_AS_RANDOM_SEED = "randomseed"; CARGO_PROFILE = profile; ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; @@ -63,6 +65,12 @@ let # weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the # right thing here. pkgsBuildHost.rustPlatform.bindgenHook + + # We don't actually depend on `jq`, but crane's `buildPackage` does, but + # its `buildDepsOnly` doesn't. This causes those two derivations to have + # differing values for `NIX_CFLAGS_COMPILE`, which contributes to spurious + # rebuilds of bindgen and its depedents. + jq ]; }; in From 33e7a46b5385ea9035c9d13c6775d63e5626a4c7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 16:53:20 -0700 Subject: [PATCH 089/617] add a nixos module --- flake.nix | 6 +- nix/modules/default/default.nix | 116 ++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 nix/modules/default/default.nix diff --git a/flake.nix b/flake.nix index 5ed0022d..8021b3de 100644 --- a/flake.nix +++ b/flake.nix @@ -94,5 +94,9 @@ devShells.default = (mkScope pkgs).shell; } - ); + ) + // + { + nixosModules.default = import ./nix/modules/default inputs; + }; } diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix new file mode 100644 index 00000000..9085a616 --- /dev/null +++ b/nix/modules/default/default.nix @@ -0,0 +1,116 @@ +inputs: + +{ config +, lib +, pkgs +, ... +}: + +let + inherit (lib) types; + + cfg = config.services.grapevine; + configFile = format.generate "config.toml" cfg.settings; + format = pkgs.formats.toml {}; +in + +{ + options.services.grapevine = { + enable = lib.mkEnableOption "grapevine"; + package = lib.mkPackageOption + inputs.self.packages.${pkgs.system} + "grapevine" + { + default = "default"; + pkgsText = "inputs.grapevine.packages.\${pkgs.system}"; + }; + + extraEnvironment = lib.mkOption { + type = types.attrsOf types.str; + description = '' + Extra environment variables to set for the process. + ''; + default = {}; + example = { RUST_BACKTRACE="yes"; }; + }; + + settings = lib.mkOption { + type = types.submodule { + freeformType = format.type; + options = { + global.address = lib.mkOption { + type = types.nonEmptyStr; + description = '' + The local IP address to bind to. + ''; + default = "::1"; + }; + global.database_path = lib.mkOption { + type = types.nonEmptyStr; + readOnly = true; + description = '' + The path to store persistent data in. + + Note that this is read-only because this module makes use of + systemd's `StateDirectory` option. + ''; + default = "/var/lib/grapevine"; + }; + global.port = lib.mkOption { + type = types.port; + description = '' + The local port to bind to. + ''; + default = 6167; + }; + }; + }; + default = {}; + description = '' + The TOML configuration file is generated from this attribute set. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.grapevine = { + description = "Grapevine (Matrix homeserver)"; + wantedBy = [ "multi-user.target" ]; + environment = lib.mkMerge [ + { + GRAPEVINE_CONFIG = configFile; + } + cfg.extraEnvironment + ]; + + # Keep sorted + serviceConfig = { + DynamicUser = true; + ExecStart = "${lib.getExe cfg.package}"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + Restart = "on-failure"; + RestartSec = 10; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + StartLimitBurst = 5; + StateDirectory = "grapevine"; + StateDirectoryMode = "0700"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + UMask = "077"; + User = "grapevine"; + }; + }; + }; +} From a25f2ec95045c5620c98eead88197a0bf13e6bb3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 30 Apr 2024 17:20:36 -0700 Subject: [PATCH 090/617] add conduit compat mode This makes it possible to deploy Grapevine while using a database originally created by Conduit, including leaving the admin bot user's localpart the same as before. --- nix/modules/default/default.nix | 19 +++++++++-- src/config.rs | 2 ++ src/database.rs | 23 ++++++++++--- src/database/abstraction/sqlite.rs | 9 ++++- src/service/admin.rs | 54 +++++++++++++++++++++++------- src/service/rooms/timeline.rs | 19 +++++++++-- 6 files changed, 104 insertions(+), 22 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 9085a616..be66012c 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -45,6 +45,13 @@ in ''; default = "::1"; }; + global.conduit_compat = lib.mkOption { + type = types.bool; + description = '' + Whether to operate as a drop-in replacement for Conduit. + ''; + default = false; + }; global.database_path = lib.mkOption { type = types.nonEmptyStr; readOnly = true; @@ -54,7 +61,9 @@ in Note that this is read-only because this module makes use of systemd's `StateDirectory` option. ''; - default = "/var/lib/grapevine"; + default = if cfg.settings.global.conduit_compat + then "/var/lib/matrix-conduit" + else "/var/lib/grapevine"; }; global.port = lib.mkOption { type = types.port; @@ -104,12 +113,16 @@ in RestrictNamespaces = true; RestrictRealtime = true; StartLimitBurst = 5; - StateDirectory = "grapevine"; + StateDirectory = if cfg.settings.global.conduit_compat + then "matrix-conduit" + else "grapevine"; StateDirectoryMode = "0700"; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" ]; UMask = "077"; - User = "grapevine"; + User = if cfg.settings.global.conduit_compat + then "conduit" + else "grapevine"; }; }; }; diff --git a/src/config.rs b/src/config.rs index b42f5f57..2313b92f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,8 @@ use self::proxy::ProxyConfig; #[allow(clippy::struct_excessive_bools)] #[derive(Clone, Debug, Deserialize)] pub(crate) struct Config { + #[serde(default = "false_fn")] + pub(crate) conduit_compat: bool, #[serde(default = "default_address")] pub(crate) address: IpAddr, #[serde(default = "default_port")] diff --git a/src/database.rs b/src/database.rs index 1410ca27..faddb9f9 100644 --- a/src/database.rs +++ b/src/database.rs @@ -176,7 +176,16 @@ impl KeyValueDatabase { fn check_db_setup(config: &Config) -> Result<()> { let path = Path::new(&config.database_path); - let sqlite_exists = path.join("grapevine.db").exists(); + let sqlite_exists = path + .join(format!( + "{}.db", + if config.conduit_compat { + "conduit" + } else { + "grapevine" + } + )) + .exists(); let rocksdb_exists = path.join("IDENTITY").exists(); let mut count = 0; @@ -401,9 +410,15 @@ impl KeyValueDatabase { // Matrix resource ownership is based on the server name; changing it // requires recreating the database from scratch. if services().users.count()? > 0 { - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse_with_server_name( + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name(), + ) + .expect("admin bot username should be valid"); if !services().users.exists(&grapevine_user)? { error!( diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index a463cb61..495f6193 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -97,7 +97,14 @@ impl Engine { impl KeyValueDatabaseEngine for Arc { fn open(config: &Config) -> Result { - let path = Path::new(&config.database_path).join("grapevine.db"); + let path = Path::new(&config.database_path).join(format!( + "{}.db", + if config.conduit_compat { + "conduit" + } else { + "grapevine" + } + )); // calculates cache-size per permanent connection // 1. convert MB to KiB diff --git a/src/service/admin.rs b/src/service/admin.rs index 3d10be63..bdfc6a9c 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -212,9 +212,16 @@ impl Service { // TODO: Use futures when we have long admin commands //let mut futures = FuturesUnordered::new(); - let grapevine_user = - UserId::parse(format!("@grapevine:{}", services().globals.server_name())) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse(format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + )) + .expect("admin bot username should be valid"); if let Ok(Some(grapevine_room)) = services().admin.get_admin_room() { loop { @@ -568,7 +575,11 @@ impl Service { if !services().users.exists(&user_id)? || user_id == UserId::parse_with_server_name( - "grapevine", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, services().globals.server_name(), ) .expect("grapevine user exists") @@ -866,9 +877,15 @@ impl Service { // Utility to turn clap's `--help` text to HTML. fn usage_to_html(text: &str, server_name: &ServerName) -> String { // Replace `@grapevine:servername:-subcmdname` with `@grapevine:servername: subcmdname` + let localpart = if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }; + let text = text.replace( - &format!("@grapevine:{server_name}:-"), - &format!("@grapevine:{server_name}: "), + &format!("@{localpart}:{server_name}:-"), + &format!("@{localpart}:{server_name}: "), ); // For the grapevine admin room, subcommands become main commands @@ -952,9 +969,16 @@ impl Service { let state_lock = mutex_state.lock().await; // Create a user for the server - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse(format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + )) + .expect("admin bot username should be valid"); services().users.create(&grapevine_user, None)?; @@ -1218,9 +1242,15 @@ impl Service { let state_lock = mutex_state.lock().await; // Use the server user to grant the new admin's power level - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse_with_server_name( + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name(), + ) + .expect("admin bot username should be valid"); // Invite and join the real user services() diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 1c57369d..42b8d113 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -483,7 +483,15 @@ impl Service { .search .index_pdu(shortroomid, &pdu_id, &body)?; - let server_user = format!("@grapevine:{}", services().globals.server_name()); + let server_user = format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + ); let to_grapevine = body.starts_with(&format!("{server_user}: ")) || body.starts_with(&format!("{server_user} ")) @@ -822,7 +830,14 @@ impl Service { .filter(|v| v.starts_with('@')) .unwrap_or(sender.as_str()); let server_name = services().globals.server_name(); - let server_user = format!("@grapevine:{server_name}"); + let server_user = format!( + "@{}:{server_name}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + ); let content = serde_json::from_str::(pdu.content.get()) .map_err(|_| Error::bad_database("Invalid content in pdu."))?; From 509b70bd827fec23b88e223b57e0df3b42cede34 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 1 May 2024 00:30:40 -0700 Subject: [PATCH 091/617] use the `version` function for s2s API too This way the extra version information can appear there as well. Mainly useful for showing off :P --- src/api/server_server.rs | 2 +- src/clap.rs | 16 +--------------- src/main.rs | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index daba63ff..b6fe20b3 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -515,7 +515,7 @@ pub(crate) async fn get_server_version_route( Ok(get_server_version::v1::Response { server: Some(get_server_version::v1::Server { name: Some(env!("CARGO_PKG_NAME").to_owned()), - version: Some(env!("CARGO_PKG_VERSION").to_owned()), + version: Some(crate::version()), }), }) } diff --git a/src/clap.rs b/src/clap.rs index f38d162a..09cd3839 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -2,23 +2,9 @@ use clap::Parser; -/// Returns the current version of the crate with extra info if supplied -/// -/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string to -/// include it in parenthesis after the SemVer version. A common value are git -/// commit hashes. -fn version() -> String { - let cargo_pkg_version = env!("CARGO_PKG_VERSION"); - - match option_env!("GRAPEVINE_VERSION_EXTRA") { - Some(x) => format!("{cargo_pkg_version} ({x})"), - None => cargo_pkg_version.to_owned(), - } -} - /// Command line arguments #[derive(Parser)] -#[clap(about, version = version())] +#[clap(about, version = crate::version())] pub(crate) struct Args; /// Parse command line arguments into structured data diff --git a/src/main.rs b/src/main.rs index def5a85a..d445d207 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,20 @@ pub(crate) fn services() -> &'static Services { .expect("SERVICES should be initialized when this is called") } +/// Returns the current version of the crate with extra info if supplied +/// +/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string to +/// include it in parenthesis after the SemVer version. A common value are git +/// commit hashes. +fn version() -> String { + let cargo_pkg_version = env!("CARGO_PKG_VERSION"); + + match option_env!("GRAPEVINE_VERSION_EXTRA") { + Some(x) => format!("{cargo_pkg_version} ({x})"), + None => cargo_pkg_version.to_owned(), + } +} + #[tokio::main] async fn main() { clap::parse(); From 87a1012ab5db3b1d7d6d201e17b9941f2910be14 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 11 May 2024 15:03:28 +0000 Subject: [PATCH 092/617] Replace Box::into_pin(Box::new( with Box::pin( --- src/database/key_value/globals.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 59aaf575..aebc3d03 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -76,9 +76,9 @@ impl service::globals::Data for KeyValueDatabase { futures.push(self.pduid_pdu.watch_prefix(&short_roomid)); // EDUs - futures.push(Box::into_pin(Box::new(async move { + futures.push(Box::pin(async move { let _result = services().rooms.edus.typing.wait_for_update(&room_id).await; - }))); + })); futures.push(self.readreceiptid_readreceipt.watch_prefix(&roomid_prefix)); From ad7a5ea7772cebb2279af7f4d131d2647231174e Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 11 May 2024 15:03:42 +0000 Subject: [PATCH 093/617] Remove useless wrapper Services --- src/database/key_value/rooms/alias.rs | 4 + src/database/key_value/rooms/directory.rs | 4 + .../key_value/rooms/edus/read_receipt.rs | 4 + src/database/key_value/rooms/metadata.rs | 1 + src/database/key_value/rooms/outlier.rs | 1 + src/database/key_value/rooms/search.rs | 3 + src/service.rs | 22 +-- src/service/account_data.rs | 51 +------ src/service/key_backups.rs | 127 +----------------- src/service/rooms/alias.rs | 33 +---- src/service/rooms/directory.rs | 30 +---- src/service/rooms/edus/read_receipt.rs | 66 +-------- src/service/rooms/metadata.rs | 28 +--- src/service/rooms/metadata/data.rs | 1 + src/service/rooms/outlier.rs | 28 +--- src/service/rooms/outlier/data.rs | 2 + src/service/rooms/search.rs | 29 +--- src/service/rooms/short.rs | 55 +------- src/service/rooms/user.rs | 55 +------- src/service/transaction_ids.rs | 29 +--- 20 files changed, 43 insertions(+), 530 deletions(-) diff --git a/src/database/key_value/rooms/alias.rs b/src/database/key_value/rooms/alias.rs index a4666834..5058cb87 100644 --- a/src/database/key_value/rooms/alias.rs +++ b/src/database/key_value/rooms/alias.rs @@ -3,6 +3,7 @@ use ruma::{api::client::error::ErrorKind, OwnedRoomAliasId, OwnedRoomId, RoomAli use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; impl service::rooms::alias::Data for KeyValueDatabase { + #[tracing::instrument(skip(self))] fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()> { self.alias_roomid .insert(alias.alias().as_bytes(), room_id.as_bytes())?; @@ -13,6 +14,7 @@ impl service::rooms::alias::Data for KeyValueDatabase { Ok(()) } + #[tracing::instrument(skip(self))] fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { if let Some(room_id) = self.alias_roomid.get(alias.alias().as_bytes())? { let mut prefix = room_id.clone(); @@ -31,6 +33,7 @@ impl service::rooms::alias::Data for KeyValueDatabase { Ok(()) } + #[tracing::instrument(skip(self))] fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result> { self.alias_roomid .get(alias.alias().as_bytes())? @@ -43,6 +46,7 @@ impl service::rooms::alias::Data for KeyValueDatabase { .transpose() } + #[tracing::instrument(skip(self))] fn local_aliases_for_room<'a>( &'a self, room_id: &RoomId, diff --git a/src/database/key_value/rooms/directory.rs b/src/database/key_value/rooms/directory.rs index e05dee82..9ed62582 100644 --- a/src/database/key_value/rooms/directory.rs +++ b/src/database/key_value/rooms/directory.rs @@ -3,18 +3,22 @@ use ruma::{OwnedRoomId, RoomId}; use crate::{database::KeyValueDatabase, service, utils, Error, Result}; impl service::rooms::directory::Data for KeyValueDatabase { + #[tracing::instrument(skip(self))] fn set_public(&self, room_id: &RoomId) -> Result<()> { self.publicroomids.insert(room_id.as_bytes(), &[]) } + #[tracing::instrument(skip(self))] fn set_not_public(&self, room_id: &RoomId) -> Result<()> { self.publicroomids.remove(room_id.as_bytes()) } + #[tracing::instrument(skip(self))] fn is_public_room(&self, room_id: &RoomId) -> Result { Ok(self.publicroomids.get(room_id.as_bytes())?.is_some()) } + #[tracing::instrument(skip(self))] fn public_rooms<'a>(&'a self) -> Box> + 'a> { Box::new(self.publicroomids.iter().map(|(bytes, _)| { RoomId::parse( diff --git a/src/database/key_value/rooms/edus/read_receipt.rs b/src/database/key_value/rooms/edus/read_receipt.rs index fa97ea34..87c4e6f0 100644 --- a/src/database/key_value/rooms/edus/read_receipt.rs +++ b/src/database/key_value/rooms/edus/read_receipt.rs @@ -48,6 +48,8 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { Ok(()) } + #[tracing::instrument(skip(self))] + #[allow(clippy::type_complexity)] fn readreceipts_since<'a>( &'a self, room_id: &RoomId, @@ -105,6 +107,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { ) } + #[tracing::instrument(skip(self))] fn private_read_set(&self, room_id: &RoomId, user_id: &UserId, count: u64) -> Result<()> { let mut key = room_id.as_bytes().to_vec(); key.push(0xff); @@ -117,6 +120,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { .insert(&key, &services().globals.next_count()?.to_be_bytes()) } + #[tracing::instrument(skip(self))] fn private_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result> { let mut key = room_id.as_bytes().to_vec(); key.push(0xff); diff --git a/src/database/key_value/rooms/metadata.rs b/src/database/key_value/rooms/metadata.rs index 57540c40..6a38f08b 100644 --- a/src/database/key_value/rooms/metadata.rs +++ b/src/database/key_value/rooms/metadata.rs @@ -3,6 +3,7 @@ use ruma::{OwnedRoomId, RoomId}; use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; impl service::rooms::metadata::Data for KeyValueDatabase { + #[tracing::instrument(skip(self))] fn exists(&self, room_id: &RoomId) -> Result { let prefix = match services().rooms.short.get_shortroomid(room_id)? { Some(b) => b.to_be_bytes().to_vec(), diff --git a/src/database/key_value/rooms/outlier.rs b/src/database/key_value/rooms/outlier.rs index 7985ba81..f4269770 100644 --- a/src/database/key_value/rooms/outlier.rs +++ b/src/database/key_value/rooms/outlier.rs @@ -19,6 +19,7 @@ impl service::rooms::outlier::Data for KeyValueDatabase { }) } + #[tracing::instrument(skip(self, pdu))] fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()> { self.eventid_outlierpdu.insert( event_id.as_bytes(), diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index 6a52b348..0154d1a1 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -3,6 +3,7 @@ use ruma::RoomId; use crate::{database::KeyValueDatabase, service, services, utils, Result}; impl service::rooms::search::Data for KeyValueDatabase { + #[tracing::instrument(skip(self))] fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { let mut batch = message_body .split_terminator(|c: char| !c.is_alphanumeric()) @@ -20,6 +21,8 @@ impl service::rooms::search::Data for KeyValueDatabase { self.tokenids.insert_batch(&mut batch) } + #[tracing::instrument(skip(self))] + #[allow(clippy::type_complexity)] fn search_pdus<'a>( &'a self, room_id: &RoomId, diff --git a/src/service.rs b/src/service.rs index fe06c40d..710edaca 100644 --- a/src/service.rs +++ b/src/service.rs @@ -60,11 +60,11 @@ impl Services { appservice: appservice::Service::build(db)?, pusher: pusher::Service { db }, rooms: rooms::Service { - alias: rooms::alias::Service { db }, + alias: db, auth_chain: rooms::auth_chain::Service { db }, - directory: rooms::directory::Service { db }, + directory: db, edus: rooms::edus::Service { - read_receipt: rooms::edus::read_receipt::Service { db }, + read_receipt: db, typing: rooms::edus::typing::Service { typing: RwLock::new(BTreeMap::new()), last_typing_update: RwLock::new(BTreeMap::new()), @@ -76,11 +76,11 @@ impl Services { db, lazy_load_waiting: Mutex::new(HashMap::new()), }, - metadata: rooms::metadata::Service { db }, - outlier: rooms::outlier::Service { db }, + metadata: db, + outlier: db, pdu_metadata: rooms::pdu_metadata::Service { db }, - search: rooms::search::Service { db }, - short: rooms::short::Service { db }, + search: db, + short: db, state: rooms::state::Service { db }, state_accessor: rooms::state_accessor::Service { db, @@ -121,17 +121,17 @@ impl Services { spaces: rooms::spaces::Service { roomid_spacechunk_cache: Mutex::new(LruCache::new(200)), }, - user: rooms::user::Service { db }, + user: db, }, - transaction_ids: transaction_ids::Service { db }, + transaction_ids: db, uiaa: uiaa::Service { db }, users: users::Service { db, connections: StdMutex::new(BTreeMap::new()), }, - account_data: account_data::Service { db }, + account_data: db, admin: admin::Service::build(), - key_backups: key_backups::Service { db }, + key_backups: db, media: media::Service { db }, sending: sending::Service::build(db, &config), diff --git a/src/service/account_data.rs b/src/service/account_data.rs index 515b970e..411199f4 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -1,53 +1,4 @@ mod data; pub(crate) use data::Data; - -use ruma::{ - events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, - serde::Raw, - RoomId, UserId, -}; - -use std::collections::HashMap; - -use crate::Result; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - /// Places one event in the account data of the user and removes the previous entry. - #[tracing::instrument(skip(self, room_id, user_id, event_type, data))] - pub(crate) fn update( - &self, - room_id: Option<&RoomId>, - user_id: &UserId, - event_type: RoomAccountDataEventType, - data: &serde_json::Value, - ) -> Result<()> { - self.db.update(room_id, user_id, event_type, data) - } - - /// Searches the account data for a specific kind. - #[tracing::instrument(skip(self, room_id, user_id, event_type))] - pub(crate) fn get( - &self, - room_id: Option<&RoomId>, - user_id: &UserId, - event_type: RoomAccountDataEventType, - ) -> Result>> { - self.db.get(room_id, user_id, event_type) - } - - /// Returns all changes to the account data that happened after `since`. - #[tracing::instrument(skip(self, room_id, user_id, since))] - pub(crate) fn changes_since( - &self, - room_id: Option<&RoomId>, - user_id: &UserId, - since: u64, - ) -> Result>> { - self.db.changes_since(room_id, user_id, since) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/key_backups.rs b/src/service/key_backups.rs index 542db640..411199f4 100644 --- a/src/service/key_backups.rs +++ b/src/service/key_backups.rs @@ -1,127 +1,4 @@ mod data; + pub(crate) use data::Data; - -use crate::Result; -use ruma::{ - api::client::backup::{BackupAlgorithm, KeyBackupData, RoomKeyBackup}, - serde::Raw, - OwnedRoomId, RoomId, UserId, -}; -use std::collections::BTreeMap; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - pub(crate) fn create_backup( - &self, - user_id: &UserId, - backup_metadata: &Raw, - ) -> Result { - self.db.create_backup(user_id, backup_metadata) - } - - pub(crate) fn delete_backup(&self, user_id: &UserId, version: &str) -> Result<()> { - self.db.delete_backup(user_id, version) - } - - pub(crate) fn update_backup( - &self, - user_id: &UserId, - version: &str, - backup_metadata: &Raw, - ) -> Result { - self.db.update_backup(user_id, version, backup_metadata) - } - - pub(crate) fn get_latest_backup_version(&self, user_id: &UserId) -> Result> { - self.db.get_latest_backup_version(user_id) - } - - pub(crate) fn get_latest_backup( - &self, - user_id: &UserId, - ) -> Result)>> { - self.db.get_latest_backup(user_id) - } - - pub(crate) fn get_backup( - &self, - user_id: &UserId, - version: &str, - ) -> Result>> { - self.db.get_backup(user_id, version) - } - - pub(crate) fn add_key( - &self, - user_id: &UserId, - version: &str, - room_id: &RoomId, - session_id: &str, - key_data: &Raw, - ) -> Result<()> { - self.db - .add_key(user_id, version, room_id, session_id, key_data) - } - - pub(crate) fn count_keys(&self, user_id: &UserId, version: &str) -> Result { - self.db.count_keys(user_id, version) - } - - pub(crate) fn get_etag(&self, user_id: &UserId, version: &str) -> Result { - self.db.get_etag(user_id, version) - } - - pub(crate) fn get_all( - &self, - user_id: &UserId, - version: &str, - ) -> Result> { - self.db.get_all(user_id, version) - } - - pub(crate) fn get_room( - &self, - user_id: &UserId, - version: &str, - room_id: &RoomId, - ) -> Result>> { - self.db.get_room(user_id, version, room_id) - } - - pub(crate) fn get_session( - &self, - user_id: &UserId, - version: &str, - room_id: &RoomId, - session_id: &str, - ) -> Result>> { - self.db.get_session(user_id, version, room_id, session_id) - } - - pub(crate) fn delete_all_keys(&self, user_id: &UserId, version: &str) -> Result<()> { - self.db.delete_all_keys(user_id, version) - } - - pub(crate) fn delete_room_keys( - &self, - user_id: &UserId, - version: &str, - room_id: &RoomId, - ) -> Result<()> { - self.db.delete_room_keys(user_id, version, room_id) - } - - pub(crate) fn delete_room_key( - &self, - user_id: &UserId, - version: &str, - room_id: &RoomId, - session_id: &str, - ) -> Result<()> { - self.db - .delete_room_key(user_id, version, room_id, session_id) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/alias.rs b/src/service/rooms/alias.rs index d078611a..411199f4 100644 --- a/src/service/rooms/alias.rs +++ b/src/service/rooms/alias.rs @@ -1,35 +1,4 @@ mod data; pub(crate) use data::Data; - -use crate::Result; -use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - #[tracing::instrument(skip(self))] - pub(crate) fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()> { - self.db.set_alias(alias, room_id) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { - self.db.remove_alias(alias) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result> { - self.db.resolve_local_alias(alias) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn local_aliases_for_room<'a>( - &'a self, - room_id: &RoomId, - ) -> Box> + 'a> { - self.db.local_aliases_for_room(room_id) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/directory.rs b/src/service/rooms/directory.rs index 9e43ac45..411199f4 100644 --- a/src/service/rooms/directory.rs +++ b/src/service/rooms/directory.rs @@ -1,32 +1,4 @@ mod data; pub(crate) use data::Data; -use ruma::{OwnedRoomId, RoomId}; - -use crate::Result; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - #[tracing::instrument(skip(self))] - pub(crate) fn set_public(&self, room_id: &RoomId) -> Result<()> { - self.db.set_public(room_id) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn set_not_public(&self, room_id: &RoomId) -> Result<()> { - self.db.set_not_public(room_id) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn is_public_room(&self, room_id: &RoomId) -> Result { - self.db.is_public_room(room_id) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn public_rooms(&self) -> impl Iterator> + '_ { - self.db.public_rooms() - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/edus/read_receipt.rs b/src/service/rooms/edus/read_receipt.rs index 54628203..411199f4 100644 --- a/src/service/rooms/edus/read_receipt.rs +++ b/src/service/rooms/edus/read_receipt.rs @@ -1,68 +1,4 @@ mod data; pub(crate) use data::Data; - -use crate::Result; -use ruma::{events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId}; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - /// Replaces the previous read receipt. - pub(crate) fn readreceipt_update( - &self, - user_id: &UserId, - room_id: &RoomId, - event: ReceiptEvent, - ) -> Result<()> { - self.db.readreceipt_update(user_id, room_id, event) - } - - /// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`. - #[tracing::instrument(skip(self))] - pub(crate) fn readreceipts_since<'a>( - &'a self, - room_id: &RoomId, - since: u64, - ) -> impl Iterator< - Item = Result<( - OwnedUserId, - u64, - Raw, - )>, - > + 'a { - self.db.readreceipts_since(room_id, since) - } - - /// Sets a private read marker at `count`. - #[tracing::instrument(skip(self))] - pub(crate) fn private_read_set( - &self, - room_id: &RoomId, - user_id: &UserId, - count: u64, - ) -> Result<()> { - self.db.private_read_set(room_id, user_id, count) - } - - /// Returns the private read marker. - #[tracing::instrument(skip(self))] - pub(crate) fn private_read_get( - &self, - room_id: &RoomId, - user_id: &UserId, - ) -> Result> { - self.db.private_read_get(room_id, user_id) - } - - /// Returns the count of the last typing update in this room. - pub(crate) fn last_privateread_update( - &self, - user_id: &UserId, - room_id: &RoomId, - ) -> Result { - self.db.last_privateread_update(user_id, room_id) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/metadata.rs b/src/service/rooms/metadata.rs index 5205a94a..411199f4 100644 --- a/src/service/rooms/metadata.rs +++ b/src/service/rooms/metadata.rs @@ -1,30 +1,4 @@ mod data; pub(crate) use data::Data; -use ruma::{OwnedRoomId, RoomId}; - -use crate::Result; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - /// Checks if a room exists. - #[tracing::instrument(skip(self))] - pub(crate) fn exists(&self, room_id: &RoomId) -> Result { - self.db.exists(room_id) - } - - pub(crate) fn iter_ids<'a>(&'a self) -> Box> + 'a> { - self.db.iter_ids() - } - - pub(crate) fn is_disabled(&self, room_id: &RoomId) -> Result { - self.db.is_disabled(room_id) - } - - pub(crate) fn disable_room(&self, room_id: &RoomId, disabled: bool) -> Result<()> { - self.db.disable_room(room_id, disabled) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/metadata/data.rs b/src/service/rooms/metadata/data.rs index 9b49fdeb..48317458 100644 --- a/src/service/rooms/metadata/data.rs +++ b/src/service/rooms/metadata/data.rs @@ -2,6 +2,7 @@ use crate::Result; use ruma::{OwnedRoomId, RoomId}; pub(crate) trait Data: Send + Sync { + /// Checks if a room exists. fn exists(&self, room_id: &RoomId) -> Result; fn iter_ids<'a>(&'a self) -> Box> + 'a>; fn is_disabled(&self, room_id: &RoomId) -> Result; diff --git a/src/service/rooms/outlier.rs b/src/service/rooms/outlier.rs index c0e9dc28..411199f4 100644 --- a/src/service/rooms/outlier.rs +++ b/src/service/rooms/outlier.rs @@ -1,30 +1,4 @@ mod data; pub(crate) use data::Data; -use ruma::{CanonicalJsonObject, EventId}; - -use crate::Result; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - /// Returns the pdu from the outlier tree. - pub(crate) fn get_outlier_pdu_json( - &self, - event_id: &EventId, - ) -> Result> { - self.db.get_outlier_pdu_json(event_id) - } - - /// Append the PDU as an outlier. - #[tracing::instrument(skip(self, pdu))] - pub(crate) fn add_pdu_outlier( - &self, - event_id: &EventId, - pdu: &CanonicalJsonObject, - ) -> Result<()> { - self.db.add_pdu_outlier(event_id, pdu) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/outlier/data.rs b/src/service/rooms/outlier/data.rs index 34505640..8956b491 100644 --- a/src/service/rooms/outlier/data.rs +++ b/src/service/rooms/outlier/data.rs @@ -3,7 +3,9 @@ use ruma::{CanonicalJsonObject, EventId}; use crate::{PduEvent, Result}; pub(crate) trait Data: Send + Sync { + /// Returns the pdu from the outlier tree. fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result>; fn get_outlier_pdu(&self, event_id: &EventId) -> Result>; + /// Append the PDU as an outlier. fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()>; } diff --git a/src/service/rooms/search.rs b/src/service/rooms/search.rs index 8f2ae8aa..411199f4 100644 --- a/src/service/rooms/search.rs +++ b/src/service/rooms/search.rs @@ -1,31 +1,4 @@ mod data; pub(crate) use data::Data; - -use crate::Result; -use ruma::RoomId; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - #[tracing::instrument(skip(self))] - pub(crate) fn index_pdu( - &self, - shortroomid: u64, - pdu_id: &[u8], - message_body: &str, - ) -> Result<()> { - self.db.index_pdu(shortroomid, pdu_id, message_body) - } - - #[tracing::instrument(skip(self))] - pub(crate) fn search_pdus<'a>( - &'a self, - room_id: &RoomId, - search_string: &str, - ) -> Result> + 'a, Vec)>> { - self.db.search_pdus(room_id, search_string) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index f8d5be16..411199f4 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,57 +1,4 @@ mod data; -use std::sync::Arc; pub(crate) use data::Data; -use ruma::{events::StateEventType, EventId, RoomId}; - -use crate::Result; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - pub(crate) fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { - self.db.get_or_create_shorteventid(event_id) - } - - pub(crate) fn get_shortstatekey( - &self, - event_type: &StateEventType, - state_key: &str, - ) -> Result> { - self.db.get_shortstatekey(event_type, state_key) - } - - pub(crate) fn get_or_create_shortstatekey( - &self, - event_type: &StateEventType, - state_key: &str, - ) -> Result { - self.db.get_or_create_shortstatekey(event_type, state_key) - } - - pub(crate) fn get_eventid_from_short(&self, shorteventid: u64) -> Result> { - self.db.get_eventid_from_short(shorteventid) - } - - pub(crate) fn get_statekey_from_short( - &self, - shortstatekey: u64, - ) -> Result<(StateEventType, String)> { - self.db.get_statekey_from_short(shortstatekey) - } - - /// Returns `(shortstatehash, already_existed)` - pub(crate) fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { - self.db.get_or_create_shortstatehash(state_hash) - } - - pub(crate) fn get_shortroomid(&self, room_id: &RoomId) -> Result> { - self.db.get_shortroomid(room_id) - } - - pub(crate) fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { - self.db.get_or_create_shortroomid(room_id) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/user.rs b/src/service/rooms/user.rs index f50754f8..411199f4 100644 --- a/src/service/rooms/user.rs +++ b/src/service/rooms/user.rs @@ -1,57 +1,4 @@ mod data; pub(crate) use data::Data; -use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; - -use crate::Result; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - pub(crate) fn reset_notification_counts( - &self, - user_id: &UserId, - room_id: &RoomId, - ) -> Result<()> { - self.db.reset_notification_counts(user_id, room_id) - } - - pub(crate) fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { - self.db.notification_count(user_id, room_id) - } - - pub(crate) fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { - self.db.highlight_count(user_id, room_id) - } - - pub(crate) fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result { - self.db.last_notification_read(user_id, room_id) - } - - pub(crate) fn associate_token_shortstatehash( - &self, - room_id: &RoomId, - token: u64, - shortstatehash: u64, - ) -> Result<()> { - self.db - .associate_token_shortstatehash(room_id, token, shortstatehash) - } - - pub(crate) fn get_token_shortstatehash( - &self, - room_id: &RoomId, - token: u64, - ) -> Result> { - self.db.get_token_shortstatehash(room_id, token) - } - - pub(crate) fn get_shared_rooms( - &self, - users: Vec, - ) -> Result>> { - self.db.get_shared_rooms(users) - } -} +pub(crate) type Service = &'static dyn Data; diff --git a/src/service/transaction_ids.rs b/src/service/transaction_ids.rs index b28919c5..411199f4 100644 --- a/src/service/transaction_ids.rs +++ b/src/service/transaction_ids.rs @@ -1,31 +1,4 @@ mod data; pub(crate) use data::Data; - -use crate::Result; -use ruma::{DeviceId, TransactionId, UserId}; - -pub(crate) struct Service { - pub(crate) db: &'static dyn Data, -} - -impl Service { - pub(crate) fn add_txnid( - &self, - user_id: &UserId, - device_id: Option<&DeviceId>, - txn_id: &TransactionId, - data: &[u8], - ) -> Result<()> { - self.db.add_txnid(user_id, device_id, txn_id, data) - } - - pub(crate) fn existing_txnid( - &self, - user_id: &UserId, - device_id: Option<&DeviceId>, - txn_id: &TransactionId, - ) -> Result>> { - self.db.existing_txnid(user_id, device_id, txn_id) - } -} +pub(crate) type Service = &'static dyn Data; From 4aab4c9b2ec4e75e0947e0c6f12604f9519847a1 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 11 May 2024 15:03:52 +0000 Subject: [PATCH 094/617] service/appservice: fix weird indirection through global services --- src/service/appservice.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/service/appservice.rs b/src/service/appservice.rs index ebfc82be..665d5a31 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -138,9 +138,7 @@ impl Service { /// Registers an appservice and returns the ID to the caller. pub(crate) async fn register_appservice(&self, yaml: Registration) -> Result { //TODO: Check for collisions between exclusive appservice namespaces - services() - .appservice - .registration_info + self.registration_info .write() .await .insert(yaml.id.clone(), yaml.clone().try_into()?); From 46731d1f8518f89c31c662fb7715b28fc590e911 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 15:51:31 -0700 Subject: [PATCH 095/617] how did clippy not catch this --- src/api/client_server/directory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index f4141fa9..7736f88b 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -305,7 +305,7 @@ pub(crate) async fn get_public_rooms_filtered_helper( }; Ok(chunk) }) - .filter_map(|r: Result<_>| r.ok()) // Filter out buggy rooms + .filter_map(Result::<_>::ok) // Filter out buggy rooms .filter(|chunk| { if let Some(query) = filter .generic_search_term From f9f066417bda23fe577286fb3107266cad5c1ea9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 16:28:40 -0700 Subject: [PATCH 096/617] remove pointless else branch --- src/service/rooms/lazy_loading.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/service/rooms/lazy_loading.rs b/src/service/rooms/lazy_loading.rs index 023e7fc2..54a10480 100644 --- a/src/service/rooms/lazy_loading.rs +++ b/src/service/rooms/lazy_loading.rs @@ -70,8 +70,6 @@ impl Service { room_id, &mut user_ids.iter().map(|u| &**u), )?; - } else { - // Ignore } Ok(()) From 034169bb8a17d56aa676d3c57b4d4c9862eb40c9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 16:31:38 -0700 Subject: [PATCH 097/617] remove obvious comments --- src/service/rooms/state_accessor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 6c1dd527..420ad76f 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -89,7 +89,7 @@ impl Service { fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) .map(|s| s == MembershipState::Join) - .unwrap_or_default() // Return sensible default, i.e. false + .unwrap_or_default() } /// The user was an invited or joined room member at this state (potentially @@ -97,7 +97,7 @@ impl Service { fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) .map(|s| s == MembershipState::Join || s == MembershipState::Invite) - .unwrap_or_default() // Return sensible default, i.e. false + .unwrap_or_default() } /// Whether a server is allowed to see an event through federation, based on From 40f9aa6f60c4410672ba547d293207331c79ced0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 23:39:18 -0700 Subject: [PATCH 098/617] remove stale comments Should've been removed in 98f1480e2b0900d02d92c6bcb8a872cd966d9205. --- src/api/server_server.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index b6fe20b3..7b0e9afe 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -667,14 +667,6 @@ pub(crate) async fn send_transaction_message_route( let pub_key_map = RwLock::new(BTreeMap::new()); - // This is all the auth_events that have been recursively fetched so they don't have to be - // deserialized over and over again. - // TODO: make this persist across requests but not in a DB Tree (in globals?) - // TODO: This could potentially also be some sort of trie (suffix tree) like structure so - // that once an auth event is known it would know (using indexes maybe) all of the auth - // events that it references. - // let mut auth_cache = EventMap::new(); - for pdu in &body.pdus { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { warn!("Error parsing incoming event {:?}: {:?}", pdu, e); @@ -1492,7 +1484,6 @@ async fn create_join_event( ))?; let pub_key_map = RwLock::new(BTreeMap::new()); - // let mut auth_cache = EventMap::new(); // We do not add the event_id field to the pdu here because of signature and hashes checks let room_version_id = services().rooms.state.get_room_version(room_id)?; From 3efe3fb337b3c51e03f6934359d0a45ce398a015 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 15:43:56 -0700 Subject: [PATCH 099/617] remove comments about filtering buggy items --- src/api/client_server/context.rs | 4 ++-- src/api/client_server/device.rs | 2 +- src/api/client_server/directory.rs | 2 +- src/api/client_server/message.rs | 4 ++-- src/api/client_server/sync.rs | 6 ++---- src/api/client_server/user_directory.rs | 1 - src/database/key_value/rooms/timeline.rs | 1 - src/service/rooms/pdu_metadata.rs | 4 ++-- 8 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index a063a2ec..7642ebe3 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -82,7 +82,7 @@ pub(crate) async fn get_context_route( .timeline .pdus_until(sender_user, &room_id, base_token)? .take(half_limit) - .filter_map(Result::ok) // Remove buggy events + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms @@ -118,7 +118,7 @@ pub(crate) async fn get_context_route( .timeline .pdus_after(sender_user, &room_id, base_token)? .take(half_limit) - .filter_map(Result::ok) // Remove buggy events + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 50ad47c1..0db1e833 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -18,7 +18,7 @@ pub(crate) async fn get_devices_route( let devices: Vec = services() .users .all_devices_metadata(sender_user) - .filter_map(Result::ok) // Filter out buggy devices + .filter_map(Result::ok) .collect(); Ok(get_devices::v3::Response { devices }) diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 7736f88b..3da2ca55 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -305,7 +305,7 @@ pub(crate) async fn get_public_rooms_filtered_helper( }; Ok(chunk) }) - .filter_map(Result::<_>::ok) // Filter out buggy rooms + .filter_map(Result::<_>::ok) .filter(|chunk| { if let Some(query) = filter .generic_search_term diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 76b367c8..a0c7bca5 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -157,7 +157,7 @@ pub(crate) async fn get_message_events_route( .timeline .pdus_after(sender_user, &body.room_id, from)? .take(limit) - .filter_map(Result::ok) // Filter out buggy events + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms @@ -206,7 +206,7 @@ pub(crate) async fn get_message_events_route( .timeline .pdus_until(sender_user, &body.room_id, from)? .take(limit) - .filter_map(Result::ok) // Filter out buggy events + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 7655c66d..d787769c 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -640,7 +640,7 @@ async fn load_joined_room( .rooms .timeline .all_pdus(sender_user, room_id)? - .filter_map(Result::ok) // Ignore all broken pdus + .filter_map(Result::ok) .filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember) .map(|(_, pdu)| { let content: RoomMemberEventContent = @@ -674,7 +674,6 @@ async fn load_joined_room( Ok(None) } }) - // Filter out buggy users .filter_map(Result::ok) // Filter for possible heroes .flatten() @@ -1019,7 +1018,7 @@ async fn load_joined_room( .edus .read_receipt .readreceipts_since(room_id, since) - .filter_map(Result::ok) // Filter out buggy events + .filter_map(Result::ok) .map(|(_, _, v)| v) .collect(); @@ -1104,7 +1103,6 @@ fn load_timeline( .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); } diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 560b0441..66f40a86 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -20,7 +20,6 @@ pub(crate) async fn search_users_route( let limit = body.limit.try_into().unwrap_or(usize::MAX); let mut users = services().users.iter().filter_map(|user_id| { - // Filter out buggy users (they should not exist, but you never know...) let user_id = user_id.ok()?; let user = search_users::v3::User { diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 438e57e6..468d8a1f 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -21,7 +21,6 @@ impl service::rooms::timeline::Data for KeyValueDatabase { if let Some(last_count) = self .pdus_until(sender_user, room_id, PduCount::MAX)? .find_map(|r| { - // Filter out buggy events if r.is_err() { error!("Bad pdu in pdus_since: {:?}", r); } diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 13f29663..61128e5e 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -82,7 +82,7 @@ impl Service { }) }) .take(limit) - .filter_map(Result::ok) // Filter out buggy events + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms @@ -129,7 +129,7 @@ impl Service { }) }) .take(limit) - .filter_map(Result::ok) // Filter out buggy events + .filter_map(Result::ok) .filter(|(_, pdu)| { services() .rooms From bac8b34faff81440a90b08cb9cf5942c62fa1f2a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 23:59:43 -0700 Subject: [PATCH 100/617] clarify comments about lazy loading --- src/api/client_server/message.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index a0c7bca5..852a3514 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -169,9 +169,11 @@ pub(crate) async fn get_message_events_route( .collect(); for (_, event) in &events_after { - /* TODO: Remove this when these are resolved: - * https://github.com/vector-im/element-android/issues/3417 - * https://github.com/vector-im/element-web/issues/21034 + // * https://github.com/vector-im/element-android/issues/3417 + // * https://github.com/vector-im/element-web/issues/21034 + // + // TODO: When the above issues are resolved, uncomment this: + /* if !services().rooms.lazy_loading.lazy_load_was_sent_before( sender_user, sender_device, @@ -181,6 +183,7 @@ pub(crate) async fn get_message_events_route( lazy_loaded.insert(event.sender.clone()); } */ + // And delete this line: lazy_loaded.insert(event.sender.clone()); } @@ -218,9 +221,11 @@ pub(crate) async fn get_message_events_route( .collect(); for (_, event) in &events_before { - /* TODO: Remove this when these are resolved: - * https://github.com/vector-im/element-android/issues/3417 - * https://github.com/vector-im/element-web/issues/21034 + // * https://github.com/vector-im/element-android/issues/3417 + // * https://github.com/vector-im/element-web/issues/21034 + // + // TODO: When the above issues are resolved, uncomment this: + /* if !services().rooms.lazy_loading.lazy_load_was_sent_before( sender_user, sender_device, @@ -230,6 +235,7 @@ pub(crate) async fn get_message_events_route( lazy_loaded.insert(event.sender.clone()); } */ + // And delete this line: lazy_loaded.insert(event.sender.clone()); } @@ -257,7 +263,10 @@ pub(crate) async fn get_message_events_route( } } - // TODO: enable again when we are sure clients can handle it + // * https://github.com/vector-im/element-android/issues/3417 + // * https://github.com/vector-im/element-web/issues/21034 + // + // TODO: When the above issues are resolved, uncomment this: /* if let Some(next_token) = next_token { services().rooms.lazy_loading.lazy_load_mark_sent( From de662a88f52e399e1931137565471054695c39f9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 00:15:24 -0700 Subject: [PATCH 101/617] clarify the meaning of this comment --- src/service/pdu.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/service/pdu.rs b/src/service/pdu.rs index ec192c78..6e1a6ae3 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -45,8 +45,11 @@ pub(crate) struct PduEvent { #[serde(default, skip_serializing_if = "Option::is_none")] pub(crate) unsigned: Option>, pub(crate) hashes: EventHash, + + // The schema of this `RawJsonValue` is `BTreeMap, + // BTreeMap>` #[serde(default, skip_serializing_if = "Option::is_none")] - pub(crate) signatures: Option>, // BTreeMap, BTreeMap> + pub(crate) signatures: Option>, } impl PduEvent { From f8420883a1a2af4f18bf4c4f96d6bf3e7f4c95cf Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 15:20:26 -0700 Subject: [PATCH 102/617] expand abbreviation for clarity This looked like a typo before. --- src/service/rooms/state_compressor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 8881c652..4f91d2e4 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -21,7 +21,7 @@ pub(crate) struct Service { LruCache< u64, Vec<( - u64, // sstatehash + u64, // shortstatehash Arc>, // full state Arc>, // added Arc>, // removed @@ -41,7 +41,7 @@ impl Service { shortstatehash: u64, ) -> Result< Vec<( - u64, // sstatehash + u64, // shortstatehash Arc>, // full state Arc>, // added Arc>, // removed @@ -152,7 +152,7 @@ impl Service { statediffremoved: Arc>, diff_to_sibling: usize, mut parent_states: Vec<( - u64, // sstatehash + u64, // shortstatehash Arc>, // full state Arc>, // added Arc>, // removed From d3b7eecaed930bf5385ad40bff1ca997ed3f3473 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 00:30:33 -0700 Subject: [PATCH 103/617] swap commented code for prose about "ownership" This more clearly communicates the purpose of the comments, and thus also the order of fields in the struct. --- src/database.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/database.rs b/src/database.rs index faddb9f9..5b14ebe6 100644 --- a/src/database.rs +++ b/src/database.rs @@ -32,11 +32,11 @@ use tracing::{debug, error, info, warn}; pub(crate) struct KeyValueDatabase { db: Arc, - //pub(crate) globals: globals::Globals, + // Trees "owned" by `self::key_value::globals` pub(super) global: Arc, pub(super) server_signingkeys: Arc, - //pub(crate) users: users::Users, + // Trees "owned" by `self::key_value::users` pub(super) userid_password: Arc, pub(super) userid_displayname: Arc, pub(super) userid_avatarurl: Arc, @@ -58,12 +58,12 @@ pub(crate) struct KeyValueDatabase { pub(super) todeviceid_events: Arc, // ToDeviceId = UserId + DeviceId + Count - //pub(crate) uiaa: uiaa::Uiaa, + // Trees "owned" by `self::key_value::uiaa` pub(super) userdevicesessionid_uiaainfo: Arc, // User-interactive authentication pub(super) userdevicesessionid_uiaarequest: RwLock>, - //pub(crate) edus: RoomEdus, + // Trees "owned" by `self::key_value::rooms::edus` pub(super) readreceiptid_readreceipt: Arc, // ReadReceiptId = RoomId + Count + UserId pub(super) roomuserid_privateread: Arc, // RoomUserId = Room + User, PrivateRead = Count pub(super) roomuserid_lastprivatereadupdate: Arc, // LastPrivateReadUpdate = Count @@ -74,7 +74,7 @@ pub(crate) struct KeyValueDatabase { #[allow(dead_code)] pub(super) userid_lastpresenceupdate: Arc, // LastPresenceUpdate = Count - //pub(crate) rooms: rooms::Rooms, + // Trees "owned" by `self::key_value::rooms` pub(super) pduid_pdu: Arc, // PduId = ShortRoomId + Count pub(super) eventid_pduid: Arc, pub(super) roomid_pduleaves: Arc, @@ -137,30 +137,31 @@ pub(crate) struct KeyValueDatabase { /// RoomId + EventId -> Parent PDU EventId. pub(super) referencedevents: Arc, - //pub(crate) account_data: account_data::AccountData, + // Trees "owned" by `self::key_value::account_data` pub(super) roomuserdataid_accountdata: Arc, // RoomUserDataId = Room + User + Count + Type pub(super) roomusertype_roomuserdataid: Arc, // RoomUserType = Room + User + Type - //pub(crate) media: media::Media, + // Trees "owned" by `self::key_value::media` pub(super) mediaid_file: Arc, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType - //pub(crate) key_backups: key_backups::KeyBackups, + // Trees "owned" by `self::key_value::key_backups` pub(super) backupid_algorithm: Arc, // BackupId = UserId + Version(Count) pub(super) backupid_etag: Arc, // BackupId = UserId + Version(Count) pub(super) backupkeyid_backup: Arc, // BackupKeyId = UserId + Version + RoomId + SessionId - //pub(crate) transaction_ids: transaction_ids::TransactionIds, + // Trees "owned" by `self::key_value::transaction_ids` pub(super) userdevicetxnid_response: Arc, // Response can be empty (/sendToDevice) or the event id (/send) - //pub(crate) sending: sending::Sending, + // Trees "owned" by `self::key_value::sending` pub(super) servername_educount: Arc, // EduCount: Count of last EDU sync pub(super) servernameevent_data: Arc, // ServernameEvent = (+ / $)SenderKey / ServerName / UserId + PduId / Id (for edus), Data = EDU content pub(super) servercurrentevent_data: Arc, // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / Id (for edus), Data = EDU content - //pub(crate) appservice: appservice::Appservice, + // Trees "owned" by `self::key_value::appservice` pub(super) id_appserviceregistrations: Arc, - //pub(crate) pusher: pusher::PushData, + // Trees "owned" by `self::key_value::pusher` pub(super) senderkey_pusher: Arc, + // Uncategorized trees pub(super) pdu_cache: Mutex>>, pub(super) shorteventid_cache: Mutex>>, pub(super) auth_chain_cache: Mutex, Arc>>>, From 0915aba44c424686dcd59992b030b1e5578384fd Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 15:07:26 -0700 Subject: [PATCH 104/617] remove commented-out code --- src/api/client_server/session.rs | 1 - src/database.rs | 22 ---------------------- src/database/abstraction/sqlite.rs | 4 ---- src/database/key_value.rs | 2 -- src/main.rs | 1 - src/service/admin.rs | 1 - src/service/pdu.rs | 7 ------- src/service/pusher.rs | 3 --- src/service/rooms/spaces.rs | 17 ----------------- src/service/rooms/timeline.rs | 23 ----------------------- 10 files changed, 81 deletions(-) diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 7a58a992..4f2e2839 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -20,7 +20,6 @@ use tracing::{info, warn}; #[derive(Debug, Deserialize)] struct Claims { sub: String, - //exp: usize, } /// # `GET /_matrix/client/r0/login` diff --git a/src/database.rs b/src/database.rs index 5b14ebe6..94de39c9 100644 --- a/src/database.rs +++ b/src/database.rs @@ -605,28 +605,6 @@ impl KeyValueDatabase { states_parents, )?; - /* - let mut tmp = services().rooms.load_shortstatehash_info(¤t_sstatehash)?; - let state = tmp.pop().unwrap(); - println!( - "{}\t{}{:?}: {:?} + {:?} - {:?}", - current_room, - " ".repeat(tmp.len()), - utils::u64_from_bytes(¤t_sstatehash).unwrap(), - tmp.last().map(|b| utils::u64_from_bytes(&b.0).unwrap()), - state - .2 - .iter() - .map(|b| utils::u64_from_bytes(&b[size_of::()..]).unwrap()) - .collect::>(), - state - .3 - .iter() - .map(|b| utils::u64_from_bytes(&b[size_of::()..]).unwrap()) - .collect::>() - ); - */ - Ok::<_, Error>(()) }; diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 495f6193..7609698a 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -195,8 +195,6 @@ impl SqliteTable { let statement_ref = AliasableBox(statement); - //let name = self.name.clone(); - let iterator = Box::new( statement .query_map([], |row| Ok((row.get_unwrap(0), row.get_unwrap(1)))) @@ -279,8 +277,6 @@ impl KvTree for SqliteTable { let guard = self.engine.read_lock_iterator(); let from = from.to_vec(); // TODO change interface? - //let name = self.name.clone(); - if backwards { let statement = Box::leak(Box::new( guard diff --git a/src/database/key_value.rs b/src/database/key_value.rs index c4496af8..098f3391 100644 --- a/src/database/key_value.rs +++ b/src/database/key_value.rs @@ -1,10 +1,8 @@ mod account_data; -//mod admin; mod appservice; mod globals; mod key_backups; mod media; -//mod pdu; mod pusher; mod rooms; mod sending; diff --git a/src/main.rs b/src/main.rs index d445d207..bd266bb4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -426,7 +426,6 @@ fn routes(config: &Config) -> Router { .ruma_route(client_server::get_key_changes_route) .ruma_route(client_server::get_pushers_route) .ruma_route(client_server::set_pushers_route) - // .ruma_route(client_server::third_party_route) .ruma_route(client_server::upgrade_room_route) .ruma_route(client_server::get_threads_route) .ruma_route(client_server::get_relating_events_with_rel_type_and_event_type_route) diff --git a/src/service/admin.rs b/src/service/admin.rs index bdfc6a9c..55a456af 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -210,7 +210,6 @@ impl Service { async fn handler(&self) { let mut receiver = self.receiver.lock().await; // TODO: Use futures when we have long admin commands - //let mut futures = FuturesUnordered::new(); let grapevine_user = UserId::parse(format!( "@{}:{}", diff --git a/src/service/pdu.rs b/src/service/pdu.rs index 6e1a6ae3..1de3d852 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -327,13 +327,6 @@ impl PduEvent { pdu_json.remove("event_id"); - // TODO: another option would be to convert it to a canonical string to validate size - // and return a Result> - // serde_json::from_str::>( - // ruma::serde::to_canonical_json_string(pdu_json).expect("CanonicalJson is valid serde_json::Value"), - // ) - // .expect("Raw::from_value always works") - to_raw_value(&pdu_json).expect("CanonicalJson is valid serde_json::Value") } diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 2e54f867..6e0fc755 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -72,9 +72,6 @@ impl Service { let reqwest_request = reqwest::Request::try_from(http_request)?; - // TODO: we could keep this very short and let expo backoff do it's thing... - //*reqwest_request.timeout_mut() = Some(Duration::from_secs(5)); - let url = reqwest_request.url().clone(); let response = services() .globals diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index bd1f5702..c478b2f2 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -32,7 +32,6 @@ use tracing::{debug, error, warn}; use crate::{services, Error, PduEvent, Result}; pub(crate) enum CachedJoinRule { - //Simplified(SpaceRoomJoinRule), Full(JoinRule), } @@ -87,9 +86,6 @@ impl Service { { if let Some(cached) = cached { 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)? } @@ -275,19 +271,6 @@ impl Service { join_rule: CachedJoinRule::Full(join_rule), }), ); - - /* TODO: - for child in response.children { - roomid_spacechunk_cache.insert( - current_room.clone(), - CachedSpaceChunk { - chunk: child.chunk, - children, - join_rule, - }, - ); - } - */ } else { self.roomid_spacechunk_cache .lock() diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 42b8d113..eb7db36f 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -114,29 +114,6 @@ impl Service { self.db.get_pdu_count(event_id) } - // TODO Is this the same as the function above? - /* - #[tracing::instrument(skip(self))] - pub(crate) fn latest_pdu_count(&self, room_id: &RoomId) -> Result { - let prefix = self - .get_shortroomid(room_id)? - .expect("room exists") - .to_be_bytes() - .to_vec(); - - let mut last_possible_key = prefix.clone(); - last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes()); - - self.pduid_pdu - .iter_from(&last_possible_key, true) - .take_while(move |(k, _)| k.starts_with(&prefix)) - .next() - .map(|b| self.pdu_count(&b.0)) - .transpose() - .map(|op| op.unwrap_or_default()) - } - */ - /// Returns the json of a pdu. pub(crate) fn get_pdu_json(&self, event_id: &EventId) -> Result> { self.db.get_pdu_json(event_id) From 1911ad34d90d084992341403e5801b76147dbd2a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 15 May 2024 15:40:56 -0700 Subject: [PATCH 105/617] stop putting comments and code on the same line --- src/api/appservice_server.rs | 3 +- src/api/client_server/keys.rs | 12 +- src/api/client_server/membership.rs | 6 +- src/api/client_server/room.rs | 6 +- src/api/client_server/search.rs | 9 +- src/api/client_server/state.rs | 6 +- src/api/client_server/sync.rs | 18 ++- src/api/ruma_wrapper/axum.rs | 3 +- src/api/server_server.rs | 6 +- src/config.rs | 6 +- src/config/proxy.rs | 12 +- src/database.rs | 147 +++++++++++++----- src/database/abstraction/sqlite.rs | 3 +- .../key_value/rooms/edus/read_receipt.rs | 3 +- src/database/key_value/rooms/search.rs | 6 +- src/database/key_value/rooms/state.rs | 6 +- src/database/key_value/rooms/state_cache.rs | 3 +- src/database/key_value/rooms/user.rs | 3 +- src/database/key_value/users.rs | 9 +- src/service/globals.rs | 16 +- src/service/media.rs | 3 +- src/service/pusher.rs | 6 +- src/service/rooms/edus/typing.rs | 6 +- src/service/rooms/event_handler.rs | 6 +- src/service/rooms/pdu_metadata.rs | 15 +- src/service/rooms/state.rs | 12 +- src/service/rooms/state/data.rs | 6 +- src/service/rooms/state_cache.rs | 6 +- src/service/rooms/state_compressor.rs | 39 +++-- src/service/rooms/timeline.rs | 18 ++- src/service/sending.rs | 27 ++-- src/service/uiaa.rs | 3 +- src/service/users.rs | 3 +- src/utils.rs | 11 +- src/utils/error.rs | 3 +- 35 files changed, 305 insertions(+), 142 deletions(-) diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index 276a1c57..98097d22 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -80,10 +80,11 @@ where .expect("http::response::Builder is usable"), ); + // TODO: handle timeout let body = response.bytes().await.unwrap_or_else(|e| { warn!("server error: {}", e); Vec::new().into() - }); // TODO: handle timeout + }); if status != 200 { warn!( diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 8a9191d1..06c06dcb 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -138,7 +138,8 @@ pub(crate) async fn upload_signing_keys_route( master_key, &body.self_signing_key, &body.user_signing_key, - true, // notify so that other users see the new keys + // notify so that other users see the new keys + true, )?; } @@ -196,7 +197,8 @@ pub(crate) async fn upload_signatures_route( } Ok(upload_signatures::v3::Response { - failures: BTreeMap::new(), // TODO: integrate + // TODO: integrate + failures: BTreeMap::new(), }) } @@ -252,7 +254,8 @@ pub(crate) async fn get_key_changes_route( } Ok(get_key_changes::v3::Response { changed: device_list_updates.into_iter().collect(), - left: Vec::new(), // TODO + // TODO + left: Vec::new(), }) } @@ -422,7 +425,8 @@ pub(crate) async fn get_keys_helper bool>( let raw = serde_json::from_value(json).expect("Raw::from_value always works"); services().users.add_cross_signing_keys( &user, &raw, &None, &None, - false, // Dont notify. A notification would trigger another key request resulting in an endless loop + // Dont notify. A notification would trigger another key request resulting in an endless loop + false, )?; master_keys.insert(user, raw); } diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 005ce01d..be3d59fa 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -49,7 +49,8 @@ pub(crate) async fn join_room_by_id_route( ) -> Result { 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 + // There is no body.server_name for /roomId/join + let mut servers = Vec::new(); servers.extend( services() .rooms @@ -1012,7 +1013,8 @@ async fn join_room_by_id_helper( let authenticated = state_res::event_auth::auth_check( &state_res::RoomVersion::new(&room_version_id).expect("room version is supported"), &parsed_join_pdu, - None::, // TODO: third party invite + // TODO: third party invite + None::, |k, s| { services() .rooms diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 1b266e25..b34f04bc 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -163,7 +163,8 @@ pub(crate) async fn create_room_route( })?, ); } - RoomVersionId::V11 => {} // V11 removed the "creator" key + // V11 removed the "creator" key + RoomVersionId::V11 => {} _ => unreachable!("Validity of room version already checked"), } @@ -790,7 +791,8 @@ pub(crate) async fn upgrade_room_route( .room_state_get(&body.room_id, &event_type, "")? { Some(v) => v.content.clone(), - None => continue, // Skipping missing events. + // Skipping missing events. + None => continue, }; services() diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 9d69d3b8..2b6c9f12 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -74,7 +74,8 @@ pub(crate) async fn search_events_route( "Invalid next_batch token.", )) } - None => 0, // Default to the start + // Default to the start + None => 0, }; let mut results = Vec::new(); @@ -133,10 +134,12 @@ pub(crate) async fn search_events_route( Ok(search_events::v3::Response::new(ResultCategories { room_events: ResultRoomEvents { count: None, - groups: BTreeMap::new(), // TODO + // TODO + groups: BTreeMap::new(), next_batch, results, - state: BTreeMap::new(), // TODO + // TODO + state: BTreeMap::new(), highlights: search_criteria .search_term .split_terminator(|c: char| !c.is_alphanumeric()) diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 8ac33241..dda83e10 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -30,7 +30,8 @@ pub(crate) async fn send_state_event_for_key_route( sender_user, &body.room_id, &body.event_type, - &body.body.body, // Yes, I hate it too + // Yes, I hate it too + &body.body.body, body.state_key.clone(), ) .await?; @@ -210,7 +211,8 @@ async fn send_state_event_for_key_helper( .rooms .alias .resolve_local_alias(&alias)? - .filter(|room| room == room_id) // Make sure it's the right room + // Make sure it's the right room + .filter(|room| room == room_id) .is_none() { return Err(Error::BadRequest( diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index d787769c..9f7a2626 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -209,7 +209,8 @@ async fn sync_helper( .unwrap_or(0); let sincecount = PduCount::Normal(since); - let mut left_encrypted_users = HashSet::new(); // Users that have left any encrypted rooms the sender was in + // Users that have left any encrypted rooms the sender was in + let mut left_encrypted_users = HashSet::new(); let mut device_list_updates = HashSet::new(); let mut device_list_left = HashSet::new(); @@ -492,7 +493,8 @@ async fn sync_helper( leave: left_rooms, join: joined_rooms, invite: invited_rooms, - knock: BTreeMap::new(), // TODO + // TODO + knock: BTreeMap::new(), }, presence: Presence::default(), account_data: GlobalAccountData { @@ -543,7 +545,8 @@ async fn sync_helper( }; Ok((response, false)) } else { - Ok((response, since != next_batch)) // Only cache if we made progress + // Only cache if we made progress + Ok((response, since != next_batch)) } } @@ -1201,7 +1204,8 @@ pub(crate) async fn sync_events_v4_route( .remove_to_device_events(&sender_user, &sender_device, globalsince)?; } - let mut left_encrypted_users = HashSet::new(); // Users that have left any encrypted rooms the sender was in + // Users that have left any encrypted rooms the sender was in + let mut left_encrypted_users = HashSet::new(); let mut device_list_changes = HashSet::new(); let mut device_list_left = HashSet::new(); @@ -1381,7 +1385,8 @@ pub(crate) async fn sync_events_v4_route( } let mut lists = BTreeMap::new(); - let mut todo_rooms = BTreeMap::new(); // and required state + // and required state + let mut todo_rooms = BTreeMap::new(); for (list_id, list) in body.lists { if list.filters.and_then(|f| f.is_invite).unwrap_or(false) { @@ -1646,7 +1651,8 @@ pub(crate) async fn sync_events_v4_route( .map(UInt::new_saturating) .unwrap_or(uint!(0)), ), - num_live: None, // Count events in timeline greater than global sync counter + // Count events in timeline greater than global sync counter + num_live: None, timestamp: None, }, ); diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 22931cf2..55485023 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -334,7 +334,8 @@ where struct XMatrix { origin: OwnedServerName, - key: String, // KeyName? + // KeyName? + key: String, sig: String, } diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 7b0e9afe..86c34276 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -260,10 +260,11 @@ where ); debug!("Getting response bytes from {destination}"); + // TODO: handle timeout let body = response.bytes().await.unwrap_or_else(|e| { warn!("server error {}", e); Vec::new().into() - }); // TODO: handle timeout + }); debug!("Got response bytes from {destination}"); if status != 200 { @@ -1555,7 +1556,8 @@ async fn create_join_event( .filter_map(|(_, id)| services().rooms.timeline.get_pdu_json(id).ok().flatten()) .map(PduEvent::convert_to_outgoing_federation_event) .collect(), - event: None, // TODO: handle restricted joins + // TODO: handle restricted joins + event: None, }) } diff --git a/src/config.rs b/src/config.rs index 2313b92f..0f9a9c9c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -239,11 +239,13 @@ fn default_pdu_cache_capacity() -> u32 { } fn default_cleanup_second_interval() -> u32 { - 60 // every minute + // every minute + 60 } fn default_max_request_size() -> u32 { - 20 * 1024 * 1024 // Default to 20 MB + // Default to 20 MB + 20 * 1024 * 1024 } fn default_max_concurrent_requests() -> u16 { diff --git a/src/config/proxy.rs b/src/config/proxy.rs index 2adab9f6..91ada136 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -45,7 +45,8 @@ impl ProxyConfig { ProxyConfig::None => None, ProxyConfig::Global { url } => Some(Proxy::all(url)?), ProxyConfig::ByDomain(proxies) => Some(Proxy::custom(move |url| { - proxies.iter().find_map(|proxy| proxy.for_url(url)).cloned() // first matching proxy + // first matching proxy + proxies.iter().find_map(|proxy| proxy.for_url(url)).cloned() })), }) } @@ -63,8 +64,10 @@ pub(crate) struct PartialProxyConfig { impl PartialProxyConfig { pub(crate) fn for_url(&self, url: &Url) -> Option<&Url> { let domain = url.domain()?; - let mut included_because = None; // most specific reason it was included - let mut excluded_because = None; // most specific reason it was excluded + // most specific reason it was included + let mut included_because = None; + // most specific reason it was excluded + let mut excluded_because = None; if self.include.is_empty() { // treat empty include list as `*` included_because = Some(&WildCardedDomain::WildCard); @@ -86,7 +89,8 @@ impl PartialProxyConfig { } } match (included_because, excluded_because) { - (Some(a), Some(b)) if a.more_specific_than(b) => Some(&self.url), // included for a more specific reason than excluded + // included for a more specific reason than excluded + (Some(a), Some(b)) if a.more_specific_than(b) => Some(&self.url), (Some(_), None) => Some(&self.url), _ => None, } diff --git a/src/database.rs b/src/database.rs index 94de39c9..ea964a52 100644 --- a/src/database.rs +++ b/src/database.rs @@ -42,77 +42,122 @@ pub(crate) struct KeyValueDatabase { pub(super) userid_avatarurl: Arc, pub(super) userid_blurhash: Arc, pub(super) userdeviceid_token: Arc, - pub(super) userdeviceid_metadata: Arc, // This is also used to check if a device exists - pub(super) userid_devicelistversion: Arc, // DevicelistVersion = u64 + + // This is also used to check if a device exists + pub(super) userdeviceid_metadata: Arc, + + // DevicelistVersion = u64 + pub(super) userid_devicelistversion: Arc, pub(super) token_userdeviceid: Arc, - pub(super) onetimekeyid_onetimekeys: Arc, // OneTimeKeyId = UserId + DeviceKeyId - pub(super) userid_lastonetimekeyupdate: Arc, // LastOneTimeKeyUpdate = Count - pub(super) keychangeid_userid: Arc, // KeyChangeId = UserId/RoomId + Count - pub(super) keyid_key: Arc, // KeyId = UserId + KeyId (depends on key type) + // OneTimeKeyId = UserId + DeviceKeyId + pub(super) onetimekeyid_onetimekeys: Arc, + + // LastOneTimeKeyUpdate = Count + pub(super) userid_lastonetimekeyupdate: Arc, + + // KeyChangeId = UserId/RoomId + Count + pub(super) keychangeid_userid: Arc, + + // KeyId = UserId + KeyId (depends on key type) + pub(super) keyid_key: Arc, pub(super) userid_masterkeyid: Arc, pub(super) userid_selfsigningkeyid: Arc, pub(super) userid_usersigningkeyid: Arc, - pub(super) userfilterid_filter: Arc, // UserFilterId = UserId + FilterId + // UserFilterId = UserId + FilterId + pub(super) userfilterid_filter: Arc, - pub(super) todeviceid_events: Arc, // ToDeviceId = UserId + DeviceId + Count + // ToDeviceId = UserId + DeviceId + Count + pub(super) todeviceid_events: Arc, // Trees "owned" by `self::key_value::uiaa` - pub(super) userdevicesessionid_uiaainfo: Arc, // User-interactive authentication + // User-interactive authentication + pub(super) userdevicesessionid_uiaainfo: Arc, pub(super) userdevicesessionid_uiaarequest: RwLock>, // Trees "owned" by `self::key_value::rooms::edus` - pub(super) readreceiptid_readreceipt: Arc, // ReadReceiptId = RoomId + Count + UserId - pub(super) roomuserid_privateread: Arc, // RoomUserId = Room + User, PrivateRead = Count - pub(super) roomuserid_lastprivatereadupdate: Arc, // LastPrivateReadUpdate = Count + // ReadReceiptId = RoomId + Count + UserId + pub(super) readreceiptid_readreceipt: Arc, + + // RoomUserId = Room + User, PrivateRead = Count + pub(super) roomuserid_privateread: Arc, + + // LastPrivateReadUpdate = Count + pub(super) roomuserid_lastprivatereadupdate: Arc, + + // PresenceId = RoomId + Count + UserId // This exists in the database already but is currently unused #[allow(dead_code)] - pub(super) presenceid_presence: Arc, // PresenceId = RoomId + Count + UserId + pub(super) presenceid_presence: Arc, + + // LastPresenceUpdate = Count // This exists in the database already but is currently unused #[allow(dead_code)] - pub(super) userid_lastpresenceupdate: Arc, // LastPresenceUpdate = Count + pub(super) userid_lastpresenceupdate: Arc, // Trees "owned" by `self::key_value::rooms` - pub(super) pduid_pdu: Arc, // PduId = ShortRoomId + Count + // PduId = ShortRoomId + Count + pub(super) pduid_pdu: Arc, pub(super) eventid_pduid: Arc, pub(super) roomid_pduleaves: Arc, pub(super) alias_roomid: Arc, - pub(super) aliasid_alias: Arc, // AliasId = RoomId + Count + + // AliasId = RoomId + Count + pub(super) aliasid_alias: Arc, pub(super) publicroomids: Arc, - pub(super) threadid_userids: Arc, // ThreadId = RoomId + Count + // ThreadId = RoomId + Count + pub(super) threadid_userids: Arc, - pub(super) tokenids: Arc, // TokenId = ShortRoomId + Token + PduIdCount + // TokenId = ShortRoomId + Token + PduIdCount + pub(super) tokenids: Arc, /// Participating servers in a room. - pub(super) roomserverids: Arc, // RoomServerId = RoomId + ServerName - pub(super) serverroomids: Arc, // ServerRoomId = ServerName + RoomId + // RoomServerId = RoomId + ServerName + pub(super) roomserverids: Arc, + + // ServerRoomId = ServerName + RoomId + pub(super) serverroomids: Arc, pub(super) userroomid_joined: Arc, pub(super) roomuserid_joined: Arc, pub(super) roomid_joinedcount: Arc, pub(super) roomid_invitedcount: Arc, pub(super) roomuseroncejoinedids: Arc, - pub(super) userroomid_invitestate: Arc, // InviteState = Vec> - pub(super) roomuserid_invitecount: Arc, // InviteCount = Count + + // InviteState = Vec> + pub(super) userroomid_invitestate: Arc, + + // InviteCount = Count + pub(super) roomuserid_invitecount: Arc, pub(super) userroomid_leftstate: Arc, pub(super) roomuserid_leftcount: Arc, - pub(super) disabledroomids: Arc, // Rooms where incoming federation handling is disabled + // Rooms where incoming federation handling is disabled + pub(super) disabledroomids: Arc, - pub(super) lazyloadedids: Arc, // LazyLoadedIds = UserId + DeviceId + RoomId + LazyLoadedUserId + // LazyLoadedIds = UserId + DeviceId + RoomId + LazyLoadedUserId + pub(super) lazyloadedids: Arc, - pub(super) userroomid_notificationcount: Arc, // NotifyCount = u64 - pub(super) userroomid_highlightcount: Arc, // HightlightCount = u64 - pub(super) roomuserid_lastnotificationread: Arc, // LastNotificationRead = u64 + // NotifyCount = u64 + pub(super) userroomid_notificationcount: Arc, + + // HightlightCount = u64 + pub(super) userroomid_highlightcount: Arc, + + // LastNotificationRead = u64 + pub(super) roomuserid_lastnotificationread: Arc, /// Remember the current state hash of a room. pub(super) roomid_shortstatehash: Arc, + pub(super) roomsynctoken_shortstatehash: Arc, + /// Remember the state hash at events in the past. pub(super) shorteventid_shortstatehash: Arc, + /// StateKey = EventType + StateKey, ShortStateKey = Count pub(super) statekey_shortstatekey: Arc, pub(super) shortstatekey_statekey: Arc, @@ -123,7 +168,9 @@ pub(crate) struct KeyValueDatabase { pub(super) eventid_shorteventid: Arc, pub(super) statehash_shortstatehash: Arc, - pub(super) shortstatehash_statediff: Arc, // StateDiff = parent (or 0) + (shortstatekey+shorteventid++) + 0_u64 + (shortstatekey+shorteventid--) + + // StateDiff = parent (or 0) + (shortstatekey+shorteventid++) + 0_u64 + (shortstatekey+shorteventid--) + pub(super) shortstatehash_statediff: Arc, pub(super) shorteventid_authchain: Arc, @@ -134,26 +181,44 @@ pub(crate) struct KeyValueDatabase { /// ShortEventId + ShortEventId -> (). pub(super) tofrom_relation: Arc, + /// RoomId + EventId -> Parent PDU EventId. pub(super) referencedevents: Arc, // Trees "owned" by `self::key_value::account_data` - pub(super) roomuserdataid_accountdata: Arc, // RoomUserDataId = Room + User + Count + Type - pub(super) roomusertype_roomuserdataid: Arc, // RoomUserType = Room + User + Type + // RoomUserDataId = Room + User + Count + Type + pub(super) roomuserdataid_accountdata: Arc, + + // RoomUserType = Room + User + Type + pub(super) roomusertype_roomuserdataid: Arc, // Trees "owned" by `self::key_value::media` - pub(super) mediaid_file: Arc, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType + // MediaId = MXC + WidthHeight + ContentDisposition + ContentType + pub(super) mediaid_file: Arc, + // Trees "owned" by `self::key_value::key_backups` - pub(super) backupid_algorithm: Arc, // BackupId = UserId + Version(Count) - pub(super) backupid_etag: Arc, // BackupId = UserId + Version(Count) - pub(super) backupkeyid_backup: Arc, // BackupKeyId = UserId + Version + RoomId + SessionId + // BackupId = UserId + Version(Count) + pub(super) backupid_algorithm: Arc, + + // BackupId = UserId + Version(Count) + pub(super) backupid_etag: Arc, + + // BackupKeyId = UserId + Version + RoomId + SessionId + pub(super) backupkeyid_backup: Arc, // Trees "owned" by `self::key_value::transaction_ids` - pub(super) userdevicetxnid_response: Arc, // Response can be empty (/sendToDevice) or the event id (/send) + // Response can be empty (/sendToDevice) or the event id (/send) + pub(super) userdevicetxnid_response: Arc, + // Trees "owned" by `self::key_value::sending` - pub(super) servername_educount: Arc, // EduCount: Count of last EDU sync - pub(super) servernameevent_data: Arc, // ServernameEvent = (+ / $)SenderKey / ServerName / UserId + PduId / Id (for edus), Data = EDU content - pub(super) servercurrentevent_data: Arc, // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / Id (for edus), Data = EDU content + // EduCount: Count of last EDU sync + pub(super) servername_educount: Arc, + + // ServernameEvent = (+ / $)SenderKey / ServerName / UserId + PduId / Id (for edus), Data = EDU content + pub(super) servernameevent_data: Arc, + + // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / Id (for edus), Data = EDU content + pub(super) servercurrentevent_data: Arc, // Trees "owned" by `self::key_value::appservice` pub(super) id_appserviceregistrations: Arc, @@ -278,7 +343,8 @@ impl KeyValueDatabase { userdevicesessionid_uiaainfo: builder.open_tree("userdevicesessionid_uiaainfo")?, userdevicesessionid_uiaarequest: RwLock::new(BTreeMap::new()), readreceiptid_readreceipt: builder.open_tree("readreceiptid_readreceipt")?, - roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, // "Private" read receipt + // "Private" read receipt + roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, roomuserid_lastprivatereadupdate: builder .open_tree("roomuserid_lastprivatereadupdate")?, presenceid_presence: builder.open_tree("presenceid_presence")?, @@ -601,7 +667,8 @@ impl KeyValueDatabase { current_sstatehash, Arc::new(statediffnew), Arc::new(statediffremoved), - 2, // every state change is 2 event changes on average + // every state change is 2 event changes on average + 2, states_parents, )?; diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 7609698a..1dc8a47d 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -275,7 +275,8 @@ impl KvTree for SqliteTable { backwards: bool, ) -> Box + 'a> { let guard = self.engine.read_lock_iterator(); - let from = from.to_vec(); // TODO change interface? + // TODO change interface? + let from = from.to_vec(); if backwards { let statement = Box::leak(Box::new( diff --git a/src/database/key_value/rooms/edus/read_receipt.rs b/src/database/key_value/rooms/edus/read_receipt.rs index 87c4e6f0..5b5b54aa 100644 --- a/src/database/key_value/rooms/edus/read_receipt.rs +++ b/src/database/key_value/rooms/edus/read_receipt.rs @@ -68,7 +68,8 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { let prefix2 = prefix.clone(); let mut first_possible_edu = prefix.clone(); - first_possible_edu.extend_from_slice(&(since + 1).to_be_bytes()); // +1 so we don't send the event at since + // +1 so we don't send the event at since + first_possible_edu.extend_from_slice(&(since + 1).to_be_bytes()); Box::new( self.readreceiptid_readreceipt diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index 0154d1a1..a2eba329 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -14,7 +14,8 @@ impl service::rooms::search::Data for KeyValueDatabase { let mut key = shortroomid.to_be_bytes().to_vec(); key.extend_from_slice(word.as_bytes()); key.push(0xff); - key.extend_from_slice(pdu_id); // TODO: currently we save the room id a second time here + // TODO: currently we save the room id a second time here + key.extend_from_slice(pdu_id); (key, Vec::new()) }); @@ -52,7 +53,8 @@ impl service::rooms::search::Data for KeyValueDatabase { last_possible_id.extend_from_slice(&u64::MAX.to_be_bytes()); self.tokenids - .iter_from(&last_possible_id, true) // Newest pdus first + // Newest pdus first + .iter_from(&last_possible_id, true) .take_while(move |(k, _)| k.starts_with(&prefix2)) .map(move |(key, _)| key[prefix3.len()..].to_vec()) }); diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index fd0c81e6..c7e042d2 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -21,7 +21,8 @@ impl service::rooms::state::Data for KeyValueDatabase { &self, room_id: &RoomId, new_shortstatehash: u64, - _mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { self.roomid_shortstatehash .insert(room_id.as_bytes(), &new_shortstatehash.to_be_bytes())?; @@ -53,7 +54,8 @@ impl service::rooms::state::Data for KeyValueDatabase { &self, room_id: &RoomId, event_ids: Vec, - _mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { let mut prefix = room_id.as_bytes().to_vec(); prefix.push(0xff); diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index be085b5c..a1a5025a 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -79,10 +79,11 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { userroom_id.push(0xff); userroom_id.extend_from_slice(room_id.as_bytes()); + // TODO self.userroomid_leftstate.insert( &userroom_id, &serde_json::to_vec(&Vec::>::new()).unwrap(), - )?; // TODO + )?; self.roomuserid_leftcount.insert( &roomuser_id, &services().globals.next_count()?.to_be_bytes(), diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index 6d5977dd..f3564272 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -123,7 +123,8 @@ impl service::rooms::user::Data for KeyValueDatabase { .find(|(_, &b)| b == 0xff) .ok_or_else(|| Error::bad_database("Invalid userroomid_joined in db."))? .0 - + 1; // +1 because the room id starts AFTER the separator + // +1 because the room id starts AFTER the separator + + 1; let room_id = key[roomid_index..].to_vec(); diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 0122d936..d437803f 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -215,7 +215,8 @@ impl service::users::Data for KeyValueDatabase { &serde_json::to_vec(&Device { device_id: device_id.into(), display_name: initial_device_display_name, - last_seen_ip: None, // TODO + // TODO + last_seen_ip: None, last_seen_ts: Some(MilliSecondsSinceUnixEpoch::now()), }) .expect("Device::to_string never fails."), @@ -365,7 +366,8 @@ impl service::users::Data for KeyValueDatabase { prefix.push(0xff); prefix.extend_from_slice(device_id.as_bytes()); prefix.push(0xff); - prefix.push(b'"'); // Annoying quotation mark + // Annoying quotation mark + prefix.push(b'"'); prefix.extend_from_slice(key_algorithm.as_ref().as_bytes()); prefix.push(b':'); @@ -828,7 +830,8 @@ impl service::users::Data for KeyValueDatabase { for (key, _) in self .todeviceid_events - .iter_from(&last, true) // this includes last + // this includes last + .iter_from(&last, true) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(key, _)| { Ok::<_, Error>(( diff --git a/src/service/globals.rs b/src/service/globals.rs index 98b543c8..a72a5e06 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -43,16 +43,20 @@ use base64::{engine::general_purpose, Engine as _}; type WellKnownMap = HashMap; type TlsNameMap = HashMap, u16)>; -type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries +// Time if last failed try, number of failed tries +type RateLimitState = (Instant, u32); type SyncHandle = ( - Option, // since - Receiver>>, // rx + // since + Option, + // rx + Receiver>>, ); pub(crate) struct Service { pub(crate) db: &'static dyn Data, - pub(crate) actual_destination_cache: Arc>, // actual_destination, host + // actual_destination, host + pub(crate) actual_destination_cache: Arc>, pub(crate) tls_name_override: Arc>, pub(crate) config: Config, keypair: Arc, @@ -69,7 +73,9 @@ pub(crate) struct Service { pub(crate) sync_receivers: RwLock>, pub(crate) roomid_mutex_insert: RwLock>>>, pub(crate) roomid_mutex_state: RwLock>>>, - pub(crate) roomid_mutex_federation: RwLock>>>, // this lock will be held longer + + // this lock will be held longer + pub(crate) roomid_mutex_federation: RwLock>>>, pub(crate) roomid_federationhandletime: RwLock>, pub(crate) stateres_mutex: Arc>, pub(crate) rotate: RotationHandler, diff --git a/src/service/media.rs b/src/service/media.rs index 7b12ab1f..76184374 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -113,8 +113,9 @@ impl Service { width: u32, height: u32, ) -> Result> { + // 0, 0 because that's the original file let (width, height, crop) = - Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); // 0, 0 because that's the original file + Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc.clone(), width, height) diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 6e0fc755..90a3b90d 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -93,10 +93,11 @@ impl Service { .expect("http::response::Builder is usable"), ); + // TODO: handle timeout let body = response.bytes().await.unwrap_or_else(|e| { warn!("server error {}", e); Vec::new().into() - }); // TODO: handle timeout + }); if status != 200 { info!( @@ -201,7 +202,8 @@ impl Service { let ctx = PushConditionRoomCtx { room_id: room_id.to_owned(), - member_count: 10_u32.into(), // TODO: get member count efficiently + // TODO: get member count efficiently + member_count: 10_u32.into(), user_id: user.to_owned(), user_display_name: services() .users diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 44d3e6af..972db4a1 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -6,8 +6,10 @@ use tracing::trace; use crate::{services, utils, Result}; pub(crate) struct Service { - pub(crate) typing: RwLock>>, // u64 is unix timestamp of timeout - pub(crate) last_typing_update: RwLock>, // timestamp of the last change to typing users + // u64 is unix timestamp of timeout + pub(crate) typing: RwLock>>, + // timestamp of the last change to typing users + pub(crate) last_typing_update: RwLock>, pub(crate) typing_update_sender: broadcast::Sender, } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index c334f6d8..8ff88088 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -446,7 +446,8 @@ impl Service { if !state_res::event_auth::auth_check( &room_version, &incoming_pdu, - None::, // TODO: third party invite + // TODO: third party invite + None::, |k, s| auth_events.get(&(k.to_string().into(), s.to_owned())), ) .map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed"))? @@ -748,7 +749,8 @@ impl Service { let check_result = state_res::event_auth::auth_check( &room_version, &incoming_pdu, - None::, // TODO: third party invite + // TODO: third party invite + None::, |k, s| { services() .rooms diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 61128e5e..636a3a58 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -64,7 +64,8 @@ impl Service { let events_after: Vec<_> = services() .rooms .pdu_metadata - .relations_until(sender_user, room_id, target, from)? // TODO: should be relations_after + // TODO: should be relations_after + .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t) @@ -90,14 +91,16 @@ impl Service { .user_can_see_event(sender_user, room_id, &pdu.event_id) .unwrap_or(false) }) - .take_while(|&(k, _)| Some(k) != to) // Stop at `to` + // Stop at `to` + .take_while(|&(k, _)| Some(k) != to) .collect(); next_token = events_after.last().map(|(count, _)| count).copied(); let events_after: Vec<_> = events_after .into_iter() - .rev() // relations are always most recent first + // relations are always most recent first + .rev() .map(|(_, pdu)| pdu.to_message_like_event()) .collect(); @@ -137,7 +140,8 @@ impl Service { .user_can_see_event(sender_user, room_id, &pdu.event_id) .unwrap_or(false) }) - .take_while(|&(k, _)| Some(k) != to) // Stop at `to` + // Stop at `to` + .take_while(|&(k, _)| Some(k) != to) .collect(); next_token = events_before.last().map(|(count, _)| count).copied(); @@ -167,7 +171,8 @@ impl Service { let target = match services().rooms.timeline.get_pdu_count(target)? { Some(PduCount::Normal(c)) => c, // TODO: Support backfilled relations - _ => 0, // This will result in an empty iterator + // This will result in an empty iterator + _ => 0, }; self.db.relations_until(user_id, room_id, target, until) } diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index daa17a58..0d4f91a0 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -35,7 +35,8 @@ impl Service { shortstatehash: u64, statediffnew: Arc>, _statediffremoved: Arc>, - state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + state_lock: &MutexGuard<'_, ()>, ) -> Result<()> { for event_id in statediffnew.iter().filter_map(|new| { services() @@ -169,7 +170,8 @@ impl Service { shortstatehash, statediffnew, statediffremoved, - 1_000_000, // high number because no state will be based on this one + // high number because no state will be based on this one + 1_000_000, states_parents, )?; } @@ -315,7 +317,8 @@ impl Service { &self, room_id: &RoomId, shortstatehash: u64, - mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { self.db.set_room_state(room_id, shortstatehash, mutex_lock) } @@ -358,7 +361,8 @@ impl Service { &self, room_id: &RoomId, event_ids: Vec, - state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + state_lock: &MutexGuard<'_, ()>, ) -> Result<()> { self.db .set_forward_extremities(room_id, event_ids, state_lock) diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index 1074a5dd..8e267760 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -12,7 +12,8 @@ pub(crate) trait Data: Send + Sync { &self, room_id: &RoomId, new_shortstatehash: u64, - _mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()>; /// Associates a state with an event. @@ -26,6 +27,7 @@ pub(crate) trait Data: Send + Sync { &self, room_id: &RoomId, event_ids: Vec, - _mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()>; } diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 78aba47b..9b15cd88 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -151,8 +151,10 @@ impl Service { let is_ignored = services() .account_data .get( - None, // Ignored users are in global account data - user_id, // Receiver + // Ignored users are in global account data + None, + // Receiver + user_id, GlobalAccountDataEventType::IgnoredUserList .to_string() .into(), diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 4f91d2e4..13f0912a 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -21,10 +21,14 @@ pub(crate) struct Service { LruCache< u64, Vec<( - u64, // shortstatehash - Arc>, // full state - Arc>, // added - Arc>, // removed + // shortstatehash + u64, + // full state + Arc>, + // added + Arc>, + // removed + Arc>, )>, >, >, @@ -41,10 +45,14 @@ impl Service { shortstatehash: u64, ) -> Result< Vec<( - u64, // shortstatehash - Arc>, // full state - Arc>, // added - Arc>, // removed + // shortstatehash + u64, + // full state + Arc>, + // added + Arc>, + // removed + Arc>, )>, > { if let Some(r) = self @@ -152,10 +160,14 @@ impl Service { statediffremoved: Arc>, diff_to_sibling: usize, mut parent_states: Vec<( - u64, // shortstatehash - Arc>, // full state - Arc>, // added - Arc>, // removed + // shortstatehash + u64, + // full state + Arc>, + // added + Arc>, + // removed + Arc>, )>, ) -> Result<()> { let diffsum = statediffnew.len() + statediffremoved.len(); @@ -318,7 +330,8 @@ impl Service { new_shortstatehash, statediffnew.clone(), statediffremoved.clone(), - 2, // every state change is 2 event changes on average + // every state change is 2 event changes on average + 2, states_parents, )?; }; diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index eb7db36f..42b802bc 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -177,7 +177,8 @@ impl Service { pdu: &PduEvent, mut pdu_json: CanonicalJsonObject, leaves: Vec, - state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + state_lock: &MutexGuard<'_, ()>, ) -> Result> { let shortroomid = services() .rooms @@ -527,7 +528,8 @@ impl Service { .threads .add_to_thread(&thread.event_id, pdu)?; } - _ => {} // TODO: Aggregate other types + // TODO: Aggregate other types + _ => {} } } @@ -598,7 +600,8 @@ impl Service { pdu_builder: PduBuilder, sender: &UserId, room_id: &RoomId, - _mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<(PduEvent, CanonicalJsonObject)> { let PduBuilder { event_type, @@ -702,7 +705,8 @@ impl Service { let auth_check = state_res::auth_check( &room_version, &pdu, - None::, // TODO: third_party_invite + // TODO: third_party_invite + None::, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) .map_err(|e| { @@ -781,7 +785,8 @@ impl Service { pdu_builder: PduBuilder, sender: &UserId, room_id: &RoomId, - state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + state_lock: &MutexGuard<'_, ()>, ) -> Result> { let (pdu, pdu_json) = self.create_hash_and_sign_event(pdu_builder, sender, room_id, state_lock)?; @@ -990,7 +995,8 @@ impl Service { new_room_leaves: Vec, state_ids_compressed: Arc>, soft_fail: bool, - state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex + // Take mutex guard to make sure users get the room state mutex + state_lock: &MutexGuard<'_, ()>, ) -> Result>> { // We append to state before appending the pdu, so we don't have a moment in time with the // pdu without it's state. This is okay because append_pdu can't fail. diff --git a/src/service/sending.rs b/src/service/sending.rs index 9cfb8cfe..ebfaf379 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -47,7 +47,8 @@ use tracing::{debug, error, warn}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum OutgoingKind { Appservice(String), - Push(OwnedUserId, String), // user and pushkey + // user and pushkey + Push(OwnedUserId, String), Normal(OwnedServerName), } @@ -81,8 +82,10 @@ impl OutgoingKind { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum SendingEventType { - Pdu(Vec), // pduid - Edu(Vec), // pdu json + // pduid + Pdu(Vec), + // pdu json + Edu(Vec), } pub(crate) struct Service { @@ -96,8 +99,10 @@ pub(crate) struct Service { enum TransactionStatus { Running, - Failed(u32, Instant), // number of times failed, time of last failure - Retrying(u32), // number of times failed + // number of times failed, time of last failure + Failed(u32, Instant), + // number of times failed + Retrying(u32), } impl Service { @@ -203,7 +208,8 @@ impl Service { fn select_events( &self, outgoing_kind: &OutgoingKind, - new_events: Vec<(SendingEventType, Vec)>, // Events we want to send: event and full key + // Events we want to send: event and full key + new_events: Vec<(SendingEventType, Vec)>, current_transaction_status: &mut HashMap, ) -> Result>> { let mut retry = false; @@ -214,7 +220,8 @@ impl Service { entry .and_modify(|e| match e { TransactionStatus::Running | TransactionStatus::Retrying(_) => { - allow = false; // already running + // already running + allow = false; } TransactionStatus::Failed(tries, time) => { // Fail if a request has failed recently (exponential backoff) @@ -444,7 +451,6 @@ impl Service { /// Cleanup event data /// Used for instance after we remove an appservice registration - /// #[tracing::instrument(skip(self))] pub(crate) fn cleanup_events(&self, appservice_id: String) -> Result<()> { self.db @@ -543,9 +549,8 @@ impl Service { })?, ); } - SendingEventType::Edu(_) => { - // Push gateways don't need EDUs (?) - } + // Push gateways don't need EDUs (?) + SendingEventType::Edu(_) => {} } } diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 1efd1d85..1cb026d9 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -29,7 +29,8 @@ impl Service { self.db.set_uiaa_request( user_id, device_id, - uiaainfo.session.as_ref().expect("session should be set"), // TODO: better session error handling (why is it optional in ruma?) + // TODO: better session error handling (why is it optional in ruma?) + uiaainfo.session.as_ref().expect("session should be set"), json_body, )?; self.db.update_uiaa_session( diff --git a/src/service/users.rs b/src/service/users.rs index 53882040..b69c98b0 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -28,7 +28,8 @@ use crate::{services, Error, Result}; pub(crate) struct SlidingSyncCache { lists: BTreeMap, subscriptions: BTreeMap, - known_rooms: BTreeMap>, // For every room, the roomsince number + // For every room, the roomsince number + known_rooms: BTreeMap>, extensions: ExtensionsConfig, } diff --git a/src/utils.rs b/src/utils.rs index a335e29c..194f0eef 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -27,7 +27,8 @@ pub(crate) fn increment(old: Option<&[u8]>) -> Vec { let number = u64::from_be_bytes(bytes); number + 1 } - _ => 1, // Start at one. since 0 should return the first event in the db + // Start at one. since 0 should return the first event in the db + _ => 1, }; number.to_be_bytes().to_vec() @@ -97,10 +98,12 @@ where other_iterators.iter_mut().all(|it| { while let Some(element) = it.peek() { match check_order(element, target) { - Ordering::Greater => return false, // We went too far - Ordering::Equal => return true, // Element is in both iters + // We went too far + Ordering::Greater => return false, + // Element is in both iters + Ordering::Equal => return true, + // Keep searching Ordering::Less => { - // Keep searching it.next(); } } diff --git a/src/utils/error.rs b/src/utils/error.rs index ae8a7aff..c0655de5 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -63,8 +63,9 @@ pub(crate) enum Error { Uiaa(UiaaInfo), #[error("{0}: {1}")] BadRequest(ErrorKind, &'static str), + // This is only needed for when a room alias already exists #[error("{0}")] - Conflict(&'static str), // This is only needed for when a room alias already exists + Conflict(&'static str), #[error("{0}")] Extension(#[from] axum::extract::rejection::ExtensionRejection), #[error("{0}")] From 05be778fbbd09257fe26865f6b6c2449419ce704 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 01:08:08 -0700 Subject: [PATCH 106/617] stop putting comments in the middle of call chains `rustfmt` doesn't handle this very well. --- src/api/client_server/directory.rs | 2 -- src/api/client_server/message.rs | 4 +-- src/api/client_server/space.rs | 2 +- src/api/client_server/state.rs | 1 - src/api/client_server/sync.rs | 1 - src/database/key_value/rooms/user.rs | 2 +- src/database/key_value/users.rs | 2 +- src/main.rs | 52 +++++++++++++++------------- src/service/rooms/pdu_metadata.rs | 6 ++-- src/service/rooms/spaces.rs | 1 - 10 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 3da2ca55..1e0e145f 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -266,7 +266,6 @@ pub(crate) async fn get_public_rooms_filtered_helper( }) }) .transpose()? - // url is now an Option so we must flatten .flatten(), join_rule: services() .rooms @@ -336,7 +335,6 @@ pub(crate) async fn get_public_rooms_filtered_helper( true } }) - // We need to collect all, so we can sort by member count .collect(); all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members)); diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 852a3514..2b920295 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -165,7 +165,7 @@ pub(crate) async fn get_message_events_route( .user_can_see_event(sender_user, &body.room_id, &pdu.event_id) .unwrap_or(false) }) - .take_while(|&(k, _)| Some(k) != to) // Stop at `to` + .take_while(|&(k, _)| Some(k) != to) .collect(); for (_, event) in &events_after { @@ -217,7 +217,7 @@ pub(crate) async fn get_message_events_route( .user_can_see_event(sender_user, &body.room_id, &pdu.event_id) .unwrap_or(false) }) - .take_while(|&(k, _)| Some(k) != to) // Stop at `to` + .take_while(|&(k, _)| Some(k) != to) .collect(); for (_, event) in &events_before { diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index 43463184..f01d888c 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -22,9 +22,9 @@ pub(crate) async fn get_hierarchy_route( .try_into() .expect("0-100 should fit in usize"); + // Plus one to skip the space room itself let max_depth = usize::try_from(body.max_depth.map(|x| x.min(uint!(10))).unwrap_or(uint!(3))) .expect("0-10 should fit in usize") - // Skip the space room itself + 1; services() diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index dda83e10..518ae621 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -211,7 +211,6 @@ async fn send_state_event_for_key_helper( .rooms .alias .resolve_local_alias(&alias)? - // Make sure it's the right room .filter(|room| room == room_id) .is_none() { diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 9f7a2626..8fb823e3 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -678,7 +678,6 @@ async fn load_joined_room( } }) .filter_map(Result::ok) - // Filter for possible heroes .flatten() { if heroes.contains(&hero) || hero == sender_user.as_str() { diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index f3564272..7c253d3f 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -117,13 +117,13 @@ impl service::rooms::user::Data for KeyValueDatabase { self.userroomid_joined .scan_prefix(prefix) .map(|(key, _)| { + // Plus one because the room id starts AFTER the separator let roomid_index = key .iter() .enumerate() .find(|(_, &b)| b == 0xff) .ok_or_else(|| Error::bad_database("Invalid userroomid_joined in db."))? .0 - // +1 because the room id starts AFTER the separator + 1; let room_id = key[roomid_index..].to_vec(); diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index d437803f..840e4788 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -828,9 +828,9 @@ impl service::users::Data for KeyValueDatabase { let mut last = prefix.clone(); last.extend_from_slice(&until.to_be_bytes()); + // Include last for (key, _) in self .todeviceid_events - // this includes last .iter_from(&last, true) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(key, _)| { diff --git a/src/main.rs b/src/main.rs index bd266bb4..4bb9db10 100644 --- a/src/main.rs +++ b/src/main.rs @@ -378,29 +378,6 @@ fn routes(config: &Config) -> Router { .ruma_route(client_server::send_state_event_for_key_route) .ruma_route(client_server::get_state_events_route) .ruma_route(client_server::get_state_events_for_key_route) - // Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes - // share one Ruma request / response type pair with {get,send}_state_event_for_key_route - .route( - "/_matrix/client/r0/rooms/:room_id/state/:event_type", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), - ) - .route( - "/_matrix/client/v3/rooms/:room_id/state/:event_type", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), - ) - // These two endpoints allow trailing slashes - .route( - "/_matrix/client/r0/rooms/:room_id/state/:event_type/", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), - ) - .route( - "/_matrix/client/v3/rooms/:room_id/state/:event_type/", - get(client_server::get_state_events_for_empty_key_route) - .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) @@ -431,7 +408,34 @@ fn routes(config: &Config) -> Router { .ruma_route(client_server::get_relating_events_with_rel_type_and_event_type_route) .ruma_route(client_server::get_relating_events_with_rel_type_route) .ruma_route(client_server::get_relating_events_route) - .ruma_route(client_server::get_hierarchy_route) + .ruma_route(client_server::get_hierarchy_route); + + // Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes + // share one Ruma request / response type pair with {get,send}_state_event_for_key_route. + // These two endpoints also allow trailing slashes. + let router = router + .route( + "/_matrix/client/r0/rooms/:room_id/state/:event_type", + get(client_server::get_state_events_for_empty_key_route) + .put(client_server::send_state_event_for_empty_key_route), + ) + .route( + "/_matrix/client/v3/rooms/:room_id/state/:event_type", + get(client_server::get_state_events_for_empty_key_route) + .put(client_server::send_state_event_for_empty_key_route), + ) + .route( + "/_matrix/client/r0/rooms/:room_id/state/:event_type/", + get(client_server::get_state_events_for_empty_key_route) + .put(client_server::send_state_event_for_empty_key_route), + ) + .route( + "/_matrix/client/v3/rooms/:room_id/state/:event_type/", + get(client_server::get_state_events_for_empty_key_route) + .put(client_server::send_state_event_for_empty_key_route), + ); + + let router = router .route( "/_matrix/client/r0/rooms/:room_id/initialSync", get(initial_sync), diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 636a3a58..5a778f6b 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -61,10 +61,10 @@ impl Service { //TODO: Fix ruma: match body.dir { match ruma::api::Direction::Backward { ruma::api::Direction::Forward => { + // TODO: should be relations_after let events_after: Vec<_> = services() .rooms .pdu_metadata - // TODO: should be relations_after .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { @@ -91,15 +91,14 @@ impl Service { .user_can_see_event(sender_user, room_id, &pdu.event_id) .unwrap_or(false) }) - // Stop at `to` .take_while(|&(k, _)| Some(k) != to) .collect(); next_token = events_after.last().map(|(count, _)| count).copied(); + // Reversed because relations are always most recent first let events_after: Vec<_> = events_after .into_iter() - // relations are always most recent first .rev() .map(|(_, pdu)| pdu.to_message_like_event()) .collect(); @@ -140,7 +139,6 @@ impl Service { .user_can_see_event(sender_user, room_id, &pdu.event_id) .unwrap_or(false) }) - // Stop at `to` .take_while(|&(k, _)| Some(k) != to) .collect(); diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index c478b2f2..7e97dbd4 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -371,7 +371,6 @@ impl Service { .map_err(|_| Error::bad_database("Invalid room avatar event in database.")) }) .transpose()? - // url is now an Option so we must flatten .flatten(), join_rule: { let join_rule = services() From 04184c6137fe8785f6b18ba0df724d5cf5a237e6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 15:23:06 -0700 Subject: [PATCH 107/617] use gender-neutral pronouns --- src/api/client_server/sync.rs | 2 +- src/database/key_value/globals.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 8fb823e3..cc2d56d5 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -53,7 +53,7 @@ use tracing::{debug, error, info}; /// not in the state at `since` /// - If the state we send contains a member event: Joined and invited member counts, heroes /// - Device list updates that happened after `since` -/// - If there are events in the timeline we send or the user send updated his read mark: Notification counts +/// - If there are events in the timeline we send or the user send updated their read mark: Notification counts /// - EDUs that are active now (read receipts, typing updates, presence) /// - TODO: Allow multiple sync streams to support Pantalaimon /// diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index aebc3d03..090efcb3 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -38,7 +38,7 @@ impl service::globals::Data for KeyValueDatabase { let mut futures = FuturesUnordered::new(); - // Return when *any* user changed his key + // Return when *any* user changed their key // TODO: only send for user they share a room with futures.push(self.todeviceid_events.watch_prefix(&userdeviceid_prefix)); From 94978b95a646be22ddc09b5c54c06264b4fea124 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 16:43:24 -0700 Subject: [PATCH 108/617] fix references in doc comments At least, this is all I've found so far. --- src/api/client_server/account.rs | 2 +- src/api/client_server/session.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 6d254a9c..91b5dd4d 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -65,7 +65,7 @@ pub(crate) async fn get_register_available_route( /// /// Register an account on this homeserver. /// -/// You can use [`GET /_matrix/client/r0/register/available`](fn.get_register_available_route.html) +/// You can use [`GET /_matrix/client/r0/register/available`](get_register_available_route) /// to check if the user id is valid and available. /// /// - Only works if registration is enabled diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 4f2e2839..1fb6ee43 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -44,7 +44,7 @@ pub(crate) async fn get_login_types_route( /// - If `device_id` is unknown: creates a new device /// - Returns access token that is associated with the user and device /// -/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see +/// Note: You can use [`GET /_matrix/client/r0/login`](get_login_types_route) to see /// supported login types. #[allow(clippy::too_many_lines)] pub(crate) async fn login_route(body: Ruma) -> Result { @@ -255,7 +255,7 @@ pub(crate) async fn logout_route(body: Ruma) -> Result, From 592dee368d51b515f62780723954bdaa64ba09c8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 16:51:58 -0700 Subject: [PATCH 109/617] replace comment on services with a doc comment The previous comment is pretty self-evident and also misunderstands async closures. --- src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4bb9db10..bd7b1aae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,9 +61,7 @@ static GLOBAL: Jemalloc = Jemalloc; pub(crate) static SERVICES: RwLock> = RwLock::new(None); -// Not async due to services() being used in many closures, and async closures are not stable as of writing -// This is the case for every other occurence of sync Mutex/RwLock, except for database related ones, where -// the previous maintainer has asked to not modify those +/// Convenient access to the global [`Services`] instance pub(crate) fn services() -> &'static Services { SERVICES .read() From dc096d7ad70308a51e873be9f9c718a1bf8ead59 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 16:05:23 -0700 Subject: [PATCH 110/617] abbreviate api categories This reduces the amount of characters on each line. --- src/main.rs | 290 ++++++++++++++++++++++++++-------------------------- 1 file changed, 145 insertions(+), 145 deletions(-) diff --git a/src/main.rs b/src/main.rs index bd7b1aae..1b44322f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -293,120 +293,123 @@ async fn unrecognized_method( #[allow(clippy::too_many_lines)] fn routes(config: &Config) -> Router { + use client_server as c2s; + use server_server as s2s; + let router = Router::new() - .ruma_route(client_server::get_supported_versions_route) - .ruma_route(client_server::get_register_available_route) - .ruma_route(client_server::register_route) - .ruma_route(client_server::get_login_types_route) - .ruma_route(client_server::login_route) - .ruma_route(client_server::whoami_route) - .ruma_route(client_server::logout_route) - .ruma_route(client_server::logout_all_route) - .ruma_route(client_server::change_password_route) - .ruma_route(client_server::deactivate_route) - .ruma_route(client_server::third_party_route) - .ruma_route(client_server::request_3pid_management_token_via_email_route) - .ruma_route(client_server::request_3pid_management_token_via_msisdn_route) - .ruma_route(client_server::get_capabilities_route) - .ruma_route(client_server::get_pushrules_all_route) - .ruma_route(client_server::set_pushrule_route) - .ruma_route(client_server::get_pushrule_route) - .ruma_route(client_server::set_pushrule_enabled_route) - .ruma_route(client_server::get_pushrule_enabled_route) - .ruma_route(client_server::get_pushrule_actions_route) - .ruma_route(client_server::set_pushrule_actions_route) - .ruma_route(client_server::delete_pushrule_route) - .ruma_route(client_server::get_room_event_route) - .ruma_route(client_server::get_room_aliases_route) - .ruma_route(client_server::get_filter_route) - .ruma_route(client_server::create_filter_route) - .ruma_route(client_server::set_global_account_data_route) - .ruma_route(client_server::set_room_account_data_route) - .ruma_route(client_server::get_global_account_data_route) - .ruma_route(client_server::get_room_account_data_route) - .ruma_route(client_server::set_displayname_route) - .ruma_route(client_server::get_displayname_route) - .ruma_route(client_server::set_avatar_url_route) - .ruma_route(client_server::get_avatar_url_route) - .ruma_route(client_server::get_profile_route) - .ruma_route(client_server::upload_keys_route) - .ruma_route(client_server::get_keys_route) - .ruma_route(client_server::claim_keys_route) - .ruma_route(client_server::create_backup_version_route) - .ruma_route(client_server::update_backup_version_route) - .ruma_route(client_server::delete_backup_version_route) - .ruma_route(client_server::get_latest_backup_info_route) - .ruma_route(client_server::get_backup_info_route) - .ruma_route(client_server::add_backup_keys_route) - .ruma_route(client_server::add_backup_keys_for_room_route) - .ruma_route(client_server::add_backup_keys_for_session_route) - .ruma_route(client_server::delete_backup_keys_for_room_route) - .ruma_route(client_server::delete_backup_keys_for_session_route) - .ruma_route(client_server::delete_backup_keys_route) - .ruma_route(client_server::get_backup_keys_for_room_route) - .ruma_route(client_server::get_backup_keys_for_session_route) - .ruma_route(client_server::get_backup_keys_route) - .ruma_route(client_server::set_read_marker_route) - .ruma_route(client_server::create_receipt_route) - .ruma_route(client_server::create_typing_event_route) - .ruma_route(client_server::create_room_route) - .ruma_route(client_server::redact_event_route) - .ruma_route(client_server::report_event_route) - .ruma_route(client_server::create_alias_route) - .ruma_route(client_server::delete_alias_route) - .ruma_route(client_server::get_alias_route) - .ruma_route(client_server::join_room_by_id_route) - .ruma_route(client_server::join_room_by_id_or_alias_route) - .ruma_route(client_server::joined_members_route) - .ruma_route(client_server::leave_room_route) - .ruma_route(client_server::forget_room_route) - .ruma_route(client_server::joined_rooms_route) - .ruma_route(client_server::kick_user_route) - .ruma_route(client_server::ban_user_route) - .ruma_route(client_server::unban_user_route) - .ruma_route(client_server::invite_user_route) - .ruma_route(client_server::set_room_visibility_route) - .ruma_route(client_server::get_room_visibility_route) - .ruma_route(client_server::get_public_rooms_route) - .ruma_route(client_server::get_public_rooms_filtered_route) - .ruma_route(client_server::search_users_route) - .ruma_route(client_server::get_member_events_route) - .ruma_route(client_server::get_protocols_route) - .ruma_route(client_server::send_message_event_route) - .ruma_route(client_server::send_state_event_for_key_route) - .ruma_route(client_server::get_state_events_route) - .ruma_route(client_server::get_state_events_for_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) - .ruma_route(client_server::turn_server_route) - .ruma_route(client_server::send_event_to_device_route) - .ruma_route(client_server::get_media_config_route) - .ruma_route(client_server::create_content_route) - .ruma_route(client_server::get_content_route) - .ruma_route(client_server::get_content_as_filename_route) - .ruma_route(client_server::get_content_thumbnail_route) - .ruma_route(client_server::get_devices_route) - .ruma_route(client_server::get_device_route) - .ruma_route(client_server::update_device_route) - .ruma_route(client_server::delete_device_route) - .ruma_route(client_server::delete_devices_route) - .ruma_route(client_server::get_tags_route) - .ruma_route(client_server::update_tag_route) - .ruma_route(client_server::delete_tag_route) - .ruma_route(client_server::upload_signing_keys_route) - .ruma_route(client_server::upload_signatures_route) - .ruma_route(client_server::get_key_changes_route) - .ruma_route(client_server::get_pushers_route) - .ruma_route(client_server::set_pushers_route) - .ruma_route(client_server::upgrade_room_route) - .ruma_route(client_server::get_threads_route) - .ruma_route(client_server::get_relating_events_with_rel_type_and_event_type_route) - .ruma_route(client_server::get_relating_events_with_rel_type_route) - .ruma_route(client_server::get_relating_events_route) - .ruma_route(client_server::get_hierarchy_route); + .ruma_route(c2s::get_supported_versions_route) + .ruma_route(c2s::get_register_available_route) + .ruma_route(c2s::register_route) + .ruma_route(c2s::get_login_types_route) + .ruma_route(c2s::login_route) + .ruma_route(c2s::whoami_route) + .ruma_route(c2s::logout_route) + .ruma_route(c2s::logout_all_route) + .ruma_route(c2s::change_password_route) + .ruma_route(c2s::deactivate_route) + .ruma_route(c2s::third_party_route) + .ruma_route(c2s::request_3pid_management_token_via_email_route) + .ruma_route(c2s::request_3pid_management_token_via_msisdn_route) + .ruma_route(c2s::get_capabilities_route) + .ruma_route(c2s::get_pushrules_all_route) + .ruma_route(c2s::set_pushrule_route) + .ruma_route(c2s::get_pushrule_route) + .ruma_route(c2s::set_pushrule_enabled_route) + .ruma_route(c2s::get_pushrule_enabled_route) + .ruma_route(c2s::get_pushrule_actions_route) + .ruma_route(c2s::set_pushrule_actions_route) + .ruma_route(c2s::delete_pushrule_route) + .ruma_route(c2s::get_room_event_route) + .ruma_route(c2s::get_room_aliases_route) + .ruma_route(c2s::get_filter_route) + .ruma_route(c2s::create_filter_route) + .ruma_route(c2s::set_global_account_data_route) + .ruma_route(c2s::set_room_account_data_route) + .ruma_route(c2s::get_global_account_data_route) + .ruma_route(c2s::get_room_account_data_route) + .ruma_route(c2s::set_displayname_route) + .ruma_route(c2s::get_displayname_route) + .ruma_route(c2s::set_avatar_url_route) + .ruma_route(c2s::get_avatar_url_route) + .ruma_route(c2s::get_profile_route) + .ruma_route(c2s::upload_keys_route) + .ruma_route(c2s::get_keys_route) + .ruma_route(c2s::claim_keys_route) + .ruma_route(c2s::create_backup_version_route) + .ruma_route(c2s::update_backup_version_route) + .ruma_route(c2s::delete_backup_version_route) + .ruma_route(c2s::get_latest_backup_info_route) + .ruma_route(c2s::get_backup_info_route) + .ruma_route(c2s::add_backup_keys_route) + .ruma_route(c2s::add_backup_keys_for_room_route) + .ruma_route(c2s::add_backup_keys_for_session_route) + .ruma_route(c2s::delete_backup_keys_for_room_route) + .ruma_route(c2s::delete_backup_keys_for_session_route) + .ruma_route(c2s::delete_backup_keys_route) + .ruma_route(c2s::get_backup_keys_for_room_route) + .ruma_route(c2s::get_backup_keys_for_session_route) + .ruma_route(c2s::get_backup_keys_route) + .ruma_route(c2s::set_read_marker_route) + .ruma_route(c2s::create_receipt_route) + .ruma_route(c2s::create_typing_event_route) + .ruma_route(c2s::create_room_route) + .ruma_route(c2s::redact_event_route) + .ruma_route(c2s::report_event_route) + .ruma_route(c2s::create_alias_route) + .ruma_route(c2s::delete_alias_route) + .ruma_route(c2s::get_alias_route) + .ruma_route(c2s::join_room_by_id_route) + .ruma_route(c2s::join_room_by_id_or_alias_route) + .ruma_route(c2s::joined_members_route) + .ruma_route(c2s::leave_room_route) + .ruma_route(c2s::forget_room_route) + .ruma_route(c2s::joined_rooms_route) + .ruma_route(c2s::kick_user_route) + .ruma_route(c2s::ban_user_route) + .ruma_route(c2s::unban_user_route) + .ruma_route(c2s::invite_user_route) + .ruma_route(c2s::set_room_visibility_route) + .ruma_route(c2s::get_room_visibility_route) + .ruma_route(c2s::get_public_rooms_route) + .ruma_route(c2s::get_public_rooms_filtered_route) + .ruma_route(c2s::search_users_route) + .ruma_route(c2s::get_member_events_route) + .ruma_route(c2s::get_protocols_route) + .ruma_route(c2s::send_message_event_route) + .ruma_route(c2s::send_state_event_for_key_route) + .ruma_route(c2s::get_state_events_route) + .ruma_route(c2s::get_state_events_for_key_route) + .ruma_route(c2s::sync_events_route) + .ruma_route(c2s::sync_events_v4_route) + .ruma_route(c2s::get_context_route) + .ruma_route(c2s::get_message_events_route) + .ruma_route(c2s::search_events_route) + .ruma_route(c2s::turn_server_route) + .ruma_route(c2s::send_event_to_device_route) + .ruma_route(c2s::get_media_config_route) + .ruma_route(c2s::create_content_route) + .ruma_route(c2s::get_content_route) + .ruma_route(c2s::get_content_as_filename_route) + .ruma_route(c2s::get_content_thumbnail_route) + .ruma_route(c2s::get_devices_route) + .ruma_route(c2s::get_device_route) + .ruma_route(c2s::update_device_route) + .ruma_route(c2s::delete_device_route) + .ruma_route(c2s::delete_devices_route) + .ruma_route(c2s::get_tags_route) + .ruma_route(c2s::update_tag_route) + .ruma_route(c2s::delete_tag_route) + .ruma_route(c2s::upload_signing_keys_route) + .ruma_route(c2s::upload_signatures_route) + .ruma_route(c2s::get_key_changes_route) + .ruma_route(c2s::get_pushers_route) + .ruma_route(c2s::set_pushers_route) + .ruma_route(c2s::upgrade_room_route) + .ruma_route(c2s::get_threads_route) + .ruma_route(c2s::get_relating_events_with_rel_type_and_event_type_route) + .ruma_route(c2s::get_relating_events_with_rel_type_route) + .ruma_route(c2s::get_relating_events_route) + .ruma_route(c2s::get_hierarchy_route); // Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes // share one Ruma request / response type pair with {get,send}_state_event_for_key_route. @@ -414,23 +417,23 @@ fn routes(config: &Config) -> Router { let router = router .route( "/_matrix/client/r0/rooms/:room_id/state/:event_type", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), ) .route( "/_matrix/client/v3/rooms/:room_id/state/:event_type", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), ) .route( "/_matrix/client/r0/rooms/:room_id/state/:event_type/", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), ) .route( "/_matrix/client/v3/rooms/:room_id/state/:event_type/", - get(client_server::get_state_events_for_empty_key_route) - .put(client_server::send_state_event_for_empty_key_route), + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), ); let router = router @@ -447,33 +450,30 @@ fn routes(config: &Config) -> Router { if config.allow_federation { router - .ruma_route(server_server::get_server_version_route) - .route( - "/_matrix/key/v2/server", - get(server_server::get_server_keys_route), - ) + .ruma_route(s2s::get_server_version_route) + .route("/_matrix/key/v2/server", get(s2s::get_server_keys_route)) .route( "/_matrix/key/v2/server/:key_id", - get(server_server::get_server_keys_deprecated_route), + get(s2s::get_server_keys_deprecated_route), ) - .ruma_route(server_server::get_public_rooms_route) - .ruma_route(server_server::get_public_rooms_filtered_route) - .ruma_route(server_server::send_transaction_message_route) - .ruma_route(server_server::get_event_route) - .ruma_route(server_server::get_backfill_route) - .ruma_route(server_server::get_missing_events_route) - .ruma_route(server_server::get_event_authorization_route) - .ruma_route(server_server::get_room_state_route) - .ruma_route(server_server::get_room_state_ids_route) - .ruma_route(server_server::create_join_event_template_route) - .ruma_route(server_server::create_join_event_v1_route) - .ruma_route(server_server::create_join_event_v2_route) - .ruma_route(server_server::create_invite_route) - .ruma_route(server_server::get_devices_route) - .ruma_route(server_server::get_room_information_route) - .ruma_route(server_server::get_profile_information_route) - .ruma_route(server_server::get_keys_route) - .ruma_route(server_server::claim_keys_route) + .ruma_route(s2s::get_public_rooms_route) + .ruma_route(s2s::get_public_rooms_filtered_route) + .ruma_route(s2s::send_transaction_message_route) + .ruma_route(s2s::get_event_route) + .ruma_route(s2s::get_backfill_route) + .ruma_route(s2s::get_missing_events_route) + .ruma_route(s2s::get_event_authorization_route) + .ruma_route(s2s::get_room_state_route) + .ruma_route(s2s::get_room_state_ids_route) + .ruma_route(s2s::create_join_event_template_route) + .ruma_route(s2s::create_join_event_v1_route) + .ruma_route(s2s::create_join_event_v2_route) + .ruma_route(s2s::create_invite_route) + .ruma_route(s2s::get_devices_route) + .ruma_route(s2s::get_room_information_route) + .ruma_route(s2s::get_profile_information_route) + .ruma_route(s2s::get_keys_route) + .ruma_route(s2s::claim_keys_route) } else { router .route("/_matrix/federation/*path", any(federation_disabled)) From ac53948450de7842c6996cbf57dbee19e22f183a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 17:10:49 -0700 Subject: [PATCH 111/617] use more, qualify less Doing this will allow `rustfmt` to collapse lines more efficiently. Specifically, a lot of these lines fail to wrap to 80 columns without these changes. --- src/api/client_server/alias.rs | 4 +- src/database/abstraction/rocksdb.rs | 74 +++++++++++++++-------------- src/service/admin.rs | 5 +- src/service/rooms/edus/typing.rs | 9 ++-- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 38bbe85b..c880bd5d 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -2,7 +2,7 @@ use crate::{services, Error, Result, Ruma}; use rand::seq::SliceRandom; use ruma::{ api::{ - appservice, + appservice::query::query_room_alias, client::{ alias::{create_alias, delete_alias, get_alias}, error::ErrorKind, @@ -143,7 +143,7 @@ pub(crate) async fn get_alias_helper( .sending .send_appservice_request( appservice.registration.clone(), - appservice::query::query_room_alias::v1::Request { + query_room_alias::v1::Request { room_alias: room_alias.clone(), }, ) diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 64444142..3c9c87b7 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -1,3 +1,9 @@ +use rocksdb::{ + perf::get_memory_usage_stats, BlockBasedOptions, BoundColumnFamily, Cache, + ColumnFamilyDescriptor, DBCompactionStyle, DBCompressionType, DBRecoveryMode, DBWithThreadMode, + Direction, IteratorMode, MultiThreaded, Options, ReadOptions, WriteOptions, +}; + use super::{super::Config, watchers::Watchers, KeyValueDatabaseEngine, KvTree}; use crate::{utils, Result}; use std::{ @@ -7,9 +13,9 @@ use std::{ }; pub(crate) struct Engine { - rocks: rocksdb::DBWithThreadMode, + rocks: DBWithThreadMode, max_open_files: i32, - cache: rocksdb::Cache, + cache: Cache, old_cfs: Vec, } @@ -20,8 +26,8 @@ pub(crate) struct RocksDbEngineTree<'a> { write_lock: RwLock<()>, } -fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::Options { - let mut block_based_options = rocksdb::BlockBasedOptions::default(); +fn db_options(max_open_files: i32, rocksdb_cache: &Cache) -> Options { + let mut block_based_options = BlockBasedOptions::default(); block_based_options.set_block_cache(rocksdb_cache); block_based_options.set_bloom_filter(10.0, false); block_based_options.set_block_size(4 * 1024); @@ -29,14 +35,14 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O block_based_options.set_pin_l0_filter_and_index_blocks_in_cache(true); block_based_options.set_optimize_filters_for_memory(true); - let mut db_opts = rocksdb::Options::default(); + let mut db_opts = Options::default(); db_opts.set_block_based_table_factory(&block_based_options); db_opts.create_if_missing(true); db_opts.increase_parallelism(num_cpus::get().try_into().unwrap_or(i32::MAX)); db_opts.set_max_open_files(max_open_files); - db_opts.set_compression_type(rocksdb::DBCompressionType::Lz4); - db_opts.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd); - db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level); + db_opts.set_compression_type(DBCompressionType::Lz4); + db_opts.set_bottommost_compression_type(DBCompressionType::Zstd); + db_opts.set_compaction_style(DBCompactionStyle::Level); // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning db_opts.set_level_compaction_dynamic_level_bytes(true); @@ -51,7 +57,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O // Unclean shutdowns of a Matrix homeserver are likely to be fine when // recovered in this manner as it's likely any lost information will be // restored via federation. - db_opts.set_wal_recovery_mode(rocksdb::DBRecoveryMode::TolerateCorruptedTailRecords); + db_opts.set_wal_recovery_mode(DBRecoveryMode::TolerateCorruptedTailRecords); db_opts } @@ -64,21 +70,18 @@ impl KeyValueDatabaseEngine for Arc { clippy::cast_possible_truncation )] let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize; - let rocksdb_cache = rocksdb::Cache::new_lru_cache(cache_capacity_bytes); + let rocksdb_cache = Cache::new_lru_cache(cache_capacity_bytes); let db_opts = db_options(config.rocksdb_max_open_files, &rocksdb_cache); - let cfs = rocksdb::DBWithThreadMode::::list_cf( - &db_opts, - &config.database_path, - ) - .unwrap_or_default(); + let cfs = DBWithThreadMode::::list_cf(&db_opts, &config.database_path) + .unwrap_or_default(); - let db = rocksdb::DBWithThreadMode::::open_cf_descriptors( + let db = DBWithThreadMode::::open_cf_descriptors( &db_opts, &config.database_path, cfs.iter().map(|name| { - rocksdb::ColumnFamilyDescriptor::new( + ColumnFamilyDescriptor::new( name, db_options(config.rocksdb_max_open_files, &rocksdb_cache), ) @@ -116,8 +119,7 @@ impl KeyValueDatabaseEngine for Arc { #[allow(clippy::as_conversions, clippy::cast_precision_loss)] fn memory_usage(&self) -> Result { - let stats = - rocksdb::perf::get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; + let stats = get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; Ok(format!( "Approximate memory usage of all the mem-tables: {:.3} MB\n\ Approximate memory usage of un-flushed mem-tables: {:.3} MB\n\ @@ -137,20 +139,20 @@ impl KeyValueDatabaseEngine for Arc { } impl RocksDbEngineTree<'_> { - fn cf(&self) -> Arc> { + fn cf(&self) -> Arc> { self.db.rocks.cf_handle(self.name).unwrap() } } impl KvTree for RocksDbEngineTree<'_> { fn get(&self, key: &[u8]) -> Result>> { - let readoptions = rocksdb::ReadOptions::default(); + let readoptions = ReadOptions::default(); Ok(self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?) } fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> { - let writeoptions = rocksdb::WriteOptions::default(); + let writeoptions = WriteOptions::default(); let lock = self.write_lock.read().unwrap(); self.db .rocks @@ -163,7 +165,7 @@ impl KvTree for RocksDbEngineTree<'_> { } fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { - let writeoptions = rocksdb::WriteOptions::default(); + let writeoptions = WriteOptions::default(); for (key, value) in iter { self.db .rocks @@ -174,7 +176,7 @@ impl KvTree for RocksDbEngineTree<'_> { } fn remove(&self, key: &[u8]) -> Result<()> { - let writeoptions = rocksdb::WriteOptions::default(); + let writeoptions = WriteOptions::default(); Ok(self .db .rocks @@ -182,12 +184,12 @@ impl KvTree for RocksDbEngineTree<'_> { } fn iter<'a>(&'a self) -> Box, Vec)> + 'a> { - let readoptions = rocksdb::ReadOptions::default(); + let readoptions = ReadOptions::default(); Box::new( self.db .rocks - .iterator_cf_opt(&self.cf(), readoptions, rocksdb::IteratorMode::Start) + .iterator_cf_opt(&self.cf(), readoptions, IteratorMode::Start) .map(Result::unwrap) .map(|(k, v)| (Vec::from(k), Vec::from(v))), ) @@ -198,7 +200,7 @@ impl KvTree for RocksDbEngineTree<'_> { from: &[u8], backwards: bool, ) -> Box, Vec)> + 'a> { - let readoptions = rocksdb::ReadOptions::default(); + let readoptions = ReadOptions::default(); Box::new( self.db @@ -206,12 +208,12 @@ impl KvTree for RocksDbEngineTree<'_> { .iterator_cf_opt( &self.cf(), readoptions, - rocksdb::IteratorMode::From( + IteratorMode::From( from, if backwards { - rocksdb::Direction::Reverse + Direction::Reverse } else { - rocksdb::Direction::Forward + Direction::Forward }, ), ) @@ -221,8 +223,8 @@ impl KvTree for RocksDbEngineTree<'_> { } fn increment(&self, key: &[u8]) -> Result> { - let readoptions = rocksdb::ReadOptions::default(); - let writeoptions = rocksdb::WriteOptions::default(); + let readoptions = ReadOptions::default(); + let writeoptions = WriteOptions::default(); let lock = self.write_lock.write().unwrap(); @@ -237,8 +239,8 @@ impl KvTree for RocksDbEngineTree<'_> { } fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()> { - let readoptions = rocksdb::ReadOptions::default(); - let writeoptions = rocksdb::WriteOptions::default(); + let readoptions = ReadOptions::default(); + let writeoptions = WriteOptions::default(); let lock = self.write_lock.write().unwrap(); @@ -259,7 +261,7 @@ impl KvTree for RocksDbEngineTree<'_> { &'a self, prefix: Vec, ) -> Box, Vec)> + 'a> { - let readoptions = rocksdb::ReadOptions::default(); + let readoptions = ReadOptions::default(); Box::new( self.db @@ -267,7 +269,7 @@ impl KvTree for RocksDbEngineTree<'_> { .iterator_cf_opt( &self.cf(), readoptions, - rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward), + IteratorMode::From(&prefix, Direction::Forward), ) .map(Result::unwrap) .map(|(k, v)| (Vec::from(k), Vec::from(v))) diff --git a/src/service/admin.rs b/src/service/admin.rs index 55a456af..d591a514 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -11,6 +11,7 @@ use regex::Regex; use ruma::{ api::appservice::Registration, events::{ + push_rules::{PushRulesEvent, PushRulesEventContent}, room::{ canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, @@ -644,8 +645,8 @@ impl Service { ruma::events::GlobalAccountDataEventType::PushRules .to_string() .into(), - &serde_json::to_value(ruma::events::push_rules::PushRulesEvent { - content: ruma::events::push_rules::PushRulesEventContent { + &serde_json::to_value(PushRulesEvent { + content: PushRulesEventContent { global: ruma::push::Ruleset::server_default(&user_id), }, }) diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 972db4a1..b3838d8a 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -1,4 +1,7 @@ -use ruma::{events::SyncEphemeralRoomEvent, OwnedRoomId, OwnedUserId, RoomId, UserId}; +use ruma::{ + events::{typing::TypingEventContent, SyncEphemeralRoomEvent}, + OwnedRoomId, OwnedUserId, RoomId, UserId, +}; use std::collections::BTreeMap; use tokio::sync::{broadcast, RwLock}; use tracing::trace; @@ -116,9 +119,9 @@ impl Service { pub(crate) async fn typings_all( &self, room_id: &RoomId, - ) -> Result> { + ) -> Result> { Ok(SyncEphemeralRoomEvent { - content: ruma::events::typing::TypingEventContent { + content: TypingEventContent { user_ids: self .typing .read() From 6bc17b268c797a1d9979f70d29fbc224b14218cd Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 19:04:29 -0700 Subject: [PATCH 112/617] factor select bodies into closures I would've preferred to factor these out into their own functions, but unfortunately the inner type of the `FuturesUnordered` is unnameable. `Box` or TAIT would help, but the former has a performance cost and the latter doesn't exist on stable yet. --- src/service/sending.rs | 120 +++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index ebfaf379..22216faf 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -123,6 +123,7 @@ impl Service { }); } + #[allow(clippy::too_many_lines)] async fn handler(&self) -> Result<()> { let mut receiver = self.receiver.lock().await; @@ -155,51 +156,90 @@ impl Service { futures.push(Self::handle_events(outgoing_kind.clone(), events)); } - loop { - select! { - Some(response) = futures.next() => { - match response { - Ok(outgoing_kind) => { - self.db.delete_all_active_requests_for(&outgoing_kind)?; + let handle_futures = |response, + current_transaction_status: &mut HashMap<_, _>, + futures: &mut FuturesUnordered<_>| { + match response { + Ok(outgoing_kind) => { + self.db.delete_all_active_requests_for(&outgoing_kind)?; - // Find events that have been added since starting the last request - let new_events = self.db.queued_requests(&outgoing_kind).filter_map(Result::ok).take(30).collect::>(); + // Find events that have been added since starting the + // last request + let new_events = self + .db + .queued_requests(&outgoing_kind) + .filter_map(Result::ok) + .take(30) + .collect::>(); - if new_events.is_empty() { - current_transaction_status.remove(&outgoing_kind); - } else { - // Insert pdus we found - self.db.mark_as_active(&new_events)?; + if new_events.is_empty() { + current_transaction_status.remove(&outgoing_kind); + } else { + // Insert pdus we found + self.db.mark_as_active(&new_events)?; - futures.push( - Self::handle_events( - outgoing_kind.clone(), - new_events.into_iter().map(|(event, _)| event).collect(), - ) - ); - } - } - Err((outgoing_kind, _)) => { - current_transaction_status.entry(outgoing_kind).and_modify(|e| *e = match e { - TransactionStatus::Running => TransactionStatus::Failed(1, Instant::now()), - TransactionStatus::Retrying(n) => TransactionStatus::Failed(*n+1, Instant::now()), - TransactionStatus::Failed(_, _) => { - error!("Request that was not even running failed?!"); - return - }, - }); - } - }; - }, - Some((outgoing_kind, event, key)) = receiver.recv() => { - if let Ok(Some(events)) = self.select_events( - &outgoing_kind, - vec![(event, key)], - &mut current_transaction_status, - ) { - futures.push(Self::handle_events(outgoing_kind, events)); + futures.push(Self::handle_events( + outgoing_kind.clone(), + new_events.into_iter().map(|(event, _)| event).collect(), + )); } } + Err((outgoing_kind, _)) => { + current_transaction_status + .entry(outgoing_kind) + .and_modify(|e| { + *e = match e { + TransactionStatus::Running => { + TransactionStatus::Failed(1, Instant::now()) + } + TransactionStatus::Retrying(n) => { + TransactionStatus::Failed(*n + 1, Instant::now()) + } + TransactionStatus::Failed(..) => { + error!( + "Request that was not even \ + running failed?!" + ); + return; + } + } + }); + } + }; + + Result::<_>::Ok(()) + }; + + let handle_receiver = |outgoing_kind, + event, + key, + current_transaction_status: &mut HashMap<_, _>, + futures: &mut FuturesUnordered<_>| { + if let Ok(Some(events)) = self.select_events( + &outgoing_kind, + vec![(event, key)], + current_transaction_status, + ) { + futures.push(Self::handle_events(outgoing_kind, events)); + } + }; + + loop { + select! { + Some(response) = futures.next() => + handle_futures( + response, + &mut current_transaction_status, + &mut futures, + )?, + Some((outgoing_kind, event, key)) = receiver.recv() => + handle_receiver( + outgoing_kind, + event, + key, + &mut current_transaction_status, + &mut futures, + ), } } } From 40d6ce230d8bf23ede601eba14a5b09de3fd1c14 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 17:15:30 -0700 Subject: [PATCH 113/617] reformat report formatting I manually expanded the HTML into a more readable format, the rest was rustfmt's doing. It's beyond me why/how someone would willing write a pile of HTML like that... --- src/api/client_server/report.rs | 52 ++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index 1f118622..25e8f10a 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -35,24 +35,50 @@ pub(crate) async fn report_event_route( )); }; - services().admin + services() + .admin .send_message(message::RoomMessageEventContent::text_html( format!( - "Report received from: {}\n\n\ - Event ID: {:?}\n\ - Room ID: {:?}\n\ - Sent By: {:?}\n\n\ - Report Score: {:?}\n\ - Report Reason: {:?}", + "Report received from: {}\n\nEvent ID: {:?}\nRoom ID: {:?}\nSent \ + By: {:?}\n\nReport Score: {:?}\nReport Reason: {:?}", sender_user, pdu.event_id, pdu.room_id, pdu.sender, body.score, body.reason ), format!( - "", + r#" +
+ + Report received from: + {0:?} + +
    +
  • + Event Info +
      +
    • + Event ID: + {1:?} + 🔗 +
    • +
    • + Room ID: + {2:?} +
    • +
    • + Sent By: + {3:?} +
    • +
    +
  • +
  • + Report Info +
      +
    • Report Score: {4:?}
    • +
    • Report Reason: {5}
    • +
    +
  • +
+
+ "#, sender_user, pdu.event_id, pdu.room_id, From 0afc1d2f506adcfa5e053d9a92d953fd39f77ffd Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 01:19:04 -0700 Subject: [PATCH 114/617] change rustfmt configuration This change is fully automated, except the `rustfmt.toml` changes and a few clippy directives to allow specific functions with too many lines because they are longer now. --- rustfmt.toml | 19 +- src/api/appservice_server.rs | 20 +- src/api/client_server/account.rs | 173 +-- src/api/client_server/alias.rs | 32 +- src/api/client_server/backup.rs | 121 +- src/api/client_server/capabilities.rs | 13 +- src/api/client_server/config.rs | 45 +- src/api/client_server/context.rs | 115 +- src/api/client_server/device.rs | 67 +- src/api/client_server/directory.rs | 155 ++- src/api/client_server/filter.rs | 12 +- src/api/client_server/keys.rs | 213 ++-- src/api/client_server/media.rs | 55 +- src/api/client_server/membership.rs | 628 ++++++---- src/api/client_server/message.rs | 101 +- src/api/client_server/profile.rs | 62 +- src/api/client_server/push.rs | 82 +- src/api/client_server/read_marker.rs | 53 +- src/api/client_server/redact.rs | 8 +- src/api/client_server/relations.rs | 96 +- src/api/client_server/report.rs | 4 +- src/api/client_server/room.rs | 301 +++-- src/api/client_server/search.rs | 25 +- src/api/client_server/session.rs | 170 ++- src/api/client_server/space.rs | 19 +- src/api/client_server/state.rs | 62 +- src/api/client_server/sync.rs | 1076 ++++++++++------- src/api/client_server/tag.rs | 21 +- src/api/client_server/thirdparty.rs | 5 +- src/api/client_server/threads.rs | 7 +- src/api/client_server/to_device.rs | 47 +- src/api/client_server/typing.rs | 9 +- src/api/client_server/unversioned.rs | 15 +- src/api/client_server/user_directory.rs | 22 +- src/api/client_server/voip.rs | 12 +- src/api/ruma_wrapper.rs | 12 +- src/api/ruma_wrapper/axum.rs | 150 ++- src/api/server_server.rs | 645 ++++++---- src/config.rs | 5 +- src/config/proxy.rs | 26 +- src/database.rs | 452 ++++--- src/database/abstraction.rs | 22 +- src/database/abstraction/rocksdb.rs | 86 +- src/database/abstraction/sqlite.rs | 102 +- src/database/abstraction/watchers.rs | 8 +- src/database/key_value/account_data.rs | 64 +- src/database/key_value/appservice.rs | 26 +- src/database/key_value/globals.rs | 88 +- src/database/key_value/key_backups.rs | 200 +-- src/database/key_value/media.rs | 43 +- src/database/key_value/pusher.rs | 41 +- src/database/key_value/rooms/alias.rs | 49 +- src/database/key_value/rooms/auth_chain.rs | 24 +- src/database/key_value/rooms/directory.rs | 18 +- .../key_value/rooms/edus/read_receipt.rs | 101 +- src/database/key_value/rooms/lazy_load.rs | 18 +- src/database/key_value/rooms/metadata.rs | 22 +- src/database/key_value/rooms/outlier.rs | 35 +- src/database/key_value/rooms/pdu_metadata.rs | 33 +- src/database/key_value/rooms/search.rs | 18 +- src/database/key_value/rooms/short.rs | 159 ++- src/database/key_value/rooms/state.rs | 55 +- .../key_value/rooms/state_accessor.rs | 47 +- src/database/key_value/rooms/state_cache.rs | 399 +++--- .../key_value/rooms/state_compressor.rs | 13 +- src/database/key_value/rooms/threads.rs | 55 +- src/database/key_value/rooms/timeline.rs | 114 +- src/database/key_value/rooms/user.rs | 110 +- src/database/key_value/sending.rs | 138 ++- src/database/key_value/transaction_ids.rs | 16 +- src/database/key_value/uiaa.rs | 35 +- src/database/key_value/users.rs | 535 +++++--- src/main.rs | 71 +- src/service.rs | 92 +- src/service/account_data/data.rs | 6 +- src/service/admin.rs | 543 ++++++--- src/service/appservice.rs | 49 +- src/service/appservice/data.rs | 4 +- src/service/globals.rs | 146 ++- src/service/globals/data.rs | 6 +- src/service/key_backups/data.rs | 27 +- src/service/media.rs | 68 +- src/service/pdu.rs | 121 +- src/service/pusher.rs | 101 +- src/service/pusher/data.rs | 23 +- src/service/rooms/alias/data.rs | 8 +- src/service/rooms/auth_chain.rs | 46 +- src/service/rooms/auth_chain/data.rs | 10 +- src/service/rooms/directory/data.rs | 7 +- src/service/rooms/edus/read_receipt/data.rs | 27 +- src/service/rooms/edus/typing.rs | 36 +- src/service/rooms/event_handler.rs | 721 +++++++---- src/service/rooms/lazy_loading.rs | 14 +- src/service/rooms/lazy_loading/data.rs | 3 +- src/service/rooms/metadata/data.rs | 7 +- src/service/rooms/outlier/data.rs | 11 +- src/service/rooms/pdu_metadata.rs | 91 +- src/service/rooms/pdu_metadata/data.rs | 15 +- src/service/rooms/search/data.rs | 10 +- src/service/rooms/short/data.rs | 16 +- src/service/rooms/spaces.rs | 167 ++- src/service/rooms/state.rs | 92 +- src/service/rooms/state/data.rs | 17 +- src/service/rooms/state_accessor.rs | 194 ++- src/service/rooms/state_accessor/data.rs | 17 +- src/service/rooms/state_cache.rs | 140 ++- src/service/rooms/state_cache/data.rs | 52 +- src/service/rooms/state_compressor.rs | 106 +- src/service/rooms/state_compressor/data.rs | 6 +- src/service/rooms/threads.rs | 74 +- src/service/rooms/threads/data.rs | 17 +- src/service/rooms/timeline.rs | 474 +++++--- src/service/rooms/timeline/data.rs | 38 +- src/service/rooms/user/data.rs | 33 +- src/service/sending.rs | 516 +++++--- src/service/sending/data.rs | 29 +- src/service/transaction_ids/data.rs | 3 +- src/service/uiaa.rs | 44 +- src/service/uiaa/data.rs | 3 +- src/service/users.rs | 251 ++-- src/service/users/data.rs | 85 +- src/utils.rs | 46 +- src/utils/error.rs | 56 +- 123 files changed, 7881 insertions(+), 4687 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 739b454f..58355ad5 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,17 @@ -unstable_features = true -imports_granularity="Crate" +edition = "2021" + +condense_wildcard_suffixes = true +format_code_in_doc_comments = true +format_macro_bodies = true +format_macro_matchers = true +format_strings = true +group_imports = "StdExternalCrate" +hex_literal_case = "Upper" +imports_granularity = "Crate" +max_width = 80 +newline_style = "Unix" +reorder_impl_items = true +use_field_init_shorthand = true +use_small_heuristics = "Off" +use_try_shorthand = true +wrap_comments = true diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index 98097d22..fbf03179 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -1,14 +1,18 @@ -use crate::{services, utils, Error, Result}; +use std::{fmt::Debug, mem, time::Duration}; + use bytes::BytesMut; use ruma::api::{ - appservice::Registration, IncomingResponse, MatrixVersion, OutgoingRequest, SendAccessToken, + appservice::Registration, IncomingResponse, MatrixVersion, OutgoingRequest, + SendAccessToken, }; -use std::{fmt::Debug, mem, time::Duration}; use tracing::warn; +use crate::{services, utils, Error, Result}; + /// Sends a request to an appservice /// -/// Only returns None if there is no url specified in the appservice registration file +/// Only returns None if there is no url specified in the appservice +/// registration file #[tracing::instrument(skip(request))] pub(crate) async fn send_request( registration: Registration, @@ -45,7 +49,8 @@ where .parse() .unwrap(), ); - *http_request.uri_mut() = parts.try_into().expect("our manipulation is always valid"); + *http_request.uri_mut() = + parts.try_into().expect("our manipulation is always valid"); let mut reqwest_request = reqwest::Request::try_from(http_request)?; @@ -70,9 +75,8 @@ where // reqwest::Response -> http::Response conversion let status = response.status(); - let mut http_response_builder = http::Response::builder() - .status(status) - .version(response.version()); + let mut http_response_builder = + http::Response::builder().status(status).version(response.version()); mem::swap( response.headers_mut(), http_response_builder diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 91b5dd4d..4d529b68 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -1,22 +1,25 @@ -use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH}; -use crate::{api::client_server, services, utils, Error, Result, Ruma}; +use register::RegistrationKind; use ruma::{ api::client::{ account::{ change_password, deactivate, get_3pids, get_username_availability, register::{self, LoginType}, - request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn, - whoami, ThirdPartyIdRemovalStatus, + request_3pid_management_token_via_email, + request_3pid_management_token_via_msisdn, whoami, + ThirdPartyIdRemovalStatus, }, error::ErrorKind, uiaa::{AuthFlow, AuthType, UiaaInfo}, }, - events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType}, + events::{ + room::message::RoomMessageEventContent, GlobalAccountDataEventType, + }, push, UserId, }; use tracing::{info, warn}; -use register::RegistrationKind; +use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH}; +use crate::{api::client_server, services, utils, Error, Result, Ruma}; const RANDOM_USER_ID_LENGTH: usize = 10; @@ -29,7 +32,8 @@ const RANDOM_USER_ID_LENGTH: usize = 10; /// - The server name of the user id matches this server /// - No user or appservice on this server already claimed this username /// -/// Note: This will not reserve the username, so the username might become invalid when trying to register +/// Note: This will not reserve the username, so the username might become +/// invalid when trying to register pub(crate) async fn get_register_available_route( body: Ruma, ) -> Result { @@ -40,7 +44,8 @@ pub(crate) async fn get_register_available_route( ) .ok() .filter(|user_id| { - !user_id.is_historical() && user_id.server_name() == services().globals.server_name() + !user_id.is_historical() + && user_id.server_name() == services().globals.server_name() }) .ok_or(Error::BadRequest( ErrorKind::InvalidUsername, @@ -58,27 +63,35 @@ pub(crate) async fn get_register_available_route( // TODO add check for appservice namespaces // If no if check is true we have an username that's available to be used. - Ok(get_username_availability::v3::Response { available: true }) + Ok(get_username_availability::v3::Response { + available: true, + }) } /// # `POST /_matrix/client/r0/register` /// /// Register an account on this homeserver. /// -/// You can use [`GET /_matrix/client/r0/register/available`](get_register_available_route) +/// You can use [`GET +/// /_matrix/client/r0/register/available`](get_register_available_route) /// to check if the user id is valid and available. /// /// - Only works if registration is enabled -/// - If type is guest: ignores all parameters except `initial_device_display_name` +/// - If type is guest: ignores all parameters except +/// `initial_device_display_name` /// - If sender is not appservice: Requires UIAA (but we only use a dummy stage) -/// - If type is not guest and no username is given: Always fails after UIAA check +/// - If type is not guest and no username is given: Always fails after UIAA +/// check /// - Creates a new account and populates it with default account data -/// - If `inhibit_login` is false: Creates a device and returns `device_id` and `access_token` +/// - If `inhibit_login` is false: Creates a device and returns `device_id` and +/// `access_token` #[allow(clippy::too_many_lines)] pub(crate) async fn register_route( body: Ruma, ) -> Result { - if !services().globals.allow_registration() && body.appservice_info.is_none() { + if !services().globals.allow_registration() + && body.appservice_info.is_none() + { return Err(Error::BadRequest( ErrorKind::Forbidden, "Registration has been disabled.", @@ -158,7 +171,8 @@ pub(crate) async fn register_route( }; body.appservice_info.is_some() } else { - // No registration token necessary, but clients must still go through the flow + // No registration token necessary, but clients must still go through + // the flow uiaainfo = UiaaInfo { flows: vec![AuthFlow { stages: vec![AuthType::Dummy], @@ -174,8 +188,11 @@ pub(crate) async fn register_route( if !skip_auth { if let Some(auth) = &body.auth { let (worked, uiaainfo) = services().uiaa.try_auth( - &UserId::parse_with_server_name("", services().globals.server_name()) - .expect("we know this is valid"), + &UserId::parse_with_server_name( + "", + services().globals.server_name(), + ) + .expect("we know this is valid"), "".into(), auth, &uiaainfo, @@ -187,8 +204,11 @@ pub(crate) async fn register_route( } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); services().uiaa.create( - &UserId::parse_with_server_name("", services().globals.server_name()) - .expect("we know this is valid"), + &UserId::parse_with_server_name( + "", + services().globals.server_name(), + ) + .expect("we know this is valid"), "".into(), &uiaainfo, &json, @@ -211,9 +231,7 @@ pub(crate) async fn register_route( // Default to pretty displayname let displayname = user_id.localpart().to_owned(); - services() - .users - .set_displayname(&user_id, Some(displayname.clone()))?; + services().users.set_displayname(&user_id, Some(displayname.clone()))?; // Initial account data services().account_data.update( @@ -260,29 +278,24 @@ pub(crate) async fn register_route( info!("New user {} registered on this server.", user_id); if body.appservice_info.is_none() && !is_guest { - services() - .admin - .send_message(RoomMessageEventContent::notice_plain(format!( - "New user {user_id} registered on this server." - ))); + services().admin.send_message(RoomMessageEventContent::notice_plain( + format!("New user {user_id} registered on this server."), + )); } // If this is the first real user, grant them admin privileges // Note: the server user, @grapevine:servername, is generated first if !is_guest { if let Some(admin_room) = services().admin.get_admin_room()? { - if services() - .rooms - .state_cache - .room_joined_count(&admin_room)? + if services().rooms.state_cache.room_joined_count(&admin_room)? == Some(1) { - services() - .admin - .make_user_admin(&user_id, displayname) - .await?; + services().admin.make_user_admin(&user_id, displayname).await?; - warn!("Granting {} admin privileges as the first user", user_id); + warn!( + "Granting {} admin privileges as the first user", + user_id + ); } } } @@ -302,19 +315,23 @@ pub(crate) async fn register_route( /// /// - Requires UIAA to verify user password /// - Changes the password of the sender user -/// - The password hash is calculated using argon2 with 32 character salt, the plain password is +/// - The password hash is calculated using argon2 with 32 character salt, the +/// plain password is /// not saved /// -/// If `logout_devices` is true it does the following for each device except the sender device: +/// If `logout_devices` is true it does the following for each device except the +/// sender device: /// - Invalidates access token -/// - Deletes device metadata (device ID, device display name, last seen IP, last seen timestamp) +/// - Deletes device metadata (device ID, device display name, last seen IP, +/// last seen timestamp) /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn change_password_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); let mut uiaainfo = UiaaInfo { flows: vec![AuthFlow { @@ -327,27 +344,25 @@ pub(crate) async fn change_password_route( }; if let Some(auth) = &body.auth { - let (worked, uiaainfo) = - services() - .uiaa - .try_auth(sender_user, sender_device, auth, &uiaainfo)?; + let (worked, uiaainfo) = services().uiaa.try_auth( + sender_user, + sender_device, + auth, + &uiaainfo, + )?; if !worked { return Err(Error::Uiaa(uiaainfo)); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - services() - .uiaa - .create(sender_user, sender_device, &uiaainfo, &json)?; + services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; return Err(Error::Uiaa(uiaainfo)); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } - services() - .users - .set_password(sender_user, Some(&body.new_password))?; + services().users.set_password(sender_user, Some(&body.new_password))?; if body.logout_devices { // Logout all devices except the current one @@ -362,11 +377,9 @@ pub(crate) async fn change_password_route( } info!("User {} changed their password.", sender_user); - services() - .admin - .send_message(RoomMessageEventContent::notice_plain(format!( - "User {sender_user} changed their password." - ))); + services().admin.send_message(RoomMessageEventContent::notice_plain( + format!("User {sender_user} changed their password."), + )); Ok(change_password::v3::Response {}) } @@ -376,14 +389,17 @@ pub(crate) async fn change_password_route( /// Get `user_id` of the sender user. /// /// Note: Also works for Application Services -pub(crate) async fn whoami_route(body: Ruma) -> Result { +pub(crate) async fn whoami_route( + body: Ruma, +) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device_id = body.sender_device.as_ref().cloned(); Ok(whoami::v3::Response { user_id: sender_user.clone(), device_id, - is_guest: services().users.is_deactivated(sender_user)? && body.appservice_info.is_none(), + is_guest: services().users.is_deactivated(sender_user)? + && body.appservice_info.is_none(), }) } @@ -393,7 +409,8 @@ pub(crate) async fn whoami_route(body: Ruma) -> Result, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); let mut uiaainfo = UiaaInfo { flows: vec![AuthFlow { @@ -414,19 +432,19 @@ pub(crate) async fn deactivate_route( }; if let Some(auth) = &body.auth { - let (worked, uiaainfo) = - services() - .uiaa - .try_auth(sender_user, sender_device, auth, &uiaainfo)?; + let (worked, uiaainfo) = services().uiaa.try_auth( + sender_user, + sender_device, + auth, + &uiaainfo, + )?; if !worked { return Err(Error::Uiaa(uiaainfo)); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - services() - .uiaa - .create(sender_user, sender_device, &uiaainfo, &json)?; + services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; return Err(Error::Uiaa(uiaainfo)); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); @@ -439,11 +457,9 @@ pub(crate) async fn deactivate_route( services().users.deactivate_account(sender_user)?; info!("User {} deactivated their account.", sender_user); - services() - .admin - .send_message(RoomMessageEventContent::notice_plain(format!( - "User {sender_user} deactivated their account." - ))); + services().admin.send_message(RoomMessageEventContent::notice_plain( + format!("User {sender_user} deactivated their account."), + )); Ok(deactivate::v3::Response { id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport, @@ -458,16 +474,19 @@ pub(crate) async fn deactivate_route( pub(crate) async fn third_party_route( body: Ruma, ) -> Result { - let _sender_user = body.sender_user.as_ref().expect("user is authenticated"); + let _sender_user = + body.sender_user.as_ref().expect("user is authenticated"); Ok(get_3pids::v3::Response::new(Vec::new())) } /// # `POST /_matrix/client/v3/account/3pid/email/requestToken` /// -/// "This API should be used to request validation tokens when adding an email address to an account" +/// "This API should be used to request validation tokens when adding an email +/// address to an account" /// -/// - 403 signals that The homeserver does not allow the third party identifier as a contact option. +/// - 403 signals that The homeserver does not allow the third party identifier +/// as a contact option. pub(crate) async fn request_3pid_management_token_via_email_route( _body: Ruma, ) -> Result { @@ -479,9 +498,11 @@ pub(crate) async fn request_3pid_management_token_via_email_route( /// # `POST /_matrix/client/v3/account/3pid/msisdn/requestToken` /// -/// "This API should be used to request validation tokens when adding an phone number to an account" +/// "This API should be used to request validation tokens when adding an phone +/// number to an account" /// -/// - 403 signals that The homeserver does not allow the third party identifier as a contact option. +/// - 403 signals that The homeserver does not allow the third party identifier +/// as a contact option. pub(crate) async fn request_3pid_management_token_via_msisdn_route( _body: Ruma, ) -> Result { diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index c880bd5d..5d1a0c30 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -1,4 +1,3 @@ -use crate::{services, Error, Result, Ruma}; use rand::seq::SliceRandom; use ruma::{ api::{ @@ -12,6 +11,8 @@ use ruma::{ OwnedRoomAliasId, }; +use crate::{services, Error, Result, Ruma}; + /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}` /// /// Creates a new room alias on this server. @@ -32,30 +33,18 @@ pub(crate) async fn create_alias_route( "Room alias is not in namespace.", )); } - } else if services() - .appservice - .is_exclusive_alias(&body.room_alias) - .await - { + } else if services().appservice.is_exclusive_alias(&body.room_alias).await { return Err(Error::BadRequest( ErrorKind::Exclusive, "Room alias reserved by appservice.", )); } - if services() - .rooms - .alias - .resolve_local_alias(&body.room_alias)? - .is_some() - { + if services().rooms.alias.resolve_local_alias(&body.room_alias)?.is_some() { return Err(Error::Conflict("Alias already exists.")); } - services() - .rooms - .alias - .set_alias(&body.room_alias, &body.room_id)?; + services().rooms.alias.set_alias(&body.room_alias, &body.room_id)?; Ok(create_alias::v3::Response::new()) } @@ -83,11 +72,7 @@ pub(crate) async fn delete_alias_route( "Room alias is not in namespace.", )); } - } else if services() - .appservice - .is_exclusive_alias(&body.room_alias) - .await - { + } else if services().appservice.is_exclusive_alias(&body.room_alias).await { return Err(Error::BadRequest( ErrorKind::Exclusive, "Room alias reserved by appservice.", @@ -157,7 +142,10 @@ pub(crate) async fn get_alias_helper( .alias .resolve_local_alias(&room_alias)? .ok_or_else(|| { - Error::bad_config("Appservice lied to us. Room does not exist.") + Error::bad_config( + "Appservice lied to us. Room does not \ + exist.", + ) })?, ); break; diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index daab262d..4cfffcc1 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -1,15 +1,16 @@ -use crate::{services, Error, Result, Ruma}; use ruma::api::client::{ backup::{ add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version, delete_backup_keys, delete_backup_keys_for_room, - delete_backup_keys_for_session, delete_backup_version, get_backup_info, get_backup_keys, - get_backup_keys_for_room, get_backup_keys_for_session, get_latest_backup_info, - update_backup_version, + delete_backup_keys_for_session, delete_backup_version, get_backup_info, + get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session, + get_latest_backup_info, update_backup_version, }, error::ErrorKind, }; +use crate::{services, Error, Result, Ruma}; + /// # `POST /_matrix/client/r0/room_keys/version` /// /// Creates a new backup. @@ -17,23 +18,27 @@ pub(crate) async fn create_backup_version_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let version = services() - .key_backups - .create_backup(sender_user, &body.algorithm)?; + let version = + services().key_backups.create_backup(sender_user, &body.algorithm)?; - Ok(create_backup_version::v3::Response { version }) + Ok(create_backup_version::v3::Response { + version, + }) } /// # `PUT /_matrix/client/r0/room_keys/version/{version}` /// -/// Update information about an existing backup. Only `auth_data` can be modified. +/// Update information about an existing backup. Only `auth_data` can be +/// modified. pub(crate) async fn update_backup_version_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .key_backups - .update_backup(sender_user, &body.version, &body.algorithm)?; + services().key_backups.update_backup( + sender_user, + &body.version, + &body.algorithm, + )?; Ok(update_backup_version::v3::Response {}) } @@ -88,9 +93,7 @@ pub(crate) async fn get_backup_info_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, version: body.version.clone(), }) } @@ -99,15 +102,14 @@ pub(crate) async fn get_backup_info_route( /// /// Delete an existing key backup. /// -/// - Deletes both information about the backup, as well as all key data related to the backup +/// - Deletes both information about the backup, as well as all key data related +/// to the backup pub(crate) async fn delete_backup_version_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .key_backups - .delete_backup(sender_user, &body.version)?; + services().key_backups.delete_backup(sender_user, &body.version)?; Ok(delete_backup_version::v3::Response {}) } @@ -116,7 +118,8 @@ pub(crate) async fn delete_backup_version_route( /// /// Add the received backup keys to the database. /// -/// - Only manipulating the most recently created version of the backup is allowed +/// - Only manipulating the most recently created version of the backup is +/// allowed /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_route( @@ -132,7 +135,8 @@ pub(crate) async fn add_backup_keys_route( { return Err(Error::BadRequest( ErrorKind::InvalidParam, - "You may only manipulate the most recently created version of the backup.", + "You may only manipulate the most recently created version of the \ + backup.", )); } @@ -154,9 +158,7 @@ pub(crate) async fn add_backup_keys_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, }) } @@ -164,7 +166,8 @@ pub(crate) async fn add_backup_keys_route( /// /// Add the received backup keys to the database. /// -/// - Only manipulating the most recently created version of the backup is allowed +/// - Only manipulating the most recently created version of the backup is +/// allowed /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_room_route( @@ -180,7 +183,8 @@ pub(crate) async fn add_backup_keys_for_room_route( { return Err(Error::BadRequest( ErrorKind::InvalidParam, - "You may only manipulate the most recently created version of the backup.", + "You may only manipulate the most recently created version of the \ + backup.", )); } @@ -200,9 +204,7 @@ pub(crate) async fn add_backup_keys_for_room_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, }) } @@ -210,7 +212,8 @@ pub(crate) async fn add_backup_keys_for_room_route( /// /// Add the received backup key to the database. /// -/// - Only manipulating the most recently created version of the backup is allowed +/// - Only manipulating the most recently created version of the backup is +/// allowed /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_session_route( @@ -226,7 +229,8 @@ pub(crate) async fn add_backup_keys_for_session_route( { return Err(Error::BadRequest( ErrorKind::InvalidParam, - "You may only manipulate the most recently created version of the backup.", + "You may only manipulate the most recently created version of the \ + backup.", )); } @@ -244,9 +248,7 @@ pub(crate) async fn add_backup_keys_for_session_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, }) } @@ -260,7 +262,9 @@ pub(crate) async fn get_backup_keys_route( let rooms = services().key_backups.get_all(sender_user, &body.version)?; - Ok(get_backup_keys::v3::Response { rooms }) + Ok(get_backup_keys::v3::Response { + rooms, + }) } /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}` @@ -271,11 +275,15 @@ pub(crate) async fn get_backup_keys_for_room_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sessions = services() - .key_backups - .get_room(sender_user, &body.version, &body.room_id)?; + let sessions = services().key_backups.get_room( + sender_user, + &body.version, + &body.room_id, + )?; - Ok(get_backup_keys_for_room::v3::Response { sessions }) + Ok(get_backup_keys_for_room::v3::Response { + sessions, + }) } /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}` @@ -288,13 +296,20 @@ pub(crate) async fn get_backup_keys_for_session_route( let key_data = services() .key_backups - .get_session(sender_user, &body.version, &body.room_id, &body.session_id)? + .get_session( + sender_user, + &body.version, + &body.room_id, + &body.session_id, + )? .ok_or(Error::BadRequest( ErrorKind::NotFound, "Backup key not found for this user's session.", ))?; - Ok(get_backup_keys_for_session::v3::Response { key_data }) + Ok(get_backup_keys_for_session::v3::Response { + key_data, + }) } /// # `DELETE /_matrix/client/r0/room_keys/keys` @@ -305,9 +320,7 @@ pub(crate) async fn delete_backup_keys_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .key_backups - .delete_all_keys(sender_user, &body.version)?; + services().key_backups.delete_all_keys(sender_user, &body.version)?; Ok(delete_backup_keys::v3::Response { count: services() @@ -315,9 +328,7 @@ pub(crate) async fn delete_backup_keys_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, }) } @@ -329,9 +340,11 @@ pub(crate) async fn delete_backup_keys_for_room_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .key_backups - .delete_room_keys(sender_user, &body.version, &body.room_id)?; + services().key_backups.delete_room_keys( + sender_user, + &body.version, + &body.room_id, + )?; Ok(delete_backup_keys_for_room::v3::Response { count: services() @@ -339,9 +352,7 @@ pub(crate) async fn delete_backup_keys_for_room_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, }) } @@ -366,8 +377,6 @@ pub(crate) async fn delete_backup_keys_for_session_route( .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), - etag: services() - .key_backups - .get_etag(sender_user, &body.version)?, + etag: services().key_backups.get_etag(sender_user, &body.version)?, }) } diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index f248d1b9..61152bf0 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -1,12 +1,15 @@ -use crate::{services, Result, Ruma}; +use std::collections::BTreeMap; + use ruma::api::client::discovery::get_capabilities::{ self, Capabilities, RoomVersionStability, RoomVersionsCapability, }; -use std::collections::BTreeMap; + +use crate::{services, Result, Ruma}; /// # `GET /_matrix/client/r0/capabilities` /// -/// Get information on the supported feature set and other relevent capabilities of this server. +/// Get information on the supported feature set and other relevent capabilities +/// of this server. pub(crate) async fn get_capabilities_route( _body: Ruma, ) -> Result { @@ -24,5 +27,7 @@ pub(crate) async fn get_capabilities_route( available, }; - Ok(get_capabilities::v3::Response { capabilities }) + Ok(get_capabilities::v3::Response { + capabilities, + }) } diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index c650de1f..d4265078 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -1,18 +1,21 @@ -use crate::{services, Error, Result, Ruma}; use ruma::{ api::client::{ config::{ - get_global_account_data, get_room_account_data, set_global_account_data, - set_room_account_data, + get_global_account_data, get_room_account_data, + set_global_account_data, set_room_account_data, }, error::ErrorKind, }, - events::{AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent}, + events::{ + AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent, + }, serde::Raw, }; use serde::Deserialize; use serde_json::{json, value::RawValue as RawJsonValue}; +use crate::{services, Error, Result, Ruma}; + /// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}` /// /// Sets some account data for the sender user. @@ -22,7 +25,9 @@ pub(crate) async fn set_global_account_data_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let data: serde_json::Value = serde_json::from_str(body.data.json().get()) - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?; + .map_err(|_| { + Error::BadRequest(ErrorKind::BadJson, "Data is invalid.") + })?; let event_type = body.event_type.to_string(); @@ -48,7 +53,9 @@ pub(crate) async fn set_room_account_data_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let data: serde_json::Value = serde_json::from_str(body.data.json().get()) - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?; + .map_err(|_| { + Error::BadRequest(ErrorKind::BadJson, "Data is invalid.") + })?; let event_type = body.event_type.to_string(); @@ -78,11 +85,16 @@ pub(crate) async fn get_global_account_data_route( .get(None, sender_user, body.event_type.to_string().into())? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - let account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; + let account_data = + serde_json::from_str::(event.get()) + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })? + .content; - Ok(get_global_account_data::v3::Response { account_data }) + Ok(get_global_account_data::v3::Response { + account_data, + }) } /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}` @@ -98,11 +110,16 @@ pub(crate) async fn get_room_account_data_route( .get(Some(&body.room_id), sender_user, body.event_type.clone())? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - let account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; + let account_data = + serde_json::from_str::(event.get()) + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })? + .content; - Ok(get_room_account_data::v3::Response { account_data }) + Ok(get_room_account_data::v3::Response { + account_data, + }) } #[derive(Deserialize)] diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 7642ebe3..0ada0bbc 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -1,60 +1,57 @@ -use crate::{services, Error, Result, Ruma}; +use std::collections::HashSet; + use ruma::{ - api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions}, + api::client::{ + context::get_context, error::ErrorKind, filter::LazyLoadOptions, + }, events::StateEventType, uint, }; -use std::collections::HashSet; use tracing::error; +use crate::{services, Error, Result, Ruma}; + /// # `GET /_matrix/client/r0/rooms/{roomId}/context` /// /// Allows loading room history around an event. /// -/// - Only works if the user is joined (TODO: always allow, but only show events if the user was +/// - Only works if the user is joined (TODO: always allow, but only show events +/// if the user was /// joined, depending on `history_visibility`) #[allow(clippy::too_many_lines)] pub(crate) async fn get_context_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); - let (lazy_load_enabled, lazy_load_send_redundant) = match &body.filter.lazy_load_options { - LazyLoadOptions::Enabled { - include_redundant_members, - } => (true, *include_redundant_members), - LazyLoadOptions::Disabled => (false, false), - }; + let (lazy_load_enabled, lazy_load_send_redundant) = + match &body.filter.lazy_load_options { + LazyLoadOptions::Enabled { + include_redundant_members, + } => (true, *include_redundant_members), + LazyLoadOptions::Disabled => (false, false), + }; let mut lazy_loaded = HashSet::new(); - let base_token = services() - .rooms - .timeline - .get_pdu_count(&body.event_id)? - .ok_or(Error::BadRequest( - ErrorKind::NotFound, - "Base event id not found.", - ))?; + let base_token = + services().rooms.timeline.get_pdu_count(&body.event_id)?.ok_or( + Error::BadRequest(ErrorKind::NotFound, "Base event id not found."), + )?; - let base_event = - services() - .rooms - .timeline - .get_pdu(&body.event_id)? - .ok_or(Error::BadRequest( - ErrorKind::NotFound, - "Base event not found.", - ))?; + let base_event = services().rooms.timeline.get_pdu(&body.event_id)?.ok_or( + Error::BadRequest(ErrorKind::NotFound, "Base event not found."), + )?; let room_id = base_event.room_id.clone(); - if !services() - .rooms - .state_accessor - .user_can_see_event(sender_user, &room_id, &body.event_id)? - { + if !services().rooms.state_accessor.user_can_see_event( + sender_user, + &room_id, + &body.event_id, + )? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this event.", @@ -72,8 +69,8 @@ pub(crate) async fn get_context_route( } // Use limit with maximum 100 - let half_limit = - usize::try_from(body.limit.min(uint!(100)) / uint!(2)).expect("0-50 should fit in usize"); + let half_limit = usize::try_from(body.limit.min(uint!(100)) / uint!(2)) + .expect("0-50 should fit in usize"); let base_event = base_event.to_room_event(); @@ -108,10 +105,8 @@ pub(crate) async fn get_context_route( .last() .map_or_else(|| base_token.stringify(), |(count, _)| count.stringify()); - let events_before: Vec<_> = events_before - .into_iter() - .map(|(_, pdu)| pdu.to_room_event()) - .collect(); + let events_before: Vec<_> = + events_before.into_iter().map(|(_, pdu)| pdu.to_room_event()).collect(); let events_after: Vec<_> = services() .rooms @@ -140,41 +135,33 @@ pub(crate) async fn get_context_route( } } - let shortstatehash = match services().rooms.state_accessor.pdu_shortstatehash( - events_after - .last() - .map_or(&*body.event_id, |(_, e)| &*e.event_id), - )? { - Some(s) => s, - None => services() - .rooms - .state - .get_room_shortstatehash(&room_id)? - .expect("All rooms have state"), - }; + let shortstatehash = + match services().rooms.state_accessor.pdu_shortstatehash( + events_after.last().map_or(&*body.event_id, |(_, e)| &*e.event_id), + )? { + Some(s) => s, + None => services() + .rooms + .state + .get_room_shortstatehash(&room_id)? + .expect("All rooms have state"), + }; - let state_ids = services() - .rooms - .state_accessor - .state_full_ids(shortstatehash) - .await?; + let state_ids = + services().rooms.state_accessor.state_full_ids(shortstatehash).await?; let end_token = events_after .last() .map_or_else(|| base_token.stringify(), |(count, _)| count.stringify()); - let events_after: Vec<_> = events_after - .into_iter() - .map(|(_, pdu)| pdu.to_room_event()) - .collect(); + let events_after: Vec<_> = + events_after.into_iter().map(|(_, pdu)| pdu.to_room_event()).collect(); let mut state = Vec::new(); for (shortstatekey, id) in state_ids { - let (event_type, state_key) = services() - .rooms - .short - .get_statekey_from_short(shortstatekey)?; + let (event_type, state_key) = + services().rooms.short.get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 0db1e833..59ae4710 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -1,11 +1,14 @@ -use crate::{services, utils, Error, Result, Ruma}; use ruma::api::client::{ - device::{self, delete_device, delete_devices, get_device, get_devices, update_device}, + device::{ + self, delete_device, delete_devices, get_device, get_devices, + update_device, + }, error::ErrorKind, uiaa::{AuthFlow, AuthType, UiaaInfo}, }; use super::SESSION_ID_LENGTH; +use crate::{services, utils, Error, Result, Ruma}; /// # `GET /_matrix/client/r0/devices` /// @@ -21,7 +24,9 @@ pub(crate) async fn get_devices_route( .filter_map(Result::ok) .collect(); - Ok(get_devices::v3::Response { devices }) + Ok(get_devices::v3::Response { + devices, + }) } /// # `GET /_matrix/client/r0/devices/{deviceId}` @@ -37,7 +42,9 @@ pub(crate) async fn get_device_route( .get_device_metadata(sender_user, &body.body.device_id)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?; - Ok(get_device::v3::Response { device }) + Ok(get_device::v3::Response { + device, + }) } /// # `PUT /_matrix/client/r0/devices/{deviceId}` @@ -55,9 +62,11 @@ pub(crate) async fn update_device_route( device.display_name = body.display_name.clone(); - services() - .users - .update_device_metadata(sender_user, &body.device_id, &device)?; + services().users.update_device_metadata( + sender_user, + &body.device_id, + &device, + )?; Ok(update_device::v3::Response {}) } @@ -68,14 +77,16 @@ pub(crate) async fn update_device_route( /// /// - Requires UIAA to verify user password /// - Invalidates access token -/// - Deletes device metadata (device id, device display name, last seen ip, last seen ts) +/// - Deletes device metadata (device id, device display name, last seen ip, +/// last seen ts) /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn delete_device_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); // UIAA let mut uiaainfo = UiaaInfo { @@ -89,27 +100,25 @@ pub(crate) async fn delete_device_route( }; if let Some(auth) = &body.auth { - let (worked, uiaainfo) = - services() - .uiaa - .try_auth(sender_user, sender_device, auth, &uiaainfo)?; + let (worked, uiaainfo) = services().uiaa.try_auth( + sender_user, + sender_device, + auth, + &uiaainfo, + )?; if !worked { return Err(Error::Uiaa(uiaainfo)); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - services() - .uiaa - .create(sender_user, sender_device, &uiaainfo, &json)?; + services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; return Err(Error::Uiaa(uiaainfo)); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } - services() - .users - .remove_device(sender_user, &body.device_id)?; + services().users.remove_device(sender_user, &body.device_id)?; Ok(delete_device::v3::Response {}) } @@ -122,14 +131,16 @@ pub(crate) async fn delete_device_route( /// /// For each device: /// - Invalidates access token -/// - Deletes device metadata (device id, device display name, last seen ip, last seen ts) +/// - Deletes device metadata (device id, device display name, last seen ip, +/// last seen ts) /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn delete_devices_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); // UIAA let mut uiaainfo = UiaaInfo { @@ -143,19 +154,19 @@ pub(crate) async fn delete_devices_route( }; if let Some(auth) = &body.auth { - let (worked, uiaainfo) = - services() - .uiaa - .try_auth(sender_user, sender_device, auth, &uiaainfo)?; + let (worked, uiaainfo) = services().uiaa.try_auth( + sender_user, + sender_device, + auth, + &uiaainfo, + )?; if !worked { return Err(Error::Uiaa(uiaainfo)); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - services() - .uiaa - .create(sender_user, sender_device, &uiaainfo, &json)?; + services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; return Err(Error::Uiaa(uiaainfo)); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 1e0e145f..6776818d 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -1,10 +1,9 @@ -use crate::{services, Error, Result, Ruma}; use ruma::{ api::{ client::{ directory::{ - get_public_rooms, get_public_rooms_filtered, get_room_visibility, - set_room_visibility, + get_public_rooms, get_public_rooms_filtered, + get_room_visibility, set_room_visibility, }, error::ErrorKind, room, @@ -18,7 +17,9 @@ use ruma::{ canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, - history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, + history_visibility::{ + HistoryVisibility, RoomHistoryVisibilityEventContent, + }, join_rules::{JoinRule, RoomJoinRulesEventContent}, topic::RoomTopicEventContent, }, @@ -28,6 +29,8 @@ use ruma::{ }; use tracing::{error, info, warn}; +use crate::{services, Error, Result, Ruma}; + /// # `POST /_matrix/client/r0/publicRooms` /// /// Lists the public rooms on this server. @@ -91,7 +94,9 @@ pub(crate) async fn set_room_visibility_route( services().rooms.directory.set_public(&body.room_id)?; info!("{} made {} public", sender_user, body.room_id); } - room::Visibility::Private => services().rooms.directory.set_not_public(&body.room_id)?, + room::Visibility::Private => { + services().rooms.directory.set_not_public(&body.room_id)?; + } _ => { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -115,7 +120,11 @@ pub(crate) async fn get_room_visibility_route( } Ok(get_room_visibility::v3::Response { - visibility: if services().rooms.directory.is_public_room(&body.room_id)? { + visibility: if services() + .rooms + .directory + .is_public_room(&body.room_id)? + { room::Visibility::Public } else { room::Visibility::Private @@ -131,8 +140,8 @@ pub(crate) async fn get_public_rooms_filtered_helper( filter: &Filter, _network: &RoomNetwork, ) -> Result { - if let Some(other_server) = - server.filter(|server| *server != services().globals.server_name().as_str()) + if let Some(other_server) = server + .filter(|server| *server != services().globals.server_name().as_str()) { let response = services() .sending @@ -174,10 +183,9 @@ pub(crate) async fn get_public_rooms_filtered_helper( } }; - num_since = characters - .collect::() - .parse() - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token."))?; + num_since = characters.collect::().parse().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token.") + })?; if backwards { num_since = num_since.saturating_sub(limit); @@ -195,12 +203,19 @@ pub(crate) async fn get_public_rooms_filtered_helper( canonical_alias: services() .rooms .state_accessor - .room_state_get(&room_id, &StateEventType::RoomCanonicalAlias, "")? + .room_state_get( + &room_id, + &StateEventType::RoomCanonicalAlias, + "", + )? .map_or(Ok(None), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomCanonicalAliasEventContent| c.alias) .map_err(|_| { - Error::bad_database("Invalid canonical alias event in database.") + Error::bad_database( + "Invalid canonical alias event in \ + database.", + ) }) })?, name: services().rooms.state_accessor.get_name(&room_id)?, @@ -222,36 +237,55 @@ pub(crate) async fn get_public_rooms_filtered_helper( serde_json::from_str(s.content.get()) .map(|c: RoomTopicEventContent| Some(c.topic)) .map_err(|_| { - error!("Invalid room topic event in database for room {}", room_id); - Error::bad_database("Invalid room topic event in database.") + error!( + "Invalid room topic event in database for \ + room {}", + room_id + ); + Error::bad_database( + "Invalid room topic event in database.", + ) }) })?, world_readable: services() .rooms .state_accessor - .room_state_get(&room_id, &StateEventType::RoomHistoryVisibility, "")? + .room_state_get( + &room_id, + &StateEventType::RoomHistoryVisibility, + "", + )? .map_or(Ok(false), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomHistoryVisibilityEventContent| { - c.history_visibility == HistoryVisibility::WorldReadable + c.history_visibility + == HistoryVisibility::WorldReadable }) .map_err(|_| { Error::bad_database( - "Invalid room history visibility event in database.", + "Invalid room history visibility event in \ + database.", ) }) })?, guest_can_join: services() .rooms .state_accessor - .room_state_get(&room_id, &StateEventType::RoomGuestAccess, "")? + .room_state_get( + &room_id, + &StateEventType::RoomGuestAccess, + "", + )? .map_or(Ok(false), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomGuestAccessEventContent| { c.guest_access == GuestAccess::CanJoin }) .map_err(|_| { - Error::bad_database("Invalid room guest access event in database.") + Error::bad_database( + "Invalid room guest access event in \ + database.", + ) }) })?, avatar_url: services() @@ -262,7 +296,9 @@ pub(crate) async fn get_public_rooms_filtered_helper( serde_json::from_str(s.content.get()) .map(|c: RoomAvatarEventContent| c.url) .map_err(|_| { - Error::bad_database("Invalid room avatar event in database.") + Error::bad_database( + "Invalid room avatar event in database.", + ) }) }) .transpose()? @@ -270,33 +306,59 @@ pub(crate) async fn get_public_rooms_filtered_helper( join_rule: services() .rooms .state_accessor - .room_state_get(&room_id, &StateEventType::RoomJoinRules, "")? + .room_state_get( + &room_id, + &StateEventType::RoomJoinRules, + "", + )? .map(|s| { serde_json::from_str(s.content.get()) - .map(|c: RoomJoinRulesEventContent| match c.join_rule { - JoinRule::Public => Some(PublicRoomJoinRule::Public), - JoinRule::Knock => Some(PublicRoomJoinRule::Knock), - _ => None, + .map(|c: RoomJoinRulesEventContent| { + match c.join_rule { + JoinRule::Public => { + Some(PublicRoomJoinRule::Public) + } + JoinRule::Knock => { + Some(PublicRoomJoinRule::Knock) + } + _ => None, + } }) .map_err(|e| { - error!("Invalid room join rule event in database: {}", e); - Error::BadDatabase("Invalid room join rule event in database.") + error!( + "Invalid room join rule event in \ + database: {}", + e + ); + Error::BadDatabase( + "Invalid room join rule event in database.", + ) }) }) .transpose()? .flatten() - .ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?, + .ok_or_else(|| { + Error::bad_database( + "Missing room join rule event for room.", + ) + })?, room_type: services() .rooms .state_accessor .room_state_get(&room_id, &StateEventType::RoomCreate, "")? .map(|s| { - serde_json::from_str::(s.content.get()).map_err( - |e| { - error!("Invalid room create event in database: {}", e); - Error::BadDatabase("Invalid room create event in database.") - }, + serde_json::from_str::( + s.content.get(), ) + .map_err(|e| { + error!( + "Invalid room create event in database: {}", + e + ); + Error::BadDatabase( + "Invalid room create event in database.", + ) + }) }) .transpose()? .and_then(|e| e.room_type), @@ -306,10 +368,8 @@ pub(crate) async fn get_public_rooms_filtered_helper( }) .filter_map(Result::<_>::ok) .filter(|chunk| { - if let Some(query) = filter - .generic_search_term - .as_ref() - .map(|q| q.to_lowercase()) + if let Some(query) = + filter.generic_search_term.as_ref().map(|q| q.to_lowercase()) { if let Some(name) = &chunk.name { if name.as_str().to_lowercase().contains(&query) { @@ -324,7 +384,8 @@ pub(crate) async fn get_public_rooms_filtered_helper( } if let Some(canonical_alias) = &chunk.canonical_alias { - if canonical_alias.as_str().to_lowercase().contains(&query) { + if canonical_alias.as_str().to_lowercase().contains(&query) + { return true; } } @@ -339,7 +400,8 @@ pub(crate) async fn get_public_rooms_filtered_helper( all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members)); - let total_room_count_estimate = all_rooms.len().try_into().unwrap_or(UInt::MAX); + let total_room_count_estimate = + all_rooms.len().try_into().unwrap_or(UInt::MAX); let chunk: Vec<_> = all_rooms .into_iter() @@ -353,11 +415,12 @@ pub(crate) async fn get_public_rooms_filtered_helper( Some(format!("p{num_since}")) }; - let next_batch = if chunk.len() < limit.try_into().expect("UInt should fit in usize") { - None - } else { - Some(format!("n{}", num_since + limit)) - }; + let next_batch = + if chunk.len() < limit.try_into().expect("UInt should fit in usize") { + None + } else { + Some(format!("n{}", num_since + limit)) + }; Ok(get_public_rooms_filtered::v3::Response { chunk, diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index 0ceefe40..fc2f2a1c 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -1,9 +1,10 @@ -use crate::{services, Error, Result, Ruma}; use ruma::api::client::{ error::ErrorKind, filter::{create_filter, get_filter}, }; +use crate::{services, Error, Result, Ruma}; + /// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}` /// /// Loads a filter that was previously created. @@ -13,8 +14,13 @@ pub(crate) async fn get_filter_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? else { - return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found.")); + let Some(filter) = + services().users.get_filter(sender_user, &body.filter_id)? + else { + return Err(Error::BadRequest( + ErrorKind::NotFound, + "Filter not found.", + )); }; Ok(get_filter::v3::Response::new(filter)) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 06c06dcb..4e68e81a 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -1,13 +1,16 @@ -use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Result, Ruma}; +use std::{ + collections::{hash_map, BTreeMap, HashMap, HashSet}, + time::{Duration, Instant}, +}; + use futures_util::{stream::FuturesUnordered, StreamExt}; use ruma::{ api::{ client::{ error::ErrorKind, keys::{ - claim_keys, get_key_changes, get_keys, upload_keys, upload_signatures, - upload_signing_keys, + claim_keys, get_key_changes, get_keys, upload_keys, + upload_signatures, upload_signing_keys, }, uiaa::{AuthFlow, AuthType, UiaaInfo}, }, @@ -17,28 +20,32 @@ use ruma::{ DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId, }; use serde_json::json; -use std::{ - collections::{hash_map, BTreeMap, HashMap, HashSet}, - time::{Duration, Instant}, -}; use tracing::debug; +use super::SESSION_ID_LENGTH; +use crate::{services, utils, Error, Result, Ruma}; + /// # `POST /_matrix/client/r0/keys/upload` /// /// Publish end-to-end encryption keys for the sender device. /// /// - Adds one time keys -/// - If there are no device keys yet: Adds device keys (TODO: merge with existing keys?) +/// - If there are no device keys yet: Adds device keys (TODO: merge with +/// existing keys?) pub(crate) async fn upload_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); for (key_key, key_value) in &body.one_time_keys { - services() - .users - .add_one_time_key(sender_user, sender_device, key_key, key_value)?; + services().users.add_one_time_key( + sender_user, + sender_device, + key_key, + key_value, + )?; } if let Some(device_keys) = &body.device_keys { @@ -49,9 +56,11 @@ pub(crate) async fn upload_keys_route( .get_device_keys(sender_user, sender_device)? .is_none() { - services() - .users - .add_device_keys(sender_user, sender_device, device_keys)?; + services().users.add_device_keys( + sender_user, + sender_device, + device_keys, + )?; } } @@ -68,14 +77,17 @@ pub(crate) async fn upload_keys_route( /// /// - Always fetches users from other servers over federation /// - Gets master keys, self-signing keys, user signing keys and device keys. -/// - The master and self-signing keys contain signatures that the user is allowed to see +/// - The master and self-signing keys contain signatures that the user is +/// allowed to see pub(crate) async fn get_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let response = - get_keys_helper(Some(sender_user), &body.device_keys, |u| u == sender_user).await?; + let response = get_keys_helper(Some(sender_user), &body.device_keys, |u| { + u == sender_user + }) + .await?; Ok(response) } @@ -100,7 +112,8 @@ pub(crate) async fn upload_signing_keys_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); // UIAA let mut uiaainfo = UiaaInfo { @@ -114,19 +127,19 @@ pub(crate) async fn upload_signing_keys_route( }; if let Some(auth) = &body.auth { - let (worked, uiaainfo) = - services() - .uiaa - .try_auth(sender_user, sender_device, auth, &uiaainfo)?; + let (worked, uiaainfo) = services().uiaa.try_auth( + sender_user, + sender_device, + auth, + &uiaainfo, + )?; if !worked { return Err(Error::Uiaa(uiaainfo)); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - services() - .uiaa - .create(sender_user, sender_device, &uiaainfo, &json)?; + services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; return Err(Error::Uiaa(uiaainfo)); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); @@ -156,8 +169,9 @@ pub(crate) async fn upload_signatures_route( for (user_id, keys) in &body.signed_keys { for (key_id, key) in keys { - let key = serde_json::to_value(key) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid key JSON"))?; + let key = serde_json::to_value(key).map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Invalid key JSON") + })?; for signature in key .get("signatures") @@ -189,9 +203,12 @@ pub(crate) async fn upload_signatures_route( ))? .to_owned(), ); - services() - .users - .sign_key(user_id, key_id, signature, sender_user)?; + services().users.sign_key( + user_id, + key_id, + signature, + sender_user, + )?; } } } @@ -204,7 +221,8 @@ pub(crate) async fn upload_signatures_route( /// # `POST /_matrix/client/r0/keys/changes` /// -/// Gets a list of users who have updated their device identity keys since the previous sync token. +/// Gets a list of users who have updated their device identity keys since the +/// previous sync token. /// /// - TODO: left users pub(crate) async fn get_key_changes_route( @@ -219,14 +237,15 @@ pub(crate) async fn get_key_changes_route( .users .keys_changed( sender_user.as_str(), - body.from - .parse() - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`."))?, - Some( - body.to - .parse() - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`."))?, - ), + body.from.parse().map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid `from`.", + ) + })?, + Some(body.to.parse().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`.") + })?), ) .filter_map(Result::ok), ); @@ -243,10 +262,16 @@ pub(crate) async fn get_key_changes_route( .keys_changed( room_id.as_ref(), body.from.parse().map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`.") + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid `from`.", + ) })?, Some(body.to.parse().map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`.") + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid `to`.", + ) })?), ) .filter_map(Result::ok), @@ -287,16 +312,24 @@ pub(crate) async fn get_keys_helper bool>( let mut container = BTreeMap::new(); for device_id in services().users.all_device_ids(user_id) { let device_id = device_id?; - if let Some(mut keys) = services().users.get_device_keys(user_id, &device_id)? { + if let Some(mut keys) = + services().users.get_device_keys(user_id, &device_id)? + { let metadata = services() .users .get_device_metadata(user_id, &device_id)? .ok_or_else(|| { - Error::bad_database("all_device_keys contained nonexistent device.") + Error::bad_database( + "all_device_keys contained nonexistent device.", + ) })?; add_unsigned_device_display_name(&mut keys, metadata) - .map_err(|_| Error::bad_database("invalid device keys in database"))?; + .map_err(|_| { + Error::bad_database( + "invalid device keys in database", + ) + })?; container.insert(device_id, keys); } } @@ -304,7 +337,9 @@ pub(crate) async fn get_keys_helper bool>( } else { for device_id in device_ids { let mut container = BTreeMap::new(); - if let Some(mut keys) = services().users.get_device_keys(user_id, device_id)? { + if let Some(mut keys) = + services().users.get_device_keys(user_id, device_id)? + { let metadata = services() .users .get_device_metadata(user_id, device_id)? @@ -314,29 +349,35 @@ pub(crate) async fn get_keys_helper bool>( ))?; add_unsigned_device_display_name(&mut keys, metadata) - .map_err(|_| Error::bad_database("invalid device keys in database"))?; + .map_err(|_| { + Error::bad_database( + "invalid device keys in database", + ) + })?; container.insert(device_id.to_owned(), keys); } device_keys.insert(user_id.to_owned(), container); } } - if let Some(master_key) = - services() - .users - .get_master_key(sender_user, user_id, &allowed_signatures)? - { + if let Some(master_key) = services().users.get_master_key( + sender_user, + user_id, + &allowed_signatures, + )? { master_keys.insert(user_id.to_owned(), master_key); } - if let Some(self_signing_key) = - services() - .users - .get_self_signing_key(sender_user, user_id, &allowed_signatures)? - { + if let Some(self_signing_key) = services().users.get_self_signing_key( + sender_user, + user_id, + &allowed_signatures, + )? { self_signing_keys.insert(user_id.to_owned(), self_signing_key); } if Some(user_id) == sender_user { - if let Some(user_signing_key) = services().users.get_user_signing_key(user_id)? { + if let Some(user_signing_key) = + services().users.get_user_signing_key(user_id)? + { user_signing_keys.insert(user_id.to_owned(), user_signing_key); } } @@ -345,17 +386,13 @@ pub(crate) async fn get_keys_helper bool>( let mut failures = BTreeMap::new(); let back_off = |id| async { - match services() - .globals - .bad_query_ratelimiter - .write() - .await - .entry(id) - { + match services().globals.bad_query_ratelimiter.write().await.entry(id) { hash_map::Entry::Vacant(e) => { e.insert((Instant::now(), 1)); } - hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1), + hash_map::Entry::Occupied(mut e) => { + *e.get_mut() = (Instant::now(), e.get().1 + 1); + } } }; @@ -370,7 +407,8 @@ pub(crate) async fn get_keys_helper bool>( .get(server) { // Exponential backoff - let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries); + let mut min_elapsed_duration = + Duration::from_secs(30) * (*tries) * (*tries); if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { min_elapsed_duration = Duration::from_secs(60 * 60 * 24); } @@ -379,7 +417,9 @@ pub(crate) async fn get_keys_helper bool>( debug!("Backing off query from {:?}", server); return ( server, - Err(Error::BadServerResponse("bad query, still backing off")), + Err(Error::BadServerResponse( + "bad query, still backing off", + )), ); } } @@ -417,15 +457,19 @@ pub(crate) async fn get_keys_helper bool>( &user, &allowed_signatures, )? { - let (_, our_master_key) = - services().users.parse_master_key(&user, &our_master_key)?; + let (_, our_master_key) = services() + .users + .parse_master_key(&user, &our_master_key)?; master_key.signatures.extend(our_master_key.signatures); } - let json = serde_json::to_value(master_key).expect("to_value always works"); - let raw = serde_json::from_value(json).expect("Raw::from_value always works"); + let json = serde_json::to_value(master_key) + .expect("to_value always works"); + let raw = serde_json::from_value(json) + .expect("Raw::from_value always works"); services().users.add_cross_signing_keys( &user, &raw, &None, &None, - // Dont notify. A notification would trigger another key request resulting in an endless loop + // Dont notify. A notification would trigger another key + // request resulting in an endless loop false, )?; master_keys.insert(user, raw); @@ -454,11 +498,13 @@ fn add_unsigned_device_display_name( metadata: ruma::api::client::device::Device, ) -> serde_json::Result<()> { if let Some(display_name) = metadata.display_name { - let mut object = keys.deserialize_as::>()?; + let mut object = keys + .deserialize_as::>()?; let unsigned = object.entry("unsigned").or_insert_with(|| json!({})); if let serde_json::Value::Object(unsigned_object) = unsigned { - unsigned_object.insert("device_display_name".to_owned(), display_name.into()); + unsigned_object + .insert("device_display_name".to_owned(), display_name.into()); } *keys = Raw::from_json(serde_json::value::to_raw_value(&object)?); @@ -468,7 +514,10 @@ fn add_unsigned_device_display_name( } pub(crate) async fn claim_keys_helper( - one_time_keys_input: &BTreeMap>, + one_time_keys_input: &BTreeMap< + OwnedUserId, + BTreeMap, + >, ) -> Result { let mut one_time_keys = BTreeMap::new(); @@ -484,11 +533,11 @@ pub(crate) async fn claim_keys_helper( let mut container = BTreeMap::new(); for (device_id, key_algorithm) in map { - if let Some(one_time_keys) = - services() - .users - .take_one_time_key(user_id, device_id, key_algorithm)? - { + if let Some(one_time_keys) = services().users.take_one_time_key( + user_id, + device_id, + key_algorithm, + )? { let mut c = BTreeMap::new(); c.insert(one_time_keys.0, one_time_keys.1); container.insert(device_id.clone(), c); diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 2a0f05e7..e4b90ed3 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -1,14 +1,15 @@ use std::time::Duration; -use crate::{service::media::FileMeta, services, utils, Error, Result, Ruma}; use ruma::api::client::{ error::ErrorKind, media::{ - create_content, get_content, get_content_as_filename, get_content_thumbnail, - get_media_config, + create_content, get_content, get_content_as_filename, + get_content_thumbnail, get_media_config, }, }; +use crate::{service::media::FileMeta, services, utils, Error, Result, Ruma}; + const MXC_LENGTH: usize = 32; /// # `GET /_matrix/media/r0/config` @@ -110,9 +111,12 @@ pub(crate) async fn get_content_route( content_disposition, cross_origin_resource_policy: Some("cross-origin".to_owned()), }) - } else if &*body.server_name != services().globals.server_name() && body.allow_remote { + } else if &*body.server_name != services().globals.server_name() + && body.allow_remote + { let remote_content_response = - get_remote_content(&mxc, &body.server_name, body.media_id.clone()).await?; + get_remote_content(&mxc, &body.server_name, body.media_id.clone()) + .await?; Ok(remote_content_response) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) @@ -130,21 +134,32 @@ pub(crate) async fn get_content_as_filename_route( let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { - content_type, file, .. + content_type, + file, + .. }) = services().media.get(mxc.clone()).await? { Ok(get_content_as_filename::v3::Response { file, content_type, - content_disposition: Some(format!("inline; filename={}", body.filename)), + content_disposition: Some(format!( + "inline; filename={}", + body.filename + )), cross_origin_resource_policy: Some("cross-origin".to_owned()), }) - } else if &*body.server_name != services().globals.server_name() && body.allow_remote { + } else if &*body.server_name != services().globals.server_name() + && body.allow_remote + { let remote_content_response = - get_remote_content(&mxc, &body.server_name, body.media_id.clone()).await?; + get_remote_content(&mxc, &body.server_name, body.media_id.clone()) + .await?; Ok(get_content_as_filename::v3::Response { - content_disposition: Some(format!("inline: filename={}", body.filename)), + content_disposition: Some(format!( + "inline: filename={}", + body.filename + )), content_type: remote_content_response.content_type, file: remote_content_response.file, cross_origin_resource_policy: Some("cross-origin".to_owned()), @@ -165,17 +180,19 @@ pub(crate) async fn get_content_thumbnail_route( let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { - content_type, file, .. + content_type, + file, + .. }) = services() .media .get_thumbnail( mxc.clone(), - body.width - .try_into() - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, - body.height - .try_into() - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, + body.width.try_into().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") + })?, + body.height.try_into().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") + })?, ) .await? { @@ -184,7 +201,9 @@ pub(crate) async fn get_content_thumbnail_route( content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), }) - } else if &*body.server_name != services().globals.server_name() && body.allow_remote { + } else if &*body.server_name != services().globals.server_name() + && body.allow_remote + { let get_thumbnail_response = services() .sending .send_federation_request( diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index be3d59fa..fa044f27 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1,11 +1,18 @@ +use std::{ + collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, + sync::Arc, + time::{Duration, Instant}, +}; + use ruma::{ api::{ client::{ error::ErrorKind, 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, - unban_user, ThirdPartySigned, + 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, unban_user, + ThirdPartySigned, }, }, federation::{self, membership::create_invite}, @@ -19,31 +26,27 @@ use ruma::{ StateEventType, TimelineEventType, }, serde::Base64, - state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, - OwnedServerName, OwnedUserId, RoomId, RoomVersionId, UserId, + state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, + OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, RoomVersionId, UserId, }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; -use std::{ - collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, - sync::Arc, - time::{Duration, Instant}, -}; use tokio::sync::RwLock; use tracing::{debug, error, info, warn}; +use super::get_alias_helper; 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 +/// - 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(crate) async fn join_room_by_id_route( body: Ruma, ) -> Result { @@ -86,15 +89,19 @@ pub(crate) async fn join_room_by_id_route( /// /// 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 +/// - 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(crate) async fn join_room_by_id_or_alias_route( body: Ruma, ) -> Result { - let sender_user = body.sender_user.as_deref().expect("user is authenticated"); + let sender_user = + body.sender_user.as_deref().expect("user is authenticated"); let body = body.body; - let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) { + let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) + { Ok(room_id) => { let mut servers = body.server_name.clone(); servers.extend( @@ -104,8 +111,12 @@ pub(crate) async fn join_room_by_id_or_alias_route( .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(|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(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()), @@ -164,7 +175,10 @@ pub(crate) async fn invite_user_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let invite_user::v3::InvitationRecipient::UserId { user_id } = &body.recipient { + if let invite_user::v3::InvitationRecipient::UserId { + user_id, + } = &body.recipient + { invite_helper( sender_user, user_id, @@ -187,10 +201,8 @@ pub(crate) async fn kick_user_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Ok(true) = services() - .rooms - .state_cache - .is_left(sender_user, &body.room_id) + if let Ok(true) = + services().rooms.state_cache.is_left(sender_user, &body.room_id) { return Ok(kick_user::v3::Response {}); } @@ -233,7 +245,8 @@ pub(crate) async fn kick_user_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomMember, - content: to_raw_value(&event).expect("event is valid, we just created it"), + content: to_raw_value(&event) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(body.user_id.to_string()), redacts: None, @@ -257,10 +270,8 @@ pub(crate) async fn ban_user_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Ok(Some(membership_event)) = services() - .rooms - .state_accessor - .get_member(&body.room_id, sender_user) + if let Ok(Some(membership_event)) = + services().rooms.state_accessor.get_member(&body.room_id, sender_user) { if membership_event.membership == MembershipState::Ban { return Ok(ban_user::v3::Response {}); @@ -288,12 +299,16 @@ pub(crate) async fn ban_user_route( }), |event| { serde_json::from_str(event.content.get()) - .map(|event: RoomMemberEventContent| RoomMemberEventContent { - membership: MembershipState::Ban, - join_authorized_via_users_server: None, - ..event + .map(|event: RoomMemberEventContent| { + RoomMemberEventContent { + membership: MembershipState::Ban, + join_authorized_via_users_server: None, + ..event + } + }) + .map_err(|_| { + Error::bad_database("Invalid member event in database.") }) - .map_err(|_| Error::bad_database("Invalid member event in database.")) }, )?; @@ -314,7 +329,8 @@ pub(crate) async fn ban_user_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomMember, - content: to_raw_value(&event).expect("event is valid, we just created it"), + content: to_raw_value(&event) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(body.user_id.to_string()), redacts: None, @@ -338,10 +354,8 @@ pub(crate) async fn unban_user_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Ok(Some(membership_event)) = services() - .rooms - .state_accessor - .get_member(&body.room_id, sender_user) + if let Ok(Some(membership_event)) = + services().rooms.state_accessor.get_member(&body.room_id, sender_user) { if membership_event.membership != MembershipState::Ban { return Ok(unban_user::v3::Response {}); @@ -386,7 +400,8 @@ pub(crate) async fn unban_user_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomMember, - content: to_raw_value(&event).expect("event is valid, we just created it"), + content: to_raw_value(&event) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(body.user_id.to_string()), redacts: None, @@ -406,19 +421,17 @@ pub(crate) async fn unban_user_route( /// /// Forgets about a room. /// -/// - If the sender user currently left the room: Stops sender user from receiving information about the room +/// - If the sender user currently left the room: Stops sender user from +/// receiving information about the room /// -/// Note: Other devices of the user have no way of knowing the room was forgotten, so this has to -/// be called from every device +/// Note: Other devices of the user have no way of knowing the room was +/// forgotten, so this has to be called from every device pub(crate) async fn forget_room_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .rooms - .state_cache - .forget(&body.room_id, sender_user)?; + services().rooms.state_cache.forget(&body.room_id, sender_user)?; Ok(forget_room::v3::Response::new()) } @@ -443,7 +456,8 @@ pub(crate) async fn joined_rooms_route( /// # `POST /_matrix/client/r0/rooms/{roomId}/members` /// -/// Lists all joined users in a room (TODO: at a specific point in time, with a specific membership). +/// Lists all joined users in a room (TODO: at a specific point in time, with a +/// specific membership). /// /// - Only works if the user is currently joined pub(crate) async fn get_member_events_route( @@ -516,7 +530,9 @@ pub(crate) async fn joined_members_route( ); } - Ok(joined_members::v3::Response { joined }) + Ok(joined_members::v3::Response { + joined, + }) } #[allow(clippy::too_many_lines)] @@ -529,7 +545,9 @@ async fn join_room_by_id_helper( ) -> Result { let sender_user = sender_user.expect("user is authenticated"); - if let Ok(true) = services().rooms.state_cache.is_joined(sender_user, room_id) { + if let Ok(true) = + services().rooms.state_cache.is_joined(sender_user, room_id) + { return Ok(join_room_by_id::v3::Response { room_id: room_id.into(), }); @@ -560,19 +578,25 @@ async fn join_room_by_id_helper( "", )?; - let join_rules_event_content: Option = join_rules_event - .as_ref() - .map(|join_rules_event| { - serde_json::from_str(join_rules_event.content.get()).map_err(|e| { - warn!("Invalid join rules event: {}", e); - Error::bad_database("Invalid join rules event in db.") + let join_rules_event_content: Option = + join_rules_event + .as_ref() + .map(|join_rules_event| { + serde_json::from_str(join_rules_event.content.get()) + .map_err(|e| { + warn!("Invalid join rules event: {}", e); + Error::bad_database( + "Invalid join rules event in db.", + ) + }) }) - }) - .transpose()?; + .transpose()?; let restriction_rooms = match join_rules_event_content { Some(RoomJoinRulesEventContent { - join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted), + join_rule: + JoinRule::Restricted(restricted) + | JoinRule::KnockRestricted(restricted), }) => restricted .allow .into_iter() @@ -584,37 +608,38 @@ async fn join_room_by_id_helper( _ => Vec::new(), }; - let authorized_user = if restriction_rooms.iter().any(|restriction_room_id| { - services() - .rooms - .state_cache - .is_joined(sender_user, restriction_room_id) - .unwrap_or(false) - }) { - let mut auth_user = None; - for user in services() - .rooms - .state_cache - .room_members(room_id) - .filter_map(Result::ok) - .collect::>() - { - if user.server_name() == services().globals.server_name() - && services().rooms.state_accessor.user_can_invite( - room_id, - &user, - sender_user, - &state_lock, - ) + let authorized_user = + if restriction_rooms.iter().any(|restriction_room_id| { + services() + .rooms + .state_cache + .is_joined(sender_user, restriction_room_id) + .unwrap_or(false) + }) { + let mut auth_user = None; + for user in services() + .rooms + .state_cache + .room_members(room_id) + .filter_map(Result::ok) + .collect::>() { - auth_user = Some(user); - break; + if user.server_name() == services().globals.server_name() + && services().rooms.state_accessor.user_can_invite( + room_id, + &user, + sender_user, + &state_lock, + ) + { + auth_user = Some(user); + break; + } } - } - auth_user - } else { - None - }; + auth_user + } else { + None + }; let event = RoomMemberEventContent { membership: MembershipState::Join, @@ -634,7 +659,8 @@ async fn join_room_by_id_helper( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomMember, - content: to_raw_value(&event).expect("event is valid, we just created it"), + content: to_raw_value(&event) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(sender_user.to_string()), redacts: None, @@ -645,21 +671,24 @@ async fn join_room_by_id_helper( ) .await { - Ok(_event_id) => return Ok(join_room_by_id::v3::Response::new(room_id.to_owned())), + Ok(_event_id) => { + return Ok(join_room_by_id::v3::Response::new( + room_id.to_owned(), + )) + } Err(e) => e, }; if restriction_rooms.is_empty() - && servers - .iter() - .any(|s| *s != services().globals.server_name()) + && servers.iter().any(|s| *s != services().globals.server_name()) { return Err(error); } info!( - "We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements" - ); + "We couldn't do the join locally, maybe federation can help to \ + satisfy the restricted join requirements" + ); let (make_join_response, remote_server) = make_join_request(sender_user, room_id, servers).await?; @@ -672,24 +701,32 @@ async fn join_room_by_id_helper( { room_version_id } - _ => return Err(Error::BadServerResponse("Room version is not supported")), + _ => { + return Err(Error::BadServerResponse( + "Room version is not supported", + )) + } }; - let mut join_event_stub: CanonicalJsonObject = - serde_json::from_str(make_join_response.event.get()).map_err(|_| { - Error::BadServerResponse("Invalid make_join event json received from server.") - })?; + let mut join_event_stub: CanonicalJsonObject = serde_json::from_str( + make_join_response.event.get(), + ) + .map_err(|_| { + Error::BadServerResponse( + "Invalid make_join event json received from server.", + ) + })?; let join_authorized_via_users_server = join_event_stub .get("content") .map(|s| { - s.as_object()? - .get("join_authorised_via_users_server")? - .as_str() + s.as_object()?.get("join_authorised_via_users_server")?.as_str() }) .and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok()); // TODO: Is origin needed? join_event_stub.insert( "origin".to_owned(), - CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), + CanonicalJsonValue::String( + services().globals.server_name().as_str().to_owned(), + ), ); join_event_stub.insert( "origin_server_ts".to_owned(), @@ -714,10 +751,12 @@ async fn join_room_by_id_helper( .expect("event is valid, we just created it"), ); - // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms + // We don't leave the event id in the pdu because that's only allowed in + // v1 or v2 rooms join_event_stub.remove("event_id"); - // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present + // In order to create a compatible ref hash (EventID) the `hashes` field + // needs to be present ruma::signatures::hash_and_sign_event( services().globals.server_name().as_str(), services().globals.keypair(), @@ -729,8 +768,11 @@ async fn join_room_by_id_helper( // Generate event id let event_id = format!( "${}", - ruma::signatures::reference_hash(&join_event_stub, &room_version_id) - .expect("ruma can calculate reference hashes") + ruma::signatures::reference_hash( + &join_event_stub, + &room_version_id + ) + .expect("ruma can calculate reference hashes") ); let event_id = <&EventId>::try_from(event_id.as_str()) .expect("ruma's reference hashes are valid event ids"); @@ -751,7 +793,9 @@ async fn join_room_by_id_helper( federation::membership::create_join_event::v2::Request { room_id: room_id.to_owned(), event_id: event_id.to_owned(), - pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()), + pdu: PduEvent::convert_to_outgoing_federation_event( + join_event.clone(), + ), omit_members: false, }, ) @@ -809,27 +853,35 @@ async fn join_room_by_id_helper( { room_version } - _ => return Err(Error::BadServerResponse("Room version is not supported")), + _ => { + return Err(Error::BadServerResponse( + "Room version is not supported", + )) + } }; - let mut join_event_stub: CanonicalJsonObject = - serde_json::from_str(make_join_response.event.get()).map_err(|_| { - Error::BadServerResponse("Invalid make_join event json received from server.") - })?; + let mut join_event_stub: CanonicalJsonObject = serde_json::from_str( + make_join_response.event.get(), + ) + .map_err(|_| { + Error::BadServerResponse( + "Invalid make_join event json received from server.", + ) + })?; let join_authorized_via_users_server = join_event_stub .get("content") .map(|s| { - s.as_object()? - .get("join_authorised_via_users_server")? - .as_str() + s.as_object()?.get("join_authorised_via_users_server")?.as_str() }) .and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok()); // TODO: Is origin needed? join_event_stub.insert( "origin".to_owned(), - CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), + CanonicalJsonValue::String( + services().globals.server_name().as_str().to_owned(), + ), ); join_event_stub.insert( "origin_server_ts".to_owned(), @@ -854,10 +906,12 @@ async fn join_room_by_id_helper( .expect("event is valid, we just created it"), ); - // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms + // We don't leave the event id in the pdu because that's only allowed in + // v1 or v2 rooms join_event_stub.remove("event_id"); - // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present + // In order to create a compatible ref hash (EventID) the `hashes` field + // needs to be present ruma::signatures::hash_and_sign_event( services().globals.server_name().as_str(), services().globals.keypair(), @@ -869,8 +923,11 @@ async fn join_room_by_id_helper( // Generate event id let event_id = format!( "${}", - ruma::signatures::reference_hash(&join_event_stub, &room_version_id) - .expect("ruma can calculate reference hashes") + ruma::signatures::reference_hash( + &join_event_stub, + &room_version_id + ) + .expect("ruma can calculate reference hashes") ); let event_id = <&EventId>::try_from(event_id.as_str()) .expect("ruma's reference hashes are valid event ids"); @@ -892,7 +949,9 @@ async fn join_room_by_id_helper( federation::membership::create_join_event::v2::Request { room_id: room_id.to_owned(), event_id: event_id.to_owned(), - pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()), + pdu: PduEvent::convert_to_outgoing_federation_event( + join_event.clone(), + ), omit_members: false, }, ) @@ -901,7 +960,10 @@ async fn join_room_by_id_helper( info!("send_join finished"); if let Some(signed_raw) = &send_join_response.room_state.event { - info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event"); + info!( + "There is a signed event. This room is probably using \ + restricted joins. Adding signature to our event" + ); let Ok((signed_event_id, signed_value)) = gen_event_id_canonical_json(signed_raw, &room_version_id) else { @@ -941,7 +1003,8 @@ async fn join_room_by_id_helper( } Err(e) => { warn!( - "Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}", + "Server {remote_server} sent invalid signature in \ + sendjoin signatures for event {signed_value:?}: {e:?}", ); } } @@ -950,8 +1013,10 @@ async fn join_room_by_id_helper( services().rooms.short.get_or_create_shortroomid(room_id)?; info!("Parsing join event"); - let parsed_join_pdu = PduEvent::from_id_val(event_id, join_event.clone()) - .map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?; + let parsed_join_pdu = + PduEvent::from_id_val(event_id, join_event.clone()).map_err( + |_| Error::BadServerResponse("Invalid join event PDU."), + )?; let mut state = HashMap::new(); let pub_key_map = RwLock::new(BTreeMap::new()); @@ -960,58 +1025,61 @@ async fn join_room_by_id_helper( services() .rooms .event_handler - .fetch_join_signing_keys(&send_join_response, &room_version_id, &pub_key_map) + .fetch_join_signing_keys( + &send_join_response, + &room_version_id, + &pub_key_map, + ) .await?; info!("Going through send_join response room_state"); - for result in send_join_response - .room_state - .state - .iter() - .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) - { + for result in send_join_response.room_state.state.iter().map(|pdu| { + validate_and_add_event_id(pdu, &room_version_id, &pub_key_map) + }) { let Ok((event_id, value)) = result.await else { continue; }; - let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| { - warn!("Invalid PDU in send_join response: {} {:?}", e, value); - Error::BadServerResponse("Invalid PDU in send_join response.") - })?; + let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err( + |e| { + warn!( + "Invalid PDU in send_join response: {} {:?}", + e, value + ); + Error::BadServerResponse( + "Invalid PDU in send_join response.", + ) + }, + )?; - services() - .rooms - .outlier - .add_pdu_outlier(&event_id, &value)?; + services().rooms.outlier.add_pdu_outlier(&event_id, &value)?; if let Some(state_key) = &pdu.state_key { - let shortstatekey = services() - .rooms - .short - .get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?; + let shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &pdu.kind.to_string().into(), + state_key, + )?; state.insert(shortstatekey, pdu.event_id.clone()); } } info!("Going through send_join response auth_chain"); - for result in send_join_response - .room_state - .auth_chain - .iter() - .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) + for result in + send_join_response.room_state.auth_chain.iter().map(|pdu| { + validate_and_add_event_id(pdu, &room_version_id, &pub_key_map) + }) { let Ok((event_id, value)) = result.await else { continue; }; - services() - .rooms - .outlier - .add_pdu_outlier(&event_id, &value)?; + services().rooms.outlier.add_pdu_outlier(&event_id, &value)?; } info!("Running send_join auth check"); let authenticated = state_res::event_auth::auth_check( - &state_res::RoomVersion::new(&room_version_id).expect("room version is supported"), + &state_res::RoomVersion::new(&room_version_id) + .expect("room version is supported"), &parsed_join_pdu, // TODO: third party invite None::, @@ -1024,7 +1092,10 @@ async fn join_room_by_id_helper( &services() .rooms .short - .get_or_create_shortstatekey(&k.to_string().into(), s) + .get_or_create_shortstatekey( + &k.to_string().into(), + s, + ) .ok()?, )?, ) @@ -1044,33 +1115,42 @@ async fn join_room_by_id_helper( } info!("Saving state from send_join"); - let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state( - room_id, - Arc::new( - state - .into_iter() - .map(|(k, id)| { - services() - .rooms - .state_compressor - .compress_state_event(k, &id) - }) - .collect::>()?, - ), - )?; + let (statehash_before_join, new, removed) = + services().rooms.state_compressor.save_state( + room_id, + Arc::new( + state + .into_iter() + .map(|(k, id)| { + services() + .rooms + .state_compressor + .compress_state_event(k, &id) + }) + .collect::>()?, + ), + )?; services() .rooms .state - .force_state(room_id, statehash_before_join, new, removed, &state_lock) + .force_state( + room_id, + statehash_before_join, + new, + removed, + &state_lock, + ) .await?; info!("Updating joined counts for new room"); services().rooms.state_cache.update_joined_count(room_id)?; - // We append to state before appending the pdu, so we don't have a moment in time with the - // pdu without it's state. This is okay because append_pdu can't fail. - let statehash_after_join = services().rooms.state.append_to_state(&parsed_join_pdu)?; + // We append to state before appending the pdu, so we don't have a + // moment in time with the pdu without it's state. This is okay + // because append_pdu can't fail. + let statehash_after_join = + services().rooms.state.append_to_state(&parsed_join_pdu)?; info!("Appending new room join event"); services() @@ -1085,12 +1165,14 @@ async fn join_room_by_id_helper( .await?; info!("Setting final room state for new room"); - // We set the room state after inserting the pdu, so that we never have a moment in time - // where events in the current room state do not exist - services() - .rooms - .state - .set_room_state(room_id, statehash_after_join, &state_lock)?; + // We set the room state after inserting the pdu, so that we never have + // a moment in time where events in the current room state do + // not exist + services().rooms.state.set_room_state( + room_id, + statehash_after_join, + &state_lock, + )?; } Ok(join_room_by_id::v3::Response::new(room_id.to_owned())) @@ -1125,7 +1207,8 @@ async fn make_join_request( ) .await; - make_join_response_and_server = make_join_response.map(|r| (r, remote_server.clone())); + make_join_response_and_server = + make_join_response.map(|r| (r, remote_server.clone())); if make_join_response_and_server.is_ok() { break; @@ -1140,10 +1223,11 @@ async fn validate_and_add_event_id( room_version: &RoomVersionId, pub_key_map: &RwLock>>, ) -> Result<(OwnedEventId, CanonicalJsonObject)> { - let mut value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { - error!("Invalid PDU in server response: {:?}: {:?}", pdu, e); - Error::BadServerResponse("Invalid PDU in server response") - })?; + let mut value: CanonicalJsonObject = serde_json::from_str(pdu.get()) + .map_err(|e| { + error!("Invalid PDU in server response: {:?}: {:?}", pdu, e); + Error::BadServerResponse("Invalid PDU in server response") + })?; let event_id = EventId::parse(format!( "${}", ruma::signatures::reference_hash(&value, room_version) @@ -1152,41 +1236,39 @@ async fn validate_and_add_event_id( .expect("ruma's reference hashes are valid event ids"); let back_off = |id| async { - match services() - .globals - .bad_event_ratelimiter - .write() - .await - .entry(id) - { + match services().globals.bad_event_ratelimiter.write().await.entry(id) { Entry::Vacant(e) => { e.insert((Instant::now(), 1)); } - Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1), + Entry::Occupied(mut e) => { + *e.get_mut() = (Instant::now(), e.get().1 + 1); + } } }; - if let Some((time, tries)) = services() - .globals - .bad_event_ratelimiter - .read() - .await - .get(&event_id) + if let Some((time, tries)) = + services().globals.bad_event_ratelimiter.read().await.get(&event_id) { // Exponential backoff - let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries); + let mut min_elapsed_duration = + Duration::from_secs(30) * (*tries) * (*tries); if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { min_elapsed_duration = Duration::from_secs(60 * 60 * 24); } if time.elapsed() < min_elapsed_duration { debug!("Backing off from {}", event_id); - return Err(Error::BadServerResponse("bad event, still backing off")); + return Err(Error::BadServerResponse( + "bad event, still backing off", + )); } } - if let Err(e) = ruma::signatures::verify_event(&*pub_key_map.read().await, &value, room_version) - { + if let Err(e) = ruma::signatures::verify_event( + &*pub_key_map.read().await, + &value, + room_version, + ) { warn!("Event {} failed verification {:?} {}", event_id, pdu, e); back_off(event_id).await; return Err(Error::BadServerResponse("Event failed verification.")); @@ -1233,27 +1315,30 @@ pub(crate) async fn invite_helper( }) .expect("member event is valid value"); - let (pdu, pdu_json) = services().rooms.timeline.create_hash_and_sign_event( - PduBuilder { - event_type: TimelineEventType::RoomMember, - content, - unsigned: None, - state_key: Some(user_id.to_string()), - redacts: None, - }, - sender_user, - room_id, - &state_lock, - )?; + let (pdu, pdu_json) = + services().rooms.timeline.create_hash_and_sign_event( + PduBuilder { + event_type: TimelineEventType::RoomMember, + content, + unsigned: None, + state_key: Some(user_id.to_string()), + redacts: None, + }, + sender_user, + room_id, + &state_lock, + )?; - let invite_room_state = services().rooms.state.calculate_invite_state(&pdu)?; + let invite_room_state = + services().rooms.state.calculate_invite_state(&pdu)?; drop(state_lock); (pdu, pdu_json, invite_room_state) }; - let room_version_id = services().rooms.state.get_room_version(room_id)?; + let room_version_id = + services().rooms.state.get_room_version(room_id)?; let response = services() .sending @@ -1263,7 +1348,9 @@ pub(crate) async fn invite_helper( room_id: room_id.to_owned(), event_id: (*pdu.event_id).to_owned(), room_version: room_version_id.clone(), - event: PduEvent::convert_to_outgoing_federation_event(pdu_json.clone()), + event: PduEvent::convert_to_outgoing_federation_event( + pdu_json.clone(), + ), invite_room_state, }, ) @@ -1271,8 +1358,10 @@ pub(crate) async fn invite_helper( let pub_key_map = RwLock::new(BTreeMap::new()); - // We do not add the event_id field to the pdu here because of signature and hashes checks - let Ok((event_id, value)) = gen_event_id_canonical_json(&response.event, &room_version_id) + // We do not add the event_id field to the pdu here because of signature + // and hashes checks + let Ok((event_id, value)) = + gen_event_id_canonical_json(&response.event, &room_version_id) else { // Event could not be converted to canonical json return Err(Error::BadRequest( @@ -1282,22 +1371,42 @@ pub(crate) async fn invite_helper( }; if *pdu.event_id != *event_id { - warn!("Server {} changed invite event, that's not allowed in the spec: ours: {:?}, theirs: {:?}", user_id.server_name(), pdu_json, value); + warn!( + "Server {} changed invite event, that's not allowed in the \ + spec: ours: {:?}, theirs: {:?}", + user_id.server_name(), + pdu_json, + value + ); } let origin: OwnedServerName = serde_json::from_value( - serde_json::to_value(value.get("origin").ok_or(Error::BadRequest( - ErrorKind::InvalidParam, - "Event needs an origin field.", - ))?) + serde_json::to_value(value.get("origin").ok_or( + Error::BadRequest( + ErrorKind::InvalidParam, + "Event needs an origin field.", + ), + )?) .expect("CanonicalJson is valid json value"), ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Origin field is invalid."))?; + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Origin field is invalid.", + ) + })?; let pdu_id: Vec = services() .rooms .event_handler - .handle_incoming_pdu(&origin, &event_id, room_id, value, true, &pub_key_map) + .handle_incoming_pdu( + &origin, + &event_id, + room_id, + value, + true, + &pub_key_map, + ) .await? .ok_or(Error::BadRequest( ErrorKind::InvalidParam, @@ -1317,11 +1426,7 @@ pub(crate) async fn invite_helper( return Ok(()); } - if !services() - .rooms - .state_cache - .is_joined(sender_user, room_id)? - { + if !services().rooms.state_cache.is_joined(sender_user, room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -1387,7 +1492,9 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> { .collect::>(); for room_id in all_rooms { - let Ok(room_id) = room_id else { continue }; + let Ok(room_id) = room_id else { + continue; + }; if let Err(error) = leave_room(user_id, &room_id, None).await { warn!(%user_id, %room_id, %error, "failed to leave room"); @@ -1443,8 +1550,10 @@ pub(crate) async fn leave_room( Some(e) => e, }; - let mut event: RoomMemberEventContent = serde_json::from_str(member_event.content.get()) - .map_err(|_| Error::bad_database("Invalid member event in database."))?; + let mut event: RoomMemberEventContent = + serde_json::from_str(member_event.content.get()).map_err(|_| { + Error::bad_database("Invalid member event in database.") + })?; event.membership = MembershipState::Leave; event.reason = reason; @@ -1456,7 +1565,8 @@ pub(crate) async fn leave_room( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomMember, - content: to_raw_value(&event).expect("event is valid, we just created it"), + content: to_raw_value(&event) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(user_id.to_string()), redacts: None, @@ -1495,19 +1605,16 @@ pub(crate) async fn leave_room( Ok(()) } +#[allow(clippy::too_many_lines)] async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { let mut make_leave_response_and_server = Err(Error::BadServerResponse( "No server available to assist in leaving.", )); - let invite_state = services() - .rooms - .state_cache - .invite_state(user_id, room_id)? - .ok_or(Error::BadRequest( - ErrorKind::BadState, - "User is not invited.", - ))?; + let invite_state = + services().rooms.state_cache.invite_state(user_id, room_id)?.ok_or( + Error::BadRequest(ErrorKind::BadState, "User is not invited."), + )?; let servers: HashSet<_> = invite_state .iter() @@ -1530,7 +1637,8 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { ) .await; - make_leave_response_and_server = make_leave_response.map(|r| (r, remote_server)); + make_leave_response_and_server = + make_leave_response.map(|r| (r, remote_server)); if make_leave_response_and_server.is_ok() { break; @@ -1548,18 +1656,28 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { { version } - _ => return Err(Error::BadServerResponse("Room version is not supported")), + _ => { + return Err(Error::BadServerResponse( + "Room version is not supported", + )) + } }; let mut leave_event_stub = serde_json::from_str::( make_leave_response.event.get(), ) - .map_err(|_| Error::BadServerResponse("Invalid make_leave event json received from server."))?; + .map_err(|_| { + Error::BadServerResponse( + "Invalid make_leave event json received from server.", + ) + })?; // TODO: Is origin needed? leave_event_stub.insert( "origin".to_owned(), - CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), + CanonicalJsonValue::String( + services().globals.server_name().as_str().to_owned(), + ), ); leave_event_stub.insert( "origin_server_ts".to_owned(), @@ -1569,10 +1687,12 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { .expect("Timestamp is valid js_int value"), ), ); - // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms + // We don't leave the event id in the pdu because that's only allowed in v1 + // or v2 rooms leave_event_stub.remove("event_id"); - // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present + // In order to create a compatible ref hash (EventID) the `hashes` field + // needs to be present ruma::signatures::hash_and_sign_event( services().globals.server_name().as_str(), services().globals.keypair(), @@ -1605,7 +1725,9 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { federation::membership::create_leave_event::v2::Request { room_id: room_id.to_owned(), event_id, - pdu: PduEvent::convert_to_outgoing_federation_event(leave_event.clone()), + pdu: PduEvent::convert_to_outgoing_federation_event( + leave_event.clone(), + ), }, ) .await?; diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 2b920295..202b6c05 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -1,7 +1,8 @@ -use crate::{ - service::{pdu::PduBuilder, rooms::timeline::PduCount}, - services, utils, Error, Result, Ruma, +use std::{ + collections::{BTreeMap, HashSet}, + sync::Arc, }; + use ruma::{ api::client::{ error::ErrorKind, @@ -10,18 +11,21 @@ use ruma::{ events::{StateEventType, TimelineEventType}, uint, }; -use std::{ - collections::{BTreeMap, HashSet}, - sync::Arc, + +use crate::{ + service::{pdu::PduBuilder, rooms::timeline::PduCount}, + services, utils, Error, Result, Ruma, }; /// # `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}` /// /// Send a message event into the room. /// -/// - Is a NOOP if the txn id was already used before and returns the same event id again +/// - Is a NOOP if the txn id was already used before and returns the same event +/// id again /// - The only requirement for the content is that it has to be valid json -/// - Tries to send the event into the room, auth rules will determine if it is allowed +/// - Tries to send the event into the room, auth rules will determine if it is +/// allowed pub(crate) async fn send_message_event_route( body: Ruma, ) -> Result { @@ -50,29 +54,37 @@ pub(crate) async fn send_message_event_route( } // Check if this is a new transaction id - if let Some(response) = - services() - .transaction_ids - .existing_txnid(sender_user, sender_device, &body.txn_id)? - { + if let Some(response) = services().transaction_ids.existing_txnid( + sender_user, + sender_device, + &body.txn_id, + )? { // The client might have sent a txnid of the /sendToDevice endpoint // This txnid has no response associated with it if response.is_empty() { return Err(Error::BadRequest( ErrorKind::InvalidParam, - "Tried to use txn id already used for an incompatible endpoint.", + "Tried to use txn id already used for an incompatible \ + endpoint.", )); } let event_id = utils::string_from_bytes(&response) - .map_err(|_| Error::bad_database("Invalid txnid bytes in database."))? + .map_err(|_| { + Error::bad_database("Invalid txnid bytes in database.") + })? .try_into() - .map_err(|_| Error::bad_database("Invalid event id in txnid data."))?; - return Ok(send_message_event::v3::Response { event_id }); + .map_err(|_| { + Error::bad_database("Invalid event id in txnid data.") + })?; + return Ok(send_message_event::v3::Response { + event_id, + }); } let mut unsigned = BTreeMap::new(); - unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into()); + unsigned + .insert("transaction_id".to_owned(), body.txn_id.to_string().into()); let event_id = services() .rooms @@ -81,7 +93,12 @@ pub(crate) async fn send_message_event_route( PduBuilder { event_type: body.event_type.to_string().into(), content: serde_json::from_str(body.body.body.json().get()) - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?, + .map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Invalid JSON body.", + ) + })?, unsigned: Some(unsigned), state_key: None, redacts: None, @@ -101,23 +118,23 @@ pub(crate) async fn send_message_event_route( drop(state_lock); - Ok(send_message_event::v3::Response::new( - (*event_id).to_owned(), - )) + Ok(send_message_event::v3::Response::new((*event_id).to_owned())) } /// # `GET /_matrix/client/r0/rooms/{roomId}/messages` /// /// Allows paginating through room history. /// -/// - Only works if the user is joined (TODO: always allow, but only show events where the user was +/// - Only works if the user is joined (TODO: always allow, but only show events +/// where the user was /// joined, depending on `history_visibility`) #[allow(clippy::too_many_lines)] pub(crate) async fn get_message_events_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); let from = match body.from.clone() { Some(from) => PduCount::try_from_string(&from)?, @@ -127,15 +144,17 @@ pub(crate) async fn get_message_events_route( }, }; - let to = body - .to - .as_ref() - .and_then(|t| PduCount::try_from_string(t).ok()); + let to = body.to.as_ref().and_then(|t| PduCount::try_from_string(t).ok()); services() .rooms .lazy_loading - .lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from) + .lazy_load_confirm_delivery( + sender_user, + sender_device, + &body.room_id, + from, + ) .await?; let limit = body @@ -162,7 +181,11 @@ pub(crate) async fn get_message_events_route( services() .rooms .state_accessor - .user_can_see_event(sender_user, &body.room_id, &pdu.event_id) + .user_can_see_event( + sender_user, + &body.room_id, + &pdu.event_id, + ) .unwrap_or(false) }) .take_while(|&(k, _)| Some(k) != to) @@ -214,7 +237,11 @@ pub(crate) async fn get_message_events_route( services() .rooms .state_accessor - .user_can_see_event(sender_user, &body.room_id, &pdu.event_id) + .user_can_see_event( + sender_user, + &body.room_id, + &pdu.event_id, + ) .unwrap_or(false) }) .take_while(|&(k, _)| Some(k) != to) @@ -254,11 +281,13 @@ pub(crate) async fn get_message_events_route( resp.state = Vec::new(); for ll_id in &lazy_loaded { - if let Some(member_event) = services().rooms.state_accessor.room_state_get( - &body.room_id, - &StateEventType::RoomMember, - ll_id.as_str(), - )? { + if let Some(member_event) = + services().rooms.state_accessor.room_state_get( + &body.room_id, + &StateEventType::RoomMember, + ll_id.as_str(), + )? + { resp.state.push(member_event.to_state_event()); } } diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index f9a96904..4bcfc5af 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -1,20 +1,25 @@ -use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma}; +use std::sync::Arc; + use ruma::{ api::{ client::{ error::ErrorKind, profile::{ - get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name, + get_avatar_url, get_display_name, get_profile, set_avatar_url, + set_display_name, }, }, federation::{self, query::get_profile_information::v1::ProfileField}, }, - events::{room::member::RoomMemberEventContent, StateEventType, TimelineEventType}, + events::{ + room::member::RoomMemberEventContent, StateEventType, TimelineEventType, + }, }; use serde_json::value::to_raw_value; -use std::sync::Arc; use tracing::warn; +use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma}; + /// # `PUT /_matrix/client/r0/profile/{userId}/displayname` /// /// Updates the displayname. @@ -25,9 +30,7 @@ pub(crate) async fn set_displayname_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .users - .set_displayname(sender_user, body.displayname.clone())?; + services().users.set_displayname(sender_user, body.displayname.clone())?; // Send a new membership event and presence update into all joined rooms let all_rooms_joined: Vec<_> = services() @@ -53,14 +56,18 @@ pub(crate) async fn set_displayname_route( )? .ok_or_else(|| { Error::bad_database( - "Tried to send displayname update for user not in the \ - room.", + "Tried to send displayname update for \ + user not in the room.", ) })? .content .get(), ) - .map_err(|_| Error::bad_database("Database contains invalid PDU."))? + .map_err(|_| { + Error::bad_database( + "Database contains invalid PDU.", + ) + })? }) .expect("event is valid, we just created it"), unsigned: None, @@ -88,7 +95,12 @@ pub(crate) async fn set_displayname_route( if let Err(error) = services() .rooms .timeline - .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) + .build_and_append_pdu( + pdu_builder, + sender_user, + &room_id, + &state_lock, + ) .await { warn!(%error, "failed to add PDU"); @@ -138,13 +150,9 @@ pub(crate) async fn set_avatar_url_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .users - .set_avatar_url(sender_user, body.avatar_url.clone())?; + services().users.set_avatar_url(sender_user, body.avatar_url.clone())?; - services() - .users - .set_blurhash(sender_user, body.blurhash.clone())?; + services().users.set_blurhash(sender_user, body.blurhash.clone())?; // Send a new membership event and presence update into all joined rooms let all_joined_rooms: Vec<_> = services() @@ -170,14 +178,18 @@ pub(crate) async fn set_avatar_url_route( )? .ok_or_else(|| { Error::bad_database( - "Tried to send displayname update for user not in the \ - room.", + "Tried to send displayname update for \ + user not in the room.", ) })? .content .get(), ) - .map_err(|_| Error::bad_database("Database contains invalid PDU."))? + .map_err(|_| { + Error::bad_database( + "Database contains invalid PDU.", + ) + })? }) .expect("event is valid, we just created it"), unsigned: None, @@ -205,7 +217,12 @@ pub(crate) async fn set_avatar_url_route( if let Err(error) = services() .rooms .timeline - .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) + .build_and_append_pdu( + pdu_builder, + sender_user, + &room_id, + &state_lock, + ) .await { warn!(%error, "failed to add PDU"); @@ -219,7 +236,8 @@ pub(crate) async fn set_avatar_url_route( /// /// Returns the `avatar_url` and `blurhash` of the user. /// -/// - If user is on another server: Fetches `avatar_url` and `blurhash` over federation +/// - If user is on another server: Fetches `avatar_url` and `blurhash` over +/// federation pub(crate) async fn get_avatar_url_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 63a12715..9e101707 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -1,17 +1,18 @@ -use crate::{services, Error, Result, Ruma}; use ruma::{ api::client::{ error::ErrorKind, push::{ - delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled, - get_pushrules_all, set_pusher, set_pushrule, set_pushrule_actions, - set_pushrule_enabled, RuleScope, + delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, + get_pushrule_enabled, get_pushrules_all, set_pusher, set_pushrule, + set_pushrule_actions, set_pushrule_enabled, RuleScope, }, }, events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, }; +use crate::{services, Error, Result, Ruma}; + /// # `GET /_matrix/client/r0/pushrules` /// /// Retrieves the push rules event for this user. @@ -71,12 +72,11 @@ pub(crate) async fn get_pushrule_route( .map(Into::into); if let Some(rule) = rule { - Ok(get_pushrule::v3::Response { rule }) + Ok(get_pushrule::v3::Response { + rule, + }) } else { - Err(Error::BadRequest( - ErrorKind::NotFound, - "Push rule not found.", - )) + Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found.")) } } @@ -109,7 +109,9 @@ pub(crate) async fn set_pushrule_route( ))?; let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))?; + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if let Err(error) = account_data.content.global.insert( body.rule.clone(), @@ -119,16 +121,20 @@ pub(crate) async fn set_pushrule_route( let err = match error { InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest( ErrorKind::InvalidParam, - "Rule IDs starting with a dot are reserved for server-default rules.", + "Rule IDs starting with a dot are reserved for server-default \ + rules.", ), InsertPushRuleError::InvalidRuleId => Error::BadRequest( ErrorKind::InvalidParam, "Rule ID containing invalid characters.", ), - InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest( - ErrorKind::InvalidParam, - "Can't place a push rule relatively to a server-default rule.", - ), + InsertPushRuleError::RelativeToServerDefaultRule => { + Error::BadRequest( + ErrorKind::InvalidParam, + "Can't place a push rule relatively to a server-default \ + rule.", + ) + } InsertPushRuleError::UnknownRuleId => Error::BadRequest( ErrorKind::NotFound, "The before or after rule could not be found.", @@ -147,7 +153,8 @@ pub(crate) async fn set_pushrule_route( None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data).expect("to json value always works"), + &serde_json::to_value(account_data) + .expect("to json value always works"), )?; Ok(set_pushrule::v3::Response {}) @@ -193,7 +200,9 @@ pub(crate) async fn get_pushrule_actions_route( "Push rule not found.", ))?; - Ok(get_pushrule_actions::v3::Response { actions }) + Ok(get_pushrule_actions::v3::Response { + actions, + }) } /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions` @@ -224,7 +233,9 @@ pub(crate) async fn set_pushrule_actions_route( ))?; let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))?; + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if account_data .content @@ -242,7 +253,8 @@ pub(crate) async fn set_pushrule_actions_route( None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data).expect("to json value always works"), + &serde_json::to_value(account_data) + .expect("to json value always works"), )?; Ok(set_pushrule_actions::v3::Response {}) @@ -276,7 +288,9 @@ pub(crate) async fn get_pushrule_enabled_route( ))?; let account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))?; + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; let global = account_data.content.global; let enabled = global @@ -287,7 +301,9 @@ pub(crate) async fn get_pushrule_enabled_route( "Push rule not found.", ))?; - Ok(get_pushrule_enabled::v3::Response { enabled }) + Ok(get_pushrule_enabled::v3::Response { + enabled, + }) } /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled` @@ -318,7 +334,9 @@ pub(crate) async fn set_pushrule_enabled_route( ))?; let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))?; + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if account_data .content @@ -336,7 +354,8 @@ pub(crate) async fn set_pushrule_enabled_route( None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data).expect("to json value always works"), + &serde_json::to_value(account_data) + .expect("to json value always works"), )?; Ok(set_pushrule_enabled::v3::Response {}) @@ -370,12 +389,12 @@ pub(crate) async fn delete_pushrule_route( ))?; let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db."))?; + .map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; - if let Err(error) = account_data - .content - .global - .remove(body.kind.clone(), &body.rule_id) + if let Err(error) = + account_data.content.global.remove(body.kind.clone(), &body.rule_id) { let err = match error { RemovePushRuleError::ServerDefault => Error::BadRequest( @@ -395,7 +414,8 @@ pub(crate) async fn delete_pushrule_route( None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data).expect("to json value always works"), + &serde_json::to_value(account_data) + .expect("to json value always works"), )?; Ok(delete_pushrule::v3::Response {}) @@ -424,9 +444,7 @@ pub(crate) async fn set_pushers_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services() - .pusher - .set_pusher(sender_user, body.action.clone())?; + services().pusher.set_pusher(sender_user, body.action.clone())?; Ok(set_pusher::v3::Response::default()) } diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index a1a7bbbf..5fdfc7de 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -1,20 +1,27 @@ -use crate::{service::rooms::timeline::PduCount, services, Error, Result, Ruma}; +use std::collections::BTreeMap; + use ruma::{ - api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt}, + api::client::{ + error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt, + }, events::{ receipt::{ReceiptThread, ReceiptType}, RoomAccountDataEventType, }, MilliSecondsSinceUnixEpoch, }; -use std::collections::BTreeMap; + +use crate::{ + service::rooms::timeline::PduCount, services, Error, Result, Ruma, +}; /// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers` /// /// Sets different types of read markers. /// /// - Updates fully-read account data event to `fully_read` -/// - If `read_receipt` is set: Update private marker and public read receipt EDU +/// - If `read_receipt` is set: Update private marker and public read receipt +/// EDU pub(crate) async fn set_read_marker_route( body: Ruma, ) -> Result { @@ -30,7 +37,8 @@ pub(crate) async fn set_read_marker_route( Some(&body.room_id), sender_user, RoomAccountDataEventType::FullyRead, - &serde_json::to_value(fully_read_event).expect("to json value always works"), + &serde_json::to_value(fully_read_event) + .expect("to json value always works"), )?; } @@ -42,14 +50,9 @@ pub(crate) async fn set_read_marker_route( } if let Some(event) = &body.private_read_receipt { - let count = services() - .rooms - .timeline - .get_pdu_count(event)? - .ok_or(Error::BadRequest( - ErrorKind::InvalidParam, - "Event does not exist.", - ))?; + let count = services().rooms.timeline.get_pdu_count(event)?.ok_or( + Error::BadRequest(ErrorKind::InvalidParam, "Event does not exist."), + )?; let count = match count { PduCount::Backfilled(_) => { return Err(Error::BadRequest( @@ -59,11 +62,11 @@ pub(crate) async fn set_read_marker_route( } PduCount::Normal(c) => c, }; - services() - .rooms - .edus - .read_receipt - .private_read_set(&body.room_id, sender_user, count)?; + services().rooms.edus.read_receipt.private_read_set( + &body.room_id, + sender_user, + count, + )?; } if let Some(event) = &body.read_receipt { @@ -86,7 +89,9 @@ pub(crate) async fn set_read_marker_route( sender_user, &body.room_id, ruma::events::receipt::ReceiptEvent { - content: ruma::events::receipt::ReceiptEventContent(receipt_content), + content: ruma::events::receipt::ReceiptEventContent( + receipt_content, + ), room_id: body.room_id.clone(), }, )?; @@ -105,7 +110,8 @@ pub(crate) async fn create_receipt_route( if matches!( &body.receipt_type, - create_receipt::v3::ReceiptType::Read | create_receipt::v3::ReceiptType::ReadPrivate + create_receipt::v3::ReceiptType::Read + | create_receipt::v3::ReceiptType::ReadPrivate ) { services() .rooms @@ -124,7 +130,8 @@ pub(crate) async fn create_receipt_route( Some(&body.room_id), sender_user, RoomAccountDataEventType::FullyRead, - &serde_json::to_value(fully_read_event).expect("to json value always works"), + &serde_json::to_value(fully_read_event) + .expect("to json value always works"), )?; } create_receipt::v3::ReceiptType::Read => { @@ -146,7 +153,9 @@ pub(crate) async fn create_receipt_route( sender_user, &body.room_id, ruma::events::receipt::ReceiptEvent { - content: ruma::events::receipt::ReceiptEventContent(receipt_content), + content: ruma::events::receipt::ReceiptEventContent( + receipt_content, + ), room_id: body.room_id.clone(), }, )?; diff --git a/src/api/client_server/redact.rs b/src/api/client_server/redact.rs index 0f774dd9..65683d81 100644 --- a/src/api/client_server/redact.rs +++ b/src/api/client_server/redact.rs @@ -1,13 +1,13 @@ use std::sync::Arc; -use crate::{service::pdu::PduBuilder, services, Result, Ruma}; use ruma::{ api::client::redact::redact_event, events::{room::redaction::RoomRedactionEventContent, TimelineEventType}, }; - use serde_json::value::to_raw_value; +use crate::{service::pdu::PduBuilder, services, Result, Ruma}; + /// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}` /// /// Tries to send a redaction event into the room. @@ -54,5 +54,7 @@ pub(crate) async fn redact_event_route( drop(state_lock); let event_id = (*event_id).to_owned(); - Ok(redact_event::v3::Response { event_id }) + Ok(redact_event::v3::Response { + event_id, + }) } diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 353e4cc7..475adb9a 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -23,10 +23,7 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( }, }; - let to = body - .to - .as_ref() - .and_then(|t| PduCount::try_from_string(t).ok()); + let to = body.to.as_ref().and_then(|t| PduCount::try_from_string(t).ok()); // Use limit or else 10, with maximum 100 let limit = body @@ -36,27 +33,22 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( .try_into() .expect("0-100 should fit in usize"); - let res = services() - .rooms - .pdu_metadata - .paginate_relations_with_filter( - sender_user, - &body.room_id, - &body.event_id, - Some(&body.event_type), - Some(&body.rel_type), - from, - to, - limit, - )?; + let res = services().rooms.pdu_metadata.paginate_relations_with_filter( + sender_user, + &body.room_id, + &body.event_id, + Some(&body.event_type), + Some(&body.rel_type), + from, + to, + limit, + )?; - Ok( - get_relating_events_with_rel_type_and_event_type::v1::Response { - chunk: res.chunk, - next_batch: res.next_batch, - prev_batch: res.prev_batch, - }, - ) + Ok(get_relating_events_with_rel_type_and_event_type::v1::Response { + chunk: res.chunk, + next_batch: res.next_batch, + prev_batch: res.prev_batch, + }) } /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}` @@ -74,10 +66,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route( }, }; - let to = body - .to - .as_ref() - .and_then(|t| PduCount::try_from_string(t).ok()); + let to = body.to.as_ref().and_then(|t| PduCount::try_from_string(t).ok()); // Use limit or else 10, with maximum 100 let limit = body @@ -87,19 +76,16 @@ pub(crate) async fn get_relating_events_with_rel_type_route( .try_into() .expect("0-100 should fit in usize"); - let res = services() - .rooms - .pdu_metadata - .paginate_relations_with_filter( - sender_user, - &body.room_id, - &body.event_id, - None, - Some(&body.rel_type), - from, - to, - limit, - )?; + let res = services().rooms.pdu_metadata.paginate_relations_with_filter( + sender_user, + &body.room_id, + &body.event_id, + None, + Some(&body.rel_type), + from, + to, + limit, + )?; Ok(get_relating_events_with_rel_type::v1::Response { chunk: res.chunk, @@ -123,10 +109,7 @@ pub(crate) async fn get_relating_events_route( }, }; - let to = body - .to - .as_ref() - .and_then(|t| PduCount::try_from_string(t).ok()); + let to = body.to.as_ref().and_then(|t| PduCount::try_from_string(t).ok()); // Use limit or else 10, with maximum 100 let limit = body @@ -136,17 +119,14 @@ pub(crate) async fn get_relating_events_route( .try_into() .expect("0-100 should fit in usize"); - services() - .rooms - .pdu_metadata - .paginate_relations_with_filter( - sender_user, - &body.room_id, - &body.event_id, - None, - None, - from, - to, - limit, - ) + services().rooms.pdu_metadata.paginate_relations_with_filter( + sender_user, + &body.room_id, + &body.event_id, + None, + None, + from, + to, + limit, + ) } diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index 25e8f10a..7bcf3f25 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -1,14 +1,14 @@ -use crate::{services, Error, Result, Ruma}; use ruma::{ api::client::{error::ErrorKind, room::report_content}, events::room::message, int, }; +use crate::{services, Error, Result, Ruma}; + /// # `POST /_matrix/client/r0/rooms/{roomId}/report/{eventId}` /// /// Reports an inappropriate event to homeserver admins -/// pub(crate) async fn report_event_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index b34f04bc..b0a17e6a 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -1,6 +1,5 @@ -use crate::{ - api::client_server::invite_helper, service::pdu::PduBuilder, services, Error, Result, Ruma, -}; +use std::{cmp::max, collections::BTreeMap, sync::Arc}; + use ruma::{ api::client::{ error::ErrorKind, @@ -11,7 +10,9 @@ use ruma::{ canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, - history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, + history_visibility::{ + HistoryVisibility, RoomHistoryVisibilityEventContent, + }, join_rules::{JoinRule, RoomJoinRulesEventContent}, member::{MembershipState, RoomMemberEventContent}, name::RoomNameEventContent, @@ -26,9 +27,13 @@ use ruma::{ CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, RoomVersionId, }; use serde_json::{json, value::to_raw_value}; -use std::{cmp::max, collections::BTreeMap, sync::Arc}; use tracing::{info, warn}; +use crate::{ + api::client_server::invite_helper, service::pdu::PduBuilder, services, + Error, Result, Ruma, +}; + /// # `POST /_matrix/client/r0/createRoom` /// /// Creates a new room. @@ -79,32 +84,27 @@ pub(crate) async fn create_room_route( } let alias: Option = - body.room_alias_name - .as_ref() - .map_or(Ok(None), |localpart| { - // TODO: Check for invalid characters and maximum length - let alias = RoomAliasId::parse(format!( - "#{}:{}", - localpart, - services().globals.server_name() - )) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias."))?; - - if services() - .rooms - .alias - .resolve_local_alias(&alias)? - .is_some() - { - Err(Error::BadRequest( - ErrorKind::RoomInUse, - "Room alias already exists.", - )) - } else { - Ok(Some(alias)) - } + body.room_alias_name.as_ref().map_or(Ok(None), |localpart| { + // TODO: Check for invalid characters and maximum length + let alias = RoomAliasId::parse(format!( + "#{}:{}", + localpart, + services().globals.server_name() + )) + .map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias.") })?; + if services().rooms.alias.resolve_local_alias(&alias)?.is_some() { + Err(Error::BadRequest( + ErrorKind::RoomInUse, + "Room alias already exists.", + )) + } else { + Ok(Some(alias)) + } + })?; + if let Some(alias) = &alias { if let Some(info) = &body.appservice_info { if !info.aliases.is_match(alias.as_str()) { @@ -159,7 +159,10 @@ pub(crate) async fn create_room_route( content.insert( "creator".into(), json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Invalid creation content") + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) })?, ); } @@ -171,7 +174,10 @@ pub(crate) async fn create_room_route( content.insert( "room_version".into(), json!(room_version.as_str()).try_into().map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Invalid creation content") + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) })?, ); content @@ -187,20 +193,30 @@ pub(crate) async fn create_room_route( | RoomVersionId::V7 | RoomVersionId::V8 | RoomVersionId::V9 - | RoomVersionId::V10 => RoomCreateEventContent::new_v1(sender_user.clone()), + | RoomVersionId::V10 => { + RoomCreateEventContent::new_v1(sender_user.clone()) + } RoomVersionId::V11 => RoomCreateEventContent::new_v11(), _ => unreachable!("Validity of room version already checked"), }; let mut content = serde_json::from_str::( to_raw_value(&content) - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))? + .map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) + })? .get(), ) .unwrap(); content.insert( "room_version".into(), json!(room_version.as_str()).try_into().map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Invalid creation content") + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) })?, ); content @@ -209,9 +225,7 @@ pub(crate) async fn create_room_route( // Validate creation content let de_result = serde_json::from_str::( - to_raw_value(&content) - .expect("Invalid creation content") - .get(), + to_raw_value(&content).expect("Invalid creation content").get(), ); if de_result.is_err() { @@ -228,7 +242,8 @@ pub(crate) async fn create_room_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomCreate, - content: to_raw_value(&content).expect("event is valid, we just created it"), + content: to_raw_value(&content) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), redacts: None, @@ -285,17 +300,24 @@ pub(crate) async fn create_room_route( } } - let mut power_levels_content = serde_json::to_value(RoomPowerLevelsEventContent { - users, - ..Default::default() - }) - .expect("event is valid, we just created it"); + let mut power_levels_content = + serde_json::to_value(RoomPowerLevelsEventContent { + users, + ..Default::default() + }) + .expect("event is valid, we just created it"); - if let Some(power_level_content_override) = &body.power_level_content_override { - let json: JsonObject = serde_json::from_str(power_level_content_override.json().get()) - .map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.") - })?; + if let Some(power_level_content_override) = + &body.power_level_content_override + { + let json: JsonObject = + serde_json::from_str(power_level_content_override.json().get()) + .map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Invalid power_level_content_override.", + ) + })?; for (key, value) in json { power_levels_content[key] = value; @@ -353,11 +375,13 @@ pub(crate) async fn create_room_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomJoinRules, - content: to_raw_value(&RoomJoinRulesEventContent::new(match preset { - RoomPreset::PublicChat => JoinRule::Public, - // according to spec "invite" is the default - _ => JoinRule::Invite, - })) + content: to_raw_value(&RoomJoinRulesEventContent::new( + match preset { + RoomPreset::PublicChat => JoinRule::Public, + // according to spec "invite" is the default + _ => JoinRule::Invite, + }, + )) .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), @@ -397,10 +421,12 @@ pub(crate) async fn create_room_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomGuestAccess, - content: to_raw_value(&RoomGuestAccessEventContent::new(match preset { - RoomPreset::PublicChat => GuestAccess::Forbidden, - _ => GuestAccess::CanJoin, - })) + content: to_raw_value(&RoomGuestAccessEventContent::new( + match preset { + RoomPreset::PublicChat => GuestAccess::Forbidden, + _ => GuestAccess::CanJoin, + }, + )) .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), @@ -414,10 +440,14 @@ pub(crate) async fn create_room_route( // 6. Events listed in initial_state for event in &body.initial_state { - let mut pdu_builder = event.deserialize_as::().map_err(|e| { - warn!("Invalid initial state event: {:?}", e); - Error::BadRequest(ErrorKind::InvalidParam, "Invalid initial state event.") - })?; + let mut pdu_builder = + event.deserialize_as::().map_err(|e| { + warn!("Invalid initial state event: {:?}", e); + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid initial state event.", + ) + })?; // Implicit state key defaults to "" pdu_builder.state_key.get_or_insert_with(String::new); @@ -432,7 +462,12 @@ pub(crate) async fn create_room_route( services() .rooms .timeline - .build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock) + .build_and_append_pdu( + pdu_builder, + sender_user, + &room_id, + &state_lock, + ) .await?; } @@ -444,8 +479,10 @@ pub(crate) async fn create_room_route( .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomName, - content: to_raw_value(&RoomNameEventContent::new(name.clone())) - .expect("event is valid, we just created it"), + content: to_raw_value(&RoomNameEventContent::new( + name.clone(), + )) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), redacts: None, @@ -483,7 +520,8 @@ pub(crate) async fn create_room_route( drop(state_lock); for user_id in &body.invite { if let Err(error) = - invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await + invite_helper(sender_user, user_id, &room_id, None, body.is_direct) + .await { warn!(%error, "invite helper failed"); }; @@ -507,20 +545,19 @@ pub(crate) async fn create_room_route( /// /// Gets a single event. /// -/// - You have to currently be joined to the room (TODO: Respect history visibility) +/// - You have to currently be joined to the room (TODO: Respect history +/// visibility) pub(crate) async fn get_room_event_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services() - .rooms - .timeline - .get_pdu(&body.event_id)? - .ok_or_else(|| { + let event = services().rooms.timeline.get_pdu(&body.event_id)?.ok_or_else( + || { warn!("Event not found, event ID: {:?}", &body.event_id); Error::BadRequest(ErrorKind::NotFound, "Event not found.") - })?; + }, + )?; if !services().rooms.state_accessor.user_can_see_event( sender_user, @@ -545,17 +582,14 @@ pub(crate) async fn get_room_event_route( /// /// Lists all aliases of the room. /// -/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if `history_visibility` is world readable +/// - Only users joined to the room are allowed to call this TODO: Allow any +/// user to call it if `history_visibility` is world readable pub(crate) async fn get_room_aliases_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if !services() - .rooms - .state_cache - .is_joined(sender_user, &body.room_id)? - { + if !services().rooms.state_cache.is_joined(sender_user, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -588,10 +622,7 @@ pub(crate) async fn upgrade_room_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if !services() - .globals - .supported_room_versions() - .contains(&body.new_version) + if !services().globals.supported_room_versions().contains(&body.new_version) { return Err(Error::BadRequest( ErrorKind::UnsupportedRoomVersion, @@ -601,10 +632,7 @@ pub(crate) async fn upgrade_room_route( // Create a replacement room let replacement_room = RoomId::new(services().globals.server_name()); - services() - .rooms - .short - .get_or_create_shortroomid(&replacement_room)?; + services().rooms.short.get_or_create_shortroomid(&replacement_room)?; let mutex_state = Arc::clone( services() @@ -617,8 +645,9 @@ pub(crate) async fn upgrade_room_route( ); let state_lock = mutex_state.lock().await; - // Send a m.room.tombstone event to the old room to indicate that it is not intended to be used any further - // Fail if the sender does not have the required permissions + // Send a m.room.tombstone event to the old room to indicate that it is not + // intended to be used any further Fail if the sender does not have the + // required permissions let tombstone_event_id = services() .rooms .timeline @@ -659,7 +688,9 @@ pub(crate) async fn upgrade_room_route( .rooms .state_accessor .room_state_get(&body.room_id, &StateEventType::RoomCreate, "")? - .ok_or_else(|| Error::bad_database("Found room without m.room.create event."))? + .ok_or_else(|| { + Error::bad_database("Found room without m.room.create event.") + })? .content .get(), ) @@ -671,7 +702,8 @@ pub(crate) async fn upgrade_room_route( (*tombstone_event_id).to_owned(), )); - // Send a m.room.create event containing a predecessor field and the applicable room_version + // Send a m.room.create event containing a predecessor field and the + // applicable room_version match body.new_version { RoomVersionId::V1 | RoomVersionId::V2 @@ -686,7 +718,10 @@ pub(crate) async fn upgrade_room_route( create_event_content.insert( "creator".into(), json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Error forming creation event") + Error::BadRequest( + ErrorKind::BadJson, + "Error forming creation event", + ) })?, ); } @@ -698,15 +733,21 @@ pub(crate) async fn upgrade_room_route( } create_event_content.insert( "room_version".into(), - json!(&body.new_version) - .try_into() - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?, + json!(&body.new_version).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Error forming creation event", + ) + })?, ); create_event_content.insert( "predecessor".into(), - json!(predecessor) - .try_into() - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?, + json!(predecessor).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Error forming creation event", + ) + })?, ); // Validate creation event content @@ -784,16 +825,15 @@ pub(crate) async fn upgrade_room_route( // Replicate transferable state events to the new room for event_type in transferable_state_events { - let event_content = - match services() - .rooms - .state_accessor - .room_state_get(&body.room_id, &event_type, "")? - { - Some(v) => v.content.clone(), - // Skipping missing events. - None => continue, - }; + let event_content = match services() + .rooms + .state_accessor + .room_state_get(&body.room_id, &event_type, "")? + { + Some(v) => v.content.clone(), + // Skipping missing events. + None => continue, + }; services() .rooms @@ -820,30 +860,39 @@ pub(crate) async fn upgrade_room_route( .local_aliases_for_room(&body.room_id) .filter_map(Result::ok) { - services() - .rooms - .alias - .set_alias(&alias, &replacement_room)?; + services().rooms.alias.set_alias(&alias, &replacement_room)?; } // Get the old room power levels - let mut power_levels_event_content: RoomPowerLevelsEventContent = serde_json::from_str( - services() - .rooms - .state_accessor - .room_state_get(&body.room_id, &StateEventType::RoomPowerLevels, "")? - .ok_or_else(|| Error::bad_database("Found room without m.room.create event."))? - .content - .get(), - ) - .map_err(|_| Error::bad_database("Invalid room event in database."))?; + let mut power_levels_event_content: RoomPowerLevelsEventContent = + serde_json::from_str( + services() + .rooms + .state_accessor + .room_state_get( + &body.room_id, + &StateEventType::RoomPowerLevels, + "", + )? + .ok_or_else(|| { + Error::bad_database( + "Found room without m.room.create event.", + ) + })? + .content + .get(), + ) + .map_err(|_| Error::bad_database("Invalid room event in database."))?; - // Setting events_default and invite to the greater of 50 and users_default + 1 - let new_level = max(int!(50), power_levels_event_content.users_default + int!(1)); + // Setting events_default and invite to the greater of 50 and users_default + // + 1 + let new_level = + max(int!(50), power_levels_event_content.users_default + int!(1)); power_levels_event_content.events_default = new_level; power_levels_event_content.invite = new_level; - // Modify the power levels in the old room to prevent sending of events and inviting new users + // Modify the power levels in the old room to prevent sending of events and + // inviting new users let _ = services() .rooms .timeline @@ -865,5 +914,7 @@ pub(crate) async fn upgrade_room_route( drop(state_lock); // Return the replacement room id - Ok(upgrade_room::v3::Response { replacement_room }) + Ok(upgrade_room::v3::Response { + replacement_room, + }) } diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 2b6c9f12..340eb839 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -1,22 +1,27 @@ -use crate::{services, Error, Result, Ruma}; +use std::collections::BTreeMap; + use ruma::{ api::client::{ error::ErrorKind, search::search_events::{ self, - v3::{EventContextResult, ResultCategories, ResultRoomEvents, SearchResult}, + v3::{ + EventContextResult, ResultCategories, ResultRoomEvents, + SearchResult, + }, }, }, uint, }; -use std::collections::BTreeMap; +use crate::{services, Error, Result, Ruma}; /// # `POST /_matrix/client/r0/search` /// /// Searches rooms for messages. /// -/// - Only works if the user is currently joined to the room (TODO: Respect history visibility) +/// - Only works if the user is currently joined to the room (TODO: Respect +/// history visibility) #[allow(clippy::too_many_lines)] pub(crate) async fn search_events_route( body: Ruma, @@ -46,11 +51,7 @@ pub(crate) async fn search_events_route( let mut searches = Vec::new(); for room_id in room_ids { - if !services() - .rooms - .state_cache - .is_joined(sender_user, &room_id)? - { + if !services().rooms.state_cache.is_joined(sender_user, &room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -102,7 +103,11 @@ pub(crate) async fn search_events_route( services() .rooms .state_accessor - .user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id) + .user_can_see_event( + sender_user, + &pdu.room_id, + &pdu.event_id, + ) .unwrap_or(false) }) .map(|pdu| pdu.to_room_event()) diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 1fb6ee43..3894a05f 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -1,5 +1,3 @@ -use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; -use crate::{services, utils, Error, Result, Ruma}; use ruma::{ api::client::{ error::ErrorKind, @@ -17,6 +15,9 @@ use ruma::{ use serde::Deserialize; use tracing::{info, warn}; +use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; +use crate::{services, utils, Error, Result, Ruma}; + #[derive(Debug, Deserialize)] struct Claims { sub: String, @@ -24,30 +25,36 @@ struct Claims { /// # `GET /_matrix/client/r0/login` /// -/// Get the supported login types of this server. One of these should be used as the `type` field -/// when logging in. +/// Get the supported login types of this server. One of these should be used as +/// the `type` field when logging in. pub(crate) async fn get_login_types_route( _body: Ruma, ) -> Result { Ok(get_login_types::v3::Response::new(vec![ get_login_types::v3::LoginType::Password(PasswordLoginType::default()), - get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()), + get_login_types::v3::LoginType::ApplicationService( + ApplicationServiceLoginType::default(), + ), ])) } /// # `POST /_matrix/client/r0/login` /// -/// Authenticates the user and returns an access token it can use in subsequent requests. +/// Authenticates the user and returns an access token it can use in subsequent +/// requests. /// -/// - The user needs to authenticate using their password (or if enabled using a json web token) +/// - The user needs to authenticate using their password (or if enabled using a +/// json web token) /// - If `device_id` is known: invalidates old access token of that device /// - If `device_id` is unknown: creates a new device /// - Returns access token that is associated with the user and device /// -/// Note: You can use [`GET /_matrix/client/r0/login`](get_login_types_route) to see -/// supported login types. +/// Note: You can use [`GET /_matrix/client/r0/login`](get_login_types_route) to +/// see supported login types. #[allow(clippy::too_many_lines)] -pub(crate) async fn login_route(body: Ruma) -> Result { +pub(crate) async fn login_route( + body: Ruma, +) -> Result { // To allow deprecated login methods #![allow(deprecated)] // Validate login method @@ -59,18 +66,29 @@ pub(crate) async fn login_route(body: Ruma) -> Result { - let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier { - UserId::parse_with_server_name( - user_id.to_lowercase(), - services().globals.server_name(), - ) - } else if let Some(user) = user { - UserId::parse(user) - } else { - warn!("Bad login type: {:?}", &body.login_info); - return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type.")); - } - .map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?; + let user_id = + if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = + identifier + { + UserId::parse_with_server_name( + user_id.to_lowercase(), + services().globals.server_name(), + ) + } else if let Some(user) = user { + UserId::parse(user) + } else { + warn!("Bad login type: {:?}", &body.login_info); + return Err(Error::BadRequest( + ErrorKind::Forbidden, + "Bad login type.", + )); + } + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidUsername, + "Username is invalid.", + ) + })?; if services().appservice.is_exclusive_user_id(&user_id).await { return Err(Error::BadRequest( @@ -79,13 +97,12 @@ pub(crate) async fn login_route(body: Ruma) -> Result) -> Result) -> Result { - if let Some(jwt_decoding_key) = services().globals.jwt_decoding_key() { + login::v3::LoginInfo::Token(login::v3::Token { + token, + }) => { + if let Some(jwt_decoding_key) = + services().globals.jwt_decoding_key() + { let token = jsonwebtoken::decode::( token, jwt_decoding_key, &jsonwebtoken::Validation::default(), ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid."))?; + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidUsername, + "Token is invalid.", + ) + })?; let username = token.claims.sub.to_lowercase(); - let user_id = - UserId::parse_with_server_name(username, services().globals.server_name()) - .map_err(|_| { - Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.") - })?; + let user_id = UserId::parse_with_server_name( + username, + services().globals.server_name(), + ) + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidUsername, + "Username is invalid.", + ) + })?; if services().appservice.is_exclusive_user_id(&user_id).await { return Err(Error::BadRequest( @@ -131,26 +164,40 @@ pub(crate) async fn login_route(body: Ruma) -> Result { - let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier { - UserId::parse_with_server_name( - user_id.to_lowercase(), - services().globals.server_name(), - ) - } else if let Some(user) = user { - UserId::parse(user) - } else { - warn!("Bad login type: {:?}", &body.login_info); - return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type.")); - } - .map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?; + login::v3::LoginInfo::ApplicationService( + login::v3::ApplicationService { + identifier, + user, + }, + ) => { + let user_id = + if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = + identifier + { + UserId::parse_with_server_name( + user_id.to_lowercase(), + services().globals.server_name(), + ) + } else if let Some(user) = user { + UserId::parse(user) + } else { + warn!("Bad login type: {:?}", &body.login_info); + return Err(Error::BadRequest( + ErrorKind::Forbidden, + "Bad login type.", + )); + } + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidUsername, + "Username is invalid.", + ) + })?; if let Some(info) = &body.appservice_info { if !info.is_user_match(&user_id) { @@ -225,12 +272,16 @@ pub(crate) async fn login_route(body: Ruma) -> Result) -> Result { +pub(crate) async fn logout_route( + body: Ruma, +) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let sender_device = body.sender_device.as_ref().expect("user is authenticated"); + let sender_device = + body.sender_device.as_ref().expect("user is authenticated"); if let Some(info) = &body.appservice_info { if !info.is_user_match(sender_user) { @@ -251,12 +302,13 @@ pub(crate) async fn logout_route(body: Ruma) -> Result, ) -> Result { diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index f01d888c..a9b1af35 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,19 +1,18 @@ -use crate::{services, Result, Ruma}; use ruma::{api::client::space::get_hierarchy, uint}; +use crate::{services, Result, Ruma}; + /// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy` /// -/// Paginates over the space tree in a depth-first manner to locate child rooms of a given space. +/// Paginates over the space tree in a depth-first manner to locate child rooms +/// of a given space. pub(crate) async fn get_hierarchy_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let skip = body - .from - .as_ref() - .and_then(|s| s.parse::().ok()) - .unwrap_or(0); + let skip = + body.from.as_ref().and_then(|s| s.parse::().ok()).unwrap_or(0); let limit = body .limit @@ -23,8 +22,10 @@ pub(crate) async fn get_hierarchy_route( .expect("0-100 should fit in usize"); // Plus one to skip the space room itself - let max_depth = usize::try_from(body.max_depth.map(|x| x.min(uint!(10))).unwrap_or(uint!(3))) - .expect("0-10 should fit in usize") + let max_depth = usize::try_from( + body.max_depth.map(|x| x.min(uint!(10))).unwrap_or(uint!(3)), + ) + .expect("0-10 should fit in usize") + 1; services() diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 518ae621..ace26880 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -1,25 +1,30 @@ use std::sync::Arc; -use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma, RumaResponse}; use ruma::{ api::client::{ error::ErrorKind, state::{get_state_events, get_state_events_for_key, send_state_event}, }, events::{ - room::canonical_alias::RoomCanonicalAliasEventContent, AnyStateEventContent, StateEventType, + room::canonical_alias::RoomCanonicalAliasEventContent, + AnyStateEventContent, StateEventType, }, serde::Raw, EventId, RoomId, UserId, }; use tracing::log::warn; +use crate::{ + service::pdu::PduBuilder, services, Error, Result, Ruma, RumaResponse, +}; + /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}` /// /// Sends a state event into the room. /// /// - The only requirement for the content is that it has to be valid json -/// - Tries to send the event into the room, auth rules will determine if it is allowed +/// - Tries to send the event into the room, auth rules will determine if it is +/// allowed /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_key_route( body: Ruma, @@ -37,7 +42,9 @@ pub(crate) async fn send_state_event_for_key_route( .await?; let event_id = (*event_id).to_owned(); - Ok(send_state_event::v3::Response { event_id }) + Ok(send_state_event::v3::Response { + event_id, + }) } /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}` @@ -45,7 +52,8 @@ pub(crate) async fn send_state_event_for_key_route( /// Sends a state event into the room. /// /// - The only requirement for the content is that it has to be valid json -/// - Tries to send the event into the room, auth rules will determine if it is allowed +/// - Tries to send the event into the room, auth rules will determine if it is +/// allowed /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_empty_key_route( body: Ruma, @@ -53,7 +61,9 @@ pub(crate) async fn send_state_event_for_empty_key_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // Forbid m.room.encryption if encryption is disabled - if body.event_type == StateEventType::RoomEncryption && !services().globals.allow_encryption() { + if body.event_type == StateEventType::RoomEncryption + && !services().globals.allow_encryption() + { return Err(Error::BadRequest( ErrorKind::Forbidden, "Encryption has been disabled", @@ -70,14 +80,18 @@ pub(crate) async fn send_state_event_for_empty_key_route( .await?; let event_id = (*event_id).to_owned(); - Ok(send_state_event::v3::Response { event_id }.into()) + Ok(send_state_event::v3::Response { + event_id, + } + .into()) } /// # `GET /_matrix/client/r0/rooms/{roomid}/state` /// /// Get all state events for a room. /// -/// - If not joined: Only works if current room history visibility is world readable +/// - If not joined: Only works if current room history visibility is world +/// readable pub(crate) async fn get_state_events_route( body: Ruma, ) -> Result { @@ -110,7 +124,8 @@ pub(crate) async fn get_state_events_route( /// /// Get single state event of a room. /// -/// - If not joined: Only works if current room history visibility is world readable +/// - If not joined: Only works if current room history visibility is world +/// readable pub(crate) async fn get_state_events_for_key_route( body: Ruma, ) -> Result { @@ -140,8 +155,9 @@ pub(crate) async fn get_state_events_for_key_route( })?; Ok(get_state_events_for_key::v3::Response { - content: serde_json::from_str(event.content.get()) - .map_err(|_| Error::bad_database("Invalid event content in database"))?, + content: serde_json::from_str(event.content.get()).map_err(|_| { + Error::bad_database("Invalid event content in database") + })?, }) } @@ -149,7 +165,8 @@ pub(crate) async fn get_state_events_for_key_route( /// /// Get single state event of a room. /// -/// - If not joined: Only works if current room history visibility is world readable +/// - If not joined: Only works if current room history visibility is world +/// readable pub(crate) async fn get_state_events_for_empty_key_route( body: Ruma, ) -> Result> { @@ -179,8 +196,9 @@ pub(crate) async fn get_state_events_for_empty_key_route( })?; Ok(get_state_events_for_key::v3::Response { - content: serde_json::from_str(event.content.get()) - .map_err(|_| Error::bad_database("Invalid event content in database"))?, + content: serde_json::from_str(event.content.get()).map_err(|_| { + Error::bad_database("Invalid event content in database") + })?, } .into()) } @@ -194,10 +212,11 @@ async fn send_state_event_for_key_helper( ) -> Result> { let sender_user = sender; - // TODO: Review this check, error if event is unparsable, use event type, allow alias if it - // previously existed - if let Ok(canonical_alias) = - serde_json::from_str::(json.json().get()) + // TODO: Review this check, error if event is unparsable, use event type, + // allow alias if it previously existed + if let Ok(canonical_alias) = serde_json::from_str::< + RoomCanonicalAliasEventContent, + >(json.json().get()) { let mut aliases = canonical_alias.alt_aliases.clone(); @@ -216,8 +235,8 @@ async fn send_state_event_for_key_helper( { return Err(Error::BadRequest( ErrorKind::Forbidden, - "You are only allowed to send canonical_alias \ - events when it's aliases already exists", + "You are only allowed to send canonical_alias events when \ + it's aliases already exists", )); } } @@ -240,7 +259,8 @@ async fn send_state_event_for_key_helper( .build_and_append_pdu( PduBuilder { event_type: event_type.to_string().into(), - content: serde_json::from_str(json.json().get()).expect("content is valid json"), + content: serde_json::from_str(json.json().get()) + .expect("content is valid json"), unsigned: None, state_key: Some(state_key), redacts: None, diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index cc2d56d5..71ec8497 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1,6 +1,7 @@ -use crate::{ - service::{pdu::EventHash, rooms::timeline::PduCount}, - services, utils, Error, PduEvent, Result, Ruma, RumaResponse, +use std::{ + collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + sync::Arc, + time::Duration, }; use ruma::{ @@ -9,8 +10,9 @@ use ruma::{ sync::sync_events::{ self, v3::{ - Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, - LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice, + Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, + JoinedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, + Rooms, State, Timeline, ToDevice, }, v4::SlidingOp, DeviceLists, UnreadNotificationsCount, @@ -21,21 +23,23 @@ use ruma::{ room::member::{MembershipState, RoomMemberEventContent}, StateEventType, TimelineEventType, }, - uint, DeviceId, EventId, JsOption, OwnedDeviceId, OwnedUserId, RoomId, UInt, UserId, -}; -use std::{ - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, - sync::Arc, - time::Duration, + uint, DeviceId, EventId, JsOption, OwnedDeviceId, OwnedUserId, RoomId, + UInt, UserId, }; use tokio::sync::watch::Sender; use tracing::{debug, error, info}; +use crate::{ + service::{pdu::EventHash, rooms::timeline::PduCount}, + services, utils, Error, PduEvent, Result, Ruma, RumaResponse, +}; + /// # `GET /_matrix/client/r0/sync` /// /// Synchronize the client's state with the latest state on the server. /// -/// - This endpoint takes a `since` parameter which should be the `next_batch` value from a +/// - This endpoint takes a `since` parameter which should be the `next_batch` +/// value from a /// previous request for incremental syncs. /// /// Calling this endpoint without a `since` parameter returns: @@ -44,26 +48,34 @@ use tracing::{debug, error, info}; /// - Joined and invited member counts, heroes /// - All state events /// -/// Calling this endpoint with a `since` parameter from a previous `next_batch` returns: -/// For joined rooms: -/// - Some of the most recent events of each timeline that happened after `since` -/// - If user joined the room after `since`: All state events (unless lazy loading is activated) and +/// Calling this endpoint with a `since` parameter from a previous `next_batch` +/// returns: For joined rooms: +/// - Some of the most recent events of each timeline that happened after +/// `since` +/// - If user joined the room after `since`: All state events (unless lazy +/// loading is activated) and /// all device list updates in that room -/// - If the user was already in the room: A list of all events that are in the state now, but were +/// - If the user was already in the room: A list of all events that are in the +/// state now, but were /// not in the state at `since` -/// - If the state we send contains a member event: Joined and invited member counts, heroes +/// - If the state we send contains a member event: Joined and invited member +/// counts, heroes /// - Device list updates that happened after `since` -/// - If there are events in the timeline we send or the user send updated their read mark: Notification counts +/// - If there are events in the timeline we send or the user send updated their +/// read mark: Notification counts /// - EDUs that are active now (read receipts, typing updates, presence) /// - TODO: Allow multiple sync streams to support Pantalaimon /// /// For invited rooms: -/// - If the user was invited after `since`: A subset of the state of the room at the point of the invite +/// - If the user was invited after `since`: A subset of the state of the room +/// at the point of the invite /// /// For left rooms: -/// - If the user left after `since`: `prev_batch` token, empty state (TODO: subset of the state at the point of the leave) +/// - If the user left after `since`: `prev_batch` token, empty state (TODO: +/// subset of the state at the point of the leave) /// -/// - Sync is handled in an async task, multiple requests from the same device with the same +/// - Sync is handled in an async task, multiple requests from the same device +/// with the same /// `since` will be cached pub(crate) async fn sync_events_route( body: Ruma, @@ -154,7 +166,8 @@ async fn sync_helper_wrapper( .entry((sender_user, sender_device)) { Entry::Occupied(o) => { - // Only remove if the device didn't start a different /sync already + // Only remove if the device didn't start a different /sync + // already if o.get().0 == since { o.remove(); } @@ -164,8 +177,7 @@ async fn sync_helper_wrapper( } } - tx.send(Some(r.map(|(r, _)| r))) - .expect("receiver should not be dropped"); + tx.send(Some(r.map(|(r, _)| r))).expect("receiver should not be dropped"); } #[allow(clippy::too_many_lines)] @@ -192,21 +204,19 @@ async fn sync_helper( .unwrap_or_default(), }; - let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options { - LazyLoadOptions::Enabled { - include_redundant_members: redundant, - } => (true, redundant), - LazyLoadOptions::Disabled => (false, false), - }; + let (lazy_load_enabled, lazy_load_send_redundant) = + match filter.room.state.lazy_load_options { + LazyLoadOptions::Enabled { + include_redundant_members: redundant, + } => (true, redundant), + LazyLoadOptions::Disabled => (false, false), + }; let full_state = body.full_state; let mut joined_rooms = BTreeMap::new(); - let since = body - .since - .as_ref() - .and_then(|string| string.parse().ok()) - .unwrap_or(0); + let since = + body.since.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); let sincecount = PduCount::Normal(since); // Users that have left any encrypted rooms the sender was in @@ -252,11 +262,8 @@ async fn sync_helper( } let mut left_rooms = BTreeMap::new(); - let all_left_rooms: Vec<_> = services() - .rooms - .state_cache - .rooms_left(&sender_user) - .collect(); + let all_left_rooms: Vec<_> = + services().rooms.state_cache.rooms_left(&sender_user).collect(); for result in all_left_rooms { let (room_id, _) = result?; @@ -294,7 +301,8 @@ async fn sync_helper( .try_into() .expect("Timestamp is valid js_int value"), kind: TimelineEventType::RoomMember, - content: serde_json::from_str(r#"{ "membership": "leave"}"#).unwrap(), + content: serde_json::from_str(r#"{ "membership": "leave"}"#) + .unwrap(), state_key: Some(sender_user.to_string()), unsigned: None, // The following keys are dropped on conversion @@ -312,7 +320,9 @@ async fn sync_helper( left_rooms.insert( room_id, LeftRoom { - account_data: RoomAccountData { events: Vec::new() }, + account_data: RoomAccountData { + events: Vec::new(), + }, timeline: Timeline { limited: false, prev_batch: Some(next_batch_string.clone()), @@ -329,21 +339,22 @@ async fn sync_helper( let mut left_state_events = Vec::new(); - let since_shortstatehash = services() - .rooms - .user - .get_token_shortstatehash(&room_id, since)?; + let since_shortstatehash = + services().rooms.user.get_token_shortstatehash(&room_id, since)?; let since_state_ids = match since_shortstatehash { - Some(s) => services().rooms.state_accessor.state_full_ids(s).await?, + Some(s) => { + services().rooms.state_accessor.state_full_ids(s).await? + } None => HashMap::new(), }; - let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id( - &room_id, - &StateEventType::RoomMember, - sender_user.as_str(), - )? + let Some(left_event_id) = + services().rooms.state_accessor.room_state_get_id( + &room_id, + &StateEventType::RoomMember, + sender_user.as_str(), + )? else { error!("Left room but no left state event"); continue; @@ -364,10 +375,11 @@ async fn sync_helper( .state_full_ids(left_shortstatehash) .await?; - let leave_shortstatekey = services() - .rooms - .short - .get_or_create_shortstatekey(&StateEventType::RoomMember, sender_user.as_str())?; + let leave_shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &StateEventType::RoomMember, + sender_user.as_str(), + )?; left_state_ids.insert(leave_shortstatekey, left_event_id); @@ -383,7 +395,8 @@ async fn sync_helper( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? + else { error!("Pdu in state not found: {}", id); continue; }; @@ -401,7 +414,9 @@ async fn sync_helper( left_rooms.insert( room_id.clone(), LeftRoom { - account_data: RoomAccountData { events: Vec::new() }, + account_data: RoomAccountData { + events: Vec::new(), + }, timeline: Timeline { limited: false, prev_batch: Some(next_batch_string.clone()), @@ -415,11 +430,8 @@ async fn sync_helper( } let mut invited_rooms = BTreeMap::new(); - let all_invited_rooms: Vec<_> = services() - .rooms - .state_cache - .rooms_invited(&sender_user) - .collect(); + let all_invited_rooms: Vec<_> = + services().rooms.state_cache.rooms_invited(&sender_user).collect(); for result in all_invited_rooms { let (room_id, invite_state_events) = result?; @@ -469,23 +481,29 @@ async fn sync_helper( services() .rooms .state_accessor - .room_state_get(&other_room_id, &StateEventType::RoomEncryption, "") + .room_state_get( + &other_room_id, + &StateEventType::RoomEncryption, + "", + ) .ok()? .is_some(), ) }) .all(|encrypted| !encrypted); - // If the user doesn't share an encrypted room with the target anymore, we need to tell - // them + // If the user doesn't share an encrypted room with the target anymore, + // we need to tell them if dont_share_encrypted_room { device_list_left.insert(user_id); } } // Remove all to-device events the device received *last time* - services() - .users - .remove_to_device_events(&sender_user, &sender_device, since)?; + services().users.remove_to_device_events( + &sender_user, + &sender_device, + since, + )?; let response = sync_events::v3::Response { next_batch: next_batch_string, @@ -504,7 +522,11 @@ async fn sync_helper( .into_iter() .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) - .map_err(|_| Error::bad_database("Invalid account event in database.")) + .map_err(|_| { + Error::bad_database( + "Invalid account event in database.", + ) + }) .ok() }) .collect(), @@ -581,7 +603,8 @@ async fn load_joined_room( drop(insert_lock); } - let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?; + let (timeline_pdus, limited) = + load_timeline(sender_user, room_id, sincecount, 10)?; let send_notification_counts = !timeline_pdus.is_empty() || services() @@ -598,104 +621,126 @@ async fn load_joined_room( services() .rooms .lazy_loading - .lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount) + .lazy_load_confirm_delivery( + sender_user, + sender_device, + room_id, + sincecount, + ) .await?; // Database queries: - let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? + let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? else { error!("Room {} has no state", room_id); return Err(Error::BadDatabase("Room has no state")); }; - let since_shortstatehash = services() - .rooms - .user - .get_token_shortstatehash(room_id, since)?; + let since_shortstatehash = + services().rooms.user.get_token_shortstatehash(room_id, since)?; - let (heroes, joined_member_count, invited_member_count, joined_since_last_sync, state_events) = - if timeline_pdus.is_empty() && since_shortstatehash == Some(current_shortstatehash) { - // No state changes - (Vec::new(), None, None, false, Vec::new()) - } else { - // Calculates joined_member_count, invited_member_count and heroes - let calculate_counts = || { - let joined_member_count = services() + let ( + heroes, + joined_member_count, + invited_member_count, + joined_since_last_sync, + state_events, + ) = if timeline_pdus.is_empty() + && since_shortstatehash == Some(current_shortstatehash) + { + // No state changes + (Vec::new(), None, None, false, Vec::new()) + } else { + // Calculates joined_member_count, invited_member_count and heroes + let calculate_counts = || { + let joined_member_count = services() + .rooms + .state_cache + .room_joined_count(room_id)? + .unwrap_or(0); + let invited_member_count = services() + .rooms + .state_cache + .room_invited_count(room_id)? + .unwrap_or(0); + + // Recalculate heroes (first 5 members) + let mut heroes = Vec::new(); + + if joined_member_count + invited_member_count <= 5 { + // Go through all PDUs and for each member event, check if the + // user is still joined or invited until we have + // 5 or we reach the end + + for hero in services() .rooms - .state_cache - .room_joined_count(room_id)? - .unwrap_or(0); - let invited_member_count = services() - .rooms - .state_cache - .room_invited_count(room_id)? - .unwrap_or(0); + .timeline + .all_pdus(sender_user, room_id)? + .filter_map(Result::ok) + .filter(|(_, pdu)| { + pdu.kind == TimelineEventType::RoomMember + }) + .map(|(_, pdu)| { + let content: RoomMemberEventContent = + serde_json::from_str(pdu.content.get()).map_err( + |_| { + Error::bad_database( + "Invalid member event in database.", + ) + }, + )?; - // Recalculate heroes (first 5 members) - let mut heroes = Vec::new(); - - if joined_member_count + invited_member_count <= 5 { - // Go through all PDUs and for each member event, check if the user is still joined or - // invited until we have 5 or we reach the end - - for hero in services() - .rooms - .timeline - .all_pdus(sender_user, room_id)? - .filter_map(Result::ok) - .filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember) - .map(|(_, pdu)| { - let content: RoomMemberEventContent = - serde_json::from_str(pdu.content.get()).map_err(|_| { - Error::bad_database("Invalid member event in database.") + if let Some(state_key) = &pdu.state_key { + let user_id = UserId::parse(state_key.clone()) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in member PDU.", + ) })?; - if let Some(state_key) = &pdu.state_key { - let user_id = UserId::parse(state_key.clone()).map_err(|_| { - Error::bad_database("Invalid UserId in member PDU.") - })?; - - // The membership was and still is invite or join - if matches!( - content.membership, - MembershipState::Join | MembershipState::Invite - ) && (services() + // The membership was and still is invite or join + if matches!( + content.membership, + MembershipState::Join | MembershipState::Invite + ) && (services() + .rooms + .state_cache + .is_joined(&user_id, room_id)? + || services() .rooms .state_cache - .is_joined(&user_id, room_id)? - || services() - .rooms - .state_cache - .is_invited(&user_id, room_id)?) - { - Ok::<_, Error>(Some(state_key.clone())) - } else { - Ok(None) - } + .is_invited(&user_id, room_id)?) + { + Ok::<_, Error>(Some(state_key.clone())) } else { Ok(None) } - }) - .filter_map(Result::ok) - .flatten() - { - if heroes.contains(&hero) || hero == sender_user.as_str() { - continue; + } else { + Ok(None) } - - heroes.push(hero); + }) + .filter_map(Result::ok) + .flatten() + { + if heroes.contains(&hero) || hero == sender_user.as_str() { + continue; } + + heroes.push(hero); } + } - Ok::<_, Error>(( - Some(joined_member_count), - Some(invited_member_count), - heroes, - )) - }; + Ok::<_, Error>(( + Some(joined_member_count), + Some(invited_member_count), + heroes, + )) + }; - let since_sender_member: Option = since_shortstatehash + let since_sender_member: Option = + since_shortstatehash .and_then(|shortstatehash| { services() .rooms @@ -710,270 +755,303 @@ async fn load_joined_room( .transpose()? .and_then(|pdu| { serde_json::from_str(pdu.content.get()) - .map_err(|_| Error::bad_database("Invalid PDU in database.")) + .map_err(|_| { + Error::bad_database("Invalid PDU in database.") + }) .ok() }); - let joined_since_last_sync = since_sender_member - .map_or(true, |member| member.membership != MembershipState::Join); + let joined_since_last_sync = since_sender_member + .map_or(true, |member| member.membership != MembershipState::Join); - if since_shortstatehash.is_none() || joined_since_last_sync { - // Probably since = 0, we will do an initial sync + if since_shortstatehash.is_none() || joined_since_last_sync { + // Probably since = 0, we will do an initial sync - let (joined_member_count, invited_member_count, heroes) = calculate_counts()?; + let (joined_member_count, invited_member_count, heroes) = + calculate_counts()?; + let current_state_ids = services() + .rooms + .state_accessor + .state_full_ids(current_shortstatehash) + .await?; + + let mut state_events = Vec::new(); + let mut lazy_loaded = HashSet::new(); + + let mut i = 0; + for (shortstatekey, id) in current_state_ids { + let (event_type, state_key) = services() + .rooms + .short + .get_statekey_from_short(shortstatekey)?; + + if event_type != StateEventType::RoomMember { + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? + else { + error!("Pdu in state not found: {}", id); + continue; + }; + state_events.push(pdu); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } else if !lazy_load_enabled + || full_state + || timeline_users.contains(&state_key) + // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 + || *sender_user == state_key + { + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? + else { + error!("Pdu in state not found: {}", id); + continue; + }; + + // This check is in case a bad user ID made it into the + // database + if let Ok(uid) = UserId::parse(&state_key) { + lazy_loaded.insert(uid); + } + state_events.push(pdu); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } + } + + // Reset lazy loading because this is an initial sync + services().rooms.lazy_loading.lazy_load_reset( + sender_user, + sender_device, + room_id, + )?; + + // The state_events above should contain all timeline_users, let's + // mark them as lazy loaded. + services() + .rooms + .lazy_loading + .lazy_load_mark_sent( + sender_user, + sender_device, + room_id, + lazy_loaded, + next_batchcount, + ) + .await; + + ( + heroes, + joined_member_count, + invited_member_count, + true, + state_events, + ) + } else { + // Incremental /sync + let since_shortstatehash = since_shortstatehash.unwrap(); + + let mut state_events = Vec::new(); + let mut lazy_loaded = HashSet::new(); + + if since_shortstatehash != current_shortstatehash { let current_state_ids = services() .rooms .state_accessor .state_full_ids(current_shortstatehash) .await?; + let since_state_ids = services() + .rooms + .state_accessor + .state_full_ids(since_shortstatehash) + .await?; - let mut state_events = Vec::new(); - let mut lazy_loaded = HashSet::new(); - - let mut i = 0; - for (shortstatekey, id) in current_state_ids { - let (event_type, state_key) = services() - .rooms - .short - .get_statekey_from_short(shortstatekey)?; - - if event_type != StateEventType::RoomMember { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { - error!("Pdu in state not found: {}", id); - continue; - }; - state_events.push(pdu); - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } else if !lazy_load_enabled - || full_state - || timeline_users.contains(&state_key) - // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 - || *sender_user == state_key - { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + for (key, id) in current_state_ids { + if full_state || since_state_ids.get(&key) != Some(&id) { + let Some(pdu) = + services().rooms.timeline.get_pdu(&id)? + else { error!("Pdu in state not found: {}", id); continue; }; - // This check is in case a bad user ID made it into the database - if let Ok(uid) = UserId::parse(&state_key) { - lazy_loaded.insert(uid); + if pdu.kind == TimelineEventType::RoomMember { + match UserId::parse( + pdu.state_key + .as_ref() + .expect("State event has state key") + .clone(), + ) { + Ok(state_key_userid) => { + lazy_loaded.insert(state_key_userid); + } + Err(e) => error!( + "Invalid state key for member event: {}", + e + ), + } } - state_events.push(pdu); - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } + state_events.push(pdu); + tokio::task::yield_now().await; } } + } - // Reset lazy loading because this is an initial sync - services().rooms.lazy_loading.lazy_load_reset( + for (_, event) in &timeline_pdus { + if lazy_loaded.contains(&event.sender) { + continue; + } + + if !services().rooms.lazy_loading.lazy_load_was_sent_before( sender_user, sender_device, room_id, - )?; - - // The state_events above should contain all timeline_users, let's mark them as lazy - // loaded. - services() - .rooms - .lazy_loading - .lazy_load_mark_sent( - sender_user, - sender_device, - room_id, - lazy_loaded, - next_batchcount, - ) - .await; - - ( - heroes, - joined_member_count, - invited_member_count, - true, - state_events, - ) - } else { - // Incremental /sync - let since_shortstatehash = since_shortstatehash.unwrap(); - - let mut state_events = Vec::new(); - let mut lazy_loaded = HashSet::new(); - - if since_shortstatehash != current_shortstatehash { - let current_state_ids = services() - .rooms - .state_accessor - .state_full_ids(current_shortstatehash) - .await?; - let since_state_ids = services() - .rooms - .state_accessor - .state_full_ids(since_shortstatehash) - .await?; - - for (key, id) in current_state_ids { - if full_state || since_state_ids.get(&key) != Some(&id) { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { - error!("Pdu in state not found: {}", id); - continue; - }; - - if pdu.kind == TimelineEventType::RoomMember { - match UserId::parse( - pdu.state_key - .as_ref() - .expect("State event has state key") - .clone(), - ) { - Ok(state_key_userid) => { - lazy_loaded.insert(state_key_userid); - } - Err(e) => error!("Invalid state key for member event: {}", e), - } - } - - state_events.push(pdu); - tokio::task::yield_now().await; - } - } - } - - for (_, event) in &timeline_pdus { - if lazy_loaded.contains(&event.sender) { - continue; - } - - if !services().rooms.lazy_loading.lazy_load_was_sent_before( - sender_user, - sender_device, - room_id, - &event.sender, - )? || lazy_load_send_redundant - { - if let Some(member_event) = services().rooms.state_accessor.room_state_get( + &event.sender, + )? || lazy_load_send_redundant + { + if let Some(member_event) = + services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomMember, event.sender.as_str(), - )? { - lazy_loaded.insert(event.sender.clone()); - state_events.push(member_event); - } + )? + { + lazy_loaded.insert(event.sender.clone()); + state_events.push(member_event); } } + } - services() - .rooms - .lazy_loading - .lazy_load_mark_sent( - sender_user, - sender_device, - room_id, - lazy_loaded, - next_batchcount, - ) - .await; + services() + .rooms + .lazy_loading + .lazy_load_mark_sent( + sender_user, + sender_device, + room_id, + lazy_loaded, + next_batchcount, + ) + .await; - let encrypted_room = services() - .rooms - .state_accessor - .state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")? - .is_some(); - - let since_encryption = services().rooms.state_accessor.state_get( - since_shortstatehash, + let encrypted_room = services() + .rooms + .state_accessor + .state_get( + current_shortstatehash, &StateEventType::RoomEncryption, "", - )?; + )? + .is_some(); - // Calculations: - let new_encrypted_room = encrypted_room && since_encryption.is_none(); + let since_encryption = services().rooms.state_accessor.state_get( + since_shortstatehash, + &StateEventType::RoomEncryption, + "", + )?; - let send_member_count = state_events - .iter() - .any(|event| event.kind == TimelineEventType::RoomMember); + // Calculations: + let new_encrypted_room = + encrypted_room && since_encryption.is_none(); - if encrypted_room { - for state_event in &state_events { - if state_event.kind != TimelineEventType::RoomMember { + let send_member_count = state_events + .iter() + .any(|event| event.kind == TimelineEventType::RoomMember); + + if encrypted_room { + for state_event in &state_events { + if state_event.kind != TimelineEventType::RoomMember { + continue; + } + + if let Some(state_key) = &state_event.state_key { + let user_id = UserId::parse(state_key.clone()) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in member PDU.", + ) + })?; + + if user_id == sender_user { continue; } - if let Some(state_key) = &state_event.state_key { - let user_id = UserId::parse(state_key.clone()).map_err(|_| { - Error::bad_database("Invalid UserId in member PDU.") - })?; - - if user_id == sender_user { - continue; - } - - let new_membership = serde_json::from_str::( + let new_membership = + serde_json::from_str::( state_event.content.get(), ) - .map_err(|_| Error::bad_database("Invalid PDU in database."))? + .map_err(|_| { + Error::bad_database("Invalid PDU in database.") + })? .membership; - match new_membership { - MembershipState::Join => { - // A new user joined an encrypted room - if !share_encrypted_room(sender_user, &user_id, room_id)? { - device_list_updates.insert(user_id); - } + match new_membership { + MembershipState::Join => { + // A new user joined an encrypted room + if !share_encrypted_room( + sender_user, + &user_id, + room_id, + )? { + device_list_updates.insert(user_id); } - MembershipState::Leave => { - // Write down users that have left encrypted rooms we are in - left_encrypted_users.insert(user_id); - } - _ => {} } + MembershipState::Leave => { + // Write down users that have left encrypted + // rooms we are in + left_encrypted_users.insert(user_id); + } + _ => {} } } } + } - if joined_since_last_sync && encrypted_room || new_encrypted_room { - // If the user is in a new encrypted room, give them all joined users - device_list_updates.extend( - services() - .rooms - .state_cache - .room_members(room_id) - .flatten() - .filter(|user_id| { - // Don't send key updates from the sender to the sender - sender_user != user_id - }) - .filter(|user_id| { - // Only send keys if the sender doesn't share an encrypted room with the target already - !share_encrypted_room(sender_user, user_id, room_id) - .unwrap_or(false) - }), - ); - } + if joined_since_last_sync && encrypted_room || new_encrypted_room { + // If the user is in a new encrypted room, give them all joined + // users + device_list_updates.extend( + services() + .rooms + .state_cache + .room_members(room_id) + .flatten() + .filter(|user_id| { + // Don't send key updates from the sender to the + // sender + sender_user != user_id + }) + .filter(|user_id| { + // Only send keys if the sender doesn't share an + // encrypted room with the target already + !share_encrypted_room(sender_user, user_id, room_id) + .unwrap_or(false) + }), + ); + } - let (joined_member_count, invited_member_count, heroes) = if send_member_count { + let (joined_member_count, invited_member_count, heroes) = + if send_member_count { calculate_counts()? } else { (None, None, Vec::new()) }; - ( - heroes, - joined_member_count, - invited_member_count, - joined_since_last_sync, - state_events, - ) - } - }; + ( + heroes, + joined_member_count, + invited_member_count, + joined_since_last_sync, + state_events, + ) + } + }; // Look for device list updates in this room device_list_updates.extend( @@ -984,12 +1062,7 @@ async fn load_joined_room( ); let notification_count = send_notification_counts - .then(|| { - services() - .rooms - .user - .notification_count(sender_user, room_id) - }) + .then(|| services().rooms.user.notification_count(sender_user, room_id)) .transpose()? .map(|x| x.try_into().expect("notification count can't go that high")); @@ -998,9 +1071,9 @@ async fn load_joined_room( .transpose()? .map(|x| x.try_into().expect("highlight count can't go that high")); - let prev_batch = timeline_pdus - .first() - .map_or(Ok::<_, Error>(None), |(pdu_count, _)| { + 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?!"); @@ -1008,12 +1081,11 @@ async fn load_joined_room( } PduCount::Normal(c) => c.to_string(), })) - })?; + }, + )?; - let room_events: Vec<_> = timeline_pdus - .iter() - .map(|(_, pdu)| pdu.to_sync_room_event()) - .collect(); + let room_events: Vec<_> = + timeline_pdus.iter().map(|(_, pdu)| pdu.to_sync_room_event()).collect(); let mut edus: Vec<_> = services() .rooms @@ -1024,24 +1096,20 @@ async fn load_joined_room( .map(|(_, _, v)| v) .collect(); - if services() - .rooms - .edus - .typing - .last_typing_update(room_id) - .await? - > since - { + if services().rooms.edus.typing.last_typing_update(room_id).await? > since { edus.push( serde_json::from_str( - &serde_json::to_string(&services().rooms.edus.typing.typings_all(room_id).await?) - .expect("event is valid, we just created it"), + &serde_json::to_string( + &services().rooms.edus.typing.typings_all(room_id).await?, + ) + .expect("event is valid, we just created it"), ) .expect("event is valid, we just created it"), ); } - // Save the state after this sync so we can send the correct state diff next sync + // Save the state after this sync so we can send the correct state diff next + // sync services().rooms.user.associate_token_shortstatehash( room_id, next_batch, @@ -1056,7 +1124,11 @@ async fn load_joined_room( .into_iter() .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) - .map_err(|_| Error::bad_database("Invalid account event in database.")) + .map_err(|_| { + Error::bad_database( + "Invalid account event in database.", + ) + }) .ok() }) .collect(), @@ -1064,7 +1136,8 @@ async fn load_joined_room( summary: RoomSummary { heroes, joined_member_count: joined_member_count.map(UInt::new_saturating), - invited_member_count: invited_member_count.map(UInt::new_saturating), + invited_member_count: invited_member_count + .map(UInt::new_saturating), }, unread_notifications: UnreadNotificationsCount { highlight_count, @@ -1081,7 +1154,9 @@ async fn load_joined_room( .map(|pdu| pdu.to_sync_state_event()) .collect(), }, - ephemeral: Ephemeral { events: edus }, + ephemeral: Ephemeral { + events: edus, + }, unread_thread_notifications: BTreeMap::new(), }) } @@ -1094,10 +1169,7 @@ fn load_timeline( ) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> { let timeline_pdus; let limited; - if services() - .rooms - .timeline - .last_timeline_count(sender_user, room_id)? + if services().rooms.timeline.last_timeline_count(sender_user, room_id)? > roomsincecount { let mut non_timeline_pdus = services() @@ -1121,8 +1193,9 @@ fn load_timeline( .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 + // 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(); @@ -1147,7 +1220,11 @@ fn share_encrypted_room( services() .rooms .state_accessor - .room_state_get(&other_room_id, &StateEventType::RoomEncryption, "") + .room_state_get( + &other_room_id, + &StateEventType::RoomEncryption, + "", + ) .ok()? .is_some(), ) @@ -1167,11 +1244,8 @@ pub(crate) async fn sync_events_v4_route( let next_batch = services().globals.next_count()?; - let globalsince = body - .pos - .as_ref() - .and_then(|string| string.parse().ok()) - .unwrap_or(0); + let globalsince = + body.pos.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); if globalsince == 0 { if let Some(conn_id) = &body.conn_id { @@ -1198,9 +1272,11 @@ pub(crate) async fn sync_events_v4_route( .collect::>(); if body.extensions.to_device.enabled.unwrap_or(false) { - services() - .users - .remove_to_device_events(&sender_user, &sender_device, globalsince)?; + services().users.remove_to_device_events( + &sender_user, + &sender_device, + globalsince, + )?; } // Users that have left any encrypted rooms the sender was in @@ -1230,29 +1306,36 @@ pub(crate) async fn sync_events_v4_route( .user .get_token_shortstatehash(room_id, globalsince)?; - let since_sender_member: Option = since_shortstatehash - .and_then(|shortstatehash| { - services() - .rooms - .state_accessor - .state_get( - shortstatehash, - &StateEventType::RoomMember, - sender_user.as_str(), - ) - .transpose() - }) - .transpose()? - .and_then(|pdu| { - serde_json::from_str(pdu.content.get()) - .map_err(|_| Error::bad_database("Invalid PDU in database.")) - .ok() - }); + let since_sender_member: Option = + since_shortstatehash + .and_then(|shortstatehash| { + services() + .rooms + .state_accessor + .state_get( + shortstatehash, + &StateEventType::RoomMember, + sender_user.as_str(), + ) + .transpose() + }) + .transpose()? + .and_then(|pdu| { + serde_json::from_str(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid PDU in database.") + }) + .ok() + }); let encrypted_room = services() .rooms .state_accessor - .state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")? + .state_get( + current_shortstatehash, + &StateEventType::RoomEncryption, + "", + )? .is_some(); if let Some(since_shortstatehash) = since_shortstatehash { @@ -1261,16 +1344,20 @@ pub(crate) async fn sync_events_v4_route( continue; } - let since_encryption = services().rooms.state_accessor.state_get( - since_shortstatehash, - &StateEventType::RoomEncryption, - "", - )?; + let since_encryption = + services().rooms.state_accessor.state_get( + since_shortstatehash, + &StateEventType::RoomEncryption, + "", + )?; let joined_since_last_sync = since_sender_member - .map_or(true, |member| member.membership != MembershipState::Join); + .map_or(true, |member| { + member.membership != MembershipState::Join + }); - let new_encrypted_room = encrypted_room && since_encryption.is_none(); + let new_encrypted_room = + encrypted_room && since_encryption.is_none(); if encrypted_room { let current_state_ids = services() .rooms @@ -1285,43 +1372,58 @@ pub(crate) async fn sync_events_v4_route( for (key, id) in current_state_ids { if since_state_ids.get(&key) != Some(&id) { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + let Some(pdu) = + services().rooms.timeline.get_pdu(&id)? + else { error!("Pdu in state not found: {}", id); continue; }; if pdu.kind == TimelineEventType::RoomMember { if let Some(state_key) = &pdu.state_key { let user_id = - UserId::parse(state_key.clone()).map_err(|_| { - Error::bad_database("Invalid UserId in member PDU.") - })?; + UserId::parse(state_key.clone()) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in member \ + PDU.", + ) + })?; if user_id == sender_user { continue; } - let new_membership = serde_json::from_str::< - RoomMemberEventContent, - >( - pdu.content.get() - ) - .map_err(|_| Error::bad_database("Invalid PDU in database."))? - .membership; + let new_membership = + serde_json::from_str::< + RoomMemberEventContent, + >( + pdu.content.get() + ) + .map_err(|_| { + Error::bad_database( + "Invalid PDU in database.", + ) + })? + .membership; match new_membership { MembershipState::Join => { - // A new user joined an encrypted room + // A new user joined an encrypted + // room if !share_encrypted_room( &sender_user, &user_id, room_id, )? { - device_list_changes.insert(user_id); + device_list_changes + .insert(user_id); } } MembershipState::Leave => { - // Write down users that have left encrypted rooms we are in - left_encrypted_users.insert(user_id); + // Write down users that have left + // encrypted rooms we are in + left_encrypted_users + .insert(user_id); } _ => {} } @@ -1330,7 +1432,8 @@ pub(crate) async fn sync_events_v4_route( } } if joined_since_last_sync || new_encrypted_room { - // If the user is in a new encrypted room, give them all joined users + // If the user is in a new encrypted room, give them all + // joined users device_list_changes.extend( services() .rooms @@ -1338,13 +1441,20 @@ pub(crate) async fn sync_events_v4_route( .room_members(room_id) .flatten() .filter(|user_id| { - // Don't send key updates from the sender to the sender + // Don't send key updates from the sender to + // the sender &sender_user != user_id }) .filter(|user_id| { - // Only send keys if the sender doesn't share an encrypted room with the target already - !share_encrypted_room(&sender_user, user_id, room_id) - .unwrap_or(false) + // Only send keys if the sender doesn't + // share an encrypted room with the target + // already + !share_encrypted_room( + &sender_user, + user_id, + room_id, + ) + .unwrap_or(false) }), ); } @@ -1369,14 +1479,18 @@ pub(crate) async fn sync_events_v4_route( services() .rooms .state_accessor - .room_state_get(&other_room_id, &StateEventType::RoomEncryption, "") + .room_state_get( + &other_room_id, + &StateEventType::RoomEncryption, + "", + ) .ok()? .is_some(), ) }) .all(|encrypted| !encrypted); - // If the user doesn't share an encrypted room with the target anymore, we need to tell - // them + // If the user doesn't share an encrypted room with the target + // anymore, we need to tell them if dont_share_encrypted_room { device_list_left.insert(user_id); } @@ -1403,30 +1517,36 @@ pub(crate) async fn sync_events_v4_route( .map(|mut r| { r.0 = r.0.clamp( uint!(0), - UInt::try_from(all_joined_rooms.len() - 1).unwrap_or(UInt::MAX), + UInt::try_from(all_joined_rooms.len() - 1) + .unwrap_or(UInt::MAX), ); r.1 = r.1.clamp( r.0, - UInt::try_from(all_joined_rooms.len() - 1).unwrap_or(UInt::MAX), + UInt::try_from(all_joined_rooms.len() - 1) + .unwrap_or(UInt::MAX), ); - let room_ids = all_joined_rooms[r.0.try_into().unwrap_or(usize::MAX) + let room_ids = all_joined_rooms[r + .0 + .try_into() + .unwrap_or(usize::MAX) ..=r.1.try_into().unwrap_or(usize::MAX)] .to_vec(); new_known_rooms.extend(room_ids.iter().cloned()); for room_id in &room_ids { - let todo_room = todo_rooms.entry(room_id.clone()).or_insert(( - BTreeSet::new(), - 0, - u64::MAX, - )); + let todo_room = todo_rooms + .entry(room_id.clone()) + .or_insert((BTreeSet::new(), 0, u64::MAX)); let limit = list .room_details .timeline_limit .map_or(10, u64::from) .min(100); - todo_room - .0 - .extend(list.room_details.required_state.iter().cloned()); + todo_room.0.extend( + list.room_details + .required_state + .iter() + .cloned(), + ); todo_room.1 = todo_room.1.max(limit); // 0 means unknown because it got out of date todo_room.2 = todo_room.2.min( @@ -1446,7 +1566,8 @@ pub(crate) async fn sync_events_v4_route( } }) .collect(), - count: UInt::try_from(all_joined_rooms.len()).unwrap_or(UInt::MAX), + count: UInt::try_from(all_joined_rooms.len()) + .unwrap_or(UInt::MAX), }, ); @@ -1467,9 +1588,11 @@ pub(crate) async fn sync_events_v4_route( if !services().rooms.metadata.exists(room_id)? { continue; } - let todo_room = todo_rooms - .entry(room_id.clone()) - .or_insert((BTreeSet::new(), 0, u64::MAX)); + let todo_room = todo_rooms.entry(room_id.clone()).or_insert(( + BTreeSet::new(), + 0, + u64::MAX, + )); let limit = room.timeline_limit.map_or(10, u64::from).min(100); todo_room.0.extend(room.required_state.iter().cloned()); todo_room.1 = todo_room.1.max(limit); @@ -1510,11 +1633,17 @@ pub(crate) async fn sync_events_v4_route( } let mut rooms = BTreeMap::new(); - for (room_id, (required_state_request, timeline_limit, roomsince)) in &todo_rooms { + for (room_id, (required_state_request, timeline_limit, roomsince)) in + &todo_rooms + { let roomsincecount = PduCount::Normal(*roomsince); - let (timeline_pdus, limited) = - load_timeline(&sender_user, room_id, roomsincecount, *timeline_limit)?; + let (timeline_pdus, limited) = load_timeline( + &sender_user, + room_id, + roomsincecount, + *timeline_limit, + )?; if roomsince != &0 && timeline_pdus.is_empty() { continue; @@ -1599,12 +1728,18 @@ pub(crate) async fn sync_events_v4_route( rooms.insert( room_id.clone(), sync_events::v4::SlidingSyncRoom { - name: services().rooms.state_accessor.get_name(room_id)?.or(name), + name: services() + .rooms + .state_accessor + .get_name(room_id)? + .or(name), avatar: if let Some(avatar) = avatar { JsOption::Some(avatar) } else { match services().rooms.state_accessor.get_avatar(room_id)? { - JsOption::Some(avatar) => JsOption::from_option(avatar.url), + JsOption::Some(avatar) => { + JsOption::from_option(avatar.url) + } JsOption::Null => JsOption::Null, JsOption::Undefined => JsOption::Undefined, } @@ -1707,7 +1842,8 @@ pub(crate) async fn sync_events_v4_route( device_unused_fallback_key_types: None, }, account_data: sync_events::v4::AccountData { - global: if body.extensions.account_data.enabled.unwrap_or(false) { + global: if body.extensions.account_data.enabled.unwrap_or(false) + { services() .account_data .changes_since(None, &sender_user, globalsince)? @@ -1715,7 +1851,9 @@ pub(crate) async fn sync_events_v4_route( .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) .map_err(|_| { - Error::bad_database("Invalid account event in database.") + Error::bad_database( + "Invalid account event in database.", + ) }) .ok() }) diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 616fc618..22738af8 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -1,4 +1,5 @@ -use crate::{services, Error, Result, Ruma}; +use std::collections::BTreeMap; + use ruma::{ api::client::tag::{create_tag, delete_tag, get_tags}, events::{ @@ -6,7 +7,8 @@ use ruma::{ RoomAccountDataEventType, }, }; -use std::collections::BTreeMap; + +use crate::{services, Error, Result, Ruma}; /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}` /// @@ -33,8 +35,9 @@ pub(crate) async fn update_tag_route( }) }, |e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) + serde_json::from_str(e.get()).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + }) }, )?; @@ -78,8 +81,9 @@ pub(crate) async fn delete_tag_route( }) }, |e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) + serde_json::from_str(e.get()).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + }) }, )?; @@ -120,8 +124,9 @@ pub(crate) async fn get_tags_route( }) }, |e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) + serde_json::from_str(e.get()).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + }) }, )?; diff --git a/src/api/client_server/thirdparty.rs b/src/api/client_server/thirdparty.rs index 3c678acf..8cb77e4b 100644 --- a/src/api/client_server/thirdparty.rs +++ b/src/api/client_server/thirdparty.rs @@ -1,7 +1,8 @@ -use crate::{Result, Ruma}; +use std::collections::BTreeMap; + use ruma::api::client::thirdparty::get_protocols; -use std::collections::BTreeMap; +use crate::{Result, Ruma}; /// # `GET /_matrix/client/r0/thirdparty/protocols` /// diff --git a/src/api/client_server/threads.rs b/src/api/client_server/threads.rs index c9142610..9beb8aa2 100644 --- a/src/api/client_server/threads.rs +++ b/src/api/client_server/threads.rs @@ -9,11 +9,8 @@ pub(crate) async fn get_threads_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // Use limit or else 10, with maximum 100 - let limit = body - .limit - .and_then(|l| l.try_into().ok()) - .unwrap_or(10) - .min(100); + let limit = + body.limit.and_then(|l| l.try_into().ok()).unwrap_or(10).min(100); let from = if let Some(from) = &body.from { from.parse() diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index fbaad4d9..c29c41de 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -1,6 +1,5 @@ use std::collections::BTreeMap; -use crate::{services, Error, Result, Ruma}; use ruma::{ api::{ client::{error::ErrorKind, to_device::send_event_to_device}, @@ -9,6 +8,8 @@ use ruma::{ to_device::DeviceIdOrAllDevices, }; +use crate::{services, Error, Result, Ruma}; + /// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}` /// /// Send a to-device event to a set of client devices. @@ -29,7 +30,8 @@ pub(crate) async fn send_event_to_device_route( for (target_user_id, map) in &body.messages { for (target_device_id_maybe, event) in map { - if target_user_id.server_name() != services().globals.server_name() { + if target_user_id.server_name() != services().globals.server_name() + { let mut map = BTreeMap::new(); map.insert(target_device_id_maybe.clone(), event.clone()); let mut messages = BTreeMap::new(); @@ -38,14 +40,16 @@ pub(crate) async fn send_event_to_device_route( services().sending.send_reliable_edu( target_user_id.server_name(), - serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice( - DirectDeviceContent { - sender: sender_user.clone(), - ev_type: body.event_type.clone(), - message_id: count.to_string().into(), - messages, - }, - )) + serde_json::to_vec( + &federation::transactions::edu::Edu::DirectToDevice( + DirectDeviceContent { + sender: sender_user.clone(), + ev_type: body.event_type.clone(), + message_id: count.to_string().into(), + messages, + }, + ), + ) .expect("DirectToDevice EDU can be serialized"), count, )?; @@ -61,20 +65,28 @@ pub(crate) async fn send_event_to_device_route( target_device_id, &body.event_type.to_string(), event.deserialize_as().map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid") + Error::BadRequest( + ErrorKind::InvalidParam, + "Event is invalid", + ) })?, )?; } DeviceIdOrAllDevices::AllDevices => { - for target_device_id in services().users.all_device_ids(target_user_id) { + for target_device_id in + services().users.all_device_ids(target_user_id) + { services().users.add_to_device_event( sender_user, target_user_id, &target_device_id?, &body.event_type.to_string(), event.deserialize_as().map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid") + Error::BadRequest( + ErrorKind::InvalidParam, + "Event is invalid", + ) })?, )?; } @@ -84,9 +96,12 @@ pub(crate) async fn send_event_to_device_route( } // Save transaction id with empty data - services() - .transaction_ids - .add_txnid(sender_user, sender_device, &body.txn_id, &[])?; + services().transaction_ids.add_txnid( + sender_user, + sender_device, + &body.txn_id, + &[], + )?; Ok(send_event_to_device::v3::Response {}) } diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index d9a24530..1e7b1b68 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -1,6 +1,7 @@ -use crate::{services, utils, Error, Result, Ruma}; use ruma::api::client::{error::ErrorKind, typing::create_typing_event}; +use crate::{services, utils, Error, Result, Ruma}; + /// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}` /// /// Sets the typing state of the sender user. @@ -11,11 +12,7 @@ pub(crate) async fn create_typing_event_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if !services() - .rooms - .state_cache - .is_joined(sender_user, &body.room_id)? - { + if !services().rooms.state_cache.is_joined(sender_user, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You are not in this room.", diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 27840af4..9d3dab29 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -6,14 +6,16 @@ use crate::{Result, Ruma}; /// # `GET /_matrix/client/versions` /// -/// Get the versions of the specification and unstable features supported by this server. +/// Get the versions of the specification and unstable features supported by +/// this server. /// /// - Versions take the form MAJOR.MINOR.PATCH /// - Only the latest PATCH release will be reported for each MAJOR.MINOR value -/// - Unstable features are namespaced and may include version information in their name +/// - Unstable features are namespaced and may include version information in +/// their name /// -/// Note: Unstable features are used while developing new features. Clients should avoid using -/// unstable features in their stable releases +/// Note: Unstable features are used while developing new features. Clients +/// should avoid using unstable features in their stable releases pub(crate) async fn get_supported_versions_route( _body: Ruma, ) -> Result { @@ -27,7 +29,10 @@ pub(crate) async fn get_supported_versions_route( "v1.4".to_owned(), "v1.5".to_owned(), ], - unstable_features: BTreeMap::from_iter([("org.matrix.e2e_cross_signing".to_owned(), true)]), + unstable_features: BTreeMap::from_iter([( + "org.matrix.e2e_cross_signing".to_owned(), + true, + )]), }; Ok(resp) diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 66f40a86..fdac5c1b 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -1,4 +1,3 @@ -use crate::{services, Result, Ruma}; use ruma::{ api::client::user_directory::search_users, events::{ @@ -7,11 +6,14 @@ use ruma::{ }, }; +use crate::{services, Result, Ruma}; + /// # `POST /_matrix/client/r0/user_directory/search` /// /// Searches all known users for a match. /// -/// - Hides any local users that aren't in any public rooms (i.e. those that have the join rule set to public) +/// - Hides any local users that aren't in any public rooms (i.e. those that +/// have the join rule set to public) /// and don't share a room with the sender pub(crate) async fn search_users_route( body: Ruma, @@ -38,8 +40,7 @@ pub(crate) async fn search_users_route( .display_name .as_ref() .filter(|name| { - name.to_lowercase() - .contains(&body.search_term.to_lowercase()) + name.to_lowercase().contains(&body.search_term.to_lowercase()) }) .is_some(); @@ -62,10 +63,12 @@ pub(crate) async fn search_users_route( .room_state_get(&room, &StateEventType::RoomJoinRules, "") .map_or(false, |event| { event.map_or(false, |event| { - serde_json::from_str(event.content.get()) - .map_or(false, |r: RoomJoinRulesEventContent| { + serde_json::from_str(event.content.get()).map_or( + false, + |r: RoomJoinRulesEventContent| { r.join_rule == JoinRule::Public - }) + }, + ) }) }) }); @@ -96,5 +99,8 @@ pub(crate) async fn search_users_route( let results = users.by_ref().take(limit).collect(); let limited = users.next().is_some(); - Ok(search_users::v3::Response { results, limited }) + Ok(search_users::v3::Response { + results, + limited, + }) } diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index 657a6d20..1cd80d27 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -1,9 +1,11 @@ -use crate::{services, Result, Ruma}; +use std::time::{Duration, SystemTime}; + use base64::{engine::general_purpose, Engine as _}; use hmac::{Hmac, Mac}; use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch}; use sha1::Sha1; -use std::time::{Duration, SystemTime}; + +use crate::{services, Result, Ruma}; type HmacSha1 = Hmac; @@ -24,7 +26,8 @@ pub(crate) async fn turn_server_route( ) } else { let expiry = SecondsSinceUnixEpoch::from_system_time( - SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()), + SystemTime::now() + + Duration::from_secs(services().globals.turn_ttl()), ) .expect("time is valid"); @@ -34,7 +37,8 @@ pub(crate) async fn turn_server_route( .expect("HMAC can take key of any size"); mac.update(username.as_bytes()); - let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes()); + let password: String = + general_purpose::STANDARD.encode(mac.finalize().into_bytes()); (username, password) }; diff --git a/src/api/ruma_wrapper.rs b/src/api/ruma_wrapper.rs index e3486a73..85173fe9 100644 --- a/src/api/ruma_wrapper.rs +++ b/src/api/ruma_wrapper.rs @@ -1,10 +1,12 @@ -use crate::{service::appservice::RegistrationInfo, Error}; -use ruma::{ - api::client::uiaa::UiaaResponse, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, - OwnedUserId, -}; use std::ops::Deref; +use ruma::{ + api::client::uiaa::UiaaResponse, CanonicalJsonValue, OwnedDeviceId, + OwnedServerName, OwnedUserId, +}; + +use crate::{service::appservice::RegistrationInfo, Error}; + mod axum; /// Extractor for Ruma request structs diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 55485023..f2e3e739 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -3,7 +3,9 @@ use std::{collections::BTreeMap, iter::FromIterator, str}; use axum::{ async_trait, body::{Full, HttpBody}, - extract::{rejection::TypedHeaderRejectionReason, FromRequest, Path, TypedHeader}, + extract::{ + rejection::TypedHeaderRejectionReason, FromRequest, Path, TypedHeader, + }, headers::{ authorization::{Bearer, Credentials}, Authorization, @@ -14,7 +16,9 @@ use axum::{ use bytes::{Buf, BufMut, Bytes, BytesMut}; use http::{Request, StatusCode}; use ruma::{ - api::{client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse}, + api::{ + client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse, + }, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId, }; use serde::Deserialize; @@ -41,7 +45,10 @@ where type Rejection = Error; #[allow(clippy::too_many_lines)] - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request( + req: Request, + _state: &S, + ) -> Result { #[derive(Deserialize)] struct QueryParams { access_token: Option, @@ -51,22 +58,23 @@ where let (mut parts, mut body) = match req.with_limited_body() { Ok(limited_req) => { let (parts, body) = limited_req.into_parts(); - let body = to_bytes(body) - .await - .map_err(|_| Error::BadRequest(ErrorKind::MissingToken, "Missing token."))?; + let body = to_bytes(body).await.map_err(|_| { + Error::BadRequest(ErrorKind::MissingToken, "Missing token.") + })?; (parts, body) } Err(original_req) => { let (parts, body) = original_req.into_parts(); - let body = to_bytes(body) - .await - .map_err(|_| Error::BadRequest(ErrorKind::MissingToken, "Missing token."))?; + let body = to_bytes(body).await.map_err(|_| { + Error::BadRequest(ErrorKind::MissingToken, "Missing token.") + })?; (parts, body) } }; let metadata = T::METADATA; - let auth_header: Option>> = parts.extract().await?; + let auth_header: Option>> = + parts.extract().await?; let path_params: Path> = parts.extract().await?; let query = parts.uri.query().unwrap_or_default(); @@ -87,9 +95,13 @@ where }; let token = if let Some(token) = token { - if let Some(reg_info) = services().appservice.find_from_token(token).await { + if let Some(reg_info) = + services().appservice.find_from_token(token).await + { Token::Appservice(Box::new(reg_info.clone())) - } else if let Some((user_id, device_id)) = services().users.find_from_token(token)? { + } else if let Some((user_id, device_id)) = + services().users.find_from_token(token)? + { Token::User((user_id, OwnedDeviceId::from(device_id))) } else { Token::Invalid @@ -98,13 +110,16 @@ where Token::None }; - let mut json_body = serde_json::from_slice::(&body).ok(); + let mut json_body = + serde_json::from_slice::(&body).ok(); let (sender_user, sender_device, sender_servername, appservice_info) = match (metadata.authentication, token) { (_, Token::Invalid) => { return Err(Error::BadRequest( - ErrorKind::UnknownToken { soft_logout: false }, + ErrorKind::UnknownToken { + soft_logout: false, + }, "Unknown access token.", )) } @@ -121,7 +136,10 @@ where UserId::parse, ) .map_err(|_| { - Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.") + Error::BadRequest( + ErrorKind::InvalidUsername, + "Username is invalid.", + ) })?; if !info.is_user_match(&user_id) { @@ -153,7 +171,9 @@ where )); } ( - AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None, + AuthScheme::AccessToken + | AuthScheme::AccessTokenOptional + | AuthScheme::None, Token::User((user_id, device_id)), ) => (Some(user_id), Some(device_id), None, None), (AuthScheme::ServerSignatures, Token::None) => { @@ -161,7 +181,10 @@ where .extract::>>() .await .map_err(|e| { - warn!("Missing or invalid Authorization header: {}", e); + warn!( + "Missing or invalid Authorization header: {}", + e + ); let msg = match e.reason() { TypedHeaderRejectionReason::Missing => { @@ -189,7 +212,9 @@ where let mut request_map = BTreeMap::from_iter([ ( "method".to_owned(), - CanonicalJsonValue::String(parts.method.to_string()), + CanonicalJsonValue::String( + parts.method.to_string(), + ), ), ( "uri".to_owned(), @@ -197,12 +222,18 @@ where ), ( "origin".to_owned(), - CanonicalJsonValue::String(x_matrix.origin.as_str().to_owned()), + CanonicalJsonValue::String( + x_matrix.origin.as_str().to_owned(), + ), ), ( "destination".to_owned(), CanonicalJsonValue::String( - services().globals.server_name().as_str().to_owned(), + services() + .globals + .server_name() + .as_str() + .to_owned(), ), ), ( @@ -212,13 +243,17 @@ where ]); if let Some(json_body) = &json_body { - request_map.insert("content".to_owned(), json_body.clone()); + request_map + .insert("content".to_owned(), json_body.clone()); }; let keys_result = services() .rooms .event_handler - .fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.clone()]) + .fetch_signing_keys( + &x_matrix.origin, + vec![x_matrix.key.clone()], + ) .await; let keys = match keys_result { @@ -232,22 +267,29 @@ where } }; - let pub_key_map = - BTreeMap::from_iter([(x_matrix.origin.as_str().to_owned(), keys)]); + let pub_key_map = BTreeMap::from_iter([( + x_matrix.origin.as_str().to_owned(), + keys, + )]); - match ruma::signatures::verify_json(&pub_key_map, &request_map) { + match ruma::signatures::verify_json( + &pub_key_map, + &request_map, + ) { Ok(()) => (None, None, Some(x_matrix.origin), None), Err(e) => { warn!( - "Failed to verify json request from {}: {}\n{:?}", + "Failed to verify json request from {}: \ + {}\n{:?}", x_matrix.origin, e, request_map ); if parts.uri.to_string().contains('@') { warn!( - "Request uri contained '@' character. Make sure your \ - reverse proxy gives Grapevine the raw uri (apache: use \ - nocanon)" + "Request uri contained '@' character. \ + Make sure your reverse proxy gives \ + Grapevine the raw uri (apache: use \ + nocanon)" ); } @@ -264,27 +306,36 @@ where | AuthScheme::AccessTokenOptional, Token::None, ) => (None, None, None, None), - (AuthScheme::ServerSignatures, Token::Appservice(_) | Token::User(_)) => { + ( + AuthScheme::ServerSignatures, + Token::Appservice(_) | Token::User(_), + ) => { return Err(Error::BadRequest( ErrorKind::Unauthorized, - "Only server signatures should be used on this endpoint.", + "Only server signatures should be used on this \ + endpoint.", )); } (AuthScheme::AppserviceToken, Token::User(_)) => { return Err(Error::BadRequest( ErrorKind::Unauthorized, - "Only appservice access tokens should be used on this endpoint.", + "Only appservice access tokens should be used on this \ + endpoint.", )); } }; - let mut http_request = http::Request::builder().uri(parts.uri).method(parts.method); + let mut http_request = + http::Request::builder().uri(parts.uri).method(parts.method); *http_request.headers_mut().unwrap() = parts.headers; if let Some(CanonicalJsonValue::Object(json_body)) = &mut json_body { let user_id = sender_user.clone().unwrap_or_else(|| { - UserId::parse_with_server_name("", services().globals.server_name()) - .expect("we know this is valid") + UserId::parse_with_server_name( + "", + services().globals.server_name(), + ) + .expect("we know this is valid") }); let uiaa_request = json_body @@ -300,14 +351,17 @@ where ) }); - if let Some(CanonicalJsonValue::Object(initial_request)) = uiaa_request { + if let Some(CanonicalJsonValue::Object(initial_request)) = + uiaa_request + { for (key, value) in initial_request { json_body.entry(key).or_insert(value); } } let mut buf = BytesMut::new().writer(); - serde_json::to_writer(&mut buf, json_body).expect("value serialization can't fail"); + serde_json::to_writer(&mut buf, json_body) + .expect("value serialization can't fail"); body = buf.into_inner().freeze(); } @@ -315,11 +369,15 @@ where debug!("{:?}", http_request); - let body = T::try_from_http_request(http_request, &path_params).map_err(|e| { - warn!("try_from_http_request failed: {:?}", e); - debug!("JSON body: {:?}", json_body); - Error::BadRequest(ErrorKind::BadJson, "Failed to deserialize request.") - })?; + let body = T::try_from_http_request(http_request, &path_params) + .map_err(|e| { + warn!("try_from_http_request failed: {:?}", e); + debug!("JSON body: {:?}", json_body); + Error::BadRequest( + ErrorKind::BadJson, + "Failed to deserialize request.", + ) + })?; Ok(Ruma { body, @@ -345,7 +403,8 @@ impl Credentials for XMatrix { fn decode(value: &http::HeaderValue) -> Option { debug_assert!( value.as_bytes().starts_with(b"X-Matrix "), - "HeaderValue to decode should start with \"X-Matrix ..\", received = {value:?}", + "HeaderValue to decode should start with \"X-Matrix ..\", \ + received = {value:?}", ); let parameters = str::from_utf8(&value.as_bytes()["X-Matrix ".len()..]) @@ -359,8 +418,9 @@ impl Credentials for XMatrix { for entry in parameters.split_terminator(',') { let (name, value) = entry.split_once('=')?; - // It's not at all clear why some fields are quoted and others not in the spec, - // let's simply accept either form for every field. + // It's not at all clear why some fields are quoted and others not + // in the spec, let's simply accept either form for + // every field. let value = value .strip_prefix('"') .and_then(|rest| rest.strip_suffix('"')) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 86c34276..5ae6af46 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1,14 +1,17 @@ #![allow(deprecated)] -use crate::{ - api::client_server::{self, claim_keys_helper, get_keys_helper}, - service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Result, Ruma, +use std::{ + collections::BTreeMap, + fmt::Debug, + mem, + net::{IpAddr, SocketAddr}, + sync::Arc, + time::{Duration, Instant, SystemTime}, }; + use axum::{response::IntoResponse, Json}; use get_profile_information::v1::ProfileField; use http::header::{HeaderValue, AUTHORIZATION}; - use ruma::{ api::{ client::error::{Error as RumaError, ErrorKind}, @@ -17,18 +20,29 @@ use ruma::{ backfill::get_backfill, device::get_devices::{self, v1::UserDevice}, directory::{get_public_rooms, get_public_rooms_filtered}, - discovery::{get_server_keys, get_server_version, ServerSigningKeys, VerifyKey}, - event::{get_event, get_missing_events, get_room_state, get_room_state_ids}, + discovery::{ + get_server_keys, get_server_version, ServerSigningKeys, + VerifyKey, + }, + event::{ + get_event, get_missing_events, get_room_state, + get_room_state_ids, + }, keys::{claim_keys, get_keys}, - membership::{create_invite, create_join_event, prepare_join_event}, + membership::{ + create_invite, create_join_event, prepare_join_event, + }, query::{get_profile_information, get_room_information}, transactions::{ - edu::{DeviceListUpdateContent, DirectDeviceContent, Edu, SigningKeyUpdateContent}, + edu::{ + DeviceListUpdateContent, DirectDeviceContent, Edu, + SigningKeyUpdateContent, + }, send_transaction_message, }, }, - EndpointError, IncomingResponse, MatrixVersion, OutgoingRequest, OutgoingResponse, - SendAccessToken, + EndpointError, IncomingResponse, MatrixVersion, OutgoingRequest, + OutgoingResponse, SendAccessToken, }, directory::{Filter, RoomNetwork}, events::{ @@ -41,28 +55,25 @@ use ruma::{ }, serde::{Base64, JsonObject, Raw}, to_device::DeviceIdOrAllDevices, - uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, - OwnedEventId, OwnedRoomId, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, RoomId, - ServerName, + uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, + MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, + OwnedServerSigningKeyId, OwnedUserId, RoomId, ServerName, }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; -use std::{ - collections::BTreeMap, - fmt::Debug, - mem, - net::{IpAddr, SocketAddr}, - sync::Arc, - time::{Duration, Instant, SystemTime}, -}; use tokio::sync::RwLock; - use tracing::{debug, error, warn}; +use crate::{ + api::client_server::{self, claim_keys_helper, get_keys_helper}, + service::pdu::{gen_event_id_canonical_json, PduBuilder}, + services, utils, Error, PduEvent, Result, Ruma, +}; + /// Wraps either an literal IP address plus port, or a hostname plus complement /// (colon-plus-port if it was specified). /// -/// Note: A [`FedDest::Named`] might contain an IP address in string form if there -/// was no port specified to construct a [`SocketAddr`] with. +/// Note: A [`FedDest::Named`] might contain an IP address in string form if +/// there was no port specified to construct a [`SocketAddr`] with. /// /// # Examples: /// ```rust @@ -107,7 +118,9 @@ impl FedDest { fn port(&self) -> Option { match &self { Self::Literal(addr) => Some(addr.port()), - Self::Named(_, port) => port.strip_prefix(':').and_then(|x| x.parse().ok()), + Self::Named(_, port) => { + port.strip_prefix(':').and_then(|x| x.parse().ok()) + } } } } @@ -178,7 +191,8 @@ where ); }; - request_map.insert("method".to_owned(), T::METADATA.method.to_string().into()); + request_map + .insert("method".to_owned(), T::METADATA.method.to_string().into()); request_map.insert( "uri".to_owned(), http_request @@ -194,8 +208,8 @@ where ); request_map.insert("destination".to_owned(), destination.as_str().into()); - let mut request_json = - serde_json::from_value(request_map.into()).expect("valid JSON is valid BTreeMap"); + let mut request_json = serde_json::from_value(request_map.into()) + .expect("valid JSON is valid BTreeMap"); ruma::signatures::sign_json( services().globals.server_name().as_str(), @@ -205,17 +219,12 @@ where .expect("our request json is what ruma expects"); let request_json: serde_json::Map = - serde_json::from_slice(&serde_json::to_vec(&request_json).unwrap()).unwrap(); + serde_json::from_slice(&serde_json::to_vec(&request_json).unwrap()) + .unwrap(); - let signatures = request_json["signatures"] - .as_object() - .unwrap() - .values() - .map(|v| { - v.as_object() - .unwrap() - .iter() - .map(|(k, v)| (k, v.as_str().unwrap())) + let signatures = + request_json["signatures"].as_object().unwrap().values().map(|v| { + v.as_object().unwrap().iter().map(|(k, v)| (k, v.as_str().unwrap())) }); for signature_server in signatures { @@ -238,11 +247,8 @@ where let url = reqwest_request.url().clone(); debug!("Sending request to {destination} at {url}"); - let response = services() - .globals - .federation_client() - .execute(reqwest_request) - .await; + let response = + services().globals.federation_client().execute(reqwest_request).await; debug!("Received response from {destination} at {url}"); match response { @@ -285,7 +291,8 @@ where if status == 200 { debug!("Parsing response bytes from {destination}"); - let response = T::IncomingResponse::try_from_http_response(http_response); + let response = + T::IncomingResponse::try_from_http_response(http_response); if response.is_ok() && write_destination_to_cache { services() .globals @@ -303,7 +310,9 @@ where "Invalid 200 response from {} on: {} {}", &destination, url, e ); - Error::BadServerResponse("Server returned bad 200 response.") + Error::BadServerResponse( + "Server returned bad 200 response.", + ) }) } else { debug!("Returning error from {destination}"); @@ -343,9 +352,12 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { /// Returns: `actual_destination`, `Host` header /// Implemented according to the specification at -/// Numbers in comments below refer to bullet points in linked section of specification +/// Numbers in comments below refer to bullet points in linked section of +/// specification #[allow(clippy::too_many_lines)] -async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) { +async fn find_actual_destination( + destination: &'_ ServerName, +) -> (FedDest, FedDest) { debug!("Finding actual destination for {destination}"); let destination_str = destination.as_str().to_owned(); let mut hostname = destination_str.clone(); @@ -361,10 +373,15 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe FedDest::Named(host.to_owned(), port.to_owned()) } else { debug!("Requesting well known for {destination}"); - if let Some(delegated_hostname) = request_well_known(destination.as_str()).await { + if let Some(delegated_hostname) = + request_well_known(destination.as_str()).await + { debug!("3: A .well-known file is available"); - hostname = add_port_to_hostname(&delegated_hostname).into_uri_string(); - if let Some(host_and_port) = get_ip_with_port(&delegated_hostname) { + hostname = add_port_to_hostname(&delegated_hostname) + .into_uri_string(); + if let Some(host_and_port) = + get_ip_with_port(&delegated_hostname) + { host_and_port } else if let Some(pos) = delegated_hostname.find(':') { debug!("3.2: Hostname with port in .well-known file"); @@ -372,7 +389,8 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe FedDest::Named(host.to_owned(), port.to_owned()) } else { debug!("Delegated hostname has no port in this branch"); - if let Some(hostname_override) = query_srv_record(&delegated_hostname).await + if let Some(hostname_override) = + query_srv_record(&delegated_hostname).await { debug!("3.3: SRV lookup successful"); let force_port = hostname_override.port(); @@ -390,25 +408,39 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe .unwrap() .insert( delegated_hostname.clone(), - (override_ip.iter().collect(), force_port.unwrap_or(8448)), + ( + override_ip.iter().collect(), + force_port.unwrap_or(8448), + ), ); } else { - warn!("Using SRV record, but could not resolve to IP"); + warn!( + "Using SRV record, but could not resolve \ + to IP" + ); } if let Some(port) = force_port { - FedDest::Named(delegated_hostname, format!(":{port}")) + FedDest::Named( + delegated_hostname, + format!(":{port}"), + ) } else { add_port_to_hostname(&delegated_hostname) } } else { - debug!("3.4: No SRV records, just use the hostname from .well-known"); + debug!( + "3.4: No SRV records, just use the hostname \ + from .well-known" + ); add_port_to_hostname(&delegated_hostname) } } } else { debug!("4: No .well-known or an error occured"); - if let Some(hostname_override) = query_srv_record(&destination_str).await { + if let Some(hostname_override) = + query_srv_record(&destination_str).await + { debug!("4: SRV record found"); let force_port = hostname_override.port(); @@ -425,10 +457,15 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe .unwrap() .insert( hostname.clone(), - (override_ip.iter().collect(), force_port.unwrap_or(8448)), + ( + override_ip.iter().collect(), + force_port.unwrap_or(8448), + ), ); } else { - warn!("Using SRV record, but could not resolve to IP"); + warn!( + "Using SRV record, but could not resolve to IP" + ); } if let Some(port) = force_port { @@ -470,7 +507,11 @@ async fn query_given_srv_record(record: &str) -> Option { .map(|srv| { srv.iter().next().map(|result| { FedDest::Named( - result.target().to_string().trim_end_matches('.').to_owned(), + result + .target() + .to_string() + .trim_end_matches('.') + .to_owned(), format!(":{}", result.port()), ) }) @@ -481,7 +522,8 @@ async fn query_given_srv_record(record: &str) -> Option { async fn query_srv_record(hostname: &'_ str) -> Option { let hostname = hostname.trim_end_matches('.'); - if let Some(host_port) = query_given_srv_record(&format!("_matrix-fed._tcp.{hostname}.")).await + if let Some(host_port) = + query_given_srv_record(&format!("_matrix-fed._tcp.{hostname}.")).await { Some(host_port) } else { @@ -525,17 +567,22 @@ pub(crate) async fn get_server_version_route( /// /// Gets the public signing keys of this server. /// -/// - Matrix does not support invalidating public keys, so the key returned by this will be valid +/// - Matrix does not support invalidating public keys, so the key returned by +/// this will be valid /// forever. -// Response type for this endpoint is Json because we need to calculate a signature for the response +// Response type for this endpoint is Json because we need to calculate a +// signature for the response pub(crate) async fn get_server_keys_route() -> Result { - let mut verify_keys: BTreeMap = BTreeMap::new(); + let mut verify_keys: BTreeMap = + BTreeMap::new(); verify_keys.insert( format!("ed25519:{}", services().globals.keypair().version()) .try_into() .expect("found invalid server signing keys in DB"), VerifyKey { - key: Base64::new(services().globals.keypair().public_key().to_vec()), + key: Base64::new( + services().globals.keypair().public_key().to_vec(), + ), }, ); let mut response = serde_json::from_slice( @@ -572,7 +619,8 @@ pub(crate) async fn get_server_keys_route() -> Result { /// /// Gets the public signing keys of this server. /// -/// - Matrix does not support invalidating public keys, so the key returned by this will be valid +/// - Matrix does not support invalidating public keys, so the key returned by +/// this will be valid /// forever. pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { get_server_keys_route().await @@ -627,10 +675,11 @@ pub(crate) async fn get_public_rooms_route( pub(crate) fn parse_incoming_pdu( pdu: &RawJsonValue, ) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> { - let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { - warn!("Error parsing incoming event {:?}: {:?}", pdu, e); - Error::BadServerResponse("Invalid PDU in server response") - })?; + let value: CanonicalJsonObject = + serde_json::from_str(pdu.get()).map_err(|e| { + warn!("Error parsing incoming event {:?}: {:?}", pdu, e); + Error::BadServerResponse("Invalid PDU in server response") + })?; let room_id: OwnedRoomId = value .get("room_id") @@ -642,7 +691,9 @@ pub(crate) fn parse_incoming_pdu( let room_version_id = services().rooms.state.get_room_version(&room_id)?; - let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { + let Ok((event_id, value)) = + gen_event_id_canonical_json(pdu, &room_version_id) + else { // Event could not be converted to canonical json return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -659,20 +710,19 @@ pub(crate) fn parse_incoming_pdu( pub(crate) async fn send_transaction_message_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); let mut resolved_map = BTreeMap::new(); let pub_key_map = RwLock::new(BTreeMap::new()); for pdu in &body.pdus { - let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { - warn!("Error parsing incoming event {:?}: {:?}", pdu, e); - Error::BadServerResponse("Invalid PDU in server response") - })?; + let value: CanonicalJsonObject = serde_json::from_str(pdu.get()) + .map_err(|e| { + warn!("Error parsing incoming event {:?}: {:?}", pdu, e); + Error::BadServerResponse("Invalid PDU in server response") + })?; let room_id: OwnedRoomId = value .get("room_id") .and_then(|id| RoomId::parse(id.as_str()?).ok()) @@ -695,7 +745,8 @@ pub(crate) async fn send_transaction_message_route( continue; } }; - // We do not add the event_id field to the pdu here because of signature and hashes checks + // We do not add the event_id field to the pdu here because of signature + // and hashes checks let mutex = Arc::clone( services() @@ -767,13 +818,15 @@ pub(crate) async fn send_transaction_message_route( .max_by_key(|(_, count)| *count) { let mut user_receipts = BTreeMap::new(); - user_receipts.insert(user_id.clone(), user_updates.data); + user_receipts + .insert(user_id.clone(), user_updates.data); let mut receipts = BTreeMap::new(); receipts.insert(ReceiptType::Read, user_receipts); let mut receipt_content = BTreeMap::new(); - receipt_content.insert(event_id.to_owned(), receipts); + receipt_content + .insert(event_id.to_owned(), receipts); let event = ReceiptEvent { content: ReceiptEventContent(receipt_content), @@ -783,10 +836,15 @@ pub(crate) async fn send_transaction_message_route( .rooms .edus .read_receipt - .readreceipt_update(&user_id, &room_id, event)?; + .readreceipt_update( + &user_id, &room_id, event, + )?; } else { // TODO fetch missing events - debug!("No known event ids in read receipt: {:?}", user_updates); + debug!( + "No known event ids in read receipt: {:?}", + user_updates + ); } } } @@ -818,7 +876,10 @@ pub(crate) async fn send_transaction_message_route( } } } - Edu::DeviceListUpdate(DeviceListUpdateContent { user_id, .. }) => { + Edu::DeviceListUpdate(DeviceListUpdateContent { + user_id, + .. + }) => { services().users.mark_device_key_update(&user_id)?; } Edu::DirectToDevice(DirectDeviceContent { @@ -839,14 +900,19 @@ pub(crate) async fn send_transaction_message_route( for (target_user_id, map) in &messages { for (target_device_id_maybe, event) in map { match target_device_id_maybe { - DeviceIdOrAllDevices::DeviceId(target_device_id) => { + DeviceIdOrAllDevices::DeviceId( + target_device_id, + ) => { services().users.add_to_device_event( &sender, target_user_id, target_device_id, &ev_type.to_string(), event.deserialize_as().map_err(|e| { - warn!("To-Device event is invalid: {event:?} {e}"); + warn!( + "To-Device event is invalid: \ + {event:?} {e}" + ); Error::BadRequest( ErrorKind::InvalidParam, "Event is invalid", @@ -856,20 +922,23 @@ pub(crate) async fn send_transaction_message_route( } DeviceIdOrAllDevices::AllDevices => { - for target_device_id in - services().users.all_device_ids(target_user_id) + for target_device_id in services() + .users + .all_device_ids(target_user_id) { services().users.add_to_device_event( &sender, target_user_id, &target_device_id?, &ev_type.to_string(), - event.deserialize_as().map_err(|_| { - Error::BadRequest( - ErrorKind::InvalidParam, - "Event is invalid", - ) - })?, + event.deserialize_as().map_err( + |_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Event is invalid", + ) + }, + )?, )?; } } @@ -878,9 +947,12 @@ pub(crate) async fn send_transaction_message_route( } // Save transaction id with empty data - services() - .transaction_ids - .add_txnid(&sender, None, &message_id, &[])?; + services().transaction_ids.add_txnid( + &sender, + None, + &message_id, + &[], + )?; } Edu::SigningKeyUpdate(SigningKeyUpdateContent { user_id, @@ -916,31 +988,30 @@ pub(crate) async fn send_transaction_message_route( /// /// Retrieves a single event from the server. /// -/// - Only works if a user of this server is currently invited or joined the room +/// - Only works if a user of this server is currently invited or joined the +/// room pub(crate) async fn get_event_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); - let event = services() - .rooms - .timeline - .get_pdu_json(&body.event_id)? - .ok_or_else(|| { - warn!("Event not found, event ID: {:?}", &body.event_id); - Error::BadRequest(ErrorKind::NotFound, "Event not found.") - })?; + let event = + services().rooms.timeline.get_pdu_json(&body.event_id)?.ok_or_else( + || { + warn!("Event not found, event ID: {:?}", &body.event_id); + Error::BadRequest(ErrorKind::NotFound, "Event not found.") + }, + )?; let room_id_str = event .get("room_id") .and_then(|val| val.as_str()) .ok_or_else(|| Error::bad_database("Invalid event in database"))?; - let room_id = <&RoomId>::try_from(room_id_str) - .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; + let room_id = <&RoomId>::try_from(room_id_str).map_err(|_| { + Error::bad_database("Invalid room id field in event in database") + })?; if !services() .rooms @@ -978,10 +1049,8 @@ pub(crate) async fn get_event_route( pub(crate) async fn get_backfill_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); debug!("Got backfill request from: {}", sender_servername); @@ -1050,10 +1119,8 @@ pub(crate) async fn get_backfill_route( pub(crate) async fn get_missing_events_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); if !services() .rooms @@ -1075,19 +1142,28 @@ pub(crate) async fn get_missing_events_route( let mut events = Vec::new(); let mut i = 0; - while i < queued_events.len() && events.len() < body.limit.try_into().unwrap_or(usize::MAX) { - if let Some(pdu) = services().rooms.timeline.get_pdu_json(&queued_events[i])? { - let room_id_str = pdu - .get("room_id") - .and_then(|val| val.as_str()) - .ok_or_else(|| Error::bad_database("Invalid event in database"))?; + while i < queued_events.len() + && events.len() < body.limit.try_into().unwrap_or(usize::MAX) + { + if let Some(pdu) = + services().rooms.timeline.get_pdu_json(&queued_events[i])? + { + let room_id_str = + pdu.get("room_id").and_then(|val| val.as_str()).ok_or_else( + || Error::bad_database("Invalid event in database"), + )?; - let event_room_id = <&RoomId>::try_from(room_id_str) - .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; + let event_room_id = + <&RoomId>::try_from(room_id_str).map_err(|_| { + Error::bad_database( + "Invalid room id field in event in database", + ) + })?; if event_room_id != body.room_id { warn!( - "Evil event detected: Event {} found while searching in room {}", + "Evil event detected: Event {} found while searching in \ + room {}", queued_events[i], body.room_id ); return Err(Error::BadRequest( @@ -1112,19 +1188,29 @@ pub(crate) async fn get_missing_events_route( queued_events.extend_from_slice( &serde_json::from_value::>( - serde_json::to_value(pdu.get("prev_events").cloned().ok_or_else(|| { - Error::bad_database("Event in db has no prev_events field.") - })?) + serde_json::to_value( + pdu.get("prev_events").cloned().ok_or_else(|| { + Error::bad_database( + "Event in db has no prev_events field.", + ) + })?, + ) .expect("canonical json is valid json value"), ) - .map_err(|_| Error::bad_database("Invalid prev_events content in pdu in db."))?, + .map_err(|_| { + Error::bad_database( + "Invalid prev_events content in pdu in db.", + ) + })?, ); events.push(PduEvent::convert_to_outgoing_federation_event(pdu)); } i += 1; } - Ok(get_missing_events::v1::Response { events }) + Ok(get_missing_events::v1::Response { + events, + }) } /// # `GET /_matrix/federation/v1/event_auth/{roomId}/{eventId}` @@ -1135,10 +1221,8 @@ pub(crate) async fn get_missing_events_route( pub(crate) async fn get_event_authorization_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); if !services() .rooms @@ -1156,22 +1240,22 @@ pub(crate) async fn get_event_authorization_route( .event_handler .acl_check(sender_servername, &body.room_id)?; - let event = services() - .rooms - .timeline - .get_pdu_json(&body.event_id)? - .ok_or_else(|| { - warn!("Event not found, event ID: {:?}", &body.event_id); - Error::BadRequest(ErrorKind::NotFound, "Event not found.") - })?; + let event = + services().rooms.timeline.get_pdu_json(&body.event_id)?.ok_or_else( + || { + warn!("Event not found, event ID: {:?}", &body.event_id); + Error::BadRequest(ErrorKind::NotFound, "Event not found.") + }, + )?; let room_id_str = event .get("room_id") .and_then(|val| val.as_str()) .ok_or_else(|| Error::bad_database("Invalid event in database"))?; - let room_id = <&RoomId>::try_from(room_id_str) - .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; + let room_id = <&RoomId>::try_from(room_id_str).map_err(|_| { + Error::bad_database("Invalid room id field in event in database") + })?; let auth_chain_ids = services() .rooms @@ -1181,7 +1265,9 @@ pub(crate) async fn get_event_authorization_route( Ok(get_event_authorization::v1::Response { auth_chain: auth_chain_ids - .filter_map(|id| services().rooms.timeline.get_pdu_json(&id).ok()?) + .filter_map(|id| { + services().rooms.timeline.get_pdu_json(&id).ok()? + }) .map(PduEvent::convert_to_outgoing_federation_event) .collect(), }) @@ -1193,10 +1279,8 @@ pub(crate) async fn get_event_authorization_route( pub(crate) async fn get_room_state_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); if !services() .rooms @@ -1231,12 +1315,7 @@ pub(crate) async fn get_room_state_route( .into_values() .map(|id| { PduEvent::convert_to_outgoing_federation_event( - services() - .rooms - .timeline - .get_pdu_json(&id) - .unwrap() - .unwrap(), + services().rooms.timeline.get_pdu_json(&id).unwrap().unwrap(), ) }) .collect(); @@ -1250,7 +1329,9 @@ pub(crate) async fn get_room_state_route( Ok(get_room_state::v1::Response { auth_chain: auth_chain_ids .filter_map(|id| { - if let Some(json) = services().rooms.timeline.get_pdu_json(&id).ok()? { + if let Some(json) = + services().rooms.timeline.get_pdu_json(&id).ok()? + { Some(PduEvent::convert_to_outgoing_federation_event(json)) } else { error!("Could not find event json for {id} in db."); @@ -1268,10 +1349,8 @@ pub(crate) async fn get_room_state_route( pub(crate) async fn get_room_state_ids_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); if !services() .rooms @@ -1332,10 +1411,8 @@ pub(crate) async fn create_join_event_template_route( )); } - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); services() .rooms @@ -1353,22 +1430,26 @@ pub(crate) async fn create_join_event_template_route( ); let state_lock = mutex_state.lock().await; - // TODO: Grapevine does not implement restricted join rules yet, we always reject + // TODO: Grapevine does not implement restricted join rules yet, we always + // reject let join_rules_event = services().rooms.state_accessor.room_state_get( &body.room_id, &StateEventType::RoomJoinRules, "", )?; - let join_rules_event_content: Option = join_rules_event - .as_ref() - .map(|join_rules_event| { - serde_json::from_str(join_rules_event.content.get()).map_err(|e| { - warn!("Invalid join rules event: {}", e); - Error::bad_database("Invalid join rules event in db.") + let join_rules_event_content: Option = + join_rules_event + .as_ref() + .map(|join_rules_event| { + serde_json::from_str(join_rules_event.content.get()).map_err( + |e| { + warn!("Invalid join rules event: {}", e); + Error::bad_database("Invalid join rules event in db.") + }, + ) }) - }) - .transpose()?; + .transpose()?; if let Some(join_rules_event_content) = join_rules_event_content { if matches!( @@ -1382,7 +1463,8 @@ pub(crate) async fn create_join_event_template_route( } } - let room_version_id = services().rooms.state.get_room_version(&body.room_id)?; + let room_version_id = + services().rooms.state.get_room_version(&body.room_id)?; if !body.ver.contains(&room_version_id) { return Err(Error::BadRequest( ErrorKind::IncompatibleRoomVersion { @@ -1404,18 +1486,19 @@ pub(crate) async fn create_join_event_template_route( }) .expect("member event is valid value"); - let (_pdu, mut pdu_json) = services().rooms.timeline.create_hash_and_sign_event( - PduBuilder { - event_type: TimelineEventType::RoomMember, - content, - unsigned: None, - state_key: Some(body.user_id.to_string()), - redacts: None, - }, - &body.user_id, - &body.room_id, - &state_lock, - )?; + let (_pdu, mut pdu_json) = + services().rooms.timeline.create_hash_and_sign_event( + PduBuilder { + event_type: TimelineEventType::RoomMember, + content, + unsigned: None, + state_key: Some(body.user_id.to_string()), + redacts: None, + }, + &body.user_id, + &body.room_id, + &state_lock, + )?; drop(state_lock); @@ -1423,7 +1506,8 @@ pub(crate) async fn create_join_event_template_route( Ok(prepare_join_event::v1::Response { room_version: Some(room_version_id), - event: to_raw_value(&pdu_json).expect("CanonicalJson can be serialized to JSON"), + event: to_raw_value(&pdu_json) + .expect("CanonicalJson can be serialized to JSON"), }) } @@ -1440,27 +1524,28 @@ async fn create_join_event( )); } - services() - .rooms - .event_handler - .acl_check(sender_servername, room_id)?; + services().rooms.event_handler.acl_check(sender_servername, room_id)?; - // TODO: Grapevine does not implement restricted join rules yet, we always reject + // TODO: Grapevine does not implement restricted join rules yet, we always + // reject let join_rules_event = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomJoinRules, "", )?; - let join_rules_event_content: Option = join_rules_event - .as_ref() - .map(|join_rules_event| { - serde_json::from_str(join_rules_event.content.get()).map_err(|e| { - warn!("Invalid join rules event: {}", e); - Error::bad_database("Invalid join rules event in db.") + let join_rules_event_content: Option = + join_rules_event + .as_ref() + .map(|join_rules_event| { + serde_json::from_str(join_rules_event.content.get()).map_err( + |e| { + warn!("Invalid join rules event: {}", e); + Error::bad_database("Invalid join rules event in db.") + }, + ) }) - }) - .transpose()?; + .transpose()?; if let Some(join_rules_event_content) = join_rules_event_content { if matches!( @@ -1474,21 +1559,21 @@ async fn create_join_event( } } - // We need to return the state prior to joining, let's keep a reference to that here - let shortstatehash = services() - .rooms - .state - .get_room_shortstatehash(room_id)? - .ok_or(Error::BadRequest( - ErrorKind::NotFound, - "Pdu state not found.", - ))?; + // We need to return the state prior to joining, let's keep a reference to + // that here + let shortstatehash = + services().rooms.state.get_room_shortstatehash(room_id)?.ok_or( + Error::BadRequest(ErrorKind::NotFound, "Pdu state not found."), + )?; let pub_key_map = RwLock::new(BTreeMap::new()); - // We do not add the event_id field to the pdu here because of signature and hashes checks + // We do not add the event_id field to the pdu here because of signature and + // hashes checks let room_version_id = services().rooms.state.get_room_version(room_id)?; - let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { + let Ok((event_id, value)) = + gen_event_id_canonical_json(pdu, &room_version_id) + else { // Event could not be converted to canonical json return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -1503,7 +1588,9 @@ async fn create_join_event( ))?) .expect("CanonicalJson is valid json value"), ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Origin field is invalid."))?; + .map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Origin field is invalid.") + })?; let mutex = Arc::clone( services() @@ -1518,7 +1605,14 @@ async fn create_join_event( let pdu_id: Vec = services() .rooms .event_handler - .handle_incoming_pdu(&origin, &event_id, room_id, value, true, &pub_key_map) + .handle_incoming_pdu( + &origin, + &event_id, + room_id, + value, + true, + &pub_key_map, + ) .await? .ok_or(Error::BadRequest( ErrorKind::InvalidParam, @@ -1526,11 +1620,8 @@ async fn create_join_event( ))?; drop(mutex_lock); - let state_ids = services() - .rooms - .state_accessor - .state_full_ids(shortstatehash) - .await?; + let state_ids = + services().rooms.state_accessor.state_full_ids(shortstatehash).await?; let auth_chain_ids = services() .rooms .auth_chain @@ -1548,12 +1639,16 @@ async fn create_join_event( Ok(create_join_event::v1::RoomState { auth_chain: auth_chain_ids - .filter_map(|id| services().rooms.timeline.get_pdu_json(&id).ok().flatten()) + .filter_map(|id| { + services().rooms.timeline.get_pdu_json(&id).ok().flatten() + }) .map(PduEvent::convert_to_outgoing_federation_event) .collect(), state: state_ids .iter() - .filter_map(|(_, id)| services().rooms.timeline.get_pdu_json(id).ok().flatten()) + .filter_map(|(_, id)| { + services().rooms.timeline.get_pdu_json(id).ok().flatten() + }) .map(PduEvent::convert_to_outgoing_federation_event) .collect(), // TODO: handle restricted joins @@ -1567,14 +1662,15 @@ async fn create_join_event( pub(crate) async fn create_join_event_v1_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); - let room_state = create_join_event(sender_servername, &body.room_id, &body.pdu).await?; + let room_state = + create_join_event(sender_servername, &body.room_id, &body.pdu).await?; - Ok(create_join_event::v1::Response { room_state }) + Ok(create_join_event::v1::Response { + room_state, + }) } /// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}` @@ -1583,10 +1679,8 @@ pub(crate) async fn create_join_event_v1_route( pub(crate) async fn create_join_event_v2_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); let create_join_event::v1::RoomState { auth_chain, @@ -1601,19 +1695,20 @@ pub(crate) async fn create_join_event_v2_route( servers_in_room: None, }; - Ok(create_join_event::v2::Response { room_state }) + Ok(create_join_event::v2::Response { + room_state, + }) } /// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}` /// /// Invites a remote user to a room. +#[allow(clippy::too_many_lines)] pub(crate) async fn create_invite_route( body: Ruma, ) -> Result { - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); services() .rooms @@ -1633,8 +1728,13 @@ pub(crate) async fn create_invite_route( )); } - let mut signed_event = utils::to_canonical_object(&body.event) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?; + let mut signed_event = + utils::to_canonical_object(&body.event).map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Invite event is invalid.", + ) + })?; ruma::signatures::hash_and_sign_event( services().globals.server_name().as_str(), @@ -1642,7 +1742,9 @@ pub(crate) async fn create_invite_route( &mut signed_event, &body.room_version, ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?; + .map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event.") + })?; // Generate event id let event_id = EventId::parse(format!( @@ -1668,7 +1770,9 @@ pub(crate) async fn create_invite_route( .clone() .into(), ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "sender is not a user id."))?; + .map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "sender is not a user id.") + })?; let invited_user: Box<_> = serde_json::from_value( signed_event @@ -1680,12 +1784,22 @@ pub(crate) async fn create_invite_route( .clone() .into(), ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "state_key is not a user id."))?; + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "state_key is not a user id.", + ) + })?; let mut invite_state = body.invite_room_state.clone(); let mut event: JsonObject = serde_json::from_str(body.event.get()) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event bytes."))?; + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid invite event bytes.", + ) + })?; event.insert("event_id".to_owned(), "$dummy".into()); @@ -1696,7 +1810,8 @@ pub(crate) async fn create_invite_route( invite_state.push(pdu.to_stripped_state_event()); - // If we are active in the room, the remote server will notify us about the join via /send + // If we are active in the room, the remote server will notify us about the + // join via /send if !services() .rooms .state_cache @@ -1730,10 +1845,8 @@ pub(crate) async fn get_devices_route( )); } - let sender_servername = body - .sender_servername - .as_ref() - .expect("server is authenticated"); + let sender_servername = + body.sender_servername.as_ref().expect("server is authenticated"); Ok(get_devices::v1::Response { user_id: body.user_id.clone(), @@ -1758,14 +1871,16 @@ pub(crate) async fn get_devices_route( }) }) .collect(), - master_key: services().users.get_master_key(None, &body.user_id, &|u| { - u.server_name() == sender_servername - })?, - self_signing_key: services() - .users - .get_self_signing_key(None, &body.user_id, &|u| { - u.server_name() == sender_servername - })?, + master_key: services().users.get_master_key( + None, + &body.user_id, + &|u| u.server_name() == sender_servername, + )?, + self_signing_key: services().users.get_self_signing_key( + None, + &body.user_id, + &|u| u.server_name() == sender_servername, + )?, }) } @@ -1775,14 +1890,10 @@ pub(crate) async fn get_devices_route( pub(crate) async fn get_room_information_route( body: Ruma, ) -> Result { - let room_id = services() - .rooms - .alias - .resolve_local_alias(&body.room_alias)? - .ok_or(Error::BadRequest( - ErrorKind::NotFound, - "Room alias not found.", - ))?; + let room_id = + services().rooms.alias.resolve_local_alias(&body.room_alias)?.ok_or( + Error::BadRequest(ErrorKind::NotFound, "Room alias not found."), + )?; Ok(get_room_information::v1::Response { room_id, diff --git a/src/config.rs b/src/config.rs index 0f9a9c9c..2ea4c313 100644 --- a/src/config.rs +++ b/src/config.rs @@ -108,7 +108,10 @@ impl Config { } if was_deprecated { - warn!("Read grapevine documentation and check your configuration if any new configuration parameters should be adjusted"); + warn!( + "Read grapevine documentation and check your configuration if \ + any new configuration parameters should be adjusted" + ); } } } diff --git a/src/config/proxy.rs b/src/config/proxy.rs index 91ada136..bf365265 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -24,9 +24,10 @@ use crate::Result; /// ## Include vs. Exclude /// If include is an empty list, it is assumed to be `["*"]`. /// -/// If a domain matches both the exclude and include list, the proxy will only be used if it was -/// included because of a more specific rule than it was excluded. In the above example, the proxy -/// would be used for `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`. +/// If a domain matches both the exclude and include list, the proxy will only +/// be used if it was included because of a more specific rule than it was +/// excluded. In the above example, the proxy would be used for +/// `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`. #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "snake_case")] #[derive(Default)] @@ -43,7 +44,9 @@ impl ProxyConfig { pub(crate) fn to_proxy(&self) -> Result> { Ok(match self.clone() { ProxyConfig::None => None, - ProxyConfig::Global { url } => Some(Proxy::all(url)?), + ProxyConfig::Global { + url, + } => Some(Proxy::all(url)?), ProxyConfig::ByDomain(proxies) => Some(Proxy::custom(move |url| { // first matching proxy proxies.iter().find_map(|proxy| proxy.for_url(url)).cloned() @@ -112,25 +115,32 @@ impl WildCardedDomain { WildCardedDomain::Exact(d) => domain == d, } } + pub(crate) fn more_specific_than(&self, other: &Self) -> bool { match (self, other) { (WildCardedDomain::WildCard, WildCardedDomain::WildCard) => false, (_, WildCardedDomain::WildCard) => true, - (WildCardedDomain::Exact(a), WildCardedDomain::WildCarded(_)) => other.matches(a), - (WildCardedDomain::WildCarded(a), WildCardedDomain::WildCarded(b)) => { - a != b && a.ends_with(b) + (WildCardedDomain::Exact(a), WildCardedDomain::WildCarded(_)) => { + other.matches(a) } + ( + WildCardedDomain::WildCarded(a), + WildCardedDomain::WildCarded(b), + ) => a != b && a.ends_with(b), _ => false, } } } impl std::str::FromStr for WildCardedDomain { type Err = std::convert::Infallible; + fn from_str(s: &str) -> Result { // maybe do some domain validation? Ok(s.strip_prefix("*.") .map(|x| WildCardedDomain::WildCarded(x.to_owned())) - .or_else(|| (s == "*").then(|| WildCardedDomain::WildCarded(String::new()))) + .or_else(|| { + (s == "*").then(|| WildCardedDomain::WildCarded(String::new())) + }) .unwrap_or_else(|| WildCardedDomain::Exact(s.to_owned()))) } } diff --git a/src/database.rs b/src/database.rs index ea964a52..ffbf6ef2 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,23 +1,6 @@ pub(crate) mod abstraction; pub(crate) mod key_value; -use crate::{ - service::rooms::timeline::PduCount, services, utils, Config, Error, PduEvent, Result, Services, - SERVICES, -}; -use abstraction::{KeyValueDatabaseEngine, KvTree}; -use lru_cache::LruCache; - -use ruma::{ - events::{ - push_rules::{PushRulesEvent, PushRulesEventContent}, - room::message::RoomMessageEventContent, - GlobalAccountDataEvent, GlobalAccountDataEventType, StateEventType, - }, - push::Ruleset, - CanonicalJsonValue, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, - UserId, -}; use std::{ collections::{BTreeMap, HashMap, HashSet}, fs, @@ -27,8 +10,25 @@ use std::{ sync::{Arc, Mutex, RwLock}, }; +use abstraction::{KeyValueDatabaseEngine, KvTree}; +use lru_cache::LruCache; +use ruma::{ + events::{ + push_rules::{PushRulesEvent, PushRulesEventContent}, + room::message::RoomMessageEventContent, + GlobalAccountDataEvent, GlobalAccountDataEventType, StateEventType, + }, + push::Ruleset, + CanonicalJsonValue, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, + OwnedUserId, RoomId, UserId, +}; use tracing::{debug, error, info, warn}; +use crate::{ + service::rooms::timeline::PduCount, services, utils, Config, Error, + PduEvent, Result, Services, SERVICES, +}; + pub(crate) struct KeyValueDatabase { db: Arc, @@ -74,8 +74,9 @@ pub(crate) struct KeyValueDatabase { // Trees "owned" by `self::key_value::uiaa` // User-interactive authentication pub(super) userdevicesessionid_uiaainfo: Arc, - pub(super) userdevicesessionid_uiaarequest: - RwLock>, + pub(super) userdevicesessionid_uiaarequest: RwLock< + BTreeMap<(OwnedUserId, OwnedDeviceId, String), CanonicalJsonValue>, + >, // Trees "owned" by `self::key_value::rooms::edus` // ReadReceiptId = RoomId + Count + UserId @@ -169,13 +170,15 @@ pub(crate) struct KeyValueDatabase { pub(super) statehash_shortstatehash: Arc, - // StateDiff = parent (or 0) + (shortstatekey+shorteventid++) + 0_u64 + (shortstatekey+shorteventid--) + // StateDiff = parent (or 0) + (shortstatekey+shorteventid++) + 0_u64 + + // (shortstatekey+shorteventid--) pub(super) shortstatehash_statediff: Arc, pub(super) shorteventid_authchain: Arc, /// RoomId + EventId -> outlier PDU. - /// Any pdu that has passed the steps 1-8 in the incoming event /federation/send/txn. + /// Any pdu that has passed the steps 1-8 in the incoming event + /// /federation/send/txn. pub(super) eventid_outlierpdu: Arc, pub(super) softfailedeventids: Arc, @@ -214,10 +217,12 @@ pub(crate) struct KeyValueDatabase { // EduCount: Count of last EDU sync pub(super) servername_educount: Arc, - // ServernameEvent = (+ / $)SenderKey / ServerName / UserId + PduId / Id (for edus), Data = EDU content + // ServernameEvent = (+ / $)SenderKey / ServerName / UserId + PduId / Id + // (for edus), Data = EDU content pub(super) servernameevent_data: Arc, - // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / Id (for edus), Data = EDU content + // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / Id (for + // edus), Data = EDU content pub(super) servercurrentevent_data: Arc, // Trees "owned" by `self::key_value::appservice` @@ -231,10 +236,14 @@ pub(crate) struct KeyValueDatabase { pub(super) shorteventid_cache: Mutex>>, pub(super) auth_chain_cache: Mutex, Arc>>>, pub(super) eventidshort_cache: Mutex>, - pub(super) statekeyshort_cache: Mutex>, - pub(super) shortstatekey_cache: Mutex>, - pub(super) our_real_users_cache: RwLock>>>, - pub(super) appservice_in_room_cache: RwLock>>, + pub(super) statekeyshort_cache: + Mutex>, + pub(super) shortstatekey_cache: + Mutex>, + pub(super) our_real_users_cache: + RwLock>>>, + pub(super) appservice_in_room_cache: + RwLock>>, pub(super) lasttimelinecount_cache: Mutex>, } @@ -271,13 +280,15 @@ impl KeyValueDatabase { if sqlite_exists && config.database_backend != "sqlite" { return Err(Error::bad_config( - "Found sqlite at database_path, but is not specified in config.", + "Found sqlite at database_path, but is not specified in \ + config.", )); } if rocksdb_exists && config.database_backend != "rocksdb" { return Err(Error::bad_config( - "Found rocksdb at database_path, but is not specified in config.", + "Found rocksdb at database_path, but is not specified in \ + config.", )); } @@ -294,19 +305,30 @@ impl KeyValueDatabase { Self::check_db_setup(&config)?; if !Path::new(&config.database_path).exists() { - std::fs::create_dir_all(&config.database_path) - .map_err(|_| Error::BadConfig("Database folder doesn't exists and couldn't be created (e.g. due to missing permissions). Please create the database folder yourself."))?; + std::fs::create_dir_all(&config.database_path).map_err(|_| { + Error::BadConfig( + "Database folder doesn't exists and couldn't be created \ + (e.g. due to missing permissions). Please create the \ + database folder yourself.", + ) + })?; } #[cfg_attr( not(any(feature = "rocksdb", feature = "sqlite")), allow(unused_variables) )] - let builder: Arc = match &*config.database_backend { + let builder: Arc = match &*config + .database_backend + { #[cfg(feature = "sqlite")] - "sqlite" => Arc::new(Arc::::open(&config)?), + "sqlite" => { + Arc::new(Arc::::open(&config)?) + } #[cfg(feature = "rocksdb")] - "rocksdb" => Arc::new(Arc::::open(&config)?), + "rocksdb" => { + Arc::new(Arc::::open(&config)?) + } _ => { return Err(Error::BadConfig("Database backend not found.")); } @@ -327,28 +349,38 @@ impl KeyValueDatabase { userid_avatarurl: builder.open_tree("userid_avatarurl")?, userid_blurhash: builder.open_tree("userid_blurhash")?, userdeviceid_token: builder.open_tree("userdeviceid_token")?, - userdeviceid_metadata: builder.open_tree("userdeviceid_metadata")?, - userid_devicelistversion: builder.open_tree("userid_devicelistversion")?, + userdeviceid_metadata: builder + .open_tree("userdeviceid_metadata")?, + userid_devicelistversion: builder + .open_tree("userid_devicelistversion")?, token_userdeviceid: builder.open_tree("token_userdeviceid")?, - onetimekeyid_onetimekeys: builder.open_tree("onetimekeyid_onetimekeys")?, - userid_lastonetimekeyupdate: builder.open_tree("userid_lastonetimekeyupdate")?, + onetimekeyid_onetimekeys: builder + .open_tree("onetimekeyid_onetimekeys")?, + userid_lastonetimekeyupdate: builder + .open_tree("userid_lastonetimekeyupdate")?, keychangeid_userid: builder.open_tree("keychangeid_userid")?, keyid_key: builder.open_tree("keyid_key")?, userid_masterkeyid: builder.open_tree("userid_masterkeyid")?, - userid_selfsigningkeyid: builder.open_tree("userid_selfsigningkeyid")?, - userid_usersigningkeyid: builder.open_tree("userid_usersigningkeyid")?, + userid_selfsigningkeyid: builder + .open_tree("userid_selfsigningkeyid")?, + userid_usersigningkeyid: builder + .open_tree("userid_usersigningkeyid")?, userfilterid_filter: builder.open_tree("userfilterid_filter")?, todeviceid_events: builder.open_tree("todeviceid_events")?, - userdevicesessionid_uiaainfo: builder.open_tree("userdevicesessionid_uiaainfo")?, + userdevicesessionid_uiaainfo: builder + .open_tree("userdevicesessionid_uiaainfo")?, userdevicesessionid_uiaarequest: RwLock::new(BTreeMap::new()), - readreceiptid_readreceipt: builder.open_tree("readreceiptid_readreceipt")?, + readreceiptid_readreceipt: builder + .open_tree("readreceiptid_readreceipt")?, // "Private" read receipt - roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, + roomuserid_privateread: builder + .open_tree("roomuserid_privateread")?, roomuserid_lastprivatereadupdate: builder .open_tree("roomuserid_lastprivatereadupdate")?, presenceid_presence: builder.open_tree("presenceid_presence")?, - userid_lastpresenceupdate: builder.open_tree("userid_lastpresenceupdate")?, + userid_lastpresenceupdate: builder + .open_tree("userid_lastpresenceupdate")?, pduid_pdu: builder.open_tree("pduid_pdu")?, eventid_pduid: builder.open_tree("eventid_pduid")?, roomid_pduleaves: builder.open_tree("roomid_pduleaves")?, @@ -367,9 +399,12 @@ impl KeyValueDatabase { roomuserid_joined: builder.open_tree("roomuserid_joined")?, roomid_joinedcount: builder.open_tree("roomid_joinedcount")?, roomid_invitedcount: builder.open_tree("roomid_invitedcount")?, - roomuseroncejoinedids: builder.open_tree("roomuseroncejoinedids")?, - userroomid_invitestate: builder.open_tree("userroomid_invitestate")?, - roomuserid_invitecount: builder.open_tree("roomuserid_invitecount")?, + roomuseroncejoinedids: builder + .open_tree("roomuseroncejoinedids")?, + userroomid_invitestate: builder + .open_tree("userroomid_invitestate")?, + roomuserid_invitecount: builder + .open_tree("roomuserid_invitecount")?, userroomid_leftstate: builder.open_tree("userroomid_leftstate")?, roomuserid_leftcount: builder.open_tree("roomuserid_leftcount")?, @@ -377,41 +412,57 @@ impl KeyValueDatabase { lazyloadedids: builder.open_tree("lazyloadedids")?, - userroomid_notificationcount: builder.open_tree("userroomid_notificationcount")?, - userroomid_highlightcount: builder.open_tree("userroomid_highlightcount")?, - roomuserid_lastnotificationread: builder.open_tree("userroomid_highlightcount")?, + userroomid_notificationcount: builder + .open_tree("userroomid_notificationcount")?, + userroomid_highlightcount: builder + .open_tree("userroomid_highlightcount")?, + roomuserid_lastnotificationread: builder + .open_tree("userroomid_highlightcount")?, - statekey_shortstatekey: builder.open_tree("statekey_shortstatekey")?, - shortstatekey_statekey: builder.open_tree("shortstatekey_statekey")?, + statekey_shortstatekey: builder + .open_tree("statekey_shortstatekey")?, + shortstatekey_statekey: builder + .open_tree("shortstatekey_statekey")?, - shorteventid_authchain: builder.open_tree("shorteventid_authchain")?, + shorteventid_authchain: builder + .open_tree("shorteventid_authchain")?, roomid_shortroomid: builder.open_tree("roomid_shortroomid")?, - shortstatehash_statediff: builder.open_tree("shortstatehash_statediff")?, + shortstatehash_statediff: builder + .open_tree("shortstatehash_statediff")?, eventid_shorteventid: builder.open_tree("eventid_shorteventid")?, shorteventid_eventid: builder.open_tree("shorteventid_eventid")?, - shorteventid_shortstatehash: builder.open_tree("shorteventid_shortstatehash")?, - roomid_shortstatehash: builder.open_tree("roomid_shortstatehash")?, - roomsynctoken_shortstatehash: builder.open_tree("roomsynctoken_shortstatehash")?, - statehash_shortstatehash: builder.open_tree("statehash_shortstatehash")?, + shorteventid_shortstatehash: builder + .open_tree("shorteventid_shortstatehash")?, + roomid_shortstatehash: builder + .open_tree("roomid_shortstatehash")?, + roomsynctoken_shortstatehash: builder + .open_tree("roomsynctoken_shortstatehash")?, + statehash_shortstatehash: builder + .open_tree("statehash_shortstatehash")?, eventid_outlierpdu: builder.open_tree("eventid_outlierpdu")?, softfailedeventids: builder.open_tree("softfailedeventids")?, tofrom_relation: builder.open_tree("tofrom_relation")?, referencedevents: builder.open_tree("referencedevents")?, - roomuserdataid_accountdata: builder.open_tree("roomuserdataid_accountdata")?, - roomusertype_roomuserdataid: builder.open_tree("roomusertype_roomuserdataid")?, + roomuserdataid_accountdata: builder + .open_tree("roomuserdataid_accountdata")?, + roomusertype_roomuserdataid: builder + .open_tree("roomusertype_roomuserdataid")?, mediaid_file: builder.open_tree("mediaid_file")?, backupid_algorithm: builder.open_tree("backupid_algorithm")?, backupid_etag: builder.open_tree("backupid_etag")?, backupkeyid_backup: builder.open_tree("backupkeyid_backup")?, - userdevicetxnid_response: builder.open_tree("userdevicetxnid_response")?, + userdevicetxnid_response: builder + .open_tree("userdevicetxnid_response")?, servername_educount: builder.open_tree("servername_educount")?, servernameevent_data: builder.open_tree("servernameevent_data")?, - servercurrentevent_data: builder.open_tree("servercurrentevent_data")?, - id_appserviceregistrations: builder.open_tree("id_appserviceregistrations")?, + servercurrentevent_data: builder + .open_tree("servercurrentevent_data")?, + id_appserviceregistrations: builder + .open_tree("id_appserviceregistrations")?, senderkey_pusher: builder.open_tree("senderkey_pusher")?, global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, @@ -489,11 +540,13 @@ impl KeyValueDatabase { if !services().users.exists(&grapevine_user)? { error!( - "The {} server user does not exist, and the database is not new.", + "The {} server user does not exist, and the database is \ + not new.", grapevine_user ); return Err(Error::bad_database( - "Cannot reuse an existing database after changing the server name, please delete the old one first." + "Cannot reuse an existing database after changing the \ + server name, please delete the old one first.", )); } } @@ -505,14 +558,15 @@ impl KeyValueDatabase { // MIGRATIONS if services().globals.database_version()? < 1 { for (roomserverid, _) in db.roomserverids.iter() { - let mut parts = roomserverid.split(|&b| b == 0xff); - let room_id = parts.next().expect("split always returns one element"); + let mut parts = roomserverid.split(|&b| b == 0xFF); + let room_id = + parts.next().expect("split always returns one element"); let Some(servername) = parts.next() else { error!("Migration: Invalid roomserverid in db."); continue; }; let mut serverroomid = servername.to_vec(); - serverroomid.push(0xff); + serverroomid.push(0xFF); serverroomid.extend_from_slice(room_id); db.serverroomids.insert(&serverroomid, &[])?; @@ -524,13 +578,16 @@ impl KeyValueDatabase { } if services().globals.database_version()? < 2 { - // We accidentally inserted hashed versions of "" into the db instead of just "" + // We accidentally inserted hashed versions of "" into the db + // instead of just "" for (userid, password) in db.userid_password.iter() { let password = utils::string_from_bytes(&password); - let empty_hashed_password = password.map_or(false, |password| { - argon2::verify_encoded(&password, b"").unwrap_or(false) - }); + let empty_hashed_password = + password.map_or(false, |password| { + argon2::verify_encoded(&password, b"") + .unwrap_or(false) + }); if empty_hashed_password { db.userid_password.insert(&userid, b"")?; @@ -567,10 +624,16 @@ impl KeyValueDatabase { if services().users.is_deactivated(&our_user)? { continue; } - for room in services().rooms.state_cache.rooms_joined(&our_user) { - for user in services().rooms.state_cache.room_members(&room?) { + for room in + services().rooms.state_cache.rooms_joined(&our_user) + { + for user in + services().rooms.state_cache.room_members(&room?) + { let user = user?; - if user.server_name() != services().globals.server_name() { + if user.server_name() + != services().globals.server_name() + { info!(?user, "Migration: creating user"); services().users.create(&user, None)?; } @@ -585,16 +648,18 @@ impl KeyValueDatabase { if services().globals.database_version()? < 5 { // Upgrade user data store - for (roomuserdataid, _) in db.roomuserdataid_accountdata.iter() { - let mut parts = roomuserdataid.split(|&b| b == 0xff); + for (roomuserdataid, _) in db.roomuserdataid_accountdata.iter() + { + let mut parts = roomuserdataid.split(|&b| b == 0xFF); let room_id = parts.next().unwrap(); let user_id = parts.next().unwrap(); - let event_type = roomuserdataid.rsplit(|&b| b == 0xff).next().unwrap(); + let event_type = + roomuserdataid.rsplit(|&b| b == 0xFF).next().unwrap(); let mut key = room_id.to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(event_type); db.roomusertype_roomuserdataid @@ -611,7 +676,10 @@ impl KeyValueDatabase { for (roomid, _) in db.roomid_shortstatehash.iter() { let string = utils::string_from_bytes(&roomid).unwrap(); let room_id = <&RoomId>::try_from(string.as_str()).unwrap(); - services().rooms.state_cache.update_joined_count(room_id)?; + services() + .rooms + .state_cache + .update_joined_count(room_id)?; } services().globals.bump_database_version(6)?; @@ -621,7 +689,8 @@ impl KeyValueDatabase { if services().globals.database_version()? < 7 { // Upgrade state store - let mut last_roomstates: HashMap = HashMap::new(); + let mut last_roomstates: HashMap = + HashMap::new(); let mut current_sstatehash: Option = None; let mut current_room = None; let mut current_state = HashSet::new(); @@ -633,7 +702,8 @@ impl KeyValueDatabase { current_state: HashSet<_>, last_roomstates: &mut HashMap<_, _>| { counter += 1; - let last_roomsstatehash = last_roomstates.get(current_room); + let last_roomsstatehash = + last_roomstates.get(current_room); let states_parents = last_roomsstatehash.map_or_else( || Ok(Vec::new()), @@ -641,12 +711,16 @@ impl KeyValueDatabase { services() .rooms .state_compressor - .load_shortstatehash_info(last_roomsstatehash) + .load_shortstatehash_info( + last_roomsstatehash, + ) }, )?; let (statediffnew, statediffremoved) = - if let Some(parent_stateinfo) = states_parents.last() { + if let Some(parent_stateinfo) = + states_parents.last() + { let statediffnew = current_state .difference(&parent_stateinfo.1) .copied() @@ -663,21 +737,28 @@ impl KeyValueDatabase { (current_state, HashSet::new()) }; - services().rooms.state_compressor.save_state_from_diff( - current_sstatehash, - Arc::new(statediffnew), - Arc::new(statediffremoved), - // every state change is 2 event changes on average - 2, - states_parents, - )?; + services() + .rooms + .state_compressor + .save_state_from_diff( + current_sstatehash, + Arc::new(statediffnew), + Arc::new(statediffremoved), + // every state change is 2 event changes on + // average + 2, + states_parents, + )?; Ok::<_, Error>(()) }; - for (k, seventid) in db.db.open_tree("stateid_shorteventid")?.iter() { - let sstatehash = utils::u64_from_bytes(&k[0..size_of::()]) - .expect("number of bytes is correct"); + for (k, seventid) in + db.db.open_tree("stateid_shorteventid")?.iter() + { + let sstatehash = + utils::u64_from_bytes(&k[0..size_of::()]) + .expect("number of bytes is correct"); let sstatekey = k[size_of::()..].to_vec(); if Some(sstatehash) != current_sstatehash { if let Some(current_sstatehash) = current_sstatehash { @@ -687,15 +768,23 @@ impl KeyValueDatabase { current_state, &mut last_roomstates, )?; - last_roomstates - .insert(current_room.clone().unwrap(), current_sstatehash); + last_roomstates.insert( + current_room.clone().unwrap(), + current_sstatehash, + ); } current_state = HashSet::new(); current_sstatehash = Some(sstatehash); - let event_id = db.shorteventid_eventid.get(&seventid).unwrap().unwrap(); - let string = utils::string_from_bytes(&event_id).unwrap(); - let event_id = <&EventId>::try_from(string.as_str()).unwrap(); + let event_id = db + .shorteventid_eventid + .get(&seventid) + .unwrap() + .unwrap(); + let string = + utils::string_from_bytes(&event_id).unwrap(); + let event_id = + <&EventId>::try_from(string.as_str()).unwrap(); let pdu = services() .rooms .timeline @@ -710,7 +799,8 @@ impl KeyValueDatabase { let mut val = sstatekey; val.extend_from_slice(&seventid); - current_state.insert(val.try_into().expect("size is correct")); + current_state + .insert(val.try_into().expect("size is correct")); } if let Some(current_sstatehash) = current_sstatehash { @@ -730,7 +820,8 @@ impl KeyValueDatabase { if services().globals.database_version()? < 8 { // Generate short room ids for all rooms for (room_id, _) in db.roomid_shortstatehash.iter() { - let shortroomid = services().globals.next_count()?.to_be_bytes(); + let shortroomid = + services().globals.next_count()?.to_be_bytes(); db.roomid_shortroomid.insert(&room_id, &shortroomid)?; info!("Migration: 8"); } @@ -739,7 +830,7 @@ impl KeyValueDatabase { if !key.starts_with(b"!") { return None; } - let mut parts = key.splitn(2, |&b| b == 0xff); + let mut parts = key.splitn(2, |&b| b == 0xFF); let room_id = parts.next().unwrap(); let count = parts.next().unwrap(); @@ -757,25 +848,26 @@ impl KeyValueDatabase { db.pduid_pdu.insert_batch(&mut batch)?; - let mut batch2 = db.eventid_pduid.iter().filter_map(|(k, value)| { - if !value.starts_with(b"!") { - return None; - } - let mut parts = value.splitn(2, |&b| b == 0xff); - let room_id = parts.next().unwrap(); - let count = parts.next().unwrap(); + let mut batch2 = + db.eventid_pduid.iter().filter_map(|(k, value)| { + if !value.starts_with(b"!") { + return None; + } + let mut parts = value.splitn(2, |&b| b == 0xFF); + let room_id = parts.next().unwrap(); + let count = parts.next().unwrap(); - let short_room_id = db - .roomid_shortroomid - .get(room_id) - .unwrap() - .expect("shortroomid should exist"); + let short_room_id = db + .roomid_shortroomid + .get(room_id) + .unwrap() + .expect("shortroomid should exist"); - let mut new_value = short_room_id; - new_value.extend_from_slice(count); + let mut new_value = short_room_id; + new_value.extend_from_slice(count); - Some((k, new_value)) - }); + Some((k, new_value)) + }); db.eventid_pduid.insert_batch(&mut batch2)?; @@ -793,7 +885,7 @@ impl KeyValueDatabase { if !key.starts_with(b"!") { return None; } - let mut parts = key.splitn(4, |&b| b == 0xff); + let mut parts = key.splitn(4, |&b| b == 0xFF); let room_id = parts.next().unwrap(); let word = parts.next().unwrap(); let _pdu_id_room = parts.next().unwrap(); @@ -806,7 +898,7 @@ impl KeyValueDatabase { .expect("shortroomid should exist"); let mut new_key = short_room_id; new_key.extend_from_slice(word); - new_key.push(0xff); + new_key.push(0xFF); new_key.extend_from_slice(pdu_id_count); Some((new_key, Vec::new())) }) @@ -836,12 +928,15 @@ impl KeyValueDatabase { if services().globals.database_version()? < 10 { // Add other direction for shortstatekeys - for (statekey, shortstatekey) in db.statekey_shortstatekey.iter() { + for (statekey, shortstatekey) in + db.statekey_shortstatekey.iter() + { db.shortstatekey_statekey .insert(&shortstatekey, &statekey)?; } - // Force E2EE device list updates so we can send them over federation + // Force E2EE device list updates so we can send them over + // federation for user_id in services().users.iter().filter_map(Result::ok) { services().users.mark_device_key_update(&user_id)?; } @@ -852,9 +947,7 @@ impl KeyValueDatabase { } if services().globals.database_version()? < 11 { - db.db - .open_tree("userdevicesessionid_uiaarequest")? - .clear()?; + db.db.open_tree("userdevicesessionid_uiaarequest")?.clear()?; services().globals.bump_database_version(11)?; warn!("Migration: 10 -> 11 finished"); @@ -878,24 +971,34 @@ impl KeyValueDatabase { .get( None, &user, - GlobalAccountDataEventType::PushRules.to_string().into(), + GlobalAccountDataEventType::PushRules + .to_string() + .into(), ) .unwrap() .expect("Username is invalid"); let mut account_data = - serde_json::from_str::(raw_rules_list.get()).unwrap(); + serde_json::from_str::( + raw_rules_list.get(), + ) + .unwrap(); let rules_list = &mut account_data.content.global; //content rule { - let content_rule_transformation = - [".m.rules.contains_user_name", ".m.rule.contains_user_name"]; + let content_rule_transformation = [ + ".m.rules.contains_user_name", + ".m.rule.contains_user_name", + ]; - let rule = rules_list.content.get(content_rule_transformation[0]); + let rule = rules_list + .content + .get(content_rule_transformation[0]); if rule.is_some() { let mut rule = rule.unwrap().clone(); - rule.rule_id = content_rule_transformation[1].to_owned(); + rule.rule_id = + content_rule_transformation[1].to_owned(); rules_list .content .shift_remove(content_rule_transformation[0]); @@ -907,7 +1010,10 @@ impl KeyValueDatabase { { let underride_rule_transformation = [ [".m.rules.call", ".m.rule.call"], - [".m.rules.room_one_to_one", ".m.rule.room_one_to_one"], + [ + ".m.rules.room_one_to_one", + ".m.rule.room_one_to_one", + ], [ ".m.rules.encrypted_room_one_to_one", ".m.rule.encrypted_room_one_to_one", @@ -917,11 +1023,14 @@ impl KeyValueDatabase { ]; for transformation in underride_rule_transformation { - let rule = rules_list.underride.get(transformation[0]); + let rule = + rules_list.underride.get(transformation[0]); if let Some(rule) = rule { let mut rule = rule.clone(); rule.rule_id = transformation[1].to_owned(); - rules_list.underride.shift_remove(transformation[0]); + rules_list + .underride + .shift_remove(transformation[0]); rules_list.underride.insert(rule); } } @@ -930,8 +1039,11 @@ impl KeyValueDatabase { services().account_data.update( None, &user, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data).expect("to json value always works"), + GlobalAccountDataEventType::PushRules + .to_string() + .into(), + &serde_json::to_value(account_data) + .expect("to json value always works"), )?; } @@ -940,7 +1052,8 @@ impl KeyValueDatabase { warn!("Migration: 11 -> 12 finished"); } - // This migration can be reused as-is anytime the server-default rules are updated. + // This migration can be reused as-is anytime the server-default + // rules are updated. if services().globals.database_version()? < 13 { for username in services().users.list_local_users()? { let user = match UserId::parse_with_server_name( @@ -959,15 +1072,21 @@ impl KeyValueDatabase { .get( None, &user, - GlobalAccountDataEventType::PushRules.to_string().into(), + GlobalAccountDataEventType::PushRules + .to_string() + .into(), ) .unwrap() .expect("Username is invalid"); let mut account_data = - serde_json::from_str::(raw_rules_list.get()).unwrap(); + serde_json::from_str::( + raw_rules_list.get(), + ) + .unwrap(); - let user_default_rules = ruma::push::Ruleset::server_default(&user); + let user_default_rules = + ruma::push::Ruleset::server_default(&user); account_data .content .global @@ -976,8 +1095,11 @@ impl KeyValueDatabase { services().account_data.update( None, &user, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data).expect("to json value always works"), + GlobalAccountDataEventType::PushRules + .to_string() + .into(), + &serde_json::to_value(account_data) + .expect("to json value always works"), )?; } @@ -1018,13 +1140,24 @@ impl KeyValueDatabase { match set_emergency_access() { Ok(pwd_set) => { if pwd_set { - warn!("The Grapevine account emergency password is set! Please unset it as soon as you finish admin account recovery!"); - services().admin.send_message(RoomMessageEventContent::text_plain("The Grapevine account emergency password is set! Please unset it as soon as you finish admin account recovery!")); + warn!( + "The Grapevine account emergency password is set! \ + Please unset it as soon as you finish admin account \ + recovery!" + ); + services().admin.send_message( + RoomMessageEventContent::text_plain( + "The Grapevine account emergency password is set! \ + Please unset it as soon as you finish admin \ + account recovery!", + ), + ); } } Err(e) => { error!( - "Could not set the configured emergency password for the grapevine user: {}", + "Could not set the configured emergency password for the \ + grapevine user: {}", e ); } @@ -1050,15 +1183,15 @@ impl KeyValueDatabase { #[tracing::instrument] pub(crate) async fn start_cleanup_task() { - use tokio::time::interval; + use std::time::{Duration, Instant}; #[cfg(unix)] use tokio::signal::unix::{signal, SignalKind}; + use tokio::time::interval; - use std::time::{Duration, Instant}; - - let timer_interval = - Duration::from_secs(u64::from(services().globals.config.cleanup_second_interval)); + let timer_interval = Duration::from_secs(u64::from( + services().globals.config.cleanup_second_interval, + )); tokio::spawn(async move { let mut i = interval(timer_interval); @@ -1092,11 +1225,14 @@ impl KeyValueDatabase { } } -/// Sets the emergency password and push rules for the @grapevine account in case emergency password is set +/// Sets the emergency password and push rules for the @grapevine account in +/// case emergency password is set fn set_emergency_access() -> Result { - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is a valid UserId"); + let grapevine_user = UserId::parse_with_server_name( + "grapevine", + services().globals.server_name(), + ) + .expect("@grapevine:server_name is a valid UserId"); services().users.set_password( &grapevine_user, @@ -1113,7 +1249,9 @@ fn set_emergency_access() -> Result { &grapevine_user, GlobalAccountDataEventType::PushRules.to_string().into(), &serde_json::to_value(&GlobalAccountDataEvent { - content: PushRulesEventContent { global: ruleset }, + content: PushRulesEventContent { + global: ruleset, + }, }) .expect("to json value always works"), )?; diff --git a/src/database/abstraction.rs b/src/database/abstraction.rs index 3b64332e..09f17720 100644 --- a/src/database/abstraction.rs +++ b/src/database/abstraction.rs @@ -1,8 +1,8 @@ +use std::{future::Future, pin::Pin, sync::Arc}; + use super::Config; use crate::Result; -use std::{future::Future, pin::Pin, sync::Arc}; - #[cfg(feature = "sqlite")] pub(crate) mod sqlite; @@ -22,7 +22,8 @@ pub(crate) trait KeyValueDatabaseEngine: Send + Sync { Ok(()) } fn memory_usage(&self) -> Result { - Ok("Current database engine does not support memory usage reporting.".to_owned()) + Ok("Current database engine does not support memory usage reporting." + .to_owned()) } fn clear_caches(&self) {} } @@ -31,7 +32,10 @@ pub(crate) trait KvTree: Send + Sync { fn get(&self, key: &[u8]) -> Result>>; fn insert(&self, key: &[u8], value: &[u8]) -> Result<()>; - fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()>; + fn insert_batch( + &self, + iter: &mut dyn Iterator, Vec)>, + ) -> Result<()>; fn remove(&self, key: &[u8]) -> Result<()>; @@ -44,14 +48,20 @@ pub(crate) trait KvTree: Send + Sync { ) -> Box, Vec)> + 'a>; fn increment(&self, key: &[u8]) -> Result>; - fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()>; + fn increment_batch( + &self, + iter: &mut dyn Iterator>, + ) -> Result<()>; fn scan_prefix<'a>( &'a self, prefix: Vec, ) -> Box, Vec)> + 'a>; - fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin + Send + 'a>>; + fn watch_prefix<'a>( + &'a self, + prefix: &[u8], + ) -> Pin + Send + 'a>>; fn clear(&self) -> Result<()> { for (key, _) in self.iter() { diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 3c9c87b7..f73a4d0f 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -1,17 +1,21 @@ -use rocksdb::{ - perf::get_memory_usage_stats, BlockBasedOptions, BoundColumnFamily, Cache, - ColumnFamilyDescriptor, DBCompactionStyle, DBCompressionType, DBRecoveryMode, DBWithThreadMode, - Direction, IteratorMode, MultiThreaded, Options, ReadOptions, WriteOptions, -}; - -use super::{super::Config, watchers::Watchers, KeyValueDatabaseEngine, KvTree}; -use crate::{utils, Result}; use std::{ future::Future, pin::Pin, sync::{Arc, RwLock}, }; +use rocksdb::{ + perf::get_memory_usage_stats, BlockBasedOptions, BoundColumnFamily, Cache, + ColumnFamilyDescriptor, DBCompactionStyle, DBCompressionType, + DBRecoveryMode, DBWithThreadMode, Direction, IteratorMode, MultiThreaded, + Options, ReadOptions, WriteOptions, +}; + +use super::{ + super::Config, watchers::Watchers, KeyValueDatabaseEngine, KvTree, +}; +use crate::{utils, Result}; + pub(crate) struct Engine { rocks: DBWithThreadMode, max_open_files: i32, @@ -38,7 +42,8 @@ fn db_options(max_open_files: i32, rocksdb_cache: &Cache) -> Options { let mut db_opts = Options::default(); db_opts.set_block_based_table_factory(&block_based_options); db_opts.create_if_missing(true); - db_opts.increase_parallelism(num_cpus::get().try_into().unwrap_or(i32::MAX)); + db_opts + .increase_parallelism(num_cpus::get().try_into().unwrap_or(i32::MAX)); db_opts.set_max_open_files(max_open_files); db_opts.set_compression_type(DBCompressionType::Lz4); db_opts.set_bottommost_compression_type(DBCompressionType::Zstd); @@ -69,13 +74,17 @@ impl KeyValueDatabaseEngine for Arc { clippy::cast_sign_loss, clippy::cast_possible_truncation )] - let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize; + let cache_capacity_bytes = + (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize; let rocksdb_cache = Cache::new_lru_cache(cache_capacity_bytes); let db_opts = db_options(config.rocksdb_max_open_files, &rocksdb_cache); - let cfs = DBWithThreadMode::::list_cf(&db_opts, &config.database_path) - .unwrap_or_default(); + let cfs = DBWithThreadMode::::list_cf( + &db_opts, + &config.database_path, + ) + .unwrap_or_default(); let db = DBWithThreadMode::::open_cf_descriptors( &db_opts, @@ -119,14 +128,14 @@ impl KeyValueDatabaseEngine for Arc { #[allow(clippy::as_conversions, clippy::cast_precision_loss)] fn memory_usage(&self) -> Result { - let stats = get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; + let stats = + get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; Ok(format!( - "Approximate memory usage of all the mem-tables: {:.3} MB\n\ - Approximate memory usage of un-flushed mem-tables: {:.3} MB\n\ - Approximate memory usage of all the table readers: {:.3} MB\n\ - Approximate memory usage by cache: {:.3} MB\n\ - Approximate memory usage by cache pinned: {:.3} MB\n\ - ", + "Approximate memory usage of all the mem-tables: {:.3} \ + MB\nApproximate memory usage of un-flushed mem-tables: {:.3} \ + MB\nApproximate memory usage of all the table readers: {:.3} \ + MB\nApproximate memory usage by cache: {:.3} MB\nApproximate \ + memory usage by cache pinned: {:.3} MB\n", stats.mem_table_total as f64 / 1024.0 / 1024.0, stats.mem_table_unflushed as f64 / 1024.0 / 1024.0, stats.mem_table_readers_total as f64 / 1024.0 / 1024.0, @@ -154,9 +163,7 @@ impl KvTree for RocksDbEngineTree<'_> { fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> { let writeoptions = WriteOptions::default(); let lock = self.write_lock.read().unwrap(); - self.db - .rocks - .put_cf_opt(&self.cf(), key, value, &writeoptions)?; + self.db.rocks.put_cf_opt(&self.cf(), key, value, &writeoptions)?; drop(lock); self.watchers.wake(key); @@ -164,12 +171,13 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(()) } - fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { + fn insert_batch( + &self, + iter: &mut dyn Iterator, Vec)>, + ) -> Result<()> { let writeoptions = WriteOptions::default(); for (key, value) in iter { - self.db - .rocks - .put_cf_opt(&self.cf(), key, value, &writeoptions)?; + self.db.rocks.put_cf_opt(&self.cf(), key, value, &writeoptions)?; } Ok(()) @@ -177,10 +185,7 @@ impl KvTree for RocksDbEngineTree<'_> { fn remove(&self, key: &[u8]) -> Result<()> { let writeoptions = WriteOptions::default(); - Ok(self - .db - .rocks - .delete_cf_opt(&self.cf(), key, &writeoptions)?) + Ok(self.db.rocks.delete_cf_opt(&self.cf(), key, &writeoptions)?) } fn iter<'a>(&'a self) -> Box, Vec)> + 'a> { @@ -230,26 +235,26 @@ impl KvTree for RocksDbEngineTree<'_> { let old = self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?; let new = utils::increment(old.as_deref()); - self.db - .rocks - .put_cf_opt(&self.cf(), key, &new, &writeoptions)?; + self.db.rocks.put_cf_opt(&self.cf(), key, &new, &writeoptions)?; drop(lock); Ok(new) } - fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()> { + fn increment_batch( + &self, + iter: &mut dyn Iterator>, + ) -> Result<()> { let readoptions = ReadOptions::default(); let writeoptions = WriteOptions::default(); let lock = self.write_lock.write().unwrap(); for key in iter { - let old = self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?; + let old = + self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?; let new = utils::increment(old.as_deref()); - self.db - .rocks - .put_cf_opt(&self.cf(), key, new, &writeoptions)?; + self.db.rocks.put_cf_opt(&self.cf(), key, new, &writeoptions)?; } drop(lock); @@ -277,7 +282,10 @@ impl KvTree for RocksDbEngineTree<'_> { ) } - fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin + Send + 'a>> { + fn watch_prefix<'a>( + &'a self, + prefix: &[u8], + ) -> Pin + Send + 'a>> { self.watchers.watch(prefix) } } diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 1dc8a47d..f73ba509 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -1,7 +1,3 @@ -use super::{watchers::Watchers, KeyValueDatabaseEngine, KvTree}; -use crate::{database::Config, Result}; -use parking_lot::{Mutex, MutexGuard}; -use rusqlite::{Connection, DatabaseName::Main, OptionalExtension}; use std::{ cell::RefCell, future::Future, @@ -9,9 +5,15 @@ use std::{ pin::Pin, sync::Arc, }; + +use parking_lot::{Mutex, MutexGuard}; +use rusqlite::{Connection, DatabaseName::Main, OptionalExtension}; use thread_local::ThreadLocal; use tracing::debug; +use super::{watchers::Watchers, KeyValueDatabaseEngine, KvTree}; +use crate::{database::Config, Result}; + thread_local! { static READ_CONNECTION: RefCell> = RefCell::new(None); static READ_CONNECTION_ITERATOR: RefCell> = RefCell::new(None); @@ -68,7 +70,11 @@ impl Engine { conn.pragma_update(Some(Main), "page_size", 2048)?; conn.pragma_update(Some(Main), "journal_mode", "WAL")?; conn.pragma_update(Some(Main), "synchronous", "NORMAL")?; - conn.pragma_update(Some(Main), "cache_size", -i64::from(cache_size_kb))?; + conn.pragma_update( + Some(Main), + "cache_size", + -i64::from(cache_size_kb), + )?; conn.pragma_update(Some(Main), "wal_autocheckpoint", 0)?; Ok(conn) @@ -79,18 +85,23 @@ impl Engine { } fn read_lock(&self) -> &Connection { - self.read_conn_tls - .get_or(|| Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap()) + self.read_conn_tls.get_or(|| { + Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap() + }) } fn read_lock_iterator(&self) -> &Connection { - self.read_iterator_conn_tls - .get_or(|| Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap()) + self.read_iterator_conn_tls.get_or(|| { + Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap() + }) } pub(crate) fn flush_wal(self: &Arc) -> Result<()> { - self.write_lock() - .pragma_update(Some(Main), "wal_checkpoint", "RESTART")?; + self.write_lock().pragma_update( + Some(Main), + "wal_checkpoint", + "RESTART", + )?; Ok(()) } } @@ -108,7 +119,8 @@ impl KeyValueDatabaseEngine for Arc { // calculates cache-size per permanent connection // 1. convert MB to KiB - // 2. divide by permanent connections + permanent iter connections + write connection + // 2. divide by permanent connections + permanent iter connections + + // write connection // 3. round down to nearest integer #[allow( clippy::as_conversions, @@ -117,9 +129,11 @@ impl KeyValueDatabaseEngine for Arc { clippy::cast_sign_loss )] let cache_size_per_thread = ((config.db_cache_capacity_mb * 1024.0) - / ((num_cpus::get() as f64 * 2.0) + 1.0)) as u32; + / ((num_cpus::get() as f64 * 2.0) + 1.0)) + as u32; - let writer = Mutex::new(Engine::prepare_conn(&path, cache_size_per_thread)?); + let writer = + Mutex::new(Engine::prepare_conn(&path, cache_size_per_thread)?); let arc = Arc::new(Engine { writer, @@ -133,7 +147,13 @@ impl KeyValueDatabaseEngine for Arc { } fn open_tree(&self, name: &str) -> Result> { - self.write_lock().execute(&format!("CREATE TABLE IF NOT EXISTS {name} ( \"key\" BLOB PRIMARY KEY, \"value\" BLOB NOT NULL )"), [])?; + self.write_lock().execute( + &format!( + "CREATE TABLE IF NOT EXISTS {name} ( \"key\" BLOB PRIMARY \ + KEY, \"value\" BLOB NOT NULL )" + ), + [], + )?; Ok(Arc::new(SqliteTable { engine: Arc::clone(self), @@ -161,14 +181,26 @@ pub(crate) struct SqliteTable { type TupleOfBytes = (Vec, Vec); impl SqliteTable { - fn get_with_guard(&self, guard: &Connection, key: &[u8]) -> Result>> { + fn get_with_guard( + &self, + guard: &Connection, + key: &[u8], + ) -> Result>> { Ok(guard - .prepare(format!("SELECT value FROM {} WHERE key = ?", self.name).as_str())? + .prepare( + format!("SELECT value FROM {} WHERE key = ?", self.name) + .as_str(), + )? .query_row([key], |row| row.get(0)) .optional()?) } - fn insert_with_guard(&self, guard: &Connection, key: &[u8], value: &[u8]) -> Result<()> { + fn insert_with_guard( + &self, + guard: &Connection, + key: &[u8], + value: &[u8], + ) -> Result<()> { guard.execute( format!( "INSERT OR REPLACE INTO {} (key, value) VALUES (?, ?)", @@ -222,7 +254,10 @@ impl KvTree for SqliteTable { Ok(()) } - fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { + fn insert_batch( + &self, + iter: &mut dyn Iterator, Vec)>, + ) -> Result<()> { let guard = self.engine.write_lock(); guard.execute("BEGIN", [])?; @@ -236,7 +271,10 @@ impl KvTree for SqliteTable { Ok(()) } - fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()> { + fn increment_batch( + &self, + iter: &mut dyn Iterator>, + ) -> Result<()> { let guard = self.engine.write_lock(); guard.execute("BEGIN", [])?; @@ -282,7 +320,8 @@ impl KvTree for SqliteTable { let statement = Box::leak(Box::new( guard .prepare(&format!( - "SELECT key, value FROM {} WHERE key <= ? ORDER BY key DESC", + "SELECT key, value FROM {} WHERE key <= ? ORDER BY \ + key DESC", &self.name )) .unwrap(), @@ -292,7 +331,9 @@ impl KvTree for SqliteTable { let iterator = Box::new( statement - .query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1)))) + .query_map([from], |row| { + Ok((row.get_unwrap(0), row.get_unwrap(1))) + }) .unwrap() .map(Result::unwrap), ); @@ -304,7 +345,8 @@ impl KvTree for SqliteTable { let statement = Box::leak(Box::new( guard .prepare(&format!( - "SELECT key, value FROM {} WHERE key >= ? ORDER BY key ASC", + "SELECT key, value FROM {} WHERE key >= ? ORDER BY \ + key ASC", &self.name )) .unwrap(), @@ -314,7 +356,9 @@ impl KvTree for SqliteTable { let iterator = Box::new( statement - .query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1)))) + .query_map([from], |row| { + Ok((row.get_unwrap(0), row.get_unwrap(1))) + }) .unwrap() .map(Result::unwrap), ); @@ -338,14 +382,20 @@ impl KvTree for SqliteTable { Ok(new) } - fn scan_prefix<'a>(&'a self, prefix: Vec) -> Box + 'a> { + fn scan_prefix<'a>( + &'a self, + prefix: Vec, + ) -> Box + 'a> { Box::new( self.iter_from(&prefix, false) .take_while(move |(key, _)| key.starts_with(&prefix)), ) } - fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin + Send + 'a>> { + fn watch_prefix<'a>( + &'a self, + prefix: &[u8], + ) -> Pin + Send + 'a>> { self.watchers.watch(prefix) } diff --git a/src/database/abstraction/watchers.rs b/src/database/abstraction/watchers.rs index c8737583..9f6e5a00 100644 --- a/src/database/abstraction/watchers.rs +++ b/src/database/abstraction/watchers.rs @@ -4,12 +4,14 @@ use std::{ pin::Pin, sync::RwLock, }; + use tokio::sync::watch; #[derive(Default)] pub(super) struct Watchers { #[allow(clippy::type_complexity)] - watchers: RwLock, (watch::Sender<()>, watch::Receiver<()>)>>, + watchers: + RwLock, (watch::Sender<()>, watch::Receiver<()>)>>, } impl Watchers { @@ -17,7 +19,8 @@ impl Watchers { &'a self, prefix: &[u8], ) -> Pin + Send + 'a>> { - let mut rx = match self.watchers.write().unwrap().entry(prefix.to_vec()) { + let mut rx = match self.watchers.write().unwrap().entry(prefix.to_vec()) + { hash_map::Entry::Occupied(o) => o.get().1.clone(), hash_map::Entry::Vacant(v) => { let (tx, rx) = tokio::sync::watch::channel(()); @@ -31,6 +34,7 @@ impl Watchers { rx.changed().await.unwrap(); }) } + pub(super) fn wake(&self, key: &[u8]) { let watchers = self.watchers.read().unwrap(); let mut triggered = Vec::new(); diff --git a/src/database/key_value/account_data.rs b/src/database/key_value/account_data.rs index f08ade13..9033ac90 100644 --- a/src/database/key_value/account_data.rs +++ b/src/database/key_value/account_data.rs @@ -7,10 +7,13 @@ use ruma::{ RoomId, UserId, }; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::account_data::Data for KeyValueDatabase { - /// Places one event in the account data of the user and removes the previous entry. + /// Places one event in the account data of the user and removes the + /// previous entry. #[tracing::instrument(skip(self, room_id, user_id, event_type, data))] fn update( &self, @@ -24,13 +27,14 @@ impl service::account_data::Data for KeyValueDatabase { .unwrap_or_default() .as_bytes() .to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(user_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); let mut roomuserdataid = prefix.clone(); - roomuserdataid.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); - roomuserdataid.push(0xff); + roomuserdataid + .extend_from_slice(&services().globals.next_count()?.to_be_bytes()); + roomuserdataid.push(0xFF); roomuserdataid.extend_from_slice(event_type.to_string().as_bytes()); let mut key = prefix; @@ -45,13 +49,13 @@ impl service::account_data::Data for KeyValueDatabase { self.roomuserdataid_accountdata.insert( &roomuserdataid, - &serde_json::to_vec(&data).expect("to_vec always works on json values"), + &serde_json::to_vec(&data) + .expect("to_vec always works on json values"), )?; let prev = self.roomusertype_roomuserdataid.get(&key)?; - self.roomusertype_roomuserdataid - .insert(&key, &roomuserdataid)?; + self.roomusertype_roomuserdataid.insert(&key, &roomuserdataid)?; // Remove old entry if let Some(prev) = prev { @@ -74,17 +78,15 @@ impl service::account_data::Data for KeyValueDatabase { .unwrap_or_default() .as_bytes() .to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(kind.to_string().as_bytes()); self.roomusertype_roomuserdataid .get(&key)? .and_then(|roomuserdataid| { - self.roomuserdataid_accountdata - .get(&roomuserdataid) - .transpose() + self.roomuserdataid_accountdata.get(&roomuserdataid).transpose() }) .transpose()? .map(|data| { @@ -101,7 +103,8 @@ impl service::account_data::Data for KeyValueDatabase { room_id: Option<&RoomId>, user_id: &UserId, since: u64, - ) -> Result>> { + ) -> Result>> + { let mut userdata = HashMap::new(); let mut prefix = room_id @@ -109,9 +112,9 @@ impl service::account_data::Data for KeyValueDatabase { .unwrap_or_default() .as_bytes() .to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(user_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); // Skip the data that's exactly at since, because we sent that last time let mut first_possible = prefix.clone(); @@ -124,14 +127,27 @@ impl service::account_data::Data for KeyValueDatabase { .map(|(k, v)| { Ok::<_, Error>(( RoomAccountDataEventType::from( - utils::string_from_bytes(k.rsplit(|&b| b == 0xff).next().ok_or_else( - || Error::bad_database("RoomUserData ID in db is invalid."), - )?) - .map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?, + utils::string_from_bytes( + k.rsplit(|&b| b == 0xFF).next().ok_or_else( + || { + Error::bad_database( + "RoomUserData ID in db is invalid.", + ) + }, + )?, + ) + .map_err(|_| { + Error::bad_database( + "RoomUserData ID in db is invalid.", + ) + })?, ), - serde_json::from_slice::>(&v).map_err(|_| { - Error::bad_database("Database contains invalid account data.") - })?, + serde_json::from_slice::>(&v) + .map_err(|_| { + Error::bad_database( + "Database contains invalid account data.", + ) + })?, )) }) { diff --git a/src/database/key_value/appservice.rs b/src/database/key_value/appservice.rs index 57907f48..b1ef80c0 100644 --- a/src/database/key_value/appservice.rs +++ b/src/database/key_value/appservice.rs @@ -20,8 +20,7 @@ impl service::appservice::Data for KeyValueDatabase { /// /// * `service_name` - the name you send to register the service previously fn unregister_appservice(&self, service_name: &str) -> Result<()> { - self.id_appserviceregistrations - .remove(service_name.as_bytes())?; + self.id_appserviceregistrations.remove(service_name.as_bytes())?; Ok(()) } @@ -30,20 +29,25 @@ impl service::appservice::Data for KeyValueDatabase { .get(id.as_bytes())? .map(|bytes| { serde_yaml::from_slice(&bytes).map_err(|_| { - Error::bad_database("Invalid registration bytes in id_appserviceregistrations.") + Error::bad_database( + "Invalid registration bytes in \ + id_appserviceregistrations.", + ) }) }) .transpose() } - fn iter_ids<'a>(&'a self) -> Result> + 'a>> { - Ok(Box::new(self.id_appserviceregistrations.iter().map( - |(id, _)| { - utils::string_from_bytes(&id).map_err(|_| { - Error::bad_database("Invalid id bytes in id_appserviceregistrations.") - }) - }, - ))) + fn iter_ids<'a>( + &'a self, + ) -> Result> + 'a>> { + Ok(Box::new(self.id_appserviceregistrations.iter().map(|(id, _)| { + utils::string_from_bytes(&id).map_err(|_| { + Error::bad_database( + "Invalid id bytes in id_appserviceregistrations.", + ) + }) + }))) } fn all(&self) -> Result> { diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 090efcb3..aab8e101 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -6,10 +6,13 @@ use lru_cache::LruCache; use ruma::{ api::federation::discovery::{ServerSigningKeys, VerifyKey}, signatures::Ed25519KeyPair, - DeviceId, MilliSecondsSinceUnixEpoch, OwnedServerSigningKeyId, ServerName, UserId, + DeviceId, MilliSecondsSinceUnixEpoch, OwnedServerSigningKeyId, ServerName, + UserId, }; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; pub(crate) const COUNTER: &[u8] = b"c"; @@ -27,14 +30,18 @@ impl service::globals::Data for KeyValueDatabase { }) } - async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { + async fn watch( + &self, + user_id: &UserId, + device_id: &DeviceId, + ) -> Result<()> { let userid_bytes = user_id.as_bytes().to_vec(); let mut userid_prefix = userid_bytes.clone(); - userid_prefix.push(0xff); + userid_prefix.push(0xFF); let mut userdeviceid_prefix = userid_prefix.clone(); userdeviceid_prefix.extend_from_slice(device_id.as_bytes()); - userdeviceid_prefix.push(0xff); + userdeviceid_prefix.push(0xFF); let mut futures = FuturesUnordered::new(); @@ -46,10 +53,10 @@ impl service::globals::Data for KeyValueDatabase { futures.push(self.userroomid_invitestate.watch_prefix(&userid_prefix)); futures.push(self.userroomid_leftstate.watch_prefix(&userid_prefix)); futures.push( - self.userroomid_notificationcount - .watch_prefix(&userid_prefix), + self.userroomid_notificationcount.watch_prefix(&userid_prefix), ); - futures.push(self.userroomid_highlightcount.watch_prefix(&userid_prefix)); + futures + .push(self.userroomid_highlightcount.watch_prefix(&userid_prefix)); // Events for rooms we are in for room_id in services() @@ -70,17 +77,24 @@ impl service::globals::Data for KeyValueDatabase { let roomid_bytes = room_id.as_bytes().to_vec(); let mut roomid_prefix = roomid_bytes.clone(); - roomid_prefix.push(0xff); + roomid_prefix.push(0xFF); // PDUs futures.push(self.pduid_pdu.watch_prefix(&short_roomid)); // EDUs futures.push(Box::pin(async move { - let _result = services().rooms.edus.typing.wait_for_update(&room_id).await; + let _result = services() + .rooms + .edus + .typing + .wait_for_update(&room_id) + .await; })); - futures.push(self.readreceiptid_readreceipt.watch_prefix(&roomid_prefix)); + futures.push( + self.readreceiptid_readreceipt.watch_prefix(&roomid_prefix), + ); // Key changes futures.push(self.keychangeid_userid.watch_prefix(&roomid_prefix)); @@ -90,12 +104,11 @@ impl service::globals::Data for KeyValueDatabase { roomuser_prefix.extend_from_slice(&userid_prefix); futures.push( - self.roomusertype_roomuserdataid - .watch_prefix(&roomuser_prefix), + self.roomusertype_roomuserdataid.watch_prefix(&roomuser_prefix), ); } - let mut globaluserdata_prefix = vec![0xff]; + let mut globaluserdata_prefix = vec![0xFF]; globaluserdata_prefix.extend_from_slice(&userid_prefix); futures.push( @@ -107,7 +120,8 @@ impl service::globals::Data for KeyValueDatabase { futures.push(self.keychangeid_userid.watch_prefix(&userid_prefix)); // One time keys - futures.push(self.userid_lastonetimekeyupdate.watch_prefix(&userid_bytes)); + futures + .push(self.userid_lastonetimekeyupdate.watch_prefix(&userid_bytes)); futures.push(Box::pin(services().globals.rotate.watch())); @@ -126,10 +140,14 @@ impl service::globals::Data for KeyValueDatabase { let shorteventid_cache = self.shorteventid_cache.lock().unwrap().len(); let auth_chain_cache = self.auth_chain_cache.lock().unwrap().len(); let eventidshort_cache = self.eventidshort_cache.lock().unwrap().len(); - let statekeyshort_cache = self.statekeyshort_cache.lock().unwrap().len(); - let our_real_users_cache = self.our_real_users_cache.read().unwrap().len(); - let appservice_in_room_cache = self.appservice_in_room_cache.read().unwrap().len(); - let lasttimelinecount_cache = self.lasttimelinecount_cache.lock().unwrap().len(); + let statekeyshort_cache = + self.statekeyshort_cache.lock().unwrap().len(); + let our_real_users_cache = + self.our_real_users_cache.read().unwrap().len(); + let appservice_in_room_cache = + self.appservice_in_room_cache.read().unwrap().len(); + let lasttimelinecount_cache = + self.lasttimelinecount_cache.lock().unwrap().len(); let mut response = format!( "\ @@ -194,27 +212,29 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" |s| Ok(s.clone()), )?; - let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff); + let mut parts = keypair_bytes.splitn(2, |&b| b == 0xFF); utils::string_from_bytes( // 1. version - parts - .next() - .expect("splitn always returns at least one element"), + parts.next().expect("splitn always returns at least one element"), ) .map_err(|_| Error::bad_database("Invalid version bytes in keypair.")) .and_then(|version| { // 2. key parts .next() - .ok_or_else(|| Error::bad_database("Invalid keypair format in database.")) + .ok_or_else(|| { + Error::bad_database("Invalid keypair format in database.") + }) .map(|key| (version, key)) }) .and_then(|(version, key)| { - Ed25519KeyPair::from_der(key, version) - .map_err(|_| Error::bad_database("Private or public keys are invalid.")) + Ed25519KeyPair::from_der(key, version).map_err(|_| { + Error::bad_database("Private or public keys are invalid.") + }) }) } + fn remove_keypair(&self) -> Result<()> { self.global.remove(b"keypair") } @@ -231,7 +251,10 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" .and_then(|keys| serde_json::from_slice(&keys).ok()) .unwrap_or_else(|| { // Just insert "now", it doesn't matter - ServerSigningKeys::new(origin.to_owned(), MilliSecondsSinceUnixEpoch::now()) + ServerSigningKeys::new( + origin.to_owned(), + MilliSecondsSinceUnixEpoch::now(), + ) }); let ServerSigningKeys { @@ -245,7 +268,8 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" self.server_signingkeys.insert( origin.as_bytes(), - &serde_json::to_vec(&keys).expect("serversigningkeys can be serialized"), + &serde_json::to_vec(&keys) + .expect("serversigningkeys can be serialized"), )?; let mut tree = keys.verify_keys; @@ -258,7 +282,8 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" Ok(tree) } - /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server. + /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found + /// for the server. fn signing_keys_for( &self, origin: &ServerName, @@ -283,8 +308,9 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" fn database_version(&self) -> Result { self.global.get(b"version")?.map_or(Ok(0), |version| { - utils::u64_from_bytes(&version) - .map_err(|_| Error::bad_database("Database version id is invalid.")) + utils::u64_from_bytes(&version).map_err(|_| { + Error::bad_database("Database version id is invalid.") + }) }) } diff --git a/src/database/key_value/key_backups.rs b/src/database/key_value/key_backups.rs index c2b9a57f..84140c93 100644 --- a/src/database/key_value/key_backups.rs +++ b/src/database/key_value/key_backups.rs @@ -9,7 +9,9 @@ use ruma::{ OwnedRoomId, RoomId, UserId, }; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::key_backups::Data for KeyValueDatabase { fn create_backup( @@ -20,12 +22,13 @@ impl service::key_backups::Data for KeyValueDatabase { let version = services().globals.next_count()?.to_string(); let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); self.backupid_algorithm.insert( &key, - &serde_json::to_vec(backup_metadata).expect("BackupAlgorithm::to_vec always works"), + &serde_json::to_vec(backup_metadata) + .expect("BackupAlgorithm::to_vec always works"), )?; self.backupid_etag .insert(&key, &services().globals.next_count()?.to_be_bytes())?; @@ -34,13 +37,13 @@ impl service::key_backups::Data for KeyValueDatabase { fn delete_backup(&self, user_id: &UserId, version: &str) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); self.backupid_algorithm.remove(&key)?; self.backupid_etag.remove(&key)?; - key.push(0xff); + key.push(0xFF); for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) { self.backupkeyid_backup.remove(&outdated_key)?; @@ -56,7 +59,7 @@ impl service::key_backups::Data for KeyValueDatabase { backup_metadata: &Raw, ) -> Result { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); if self.backupid_algorithm.get(&key)?.is_none() { @@ -73,9 +76,12 @@ impl service::key_backups::Data for KeyValueDatabase { Ok(version.to_owned()) } - fn get_latest_backup_version(&self, user_id: &UserId) -> Result> { + fn get_latest_backup_version( + &self, + user_id: &UserId, + ) -> Result> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); let mut last_possible_key = prefix.clone(); last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes()); @@ -85,11 +91,13 @@ impl service::key_backups::Data for KeyValueDatabase { .next() .map(|(key, _)| { utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element"), ) - .map_err(|_| Error::bad_database("backupid_algorithm key is invalid.")) + .map_err(|_| { + Error::bad_database("backupid_algorithm key is invalid.") + }) }) .transpose() } @@ -99,7 +107,7 @@ impl service::key_backups::Data for KeyValueDatabase { user_id: &UserId, ) -> Result)>> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); let mut last_possible_key = prefix.clone(); last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes()); @@ -109,33 +117,42 @@ impl service::key_backups::Data for KeyValueDatabase { .next() .map(|(key, value)| { let version = utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element"), ) - .map_err(|_| Error::bad_database("backupid_algorithm key is invalid."))?; + .map_err(|_| { + Error::bad_database("backupid_algorithm key is invalid.") + })?; Ok(( version, serde_json::from_slice(&value).map_err(|_| { - Error::bad_database("Algorithm in backupid_algorithm is invalid.") + Error::bad_database( + "Algorithm in backupid_algorithm is invalid.", + ) })?, )) }) .transpose() } - fn get_backup(&self, user_id: &UserId, version: &str) -> Result>> { + fn get_backup( + &self, + user_id: &UserId, + version: &str, + ) -> Result>> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); - self.backupid_algorithm - .get(&key)? - .map_or(Ok(None), |bytes| { - serde_json::from_slice(&bytes) - .map_err(|_| Error::bad_database("Algorithm in backupid_algorithm is invalid.")) + self.backupid_algorithm.get(&key)?.map_or(Ok(None), |bytes| { + serde_json::from_slice(&bytes).map_err(|_| { + Error::bad_database( + "Algorithm in backupid_algorithm is invalid.", + ) }) + }) } fn add_key( @@ -147,7 +164,7 @@ impl service::key_backups::Data for KeyValueDatabase { key_data: &Raw, ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); if self.backupid_algorithm.get(&key)?.is_none() { @@ -160,9 +177,9 @@ impl service::key_backups::Data for KeyValueDatabase { self.backupid_etag .insert(&key, &services().globals.next_count()?.to_be_bytes())?; - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(session_id.as_bytes()); self.backupkeyid_backup @@ -173,7 +190,7 @@ impl service::key_backups::Data for KeyValueDatabase { fn count_keys(&self, user_id: &UserId, version: &str) -> Result { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(version.as_bytes()); Ok(self.backupkeyid_backup.scan_prefix(prefix).count()) @@ -181,7 +198,7 @@ impl service::key_backups::Data for KeyValueDatabase { fn get_etag(&self, user_id: &UserId, version: &str) -> Result { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); Ok(utils::u64_from_bytes( @@ -200,40 +217,56 @@ impl service::key_backups::Data for KeyValueDatabase { version: &str, ) -> Result> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(version.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); let mut rooms = BTreeMap::::new(); - for result in self - .backupkeyid_backup - .scan_prefix(prefix) - .map(|(key, value)| { - let mut parts = key.rsplit(|&b| b == 0xff); + for result in + self.backupkeyid_backup.scan_prefix(prefix).map(|(key, value)| { + let mut parts = key.rsplit(|&b| b == 0xFF); - let session_id = - utils::string_from_bytes(parts.next().ok_or_else(|| { - Error::bad_database("backupkeyid_backup key is invalid.") - })?) - .map_err(|_| { - Error::bad_database("backupkeyid_backup session_id is invalid.") - })?; - - let room_id = RoomId::parse( - utils::string_from_bytes(parts.next().ok_or_else(|| { - Error::bad_database("backupkeyid_backup key is invalid.") - })?) - .map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?, + let session_id = utils::string_from_bytes( + parts.next().ok_or_else(|| { + Error::bad_database( + "backupkeyid_backup key is invalid.", + ) + })?, ) .map_err(|_| { - Error::bad_database("backupkeyid_backup room_id is invalid room id.") + Error::bad_database( + "backupkeyid_backup session_id is invalid.", + ) })?; - let key_data = serde_json::from_slice(&value).map_err(|_| { - Error::bad_database("KeyBackupData in backupkeyid_backup is invalid.") + let room_id = RoomId::parse( + utils::string_from_bytes(parts.next().ok_or_else( + || { + Error::bad_database( + "backupkeyid_backup key is invalid.", + ) + }, + )?) + .map_err(|_| { + Error::bad_database( + "backupkeyid_backup room_id is invalid.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "backupkeyid_backup room_id is invalid room id.", + ) })?; + let key_data = + serde_json::from_slice(&value).map_err(|_| { + Error::bad_database( + "KeyBackupData in backupkeyid_backup is invalid.", + ) + })?; + Ok::<_, Error>((room_id, session_id, key_data)) }) { @@ -257,30 +290,38 @@ impl service::key_backups::Data for KeyValueDatabase { room_id: &RoomId, ) -> Result>> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(version.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(room_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); Ok(self .backupkeyid_backup .scan_prefix(prefix) .map(|(key, value)| { - let mut parts = key.rsplit(|&b| b == 0xff); + let mut parts = key.rsplit(|&b| b == 0xFF); - let session_id = - utils::string_from_bytes(parts.next().ok_or_else(|| { - Error::bad_database("backupkeyid_backup key is invalid.") - })?) - .map_err(|_| { - Error::bad_database("backupkeyid_backup session_id is invalid.") - })?; - - let key_data = serde_json::from_slice(&value).map_err(|_| { - Error::bad_database("KeyBackupData in backupkeyid_backup is invalid.") + let session_id = utils::string_from_bytes( + parts.next().ok_or_else(|| { + Error::bad_database( + "backupkeyid_backup key is invalid.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "backupkeyid_backup session_id is invalid.", + ) })?; + let key_data = + serde_json::from_slice(&value).map_err(|_| { + Error::bad_database( + "KeyBackupData in backupkeyid_backup is invalid.", + ) + })?; + Ok::<_, Error>((session_id, key_data)) }) .filter_map(Result::ok) @@ -295,18 +336,20 @@ impl service::key_backups::Data for KeyValueDatabase { session_id: &str, ) -> Result>> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(session_id.as_bytes()); self.backupkeyid_backup .get(&key)? .map(|value| { serde_json::from_slice(&value).map_err(|_| { - Error::bad_database("KeyBackupData in backupkeyid_backup is invalid.") + Error::bad_database( + "KeyBackupData in backupkeyid_backup is invalid.", + ) }) }) .transpose() @@ -314,9 +357,9 @@ impl service::key_backups::Data for KeyValueDatabase { fn delete_all_keys(&self, user_id: &UserId, version: &str) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); - key.push(0xff); + key.push(0xFF); for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) { self.backupkeyid_backup.remove(&outdated_key)?; @@ -325,13 +368,18 @@ impl service::key_backups::Data for KeyValueDatabase { Ok(()) } - fn delete_room_keys(&self, user_id: &UserId, version: &str, room_id: &RoomId) -> Result<()> { + fn delete_room_keys( + &self, + user_id: &UserId, + version: &str, + room_id: &RoomId, + ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); - key.push(0xff); + key.push(0xFF); for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) { self.backupkeyid_backup.remove(&outdated_key)?; @@ -348,11 +396,11 @@ impl service::key_backups::Data for KeyValueDatabase { session_id: &str, ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(version.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(session_id.as_bytes()); for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) { diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 6abe5ba5..57154a51 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -12,22 +12,19 @@ impl service::media::Data for KeyValueDatabase { content_type: Option<&str>, ) -> Result> { let mut key = mxc.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(&width.to_be_bytes()); key.extend_from_slice(&height.to_be_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice( content_disposition .as_ref() .map(|f| f.as_bytes()) .unwrap_or_default(), ); - key.push(0xff); + key.push(0xFF); key.extend_from_slice( - content_type - .as_ref() - .map(|c| c.as_bytes()) - .unwrap_or_default(), + content_type.as_ref().map(|c| c.as_bytes()).unwrap_or_default(), ); self.mediaid_file.insert(&key, &[])?; @@ -42,24 +39,25 @@ impl service::media::Data for KeyValueDatabase { height: u32, ) -> Result<(Option, Option, Vec)> { let mut prefix = mxc.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(&width.to_be_bytes()); prefix.extend_from_slice(&height.to_be_bytes()); - prefix.push(0xff); + prefix.push(0xFF); - let (key, _) = self - .mediaid_file - .scan_prefix(prefix) - .next() - .ok_or(Error::BadRequest(ErrorKind::NotFound, "Media not found"))?; + let (key, _) = + self.mediaid_file.scan_prefix(prefix).next().ok_or( + Error::BadRequest(ErrorKind::NotFound, "Media not found"), + )?; - let mut parts = key.rsplit(|&b| b == 0xff); + let mut parts = key.rsplit(|&b| b == 0xFF); let content_type = parts .next() .map(|bytes| { utils::string_from_bytes(bytes).map_err(|_| { - Error::bad_database("Content type in mediaid_file is invalid unicode.") + Error::bad_database( + "Content type in mediaid_file is invalid unicode.", + ) }) }) .transpose()?; @@ -71,11 +69,14 @@ impl service::media::Data for KeyValueDatabase { let content_disposition = if content_disposition_bytes.is_empty() { None } else { - Some( - utils::string_from_bytes(content_disposition_bytes).map_err(|_| { - Error::bad_database("Content Disposition in mediaid_file is invalid unicode.") - })?, - ) + Some(utils::string_from_bytes(content_disposition_bytes).map_err( + |_| { + Error::bad_database( + "Content Disposition in mediaid_file is invalid \ + unicode.", + ) + }, + )?) }; Ok((content_disposition, content_type, key)) } diff --git a/src/database/key_value/pusher.rs b/src/database/key_value/pusher.rs index cf61a4a0..bd80288a 100644 --- a/src/database/key_value/pusher.rs +++ b/src/database/key_value/pusher.rs @@ -6,30 +6,39 @@ use ruma::{ use crate::{database::KeyValueDatabase, service, utils, Error, Result}; impl service::pusher::Data for KeyValueDatabase { - fn set_pusher(&self, sender: &UserId, pusher: set_pusher::v3::PusherAction) -> Result<()> { + fn set_pusher( + &self, + sender: &UserId, + pusher: set_pusher::v3::PusherAction, + ) -> Result<()> { match &pusher { set_pusher::v3::PusherAction::Post(data) => { let mut key = sender.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(data.pusher.ids.pushkey.as_bytes()); self.senderkey_pusher.insert( &key, - &serde_json::to_vec(&pusher).expect("Pusher is valid JSON value"), + &serde_json::to_vec(&pusher) + .expect("Pusher is valid JSON value"), )?; Ok(()) } set_pusher::v3::PusherAction::Delete(ids) => { let mut key = sender.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(ids.pushkey.as_bytes()); self.senderkey_pusher.remove(&key).map_err(Into::into) } } } - fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result> { + fn get_pusher( + &self, + sender: &UserId, + pushkey: &str, + ) -> Result> { let mut senderkey = sender.as_bytes().to_vec(); - senderkey.push(0xff); + senderkey.push(0xFF); senderkey.extend_from_slice(pushkey.as_bytes()); self.senderkey_pusher @@ -43,7 +52,7 @@ impl service::pusher::Data for KeyValueDatabase { fn get_pushers(&self, sender: &UserId) -> Result> { let mut prefix = sender.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); self.senderkey_pusher .scan_prefix(prefix) @@ -59,16 +68,20 @@ impl service::pusher::Data for KeyValueDatabase { sender: &UserId, ) -> Box> + 'a> { let mut prefix = sender.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); Box::new(self.senderkey_pusher.scan_prefix(prefix).map(|(k, _)| { - let mut parts = k.splitn(2, |&b| b == 0xff); + let mut parts = k.splitn(2, |&b| b == 0xFF); let _senderkey = parts.next(); - let push_key = parts - .next() - .ok_or_else(|| Error::bad_database("Invalid senderkey_pusher in db"))?; - let push_key_string = utils::string_from_bytes(push_key) - .map_err(|_| Error::bad_database("Invalid pusher bytes in senderkey_pusher"))?; + let push_key = parts.next().ok_or_else(|| { + Error::bad_database("Invalid senderkey_pusher in db") + })?; + let push_key_string = + utils::string_from_bytes(push_key).map_err(|_| { + Error::bad_database( + "Invalid pusher bytes in senderkey_pusher", + ) + })?; Ok(push_key_string) })) diff --git a/src/database/key_value/rooms/alias.rs b/src/database/key_value/rooms/alias.rs index 5058cb87..a1355ed2 100644 --- a/src/database/key_value/rooms/alias.rs +++ b/src/database/key_value/rooms/alias.rs @@ -1,6 +1,11 @@ -use ruma::{api::client::error::ErrorKind, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; +use ruma::{ + api::client::error::ErrorKind, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, + RoomId, +}; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::rooms::alias::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] @@ -8,17 +13,20 @@ impl service::rooms::alias::Data for KeyValueDatabase { self.alias_roomid .insert(alias.alias().as_bytes(), room_id.as_bytes())?; let mut aliasid = room_id.as_bytes().to_vec(); - aliasid.push(0xff); - aliasid.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); + aliasid.push(0xFF); + aliasid + .extend_from_slice(&services().globals.next_count()?.to_be_bytes()); self.aliasid_alias.insert(&aliasid, alias.as_bytes())?; Ok(()) } #[tracing::instrument(skip(self))] fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { - if let Some(room_id) = self.alias_roomid.get(alias.alias().as_bytes())? { + if let Some(room_id) = + self.alias_roomid.get(alias.alias().as_bytes())? + { let mut prefix = room_id.clone(); - prefix.push(0xff); + prefix.push(0xFF); for (key, _) in self.aliasid_alias.scan_prefix(prefix) { self.aliasid_alias.remove(&key)?; @@ -34,14 +42,23 @@ impl service::rooms::alias::Data for KeyValueDatabase { } #[tracing::instrument(skip(self))] - fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result> { + fn resolve_local_alias( + &self, + alias: &RoomAliasId, + ) -> Result> { self.alias_roomid .get(alias.alias().as_bytes())? .map(|bytes| { - RoomId::parse(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Room ID in alias_roomid is invalid unicode.") - })?) - .map_err(|_| Error::bad_database("Room ID in alias_roomid is invalid.")) + RoomId::parse(utils::string_from_bytes(&bytes).map_err( + |_| { + Error::bad_database( + "Room ID in alias_roomid is invalid unicode.", + ) + }, + )?) + .map_err(|_| { + Error::bad_database("Room ID in alias_roomid is invalid.") + }) }) .transpose() } @@ -52,13 +69,17 @@ impl service::rooms::alias::Data for KeyValueDatabase { room_id: &RoomId, ) -> Box> + 'a> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); Box::new(self.aliasid_alias.scan_prefix(prefix).map(|(_, bytes)| { utils::string_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid alias bytes in aliasid_alias."))? + .map_err(|_| { + Error::bad_database("Invalid alias bytes in aliasid_alias.") + })? .try_into() - .map_err(|_| Error::bad_database("Invalid alias in aliasid_alias.")) + .map_err(|_| { + Error::bad_database("Invalid alias in aliasid_alias.") + }) })) } } diff --git a/src/database/key_value/rooms/auth_chain.rs b/src/database/key_value/rooms/auth_chain.rs index 60057ac1..dee4269e 100644 --- a/src/database/key_value/rooms/auth_chain.rs +++ b/src/database/key_value/rooms/auth_chain.rs @@ -3,9 +3,13 @@ use std::{collections::HashSet, mem::size_of, sync::Arc}; use crate::{database::KeyValueDatabase, service, utils, Result}; impl service::rooms::auth_chain::Data for KeyValueDatabase { - fn get_cached_eventid_authchain(&self, key: &[u64]) -> Result>>> { + fn get_cached_eventid_authchain( + &self, + key: &[u64], + ) -> Result>>> { // Check RAM cache - if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) { + if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) + { return Ok(Some(Arc::clone(result))); } @@ -18,7 +22,10 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { .map(|chain| { chain .chunks_exact(size_of::()) - .map(|chunk| utils::u64_from_bytes(chunk).expect("byte length is correct")) + .map(|chunk| { + utils::u64_from_bytes(chunk) + .expect("byte length is correct") + }) .collect() }); @@ -38,7 +45,11 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { Ok(None) } - fn cache_auth_chain(&self, key: Vec, auth_chain: Arc>) -> Result<()> { + fn cache_auth_chain( + &self, + key: Vec, + auth_chain: Arc>, + ) -> Result<()> { // Only persist single events in db if key.len() == 1 { self.shorteventid_authchain.insert( @@ -51,10 +62,7 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { } // Cache in RAM - self.auth_chain_cache - .lock() - .unwrap() - .insert(key, auth_chain); + self.auth_chain_cache.lock().unwrap().insert(key, auth_chain); Ok(()) } diff --git a/src/database/key_value/rooms/directory.rs b/src/database/key_value/rooms/directory.rs index 9ed62582..58964927 100644 --- a/src/database/key_value/rooms/directory.rs +++ b/src/database/key_value/rooms/directory.rs @@ -19,14 +19,18 @@ impl service::rooms::directory::Data for KeyValueDatabase { } #[tracing::instrument(skip(self))] - fn public_rooms<'a>(&'a self) -> Box> + 'a> { + fn public_rooms<'a>( + &'a self, + ) -> Box> + 'a> { Box::new(self.publicroomids.iter().map(|(bytes, _)| { - RoomId::parse( - utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Room ID in publicroomids is invalid unicode.") - })?, - ) - .map_err(|_| Error::bad_database("Room ID in publicroomids is invalid.")) + RoomId::parse(utils::string_from_bytes(&bytes).map_err(|_| { + Error::bad_database( + "Room ID in publicroomids is invalid unicode.", + ) + })?) + .map_err(|_| { + Error::bad_database("Room ID in publicroomids is invalid.") + }) })) } } diff --git a/src/database/key_value/rooms/edus/read_receipt.rs b/src/database/key_value/rooms/edus/read_receipt.rs index 5b5b54aa..28b66c27 100644 --- a/src/database/key_value/rooms/edus/read_receipt.rs +++ b/src/database/key_value/rooms/edus/read_receipt.rs @@ -1,10 +1,13 @@ use std::mem; use ruma::{ - events::receipt::ReceiptEvent, serde::Raw, CanonicalJsonObject, OwnedUserId, RoomId, UserId, + events::receipt::ReceiptEvent, serde::Raw, CanonicalJsonObject, + OwnedUserId, RoomId, UserId, }; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { fn readreceipt_update( @@ -14,7 +17,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { event: ReceiptEvent, ) -> Result<()> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); let mut last_possible_key = prefix.clone(); last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes()); @@ -25,7 +28,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { .iter_from(&last_possible_key, true) .take_while(|(key, _)| key.starts_with(&prefix)) .find(|(key, _)| { - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element") == user_id.as_bytes() @@ -36,13 +39,15 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { } let mut room_latest_id = prefix; - room_latest_id.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); - room_latest_id.push(0xff); + room_latest_id + .extend_from_slice(&services().globals.next_count()?.to_be_bytes()); + room_latest_id.push(0xFF); room_latest_id.extend_from_slice(user_id.as_bytes()); self.readreceiptid_readreceipt.insert( &room_latest_id, - &serde_json::to_vec(&event).expect("EduEvent::to_string always works"), + &serde_json::to_vec(&event) + .expect("EduEvent::to_string always works"), )?; Ok(()) @@ -64,7 +69,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { > + 'a, > { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); let prefix2 = prefix.clone(); let mut first_possible_edu = prefix.clone(); @@ -79,21 +84,35 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { let count = utils::u64_from_bytes( &k[prefix.len()..prefix.len() + mem::size_of::()], ) - .map_err(|_| Error::bad_database("Invalid readreceiptid count in db."))?; + .map_err(|_| { + Error::bad_database( + "Invalid readreceiptid count in db.", + ) + })?; let user_id = UserId::parse( - utils::string_from_bytes(&k[prefix.len() + mem::size_of::() + 1..]) - .map_err(|_| { - Error::bad_database("Invalid readreceiptid userid bytes in db.") - })?, + utils::string_from_bytes( + &k[prefix.len() + mem::size_of::() + 1..], + ) + .map_err(|_| { + Error::bad_database( + "Invalid readreceiptid userid bytes in db.", + ) + })?, ) - .map_err(|_| Error::bad_database("Invalid readreceiptid userid in db."))?; + .map_err(|_| { + Error::bad_database( + "Invalid readreceiptid userid in db.", + ) + })?; let mut json = - serde_json::from_slice::(&v).map_err(|_| { - Error::bad_database( - "Read receipt in roomlatestid_roomlatest is invalid json.", - ) - })?; + serde_json::from_slice::(&v) + .map_err(|_| { + Error::bad_database( + "Read receipt in roomlatestid_roomlatest \ + is invalid json.", + ) + })?; json.remove("room_id"); Ok(( @@ -109,36 +128,46 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { } #[tracing::instrument(skip(self))] - fn private_read_set(&self, room_id: &RoomId, user_id: &UserId, count: u64) -> Result<()> { + fn private_read_set( + &self, + room_id: &RoomId, + user_id: &UserId, + count: u64, + ) -> Result<()> { let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); - self.roomuserid_privateread - .insert(&key, &count.to_be_bytes())?; + self.roomuserid_privateread.insert(&key, &count.to_be_bytes())?; self.roomuserid_lastprivatereadupdate .insert(&key, &services().globals.next_count()?.to_be_bytes()) } #[tracing::instrument(skip(self))] - fn private_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + fn private_read_get( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); - self.roomuserid_privateread - .get(&key)? - .map_or(Ok(None), |v| { - Ok(Some(utils::u64_from_bytes(&v).map_err(|_| { - Error::bad_database("Invalid private read marker bytes") - })?)) - }) + self.roomuserid_privateread.get(&key)?.map_or(Ok(None), |v| { + Ok(Some(utils::u64_from_bytes(&v).map_err(|_| { + Error::bad_database("Invalid private read marker bytes") + })?)) + }) } - fn last_privateread_update(&self, user_id: &UserId, room_id: &RoomId) -> Result { + fn last_privateread_update( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); Ok(self @@ -146,7 +175,9 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { .get(&key)? .map(|bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Count in roomuserid_lastprivatereadupdate is invalid.") + Error::bad_database( + "Count in roomuserid_lastprivatereadupdate is invalid.", + ) }) }) .transpose()? diff --git a/src/database/key_value/rooms/lazy_load.rs b/src/database/key_value/rooms/lazy_load.rs index a19d52cb..01952379 100644 --- a/src/database/key_value/rooms/lazy_load.rs +++ b/src/database/key_value/rooms/lazy_load.rs @@ -11,11 +11,11 @@ impl service::rooms::lazy_loading::Data for KeyValueDatabase { ll_user: &UserId, ) -> Result { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(device_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(ll_user.as_bytes()); Ok(self.lazyloadedids.get(&key)?.is_some()) } @@ -28,11 +28,11 @@ impl service::rooms::lazy_loading::Data for KeyValueDatabase { confirmed_user_ids: &mut dyn Iterator, ) -> Result<()> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(device_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(room_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); for ll_id in confirmed_user_ids { let mut key = prefix.clone(); @@ -50,11 +50,11 @@ impl service::rooms::lazy_loading::Data for KeyValueDatabase { room_id: &RoomId, ) -> Result<()> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(device_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(room_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); for (key, _) in self.lazyloadedids.scan_prefix(prefix) { self.lazyloadedids.remove(&key)?; diff --git a/src/database/key_value/rooms/metadata.rs b/src/database/key_value/rooms/metadata.rs index 6a38f08b..ab7a5cb8 100644 --- a/src/database/key_value/rooms/metadata.rs +++ b/src/database/key_value/rooms/metadata.rs @@ -1,6 +1,8 @@ use ruma::{OwnedRoomId, RoomId}; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::rooms::metadata::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] @@ -19,14 +21,18 @@ impl service::rooms::metadata::Data for KeyValueDatabase { .is_some()) } - fn iter_ids<'a>(&'a self) -> Box> + 'a> { + fn iter_ids<'a>( + &'a self, + ) -> Box> + 'a> { Box::new(self.roomid_shortroomid.iter().map(|(bytes, _)| { - RoomId::parse( - utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Room ID in publicroomids is invalid unicode.") - })?, - ) - .map_err(|_| Error::bad_database("Room ID in roomid_shortroomid is invalid.")) + RoomId::parse(utils::string_from_bytes(&bytes).map_err(|_| { + Error::bad_database( + "Room ID in publicroomids is invalid unicode.", + ) + })?) + .map_err(|_| { + Error::bad_database("Room ID in roomid_shortroomid is invalid.") + }) })) } diff --git a/src/database/key_value/rooms/outlier.rs b/src/database/key_value/rooms/outlier.rs index f4269770..f41a02ee 100644 --- a/src/database/key_value/rooms/outlier.rs +++ b/src/database/key_value/rooms/outlier.rs @@ -3,24 +3,35 @@ use ruma::{CanonicalJsonObject, EventId}; use crate::{database::KeyValueDatabase, service, Error, PduEvent, Result}; impl service::rooms::outlier::Data for KeyValueDatabase { - fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result> { - self.eventid_outlierpdu - .get(event_id.as_bytes())? - .map_or(Ok(None), |pdu| { - serde_json::from_slice(&pdu).map_err(|_| Error::bad_database("Invalid PDU in db.")) - }) + fn get_outlier_pdu_json( + &self, + event_id: &EventId, + ) -> Result> { + self.eventid_outlierpdu.get(event_id.as_bytes())?.map_or( + Ok(None), + |pdu| { + serde_json::from_slice(&pdu) + .map_err(|_| Error::bad_database("Invalid PDU in db.")) + }, + ) } fn get_outlier_pdu(&self, event_id: &EventId) -> Result> { - self.eventid_outlierpdu - .get(event_id.as_bytes())? - .map_or(Ok(None), |pdu| { - serde_json::from_slice(&pdu).map_err(|_| Error::bad_database("Invalid PDU in db.")) - }) + self.eventid_outlierpdu.get(event_id.as_bytes())?.map_or( + Ok(None), + |pdu| { + serde_json::from_slice(&pdu) + .map_err(|_| Error::bad_database("Invalid PDU in db.")) + }, + ) } #[tracing::instrument(skip(self, pdu))] - fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()> { + fn add_pdu_outlier( + &self, + event_id: &EventId, + pdu: &CanonicalJsonObject, + ) -> Result<()> { self.eventid_outlierpdu.insert( event_id.as_bytes(), &serde_json::to_vec(&pdu).expect("CanonicalJsonObject is valid"), diff --git a/src/database/key_value/rooms/pdu_metadata.rs b/src/database/key_value/rooms/pdu_metadata.rs index 0641f9d8..b890985f 100644 --- a/src/database/key_value/rooms/pdu_metadata.rs +++ b/src/database/key_value/rooms/pdu_metadata.rs @@ -22,7 +22,8 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { shortroomid: u64, target: u64, until: PduCount, - ) -> Result> + 'a>> { + ) -> Result> + 'a>> + { let prefix = target.to_be_bytes().to_vec(); let mut current = prefix.clone(); @@ -40,8 +41,12 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { .iter_from(¤t, true) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(tofrom, _data)| { - let from = utils::u64_from_bytes(&tofrom[(mem::size_of::())..]) - .map_err(|_| Error::bad_database("Invalid count in tofrom_relation."))?; + let from = utils::u64_from_bytes( + &tofrom[(mem::size_of::())..], + ) + .map_err(|_| { + Error::bad_database("Invalid count in tofrom_relation.") + })?; let mut pduid = shortroomid.to_be_bytes().to_vec(); pduid.extend_from_slice(&from.to_be_bytes()); @@ -50,7 +55,11 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { .rooms .timeline .get_pdu_from_id(&pduid)? - .ok_or_else(|| Error::bad_database("Pdu in tofrom_relation is invalid."))?; + .ok_or_else(|| { + Error::bad_database( + "Pdu in tofrom_relation is invalid.", + ) + })?; if pdu.sender != user_id { pdu.remove_transaction_id()?; } @@ -59,7 +68,11 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { )) } - fn mark_as_referenced(&self, room_id: &RoomId, event_ids: &[Arc]) -> Result<()> { + fn mark_as_referenced( + &self, + room_id: &RoomId, + event_ids: &[Arc], + ) -> Result<()> { for prev in event_ids { let mut key = room_id.as_bytes().to_vec(); key.extend_from_slice(prev.as_bytes()); @@ -69,7 +82,11 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { Ok(()) } - fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result { + fn is_event_referenced( + &self, + room_id: &RoomId, + event_id: &EventId, + ) -> Result { let mut key = room_id.as_bytes().to_vec(); key.extend_from_slice(event_id.as_bytes()); Ok(self.referencedevents.get(&key)?.is_some()) @@ -80,8 +97,6 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { } fn is_event_soft_failed(&self, event_id: &EventId) -> Result { - self.softfailedeventids - .get(event_id.as_bytes()) - .map(|o| o.is_some()) + self.softfailedeventids.get(event_id.as_bytes()).map(|o| o.is_some()) } } diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index a2eba329..bd14f312 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -4,7 +4,12 @@ use crate::{database::KeyValueDatabase, service, services, utils, Result}; impl service::rooms::search::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] - fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { + fn index_pdu( + &self, + shortroomid: u64, + pdu_id: &[u8], + message_body: &str, + ) -> Result<()> { let mut batch = message_body .split_terminator(|c: char| !c.is_alphanumeric()) .filter(|s| !s.is_empty()) @@ -13,7 +18,7 @@ impl service::rooms::search::Data for KeyValueDatabase { .map(|word| { let mut key = shortroomid.to_be_bytes().to_vec(); key.extend_from_slice(word.as_bytes()); - key.push(0xff); + key.push(0xFF); // TODO: currently we save the room id a second time here key.extend_from_slice(pdu_id); (key, Vec::new()) @@ -28,7 +33,8 @@ impl service::rooms::search::Data for KeyValueDatabase { &'a self, room_id: &RoomId, search_string: &str, - ) -> Result> + 'a>, Vec)>> { + ) -> Result> + 'a>, Vec)>> + { let prefix = services() .rooms .short @@ -46,7 +52,7 @@ impl service::rooms::search::Data for KeyValueDatabase { let iterators = words.clone().into_iter().map(move |word| { let mut prefix2 = prefix.clone(); prefix2.extend_from_slice(word.as_bytes()); - prefix2.push(0xff); + prefix2.push(0xFF); let prefix3 = prefix2.clone(); let mut last_possible_id = prefix2.clone(); @@ -60,7 +66,9 @@ impl service::rooms::search::Data for KeyValueDatabase { }); // We compare b with a because we reversed the iterator earlier - let Some(common_elements) = utils::common_elements(iterators, |a, b| b.cmp(a)) else { + let Some(common_elements) = + utils::common_elements(iterators, |a, b| b.cmp(a)) + else { return Ok(None); }; diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index b8186561..2dd04f5e 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -2,26 +2,32 @@ use std::sync::Arc; use ruma::{events::StateEventType, EventId, RoomId}; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::rooms::short::Data for KeyValueDatabase { fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { - if let Some(short) = self.eventidshort_cache.lock().unwrap().get_mut(event_id) { + if let Some(short) = + self.eventidshort_cache.lock().unwrap().get_mut(event_id) + { return Ok(*short); } - let short = - if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? { - utils::u64_from_bytes(&shorteventid) - .map_err(|_| Error::bad_database("Invalid shorteventid in db."))? - } else { - let shorteventid = services().globals.next_count()?; - self.eventid_shorteventid - .insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?; - self.shorteventid_eventid - .insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?; - shorteventid - }; + let short = if let Some(shorteventid) = + self.eventid_shorteventid.get(event_id.as_bytes())? + { + utils::u64_from_bytes(&shorteventid).map_err(|_| { + Error::bad_database("Invalid shorteventid in db.") + })? + } else { + let shorteventid = services().globals.next_count()?; + self.eventid_shorteventid + .insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?; + self.shorteventid_eventid + .insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?; + shorteventid + }; self.eventidshort_cache .lock() @@ -46,15 +52,16 @@ impl service::rooms::short::Data for KeyValueDatabase { } let mut db_key = event_type.to_string().as_bytes().to_vec(); - db_key.push(0xff); + db_key.push(0xFF); db_key.extend_from_slice(state_key.as_bytes()); let short = self .statekey_shortstatekey .get(&db_key)? .map(|shortstatekey| { - utils::u64_from_bytes(&shortstatekey) - .map_err(|_| Error::bad_database("Invalid shortstatekey in db.")) + utils::u64_from_bytes(&shortstatekey).map_err(|_| { + Error::bad_database("Invalid shortstatekey in db.") + }) }) .transpose()?; @@ -83,12 +90,15 @@ impl service::rooms::short::Data for KeyValueDatabase { } let mut db_key = event_type.to_string().as_bytes().to_vec(); - db_key.push(0xff); + db_key.push(0xFF); db_key.extend_from_slice(state_key.as_bytes()); - let short = if let Some(shortstatekey) = self.statekey_shortstatekey.get(&db_key)? { - utils::u64_from_bytes(&shortstatekey) - .map_err(|_| Error::bad_database("Invalid shortstatekey in db."))? + let short = if let Some(shortstatekey) = + self.statekey_shortstatekey.get(&db_key)? + { + utils::u64_from_bytes(&shortstatekey).map_err(|_| { + Error::bad_database("Invalid shortstatekey in db.") + })? } else { let shortstatekey = services().globals.next_count()?; self.statekey_shortstatekey @@ -106,12 +116,12 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(short) } - fn get_eventid_from_short(&self, shorteventid: u64) -> Result> { - if let Some(id) = self - .shorteventid_cache - .lock() - .unwrap() - .get_mut(&shorteventid) + fn get_eventid_from_short( + &self, + shorteventid: u64, + ) -> Result> { + if let Some(id) = + self.shorteventid_cache.lock().unwrap().get_mut(&shorteventid) { return Ok(Arc::clone(id)); } @@ -119,12 +129,20 @@ impl service::rooms::short::Data for KeyValueDatabase { let bytes = self .shorteventid_eventid .get(&shorteventid.to_be_bytes())? - .ok_or_else(|| Error::bad_database("Shorteventid does not exist"))?; + .ok_or_else(|| { + Error::bad_database("Shorteventid does not exist") + })?; - let event_id = EventId::parse_arc(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("EventID in shorteventid_eventid is invalid unicode.") - })?) - .map_err(|_| Error::bad_database("EventId in shorteventid_eventid is invalid."))?; + let event_id = EventId::parse_arc( + utils::string_from_bytes(&bytes).map_err(|_| { + Error::bad_database( + "EventID in shorteventid_eventid is invalid unicode.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database("EventId in shorteventid_eventid is invalid.") + })?; self.shorteventid_cache .lock() @@ -134,12 +152,12 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(event_id) } - fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)> { - if let Some(id) = self - .shortstatekey_cache - .lock() - .unwrap() - .get_mut(&shortstatekey) + fn get_statekey_from_short( + &self, + shortstatekey: u64, + ) -> Result<(StateEventType, String)> { + if let Some(id) = + self.shortstatekey_cache.lock().unwrap().get_mut(&shortstatekey) { return Ok(id.clone()); } @@ -147,23 +165,32 @@ impl service::rooms::short::Data for KeyValueDatabase { let bytes = self .shortstatekey_statekey .get(&shortstatekey.to_be_bytes())? - .ok_or_else(|| Error::bad_database("Shortstatekey does not exist"))?; + .ok_or_else(|| { + Error::bad_database("Shortstatekey does not exist") + })?; - let mut parts = bytes.splitn(2, |&b| b == 0xff); - let eventtype_bytes = parts.next().expect("split always returns one entry"); - let statekey_bytes = parts - .next() - .ok_or_else(|| Error::bad_database("Invalid statekey in shortstatekey_statekey."))?; - - let event_type = - StateEventType::from(utils::string_from_bytes(eventtype_bytes).map_err(|_| { - Error::bad_database("Event type in shortstatekey_statekey is invalid unicode.") - })?); - - let state_key = utils::string_from_bytes(statekey_bytes).map_err(|_| { - Error::bad_database("Statekey in shortstatekey_statekey is invalid unicode.") + let mut parts = bytes.splitn(2, |&b| b == 0xFF); + let eventtype_bytes = + parts.next().expect("split always returns one entry"); + let statekey_bytes = parts.next().ok_or_else(|| { + Error::bad_database("Invalid statekey in shortstatekey_statekey.") })?; + let event_type = StateEventType::from( + utils::string_from_bytes(eventtype_bytes).map_err(|_| { + Error::bad_database( + "Event type in shortstatekey_statekey is invalid unicode.", + ) + })?, + ); + + let state_key = + utils::string_from_bytes(statekey_bytes).map_err(|_| { + Error::bad_database( + "Statekey in shortstatekey_statekey is invalid unicode.", + ) + })?; + let result = (event_type, state_key); self.shortstatekey_cache @@ -175,12 +202,18 @@ impl service::rooms::short::Data for KeyValueDatabase { } /// Returns `(shortstatehash, already_existed)` - fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { + fn get_or_create_shortstatehash( + &self, + state_hash: &[u8], + ) -> Result<(u64, bool)> { Ok( - if let Some(shortstatehash) = self.statehash_shortstatehash.get(state_hash)? { + if let Some(shortstatehash) = + self.statehash_shortstatehash.get(state_hash)? + { ( - utils::u64_from_bytes(&shortstatehash) - .map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?, + utils::u64_from_bytes(&shortstatehash).map_err(|_| { + Error::bad_database("Invalid shortstatehash in db.") + })?, true, ) } else { @@ -196,17 +229,21 @@ impl service::rooms::short::Data for KeyValueDatabase { self.roomid_shortroomid .get(room_id.as_bytes())? .map(|bytes| { - utils::u64_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid shortroomid in db.")) + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid shortroomid in db.") + }) }) .transpose() } fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { Ok( - if let Some(short) = self.roomid_shortroomid.get(room_id.as_bytes())? { - utils::u64_from_bytes(&short) - .map_err(|_| Error::bad_database("Invalid shortroomid in db."))? + if let Some(short) = + self.roomid_shortroomid.get(room_id.as_bytes())? + { + utils::u64_from_bytes(&short).map_err(|_| { + Error::bad_database("Invalid shortroomid in db.") + })? } else { let short = services().globals.next_count()?; self.roomid_shortroomid diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index c7e042d2..78863a75 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -1,20 +1,22 @@ -use ruma::{EventId, OwnedEventId, RoomId}; -use std::collections::HashSet; +use std::{collections::HashSet, sync::Arc}; -use std::sync::Arc; +use ruma::{EventId, OwnedEventId, RoomId}; use tokio::sync::MutexGuard; use crate::{database::KeyValueDatabase, service, utils, Error, Result}; impl service::rooms::state::Data for KeyValueDatabase { fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result> { - self.roomid_shortstatehash - .get(room_id.as_bytes())? - .map_or(Ok(None), |bytes| { + self.roomid_shortstatehash.get(room_id.as_bytes())?.map_or( + Ok(None), + |bytes| { Ok(Some(utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Invalid shortstatehash in roomid_shortstatehash") + Error::bad_database( + "Invalid shortstatehash in roomid_shortstatehash", + ) })?)) - }) + }, + ) } fn set_room_state( @@ -29,23 +31,40 @@ impl service::rooms::state::Data for KeyValueDatabase { Ok(()) } - fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()> { - self.shorteventid_shortstatehash - .insert(&shorteventid.to_be_bytes(), &shortstatehash.to_be_bytes())?; + fn set_event_state( + &self, + shorteventid: u64, + shortstatehash: u64, + ) -> Result<()> { + self.shorteventid_shortstatehash.insert( + &shorteventid.to_be_bytes(), + &shortstatehash.to_be_bytes(), + )?; Ok(()) } - fn get_forward_extremities(&self, room_id: &RoomId) -> Result>> { + fn get_forward_extremities( + &self, + room_id: &RoomId, + ) -> Result>> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); self.roomid_pduleaves .scan_prefix(prefix) .map(|(_, bytes)| { - EventId::parse_arc(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("EventID in roomid_pduleaves is invalid unicode.") - })?) - .map_err(|_| Error::bad_database("EventId in roomid_pduleaves is invalid.")) + EventId::parse_arc(utils::string_from_bytes(&bytes).map_err( + |_| { + Error::bad_database( + "EventID in roomid_pduleaves is invalid unicode.", + ) + }, + )?) + .map_err(|_| { + Error::bad_database( + "EventId in roomid_pduleaves is invalid.", + ) + }) }) .collect() } @@ -58,7 +77,7 @@ impl service::rooms::state::Data for KeyValueDatabase { _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); for (key, _) in self.roomid_pduleaves.scan_prefix(prefix.clone()) { self.roomid_pduleaves.remove(&key)?; diff --git a/src/database/key_value/rooms/state_accessor.rs b/src/database/key_value/rooms/state_accessor.rs index dc5a112d..ed763bf3 100644 --- a/src/database/key_value/rooms/state_accessor.rs +++ b/src/database/key_value/rooms/state_accessor.rs @@ -1,12 +1,19 @@ use std::{collections::HashMap, sync::Arc}; -use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result}; use async_trait::async_trait; use ruma::{events::StateEventType, EventId, RoomId}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, PduEvent, + Result, +}; + #[async_trait] impl service::rooms::state_accessor::Data for KeyValueDatabase { - async fn state_full_ids(&self, shortstatehash: u64) -> Result>> { + async fn state_full_ids( + &self, + shortstatehash: u64, + ) -> Result>> { let full_state = services() .rooms .state_compressor @@ -56,7 +63,11 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { pdu.kind.to_string().into(), pdu.state_key .as_ref() - .ok_or_else(|| Error::bad_database("State event has no state key."))? + .ok_or_else(|| { + Error::bad_database( + "State event has no state key.", + ) + })? .clone(), ), pdu, @@ -72,17 +83,16 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { Ok(result) } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn state_get_id( &self, shortstatehash: u64, event_type: &StateEventType, state_key: &str, ) -> Result>> { - let Some(shortstatekey) = services() - .rooms - .short - .get_shortstatekey(event_type, state_key)? + let Some(shortstatekey) = + services().rooms.short.get_shortstatekey(event_type, state_key)? else { return Ok(None); }; @@ -106,7 +116,8 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { })) } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn state_get( &self, shortstatehash: u64, @@ -121,20 +132,22 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { /// Returns the state hash for this pdu. fn pdu_shortstatehash(&self, event_id: &EventId) -> Result> { - self.eventid_shorteventid - .get(event_id.as_bytes())? - .map_or(Ok(None), |shorteventid| { + self.eventid_shorteventid.get(event_id.as_bytes())?.map_or( + Ok(None), + |shorteventid| { self.shorteventid_shortstatehash .get(&shorteventid)? .map(|bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { Error::bad_database( - "Invalid shortstatehash bytes in shorteventid_shortstatehash", + "Invalid shortstatehash bytes in \ + shorteventid_shortstatehash", ) }) }) .transpose() - }) + }, + ) } /// Returns the full room state. @@ -151,7 +164,8 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { } } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn room_state_get_id( &self, room_id: &RoomId, @@ -167,7 +181,8 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { } } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn room_state_get( &self, room_id: &RoomId, diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index a1a5025a..776f10bd 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -13,20 +13,24 @@ use crate::{ }; impl service::rooms::state_cache::Data for KeyValueDatabase { - fn mark_as_once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { + fn mark_as_once_joined( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result<()> { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); self.roomuseroncejoinedids.insert(&userroom_id, &[]) } fn mark_as_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { let mut roomuser_id = room_id.as_bytes().to_vec(); - roomuser_id.push(0xff); + roomuser_id.push(0xFF); roomuser_id.extend_from_slice(user_id.as_bytes()); let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); self.userroomid_joined.insert(&userroom_id, &[])?; @@ -46,11 +50,11 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { last_state: Option>>, ) -> Result<()> { let mut roomuser_id = room_id.as_bytes().to_vec(); - roomuser_id.push(0xff); + roomuser_id.push(0xFF); roomuser_id.extend_from_slice(user_id.as_bytes()); let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); self.userroomid_invitestate.insert( @@ -72,11 +76,11 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { fn mark_as_left(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { let mut roomuser_id = room_id.as_bytes().to_vec(); - roomuser_id.push(0xff); + roomuser_id.push(0xFF); roomuser_id.extend_from_slice(user_id.as_bytes()); let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); // TODO @@ -112,7 +116,9 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { joinedcount += 1; } - for _invited in self.room_members_invited(room_id).filter_map(Result::ok) { + for _invited in + self.room_members_invited(room_id).filter_map(Result::ok) + { invitedcount += 1; } @@ -127,15 +133,17 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .unwrap() .insert(room_id.to_owned(), Arc::new(real_users)); - for old_joined_server in self.room_servers(room_id).filter_map(Result::ok) { + for old_joined_server in + self.room_servers(room_id).filter_map(Result::ok) + { if !joined_servers.remove(&old_joined_server) { // Server not in room anymore let mut roomserver_id = room_id.as_bytes().to_vec(); - roomserver_id.push(0xff); + roomserver_id.push(0xFF); roomserver_id.extend_from_slice(old_joined_server.as_bytes()); let mut serverroom_id = old_joined_server.as_bytes().to_vec(); - serverroom_id.push(0xff); + serverroom_id.push(0xFF); serverroom_id.extend_from_slice(room_id.as_bytes()); self.roomserverids.remove(&roomserver_id)?; @@ -146,49 +154,45 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { // Now only new servers are in joined_servers anymore for server in joined_servers { let mut roomserver_id = room_id.as_bytes().to_vec(); - roomserver_id.push(0xff); + roomserver_id.push(0xFF); roomserver_id.extend_from_slice(server.as_bytes()); let mut serverroom_id = server.as_bytes().to_vec(); - serverroom_id.push(0xff); + serverroom_id.push(0xFF); serverroom_id.extend_from_slice(room_id.as_bytes()); self.roomserverids.insert(&roomserver_id, &[])?; self.serverroomids.insert(&serverroom_id, &[])?; } - self.appservice_in_room_cache - .write() - .unwrap() - .remove(room_id); + self.appservice_in_room_cache.write().unwrap().remove(room_id); Ok(()) } #[tracing::instrument(skip(self, room_id))] - fn get_our_real_users(&self, room_id: &RoomId) -> Result>> { - let maybe = self - .our_real_users_cache - .read() - .unwrap() - .get(room_id) - .cloned(); + fn get_our_real_users( + &self, + room_id: &RoomId, + ) -> Result>> { + let maybe = + self.our_real_users_cache.read().unwrap().get(room_id).cloned(); if let Some(users) = maybe { Ok(users) } else { self.update_joined_count(room_id)?; Ok(Arc::clone( - self.our_real_users_cache - .read() - .unwrap() - .get(room_id) - .unwrap(), + self.our_real_users_cache.read().unwrap().get(room_id).unwrap(), )) } } #[tracing::instrument(skip(self, room_id, appservice))] - fn appservice_in_room(&self, room_id: &RoomId, appservice: &RegistrationInfo) -> Result { + fn appservice_in_room( + &self, + room_id: &RoomId, + appservice: &RegistrationInfo, + ) -> Result { let maybe = self .appservice_in_room_cache .read() @@ -206,11 +210,13 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { ) .ok(); - let in_room = bridge_user_id - .map_or(false, |id| self.is_joined(&id, room_id).unwrap_or(false)) - || self.room_members(room_id).any(|userid| { - userid.map_or(false, |userid| appservice.users.is_match(userid.as_str())) - }); + let in_room = bridge_user_id.map_or(false, |id| { + self.is_joined(&id, room_id).unwrap_or(false) + }) || self.room_members(room_id).any(|userid| { + userid.map_or(false, |userid| { + appservice.users.is_match(userid.as_str()) + }) + }); self.appservice_in_room_cache .write() @@ -227,11 +233,11 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()> { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); let mut roomuser_id = room_id.as_bytes().to_vec(); - roomuser_id.push(0xff); + roomuser_id.push(0xFF); roomuser_id.extend_from_slice(user_id.as_bytes()); self.userroomid_leftstate.remove(&userroom_id)?; @@ -247,51 +253,66 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, ) -> Box> + 'a> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); Box::new(self.roomserverids.scan_prefix(prefix).map(|(key, _)| { ServerName::parse( utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element"), ) .map_err(|_| { - Error::bad_database("Server name in roomserverids is invalid unicode.") + Error::bad_database( + "Server name in roomserverids is invalid unicode.", + ) })?, ) - .map_err(|_| Error::bad_database("Server name in roomserverids is invalid.")) + .map_err(|_| { + Error::bad_database("Server name in roomserverids is invalid.") + }) })) } #[tracing::instrument(skip(self))] - fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result { + fn server_in_room( + &self, + server: &ServerName, + room_id: &RoomId, + ) -> Result { let mut key = server.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); self.serverroomids.get(&key).map(|o| o.is_some()) } - /// Returns an iterator of all rooms a server participates in (as far as we know). + /// Returns an iterator of all rooms a server participates in (as far as we + /// know). #[tracing::instrument(skip(self))] fn server_rooms<'a>( &'a self, server: &ServerName, ) -> Box> + 'a> { let mut prefix = server.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); Box::new(self.serverroomids.scan_prefix(prefix).map(|(key, _)| { RoomId::parse( utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element"), ) - .map_err(|_| Error::bad_database("RoomId in serverroomids is invalid unicode."))?, + .map_err(|_| { + Error::bad_database( + "RoomId in serverroomids is invalid unicode.", + ) + })?, ) - .map_err(|_| Error::bad_database("RoomId in serverroomids is invalid.")) + .map_err(|_| { + Error::bad_database("RoomId in serverroomids is invalid.") + }) })) } @@ -302,20 +323,24 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, ) -> Box> + 'a> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); Box::new(self.roomuserid_joined.scan_prefix(prefix).map(|(key, _)| { UserId::parse( utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element"), ) .map_err(|_| { - Error::bad_database("User ID in roomuserid_joined is invalid unicode.") + Error::bad_database( + "User ID in roomuserid_joined is invalid unicode.", + ) })?, ) - .map_err(|_| Error::bad_database("User ID in roomuserid_joined is invalid.")) + .map_err(|_| { + Error::bad_database("User ID in roomuserid_joined is invalid.") + }) })) } @@ -324,8 +349,9 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { self.roomid_joinedcount .get(room_id.as_bytes())? .map(|b| { - utils::u64_from_bytes(&b) - .map_err(|_| Error::bad_database("Invalid joinedcount in db.")) + utils::u64_from_bytes(&b).map_err(|_| { + Error::bad_database("Invalid joinedcount in db.") + }) }) .transpose() } @@ -335,8 +361,9 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { self.roomid_invitedcount .get(room_id.as_bytes())? .map(|b| { - utils::u64_from_bytes(&b) - .map_err(|_| Error::bad_database("Invalid joinedcount in db.")) + utils::u64_from_bytes(&b).map_err(|_| { + Error::bad_database("Invalid joinedcount in db.") + }) }) .transpose() } @@ -348,27 +375,30 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, ) -> Box> + 'a> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); - Box::new( - self.roomuseroncejoinedids - .scan_prefix(prefix) - .map(|(key, _)| { - UserId::parse( - utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) - .next() - .expect("rsplit always returns an element"), - ) - .map_err(|_| { - Error::bad_database( - "User ID in room_useroncejoined is invalid unicode.", - ) - })?, + Box::new(self.roomuseroncejoinedids.scan_prefix(prefix).map( + |(key, _)| { + UserId::parse( + utils::string_from_bytes( + key.rsplit(|&b| b == 0xFF) + .next() + .expect("rsplit always returns an element"), ) - .map_err(|_| Error::bad_database("User ID in room_useroncejoined is invalid.")) - }), - ) + .map_err(|_| { + Error::bad_database( + "User ID in room_useroncejoined is invalid \ + unicode.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "User ID in room_useroncejoined is invalid.", + ) + }) + }, + )) } /// Returns an iterator over all invited members of a room. @@ -378,53 +408,64 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, ) -> Box> + 'a> { let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); - Box::new( - self.roomuserid_invitecount - .scan_prefix(prefix) - .map(|(key, _)| { - UserId::parse( - utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) - .next() - .expect("rsplit always returns an element"), - ) - .map_err(|_| { - Error::bad_database("User ID in roomuserid_invited is invalid unicode.") - })?, + Box::new(self.roomuserid_invitecount.scan_prefix(prefix).map( + |(key, _)| { + UserId::parse( + utils::string_from_bytes( + key.rsplit(|&b| b == 0xFF) + .next() + .expect("rsplit always returns an element"), ) - .map_err(|_| Error::bad_database("User ID in roomuserid_invited is invalid.")) - }), - ) + .map_err(|_| { + Error::bad_database( + "User ID in roomuserid_invited is invalid unicode.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "User ID in roomuserid_invited is invalid.", + ) + }) + }, + )) } #[tracing::instrument(skip(self))] - fn get_invite_count(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + fn get_invite_count( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); - self.roomuserid_invitecount - .get(&key)? - .map_or(Ok(None), |bytes| { - Ok(Some(utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Invalid invitecount in db.") - })?)) - }) + self.roomuserid_invitecount.get(&key)?.map_or(Ok(None), |bytes| { + Ok(Some(utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid invitecount in db.") + })?)) + }) } #[tracing::instrument(skip(self))] - fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + fn get_left_count( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); self.roomuserid_leftcount .get(&key)? .map(|bytes| { - utils::u64_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid leftcount in db.")) + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid leftcount in db.") + }) }) .transpose() } @@ -441,15 +482,22 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .map(|(key, _)| { RoomId::parse( utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) + key.rsplit(|&b| b == 0xFF) .next() .expect("rsplit always returns an element"), ) .map_err(|_| { - Error::bad_database("Room ID in userroomid_joined is invalid unicode.") + Error::bad_database( + "Room ID in userroomid_joined is invalid \ + unicode.", + ) })?, ) - .map_err(|_| Error::bad_database("Room ID in userroomid_joined is invalid.")) + .map_err(|_| { + Error::bad_database( + "Room ID in userroomid_joined is invalid.", + ) + }) }), ) } @@ -460,35 +508,43 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { fn rooms_invited<'a>( &'a self, user_id: &UserId, - ) -> Box>)>> + 'a> { + ) -> Box< + dyn Iterator< + Item = Result<(OwnedRoomId, Vec>)>, + > + 'a, + > { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); - Box::new( - self.userroomid_invitestate - .scan_prefix(prefix) - .map(|(key, state)| { - let room_id = RoomId::parse( - utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) - .next() - .expect("rsplit always returns an element"), - ) - .map_err(|_| { - Error::bad_database("Room ID in userroomid_invited is invalid unicode.") - })?, + Box::new(self.userroomid_invitestate.scan_prefix(prefix).map( + |(key, state)| { + let room_id = RoomId::parse( + utils::string_from_bytes( + key.rsplit(|&b| b == 0xFF) + .next() + .expect("rsplit always returns an element"), ) .map_err(|_| { - Error::bad_database("Room ID in userroomid_invited is invalid.") - })?; + Error::bad_database( + "Room ID in userroomid_invited is invalid unicode.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "Room ID in userroomid_invited is invalid.", + ) + })?; - let state = serde_json::from_slice(&state).map_err(|_| { - Error::bad_database("Invalid state in userroomid_invitestate.") - })?; + let state = serde_json::from_slice(&state).map_err(|_| { + Error::bad_database( + "Invalid state in userroomid_invitestate.", + ) + })?; - Ok((room_id, state)) - }), - ) + Ok((room_id, state)) + }, + )) } #[tracing::instrument(skip(self))] @@ -498,14 +554,17 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, ) -> Result>>> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); self.userroomid_invitestate .get(&key)? .map(|state| { - let state = serde_json::from_slice(&state) - .map_err(|_| Error::bad_database("Invalid state in userroomid_invitestate."))?; + let state = serde_json::from_slice(&state).map_err(|_| { + Error::bad_database( + "Invalid state in userroomid_invitestate.", + ) + })?; Ok(state) }) @@ -519,14 +578,17 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, ) -> Result>>> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(room_id.as_bytes()); self.userroomid_leftstate .get(&key)? .map(|state| { - let state = serde_json::from_slice(&state) - .map_err(|_| Error::bad_database("Invalid state in userroomid_leftstate."))?; + let state = serde_json::from_slice(&state).map_err(|_| { + Error::bad_database( + "Invalid state in userroomid_leftstate.", + ) + })?; Ok(state) }) @@ -539,41 +601,48 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { fn rooms_left<'a>( &'a self, user_id: &UserId, - ) -> Box>)>> + 'a> { + ) -> Box< + dyn Iterator>)>> + + 'a, + > { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); - Box::new( - self.userroomid_leftstate - .scan_prefix(prefix) - .map(|(key, state)| { - let room_id = RoomId::parse( - utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) - .next() - .expect("rsplit always returns an element"), - ) - .map_err(|_| { - Error::bad_database("Room ID in userroomid_invited is invalid unicode.") - })?, + Box::new(self.userroomid_leftstate.scan_prefix(prefix).map( + |(key, state)| { + let room_id = RoomId::parse( + utils::string_from_bytes( + key.rsplit(|&b| b == 0xFF) + .next() + .expect("rsplit always returns an element"), ) .map_err(|_| { - Error::bad_database("Room ID in userroomid_invited is invalid.") - })?; + Error::bad_database( + "Room ID in userroomid_invited is invalid unicode.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "Room ID in userroomid_invited is invalid.", + ) + })?; - let state = serde_json::from_slice(&state).map_err(|_| { - Error::bad_database("Invalid state in userroomid_leftstate.") - })?; + let state = serde_json::from_slice(&state).map_err(|_| { + Error::bad_database( + "Invalid state in userroomid_leftstate.", + ) + })?; - Ok((room_id, state)) - }), - ) + Ok((room_id, state)) + }, + )) } #[tracing::instrument(skip(self))] fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); Ok(self.roomuseroncejoinedids.get(&userroom_id)?.is_some()) @@ -582,7 +651,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn is_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); Ok(self.userroomid_joined.get(&userroom_id)?.is_some()) @@ -591,7 +660,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); Ok(self.userroomid_invitestate.get(&userroom_id)?.is_some()) @@ -600,7 +669,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); Ok(self.userroomid_leftstate.get(&userroom_id)?.is_some()) diff --git a/src/database/key_value/rooms/state_compressor.rs b/src/database/key_value/rooms/state_compressor.rs index ab06d8f3..7fb24698 100644 --- a/src/database/key_value/rooms/state_compressor.rs +++ b/src/database/key_value/rooms/state_compressor.rs @@ -12,8 +12,8 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase { .shortstatehash_statediff .get(&shortstatehash.to_be_bytes())? .ok_or_else(|| Error::bad_database("State hash does not exist"))?; - let parent = - utils::u64_from_bytes(&value[0..size_of::()]).expect("bytes have right length"); + let parent = utils::u64_from_bytes(&value[0..size_of::()]) + .expect("bytes have right length"); let parent = (parent != 0).then_some(parent); let mut add_mode = true; @@ -30,7 +30,8 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase { if add_mode { added.insert(v.try_into().expect("we checked the size above")); } else { - removed.insert(v.try_into().expect("we checked the size above")); + removed + .insert(v.try_into().expect("we checked the size above")); } i += 2 * size_of::(); } @@ -42,7 +43,11 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase { }) } - fn save_statediff(&self, shortstatehash: u64, diff: StateDiff) -> Result<()> { + fn save_statediff( + &self, + shortstatehash: u64, + diff: StateDiff, + ) -> Result<()> { let mut value = diff.parent.unwrap_or(0).to_be_bytes().to_vec(); for new in diff.added.iter() { value.extend_from_slice(&new[..]); diff --git a/src/database/key_value/rooms/threads.rs b/src/database/key_value/rooms/threads.rs index 257828bb..9915198e 100644 --- a/src/database/key_value/rooms/threads.rs +++ b/src/database/key_value/rooms/threads.rs @@ -1,8 +1,14 @@ use std::mem; -use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId}; +use ruma::{ + api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, + UserId, +}; -use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, PduEvent, + Result, +}; impl service::rooms::threads::Data for KeyValueDatabase { fn threads_until<'a>( @@ -28,14 +34,22 @@ impl service::rooms::threads::Data for KeyValueDatabase { .iter_from(¤t, true) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(pduid, _users)| { - let count = utils::u64_from_bytes(&pduid[(mem::size_of::())..]) - .map_err(|_| Error::bad_database("Invalid pduid in threadid_userids."))?; + let count = utils::u64_from_bytes( + &pduid[(mem::size_of::())..], + ) + .map_err(|_| { + Error::bad_database( + "Invalid pduid in threadid_userids.", + ) + })?; let mut pdu = services() .rooms .timeline .get_pdu_from_id(&pduid)? .ok_or_else(|| { - Error::bad_database("Invalid pduid reference in threadid_userids") + Error::bad_database( + "Invalid pduid reference in threadid_userids", + ) })?; if pdu.sender != user_id { pdu.remove_transaction_id()?; @@ -45,28 +59,43 @@ impl service::rooms::threads::Data for KeyValueDatabase { )) } - fn update_participants(&self, root_id: &[u8], participants: &[OwnedUserId]) -> Result<()> { + fn update_participants( + &self, + root_id: &[u8], + participants: &[OwnedUserId], + ) -> Result<()> { let users = participants .iter() .map(|user| user.as_bytes()) .collect::>() - .join(&[0xff][..]); + .join(&[0xFF][..]); self.threadid_userids.insert(root_id, &users)?; Ok(()) } - fn get_participants(&self, root_id: &[u8]) -> Result>> { + fn get_participants( + &self, + root_id: &[u8], + ) -> Result>> { if let Some(users) = self.threadid_userids.get(root_id)? { Ok(Some( users - .split(|b| *b == 0xff) + .split(|b| *b == 0xFF) .map(|bytes| { - UserId::parse(utils::string_from_bytes(bytes).map_err(|_| { - Error::bad_database("Invalid UserId bytes in threadid_userids.") - })?) - .map_err(|_| Error::bad_database("Invalid UserId in threadid_userids.")) + UserId::parse(utils::string_from_bytes(bytes).map_err( + |_| { + Error::bad_database( + "Invalid UserId bytes in threadid_userids.", + ) + }, + )?) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in threadid_userids.", + ) + }) }) .filter_map(Result::ok) .collect(), diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 468d8a1f..9d125908 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -1,16 +1,23 @@ use std::{collections::hash_map, mem::size_of, sync::Arc}; use ruma::{ - api::client::error::ErrorKind, CanonicalJsonObject, EventId, OwnedUserId, RoomId, UserId, + api::client::error::ErrorKind, CanonicalJsonObject, EventId, OwnedUserId, + RoomId, UserId, }; +use service::rooms::timeline::PduCount; use tracing::error; -use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result}; - -use service::rooms::timeline::PduCount; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, PduEvent, + Result, +}; impl service::rooms::timeline::Data for KeyValueDatabase { - fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result { + fn last_timeline_count( + &self, + sender_user: &UserId, + room_id: &RoomId, + ) -> Result { match self .lasttimelinecount_cache .lock() @@ -45,14 +52,18 @@ impl service::rooms::timeline::Data for KeyValueDatabase { } /// Returns the json of a pdu. - fn get_pdu_json(&self, event_id: &EventId) -> Result> { + fn get_pdu_json( + &self, + event_id: &EventId, + ) -> Result> { self.get_non_outlier_pdu_json(event_id)?.map_or_else( || { self.eventid_outlierpdu .get(event_id.as_bytes())? .map(|pdu| { - serde_json::from_slice(&pdu) - .map_err(|_| Error::bad_database("Invalid PDU in db.")) + serde_json::from_slice(&pdu).map_err(|_| { + Error::bad_database("Invalid PDU in db.") + }) }) .transpose() }, @@ -61,17 +72,21 @@ impl service::rooms::timeline::Data for KeyValueDatabase { } /// Returns the json of a pdu. - fn get_non_outlier_pdu_json(&self, event_id: &EventId) -> Result> { + fn get_non_outlier_pdu_json( + &self, + event_id: &EventId, + ) -> Result> { self.eventid_pduid .get(event_id.as_bytes())? .map(|pduid| { - self.pduid_pdu - .get(&pduid)? - .ok_or_else(|| Error::bad_database("Invalid pduid in eventid_pduid.")) + self.pduid_pdu.get(&pduid)?.ok_or_else(|| { + Error::bad_database("Invalid pduid in eventid_pduid.") + }) }) .transpose()? .map(|pdu| { - serde_json::from_slice(&pdu).map_err(|_| Error::bad_database("Invalid PDU in db.")) + serde_json::from_slice(&pdu) + .map_err(|_| Error::bad_database("Invalid PDU in db.")) }) .transpose() } @@ -82,17 +97,21 @@ impl service::rooms::timeline::Data for KeyValueDatabase { } /// Returns the pdu. - fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result> { + fn get_non_outlier_pdu( + &self, + event_id: &EventId, + ) -> Result> { self.eventid_pduid .get(event_id.as_bytes())? .map(|pduid| { - self.pduid_pdu - .get(&pduid)? - .ok_or_else(|| Error::bad_database("Invalid pduid in eventid_pduid.")) + self.pduid_pdu.get(&pduid)?.ok_or_else(|| { + Error::bad_database("Invalid pduid in eventid_pduid.") + }) }) .transpose()? .map(|pdu| { - serde_json::from_slice(&pdu).map_err(|_| Error::bad_database("Invalid PDU in db.")) + serde_json::from_slice(&pdu) + .map_err(|_| Error::bad_database("Invalid PDU in db.")) }) .transpose() } @@ -112,8 +131,9 @@ impl service::rooms::timeline::Data for KeyValueDatabase { self.eventid_outlierpdu .get(event_id.as_bytes())? .map(|pdu| { - serde_json::from_slice(&pdu) - .map_err(|_| Error::bad_database("Invalid PDU in db.")) + serde_json::from_slice(&pdu).map_err(|_| { + Error::bad_database("Invalid PDU in db.") + }) }) .transpose() }, @@ -144,7 +164,10 @@ impl service::rooms::timeline::Data for KeyValueDatabase { } /// Returns the pdu as a `BTreeMap`. - fn get_pdu_json_from_id(&self, pdu_id: &[u8]) -> Result> { + fn get_pdu_json_from_id( + &self, + pdu_id: &[u8], + ) -> Result> { self.pduid_pdu.get(pdu_id)?.map_or(Ok(None), |pdu| { Ok(Some( serde_json::from_slice(&pdu) @@ -162,7 +185,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { ) -> Result<()> { self.pduid_pdu.insert( pdu_id, - &serde_json::to_vec(json).expect("CanonicalJsonObject is always a valid"), + &serde_json::to_vec(json) + .expect("CanonicalJsonObject is always a valid"), )?; self.lasttimelinecount_cache @@ -184,7 +208,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { ) -> Result<()> { self.pduid_pdu.insert( pdu_id, - &serde_json::to_vec(json).expect("CanonicalJsonObject is always a valid"), + &serde_json::to_vec(json) + .expect("CanonicalJsonObject is always a valid"), )?; self.eventid_pduid.insert(event_id.as_bytes(), pdu_id)?; @@ -203,7 +228,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { if self.pduid_pdu.get(pdu_id)?.is_some() { self.pduid_pdu.insert( pdu_id, - &serde_json::to_vec(pdu_json).expect("CanonicalJsonObject is always a valid"), + &serde_json::to_vec(pdu_json) + .expect("CanonicalJsonObject is always a valid"), )?; } else { return Err(Error::BadRequest( @@ -212,22 +238,21 @@ impl service::rooms::timeline::Data for KeyValueDatabase { )); } - self.pdu_cache - .lock() - .unwrap() - .remove(&(*pdu.event_id).to_owned()); + self.pdu_cache.lock().unwrap().remove(&(*pdu.event_id).to_owned()); Ok(()) } - /// Returns an iterator over all events and their tokens in a room that happened before the - /// event with id `until` in reverse-chronological order. + /// Returns an iterator over all events and their tokens in a room that + /// happened before the event with id `until` in reverse-chronological + /// order. fn pdus_until<'a>( &'a self, user_id: &UserId, room_id: &RoomId, until: PduCount, - ) -> Result> + 'a>> { + ) -> Result> + 'a>> + { let (prefix, current) = count_to_id(room_id, until, 1, true)?; let user_id = user_id.to_owned(); @@ -238,7 +263,9 @@ impl service::rooms::timeline::Data for KeyValueDatabase { .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(pdu_id, v)| { let mut pdu = serde_json::from_slice::(&v) - .map_err(|_| Error::bad_database("PDU in db is invalid."))?; + .map_err(|_| { + Error::bad_database("PDU in db is invalid.") + })?; if pdu.sender != user_id { pdu.remove_transaction_id()?; } @@ -254,7 +281,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { user_id: &UserId, room_id: &RoomId, from: PduCount, - ) -> Result> + 'a>> { + ) -> Result> + 'a>> + { let (prefix, current) = count_to_id(room_id, from, 1, false)?; let user_id = user_id.to_owned(); @@ -265,7 +293,9 @@ impl service::rooms::timeline::Data for KeyValueDatabase { .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(pdu_id, v)| { let mut pdu = serde_json::from_slice::(&v) - .map_err(|_| Error::bad_database("PDU in db is invalid."))?; + .map_err(|_| { + Error::bad_database("PDU in db is invalid.") + })?; if pdu.sender != user_id { pdu.remove_transaction_id()?; } @@ -286,13 +316,13 @@ impl service::rooms::timeline::Data for KeyValueDatabase { let mut highlights_batch = Vec::new(); for user in notifies { let mut userroom_id = user.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); notifies_batch.push(userroom_id); } for user in highlights { let mut userroom_id = user.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); highlights_batch.push(userroom_id); } @@ -307,10 +337,12 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Returns the `count` of this pdu's id. fn pdu_count(pdu_id: &[u8]) -> Result { - let last_u64 = utils::u64_from_bytes(&pdu_id[pdu_id.len() - size_of::()..]) - .map_err(|_| Error::bad_database("PDU has invalid count bytes."))?; + let last_u64 = + utils::u64_from_bytes(&pdu_id[pdu_id.len() - size_of::()..]) + .map_err(|_| Error::bad_database("PDU has invalid count bytes."))?; let second_last_u64 = utils::u64_from_bytes( - &pdu_id[pdu_id.len() - 2 * size_of::()..pdu_id.len() - size_of::()], + &pdu_id[pdu_id.len() - 2 * size_of::() + ..pdu_id.len() - size_of::()], ); if matches!(second_last_u64, Ok(0)) { @@ -330,7 +362,9 @@ fn count_to_id( .rooms .short .get_shortroomid(room_id)? - .ok_or_else(|| Error::bad_database("Looked for bad shortroomid in timeline"))? + .ok_or_else(|| { + Error::bad_database("Looked for bad shortroomid in timeline") + })? .to_be_bytes() .to_vec(); let mut pdu_id = prefix.clone(); diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index 7c253d3f..53461d27 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -1,14 +1,20 @@ use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; -use crate::{database::KeyValueDatabase, service, services, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, service, services, utils, Error, Result, +}; impl service::rooms::user::Data for KeyValueDatabase { - fn reset_notification_counts(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { + fn reset_notification_counts( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result<()> { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); let mut roomuser_id = room_id.as_bytes().to_vec(); - roomuser_id.push(0xff); + roomuser_id.push(0xFF); roomuser_id.extend_from_slice(user_id.as_bytes()); self.userroomid_notificationcount @@ -24,35 +30,51 @@ impl service::rooms::user::Data for KeyValueDatabase { Ok(()) } - fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { + fn notification_count( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); - self.userroomid_notificationcount - .get(&userroom_id)? - .map_or(Ok(0), |bytes| { - utils::u64_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid notification count in db.")) - }) + self.userroomid_notificationcount.get(&userroom_id)?.map_or( + Ok(0), + |bytes| { + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid notification count in db.") + }) + }, + ) } - fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { + fn highlight_count( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); - userroom_id.push(0xff); + userroom_id.push(0xFF); userroom_id.extend_from_slice(room_id.as_bytes()); - self.userroomid_highlightcount - .get(&userroom_id)? - .map_or(Ok(0), |bytes| { - utils::u64_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid highlight count in db.")) - }) + self.userroomid_highlightcount.get(&userroom_id)?.map_or( + Ok(0), + |bytes| { + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid highlight count in db.") + }) + }, + ) } - fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result { + fn last_notification_read( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); Ok(self @@ -60,7 +82,9 @@ impl service::rooms::user::Data for KeyValueDatabase { .get(&key)? .map(|bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Count in roomuserid_lastprivatereadupdate is invalid.") + Error::bad_database( + "Count in roomuserid_lastprivatereadupdate is invalid.", + ) }) }) .transpose()? @@ -86,7 +110,11 @@ impl service::rooms::user::Data for KeyValueDatabase { .insert(&key, &shortstatehash.to_be_bytes()) } - fn get_token_shortstatehash(&self, room_id: &RoomId, token: u64) -> Result> { + fn get_token_shortstatehash( + &self, + room_id: &RoomId, + token: u64, + ) -> Result> { let shortroomid = services() .rooms .short @@ -100,7 +128,10 @@ impl service::rooms::user::Data for KeyValueDatabase { .get(&key)? .map(|bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Invalid shortstatehash in roomsynctoken_shortstatehash") + Error::bad_database( + "Invalid shortstatehash in \ + roomsynctoken_shortstatehash", + ) }) }) .transpose() @@ -112,7 +143,7 @@ impl service::rooms::user::Data for KeyValueDatabase { ) -> Result> + 'a>> { let iterators = users.into_iter().map(move |user_id| { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); self.userroomid_joined .scan_prefix(prefix) @@ -121,8 +152,12 @@ impl service::rooms::user::Data for KeyValueDatabase { let roomid_index = key .iter() .enumerate() - .find(|(_, &b)| b == 0xff) - .ok_or_else(|| Error::bad_database("Invalid userroomid_joined in db."))? + .find(|(_, &b)| b == 0xFF) + .ok_or_else(|| { + Error::bad_database( + "Invalid userroomid_joined in db.", + ) + })? .0 + 1; @@ -133,15 +168,24 @@ impl service::rooms::user::Data for KeyValueDatabase { .filter_map(Result::ok) }); - // We use the default compare function because keys are sorted correctly (not reversed) + // We use the default compare function because keys are sorted correctly + // (not reversed) Ok(Box::new( utils::common_elements(iterators, Ord::cmp) .expect("users is not empty") .map(|bytes| { - RoomId::parse(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Invalid RoomId bytes in userroomid_joined") - })?) - .map_err(|_| Error::bad_database("Invalid RoomId in userroomid_joined.")) + RoomId::parse(utils::string_from_bytes(&bytes).map_err( + |_| { + Error::bad_database( + "Invalid RoomId bytes in userroomid_joined", + ) + }, + )?) + .map_err(|_| { + Error::bad_database( + "Invalid RoomId in userroomid_joined.", + ) + }) }), )) } diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 6c8e939b..bac027e6 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -12,31 +12,34 @@ use crate::{ impl service::sending::Data for KeyValueDatabase { fn active_requests<'a>( &'a self, - ) -> Box, OutgoingKind, SendingEventType)>> + 'a> { - Box::new( - self.servercurrentevent_data - .iter() - .map(|(key, v)| parse_servercurrentevent(&key, v).map(|(k, e)| (key, k, e))), - ) + ) -> Box< + dyn Iterator, OutgoingKind, SendingEventType)>> + + 'a, + > { + Box::new(self.servercurrentevent_data.iter().map(|(key, v)| { + parse_servercurrentevent(&key, v).map(|(k, e)| (key, k, e)) + })) } fn active_requests_for<'a>( &'a self, outgoing_kind: &OutgoingKind, - ) -> Box, SendingEventType)>> + 'a> { + ) -> Box, SendingEventType)>> + 'a> + { let prefix = outgoing_kind.get_prefix(); - Box::new( - self.servercurrentevent_data - .scan_prefix(prefix) - .map(|(key, v)| parse_servercurrentevent(&key, v).map(|(_, e)| (key, e))), - ) + Box::new(self.servercurrentevent_data.scan_prefix(prefix).map( + |(key, v)| parse_servercurrentevent(&key, v).map(|(_, e)| (key, e)), + )) } fn delete_active_request(&self, key: Vec) -> Result<()> { self.servercurrentevent_data.remove(&key) } - fn delete_all_active_requests_for(&self, outgoing_kind: &OutgoingKind) -> Result<()> { + fn delete_all_active_requests_for( + &self, + outgoing_kind: &OutgoingKind, + ) -> Result<()> { let prefix = outgoing_kind.get_prefix(); for (key, _) in self.servercurrentevent_data.scan_prefix(prefix) { self.servercurrentevent_data.remove(&key)?; @@ -45,9 +48,13 @@ impl service::sending::Data for KeyValueDatabase { Ok(()) } - fn delete_all_requests_for(&self, outgoing_kind: &OutgoingKind) -> Result<()> { + fn delete_all_requests_for( + &self, + outgoing_kind: &OutgoingKind, + ) -> Result<()> { let prefix = outgoing_kind.get_prefix(); - for (key, _) in self.servercurrentevent_data.scan_prefix(prefix.clone()) { + for (key, _) in self.servercurrentevent_data.scan_prefix(prefix.clone()) + { self.servercurrentevent_data.remove(&key).unwrap(); } @@ -69,7 +76,9 @@ impl service::sending::Data for KeyValueDatabase { if let SendingEventType::Pdu(value) = &event { key.extend_from_slice(value); } else { - key.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); + key.extend_from_slice( + &services().globals.next_count()?.to_be_bytes(), + ); } let value = if let SendingEventType::Edu(value) = &event { &**value @@ -79,24 +88,25 @@ impl service::sending::Data for KeyValueDatabase { batch.push((key.clone(), value.to_owned())); keys.push(key); } - self.servernameevent_data - .insert_batch(&mut batch.into_iter())?; + self.servernameevent_data.insert_batch(&mut batch.into_iter())?; Ok(keys) } fn queued_requests<'a>( &'a self, outgoing_kind: &OutgoingKind, - ) -> Box)>> + 'a> { + ) -> Box)>> + 'a> + { let prefix = outgoing_kind.get_prefix(); - return Box::new( - self.servernameevent_data - .scan_prefix(prefix) - .map(|(k, v)| parse_servercurrentevent(&k, v).map(|(_, ev)| (ev, k))), - ); + return Box::new(self.servernameevent_data.scan_prefix(prefix).map( + |(k, v)| parse_servercurrentevent(&k, v).map(|(_, ev)| (ev, k)), + )); } - fn mark_as_active(&self, events: &[(SendingEventType, Vec)]) -> Result<()> { + fn mark_as_active( + &self, + events: &[(SendingEventType, Vec)], + ) -> Result<()> { for (e, key) in events { let value = if let SendingEventType::Edu(value) = &e { &**value @@ -110,18 +120,24 @@ impl service::sending::Data for KeyValueDatabase { Ok(()) } - fn set_latest_educount(&self, server_name: &ServerName, last_count: u64) -> Result<()> { + fn set_latest_educount( + &self, + server_name: &ServerName, + last_count: u64, + ) -> Result<()> { self.servername_educount .insert(server_name.as_bytes(), &last_count.to_be_bytes()) } fn get_latest_educount(&self, server_name: &ServerName) -> Result { - self.servername_educount - .get(server_name.as_bytes())? - .map_or(Ok(0), |bytes| { - utils::u64_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid u64 in servername_educount.")) - }) + self.servername_educount.get(server_name.as_bytes())?.map_or( + Ok(0), + |bytes| { + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid u64 in servername_educount.") + }) + }, + ) } } @@ -132,15 +148,17 @@ fn parse_servercurrentevent( ) -> Result<(OutgoingKind, SendingEventType)> { // Appservices start with a plus Ok::<_, Error>(if key.starts_with(b"+") { - let mut parts = key[1..].splitn(2, |&b| b == 0xff); + let mut parts = key[1..].splitn(2, |&b| b == 0xFF); let server = parts.next().expect("splitn always returns one element"); - let event = parts - .next() - .ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?; + let event = parts.next().ok_or_else(|| { + Error::bad_database("Invalid bytes in servercurrentpdus.") + })?; let server = utils::string_from_bytes(server).map_err(|_| { - Error::bad_database("Invalid server bytes in server_currenttransaction") + Error::bad_database( + "Invalid server bytes in server_currenttransaction", + ) })?; ( @@ -152,23 +170,27 @@ fn parse_servercurrentevent( }, ) } else if key.starts_with(b"$") { - let mut parts = key[1..].splitn(3, |&b| b == 0xff); + let mut parts = key[1..].splitn(3, |&b| b == 0xFF); let user = parts.next().expect("splitn always returns one element"); - let user_string = utils::string_from_bytes(user) - .map_err(|_| Error::bad_database("Invalid user string in servercurrentevent"))?; - let user_id = UserId::parse(user_string) - .map_err(|_| Error::bad_database("Invalid user id in servercurrentevent"))?; + let user_string = utils::string_from_bytes(user).map_err(|_| { + Error::bad_database("Invalid user string in servercurrentevent") + })?; + let user_id = UserId::parse(user_string).map_err(|_| { + Error::bad_database("Invalid user id in servercurrentevent") + })?; - let pushkey = parts - .next() - .ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?; - let pushkey_string = utils::string_from_bytes(pushkey) - .map_err(|_| Error::bad_database("Invalid pushkey in servercurrentevent"))?; + let pushkey = parts.next().ok_or_else(|| { + Error::bad_database("Invalid bytes in servercurrentpdus.") + })?; + let pushkey_string = + utils::string_from_bytes(pushkey).map_err(|_| { + Error::bad_database("Invalid pushkey in servercurrentevent") + })?; - let event = parts - .next() - .ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?; + let event = parts.next().ok_or_else(|| { + Error::bad_database("Invalid bytes in servercurrentpdus.") + })?; ( OutgoingKind::Push(user_id, pushkey_string), @@ -180,20 +202,24 @@ fn parse_servercurrentevent( }, ) } else { - let mut parts = key.splitn(2, |&b| b == 0xff); + let mut parts = key.splitn(2, |&b| b == 0xFF); let server = parts.next().expect("splitn always returns one element"); - let event = parts - .next() - .ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?; + let event = parts.next().ok_or_else(|| { + Error::bad_database("Invalid bytes in servercurrentpdus.") + })?; let server = utils::string_from_bytes(server).map_err(|_| { - Error::bad_database("Invalid server bytes in server_currenttransaction") + Error::bad_database( + "Invalid server bytes in server_currenttransaction", + ) })?; ( OutgoingKind::Normal(ServerName::parse(server).map_err(|_| { - Error::bad_database("Invalid server string in server_currenttransaction") + Error::bad_database( + "Invalid server string in server_currenttransaction", + ) })?), if value.is_empty() { SendingEventType::Pdu(event.to_vec()) diff --git a/src/database/key_value/transaction_ids.rs b/src/database/key_value/transaction_ids.rs index b3bd05f4..345b74a1 100644 --- a/src/database/key_value/transaction_ids.rs +++ b/src/database/key_value/transaction_ids.rs @@ -11,9 +11,11 @@ impl service::transaction_ids::Data for KeyValueDatabase { data: &[u8], ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); - key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default()); - key.push(0xff); + key.push(0xFF); + key.extend_from_slice( + device_id.map(DeviceId::as_bytes).unwrap_or_default(), + ); + key.push(0xFF); key.extend_from_slice(txn_id.as_bytes()); self.userdevicetxnid_response.insert(&key, data)?; @@ -28,9 +30,11 @@ impl service::transaction_ids::Data for KeyValueDatabase { txn_id: &TransactionId, ) -> Result>> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); - key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default()); - key.push(0xff); + key.push(0xFF); + key.extend_from_slice( + device_id.map(DeviceId::as_bytes).unwrap_or_default(), + ); + key.push(0xFF); key.extend_from_slice(txn_id.as_bytes()); // If there's no entry, this is a new transaction diff --git a/src/database/key_value/uiaa.rs b/src/database/key_value/uiaa.rs index 20a0357d..3452b990 100644 --- a/src/database/key_value/uiaa.rs +++ b/src/database/key_value/uiaa.rs @@ -13,13 +13,10 @@ impl service::uiaa::Data for KeyValueDatabase { session: &str, request: &CanonicalJsonValue, ) -> Result<()> { - self.userdevicesessionid_uiaarequest - .write() - .unwrap() - .insert( - (user_id.to_owned(), device_id.to_owned(), session.to_owned()), - request.to_owned(), - ); + self.userdevicesessionid_uiaarequest.write().unwrap().insert( + (user_id.to_owned(), device_id.to_owned(), session.to_owned()), + request.to_owned(), + ); Ok(()) } @@ -33,7 +30,11 @@ impl service::uiaa::Data for KeyValueDatabase { self.userdevicesessionid_uiaarequest .read() .unwrap() - .get(&(user_id.to_owned(), device_id.to_owned(), session.to_owned())) + .get(&( + user_id.to_owned(), + device_id.to_owned(), + session.to_owned(), + )) .map(ToOwned::to_owned) } @@ -45,19 +46,19 @@ impl service::uiaa::Data for KeyValueDatabase { uiaainfo: Option<&UiaaInfo>, ) -> Result<()> { let mut userdevicesessionid = user_id.as_bytes().to_vec(); - userdevicesessionid.push(0xff); + userdevicesessionid.push(0xFF); userdevicesessionid.extend_from_slice(device_id.as_bytes()); - userdevicesessionid.push(0xff); + userdevicesessionid.push(0xFF); userdevicesessionid.extend_from_slice(session.as_bytes()); if let Some(uiaainfo) = uiaainfo { self.userdevicesessionid_uiaainfo.insert( &userdevicesessionid, - &serde_json::to_vec(&uiaainfo).expect("UiaaInfo::to_vec always works"), + &serde_json::to_vec(&uiaainfo) + .expect("UiaaInfo::to_vec always works"), )?; } else { - self.userdevicesessionid_uiaainfo - .remove(&userdevicesessionid)?; + self.userdevicesessionid_uiaainfo.remove(&userdevicesessionid)?; } Ok(()) @@ -70,9 +71,9 @@ impl service::uiaa::Data for KeyValueDatabase { session: &str, ) -> Result { let mut userdevicesessionid = user_id.as_bytes().to_vec(); - userdevicesessionid.push(0xff); + userdevicesessionid.push(0xFF); userdevicesessionid.extend_from_slice(device_id.as_bytes()); - userdevicesessionid.push(0xff); + userdevicesessionid.push(0xFF); userdevicesessionid.extend_from_slice(session.as_bytes()); serde_json::from_slice( @@ -84,6 +85,8 @@ impl service::uiaa::Data for KeyValueDatabase { "UIAA session does not exist.", ))?, ) - .map_err(|_| Error::bad_database("UiaaInfo in userdeviceid_uiaainfo is invalid.")) + .map_err(|_| { + Error::bad_database("UiaaInfo in userdeviceid_uiaainfo is invalid.") + }) } } diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 840e4788..0cef5414 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -5,8 +5,8 @@ use ruma::{ encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::{AnyToDeviceEvent, StateEventType}, serde::Raw, - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, - OwnedDeviceKeyId, OwnedMxcUri, OwnedUserId, UInt, UserId, + DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, + OwnedDeviceId, OwnedDeviceKeyId, OwnedMxcUri, OwnedUserId, UInt, UserId, }; use tracing::warn; @@ -40,67 +40,100 @@ impl service::users::Data for KeyValueDatabase { } /// Find out which user an access token belongs to. - fn find_from_token(&self, token: &str) -> Result> { - self.token_userdeviceid - .get(token.as_bytes())? - .map_or(Ok(None), |bytes| { - let mut parts = bytes.split(|&b| b == 0xff); + fn find_from_token( + &self, + token: &str, + ) -> Result> { + self.token_userdeviceid.get(token.as_bytes())?.map_or( + Ok(None), + |bytes| { + let mut parts = bytes.split(|&b| b == 0xFF); let user_bytes = parts.next().ok_or_else(|| { - Error::bad_database("User ID in token_userdeviceid is invalid.") + Error::bad_database( + "User ID in token_userdeviceid is invalid.", + ) })?; let device_bytes = parts.next().ok_or_else(|| { - Error::bad_database("Device ID in token_userdeviceid is invalid.") + Error::bad_database( + "Device ID in token_userdeviceid is invalid.", + ) })?; Ok(Some(( - UserId::parse(utils::string_from_bytes(user_bytes).map_err(|_| { - Error::bad_database("User ID in token_userdeviceid is invalid unicode.") - })?) + UserId::parse( + utils::string_from_bytes(user_bytes).map_err(|_| { + Error::bad_database( + "User ID in token_userdeviceid is invalid \ + unicode.", + ) + })?, + ) .map_err(|_| { - Error::bad_database("User ID in token_userdeviceid is invalid.") + Error::bad_database( + "User ID in token_userdeviceid is invalid.", + ) })?, utils::string_from_bytes(device_bytes).map_err(|_| { - Error::bad_database("Device ID in token_userdeviceid is invalid.") + Error::bad_database( + "Device ID in token_userdeviceid is invalid.", + ) })?, ))) - }) + }, + ) } /// Returns an iterator over all users on this homeserver. - fn iter<'a>(&'a self) -> Box> + 'a> { + fn iter<'a>( + &'a self, + ) -> Box> + 'a> { Box::new(self.userid_password.iter().map(|(bytes, _)| { UserId::parse(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("User ID in userid_password is invalid unicode.") + Error::bad_database( + "User ID in userid_password is invalid unicode.", + ) })?) - .map_err(|_| Error::bad_database("User ID in userid_password is invalid.")) + .map_err(|_| { + Error::bad_database("User ID in userid_password is invalid.") + }) })) } /// Returns a list of local users as list of usernames. /// - /// A user account is considered `local` if the length of it's password is greater then zero. + /// A user account is considered `local` if the length of it's password is + /// greater then zero. fn list_local_users(&self) -> Result> { let users: Vec = self .userid_password .iter() - .filter_map(|(username, pw)| get_username_with_valid_password(&username, &pw)) + .filter_map(|(username, pw)| { + get_username_with_valid_password(&username, &pw) + }) .collect(); Ok(users) } /// Returns the password hash for the given user. fn password_hash(&self, user_id: &UserId) -> Result> { - self.userid_password - .get(user_id.as_bytes())? - .map_or(Ok(None), |bytes| { + self.userid_password.get(user_id.as_bytes())?.map_or( + Ok(None), + |bytes| { Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Password hash in db is not valid string.") + Error::bad_database( + "Password hash in db is not valid string.", + ) })?)) - }) + }, + ) } /// Hash and set the user's password to the Argon2 hash - fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { + fn set_password( + &self, + user_id: &UserId, + password: Option<&str>, + ) -> Result<()> { if let Some(password) = password { if let Ok(hash) = utils::calculate_password_hash(password) { self.userid_password @@ -120,17 +153,23 @@ impl service::users::Data for KeyValueDatabase { /// Returns the `displayname` of a user on this homeserver. fn displayname(&self, user_id: &UserId) -> Result> { - self.userid_displayname - .get(user_id.as_bytes())? - .map_or(Ok(None), |bytes| { + self.userid_displayname.get(user_id.as_bytes())?.map_or( + Ok(None), + |bytes| { Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| { Error::bad_database("Displayname in db is invalid.") })?)) - }) + }, + ) } - /// Sets a new `displayname` or removes it if `displayname` is `None`. You still need to nofify all rooms of this change. - fn set_displayname(&self, user_id: &UserId, displayname: Option) -> Result<()> { + /// Sets a new `displayname` or removes it if `displayname` is `None`. You + /// still need to nofify all rooms of this change. + fn set_displayname( + &self, + user_id: &UserId, + displayname: Option, + ) -> Result<()> { if let Some(displayname) = displayname { self.userid_displayname .insert(user_id.as_bytes(), displayname.as_bytes())?; @@ -147,17 +186,25 @@ impl service::users::Data for KeyValueDatabase { .get(user_id.as_bytes())? .map(|bytes| { utils::string_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Avatar URL in db is invalid.")) + .map_err(|_| { + Error::bad_database("Avatar URL in db is invalid.") + }) .map(Into::into) }) .transpose() } /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. - fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()> { + fn set_avatar_url( + &self, + user_id: &UserId, + avatar_url: Option, + ) -> Result<()> { if let Some(avatar_url) = avatar_url { - self.userid_avatarurl - .insert(user_id.as_bytes(), avatar_url.to_string().as_bytes())?; + self.userid_avatarurl.insert( + user_id.as_bytes(), + avatar_url.to_string().as_bytes(), + )?; } else { self.userid_avatarurl.remove(user_id.as_bytes())?; } @@ -170,8 +217,9 @@ impl service::users::Data for KeyValueDatabase { self.userid_blurhash .get(user_id.as_bytes())? .map(|bytes| { - let s = utils::string_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Avatar URL in db is invalid."))?; + let s = utils::string_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Avatar URL in db is invalid.") + })?; Ok(s) }) @@ -179,7 +227,11 @@ impl service::users::Data for KeyValueDatabase { } /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. - fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { + fn set_blurhash( + &self, + user_id: &UserId, + blurhash: Option, + ) -> Result<()> { if let Some(blurhash) = blurhash { self.userid_blurhash .insert(user_id.as_bytes(), blurhash.as_bytes())?; @@ -204,11 +256,10 @@ impl service::users::Data for KeyValueDatabase { ); let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); - self.userid_devicelistversion - .increment(user_id.as_bytes())?; + self.userid_devicelistversion.increment(user_id.as_bytes())?; self.userdeviceid_metadata.insert( &userdeviceid, @@ -228,9 +279,13 @@ impl service::users::Data for KeyValueDatabase { } /// Removes a device from a user. - fn remove_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { + fn remove_device( + &self, + user_id: &UserId, + device_id: &DeviceId, + ) -> Result<()> { let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); // Remove tokens @@ -241,7 +296,7 @@ impl service::users::Data for KeyValueDatabase { // Remove todevice events let mut prefix = userdeviceid.clone(); - prefix.push(0xff); + prefix.push(0xFF); for (key, _) in self.todeviceid_events.scan_prefix(prefix) { self.todeviceid_events.remove(&key)?; @@ -249,8 +304,7 @@ impl service::users::Data for KeyValueDatabase { // TODO: Remove onetimekeys - self.userid_devicelistversion - .increment(user_id.as_bytes())?; + self.userid_devicelistversion.increment(user_id.as_bytes())?; self.userdeviceid_metadata.remove(&userdeviceid)?; @@ -263,29 +317,34 @@ impl service::users::Data for KeyValueDatabase { user_id: &UserId, ) -> Box> + 'a> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); // All devices have metadata - Box::new( - self.userdeviceid_metadata - .scan_prefix(prefix) - .map(|(bytes, _)| { - Ok(utils::string_from_bytes( - bytes.rsplit(|&b| b == 0xff).next().ok_or_else(|| { - Error::bad_database("UserDevice ID in db is invalid.") - })?, + Box::new(self.userdeviceid_metadata.scan_prefix(prefix).map( + |(bytes, _)| { + Ok(utils::string_from_bytes( + bytes.rsplit(|&b| b == 0xFF).next().ok_or_else(|| { + Error::bad_database("UserDevice ID in db is invalid.") + })?, + ) + .map_err(|_| { + Error::bad_database( + "Device ID in userdeviceid_metadata is invalid.", ) - .map_err(|_| { - Error::bad_database("Device ID in userdeviceid_metadata is invalid.") - })? - .into()) - }), - ) + })? + .into()) + }, + )) } /// Replaces the access token of one device. - fn set_token(&self, user_id: &UserId, device_id: &DeviceId, token: &str) -> Result<()> { + fn set_token( + &self, + user_id: &UserId, + device_id: &DeviceId, + token: &str, + ) -> Result<()> { let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); assert!( @@ -300,10 +359,8 @@ impl service::users::Data for KeyValueDatabase { } // Assign token to user device combination - self.userdeviceid_token - .insert(&userdeviceid, token.as_bytes())?; - self.token_userdeviceid - .insert(token.as_bytes(), &userdeviceid)?; + self.userdeviceid_token.insert(&userdeviceid, token.as_bytes())?; + self.token_userdeviceid.insert(token.as_bytes(), &userdeviceid)?; Ok(()) } @@ -316,17 +373,19 @@ impl service::users::Data for KeyValueDatabase { one_time_key_value: &Raw, ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(device_id.as_bytes()); assert!( self.userdeviceid_metadata.get(&key)?.is_some(), - "devices should have metadata and this method should only be called with existing devices" + "devices should have metadata and this method should only be \ + called with existing devices" ); - key.push(0xff); - // TODO: Use DeviceKeyId::to_string when it's available (and update everything, - // because there are no wrapping quotation marks anymore) + key.push(0xFF); + // TODO: Use DeviceKeyId::to_string when it's available (and update + // everything, because there are no wrapping quotation marks + // anymore) key.extend_from_slice( serde_json::to_string(one_time_key_key) .expect("DeviceKeyId::to_string always works") @@ -335,7 +394,8 @@ impl service::users::Data for KeyValueDatabase { self.onetimekeyid_onetimekeys.insert( &key, - &serde_json::to_vec(&one_time_key_value).expect("OneTimeKey::to_vec always works"), + &serde_json::to_vec(&one_time_key_value) + .expect("OneTimeKey::to_vec always works"), )?; self.userid_lastonetimekeyupdate.insert( @@ -347,13 +407,16 @@ impl service::users::Data for KeyValueDatabase { } fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { - self.userid_lastonetimekeyupdate - .get(user_id.as_bytes())? - .map_or(Ok(0), |bytes| { + self.userid_lastonetimekeyupdate.get(user_id.as_bytes())?.map_or( + Ok(0), + |bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.") + Error::bad_database( + "Count in roomid_lastroomactiveupdate is invalid.", + ) }) - }) + }, + ) } fn take_one_time_key( @@ -363,9 +426,9 @@ impl service::users::Data for KeyValueDatabase { key_algorithm: &DeviceKeyAlgorithm, ) -> Result)>> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(device_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); // Annoying quotation mark prefix.push(b'"'); prefix.extend_from_slice(key_algorithm.as_ref().as_bytes()); @@ -384,13 +447,18 @@ impl service::users::Data for KeyValueDatabase { Ok(( serde_json::from_slice( - key.rsplit(|&b| b == 0xff) - .next() - .ok_or_else(|| Error::bad_database("OneTimeKeyId in db is invalid."))?, + key.rsplit(|&b| b == 0xFF).next().ok_or_else(|| { + Error::bad_database( + "OneTimeKeyId in db is invalid.", + ) + })?, ) - .map_err(|_| Error::bad_database("OneTimeKeyId in db is invalid."))?, - serde_json::from_slice(&value) - .map_err(|_| Error::bad_database("OneTimeKeys in db are invalid."))?, + .map_err(|_| { + Error::bad_database("OneTimeKeyId in db is invalid.") + })?, + serde_json::from_slice(&value).map_err(|_| { + Error::bad_database("OneTimeKeys in db are invalid.") + })?, )) }) .transpose() @@ -402,25 +470,31 @@ impl service::users::Data for KeyValueDatabase { device_id: &DeviceId, ) -> Result> { let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); let mut counts = BTreeMap::new(); - for algorithm in - self.onetimekeyid_onetimekeys - .scan_prefix(userdeviceid) - .map(|(bytes, _)| { - Ok::<_, Error>( - serde_json::from_slice::( - bytes.rsplit(|&b| b == 0xff).next().ok_or_else(|| { - Error::bad_database("OneTimeKey ID in db is invalid.") - })?, - ) - .map_err(|_| Error::bad_database("DeviceKeyId in db is invalid."))? - .algorithm(), + for algorithm in self + .onetimekeyid_onetimekeys + .scan_prefix(userdeviceid) + .map(|(bytes, _)| { + Ok::<_, Error>( + serde_json::from_slice::( + bytes.rsplit(|&b| b == 0xFF).next().ok_or_else( + || { + Error::bad_database( + "OneTimeKey ID in db is invalid.", + ) + }, + )?, ) - }) + .map_err(|_| { + Error::bad_database("DeviceKeyId in db is invalid.") + })? + .algorithm(), + ) + }) { *counts.entry(algorithm?).or_default() += UInt::from(1_u32); } @@ -435,12 +509,13 @@ impl service::users::Data for KeyValueDatabase { device_keys: &Raw, ) -> Result<()> { let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); self.keyid_key.insert( &userdeviceid, - &serde_json::to_vec(&device_keys).expect("DeviceKeys::to_vec always works"), + &serde_json::to_vec(&device_keys) + .expect("DeviceKeys::to_vec always works"), )?; self.mark_device_key_update(user_id)?; @@ -458,30 +533,33 @@ impl service::users::Data for KeyValueDatabase { ) -> Result<()> { // TODO: Check signatures let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); let (master_key_key, _) = self.parse_master_key(user_id, master_key)?; self.keyid_key .insert(&master_key_key, master_key.json().get().as_bytes())?; - self.userid_masterkeyid - .insert(user_id.as_bytes(), &master_key_key)?; + self.userid_masterkeyid.insert(user_id.as_bytes(), &master_key_key)?; // Self-signing key if let Some(self_signing_key) = self_signing_key { let mut self_signing_key_ids = self_signing_key .deserialize() .map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Invalid self signing key") + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid self signing key", + ) })? .keys .into_values(); - let self_signing_key_id = self_signing_key_ids.next().ok_or(Error::BadRequest( - ErrorKind::InvalidParam, - "Self signing key contained no key.", - ))?; + let self_signing_key_id = + self_signing_key_ids.next().ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Self signing key contained no key.", + ))?; if self_signing_key_ids.next().is_some() { return Err(Error::BadRequest( @@ -491,7 +569,8 @@ impl service::users::Data for KeyValueDatabase { } let mut self_signing_key_key = prefix.clone(); - self_signing_key_key.extend_from_slice(self_signing_key_id.as_bytes()); + self_signing_key_key + .extend_from_slice(self_signing_key_id.as_bytes()); self.keyid_key.insert( &self_signing_key_key, @@ -507,15 +586,19 @@ impl service::users::Data for KeyValueDatabase { let mut user_signing_key_ids = user_signing_key .deserialize() .map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Invalid user signing key") + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid user signing key", + ) })? .keys .into_values(); - let user_signing_key_id = user_signing_key_ids.next().ok_or(Error::BadRequest( - ErrorKind::InvalidParam, - "User signing key contained no key.", - ))?; + let user_signing_key_id = + user_signing_key_ids.next().ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "User signing key contained no key.", + ))?; if user_signing_key_ids.next().is_some() { return Err(Error::BadRequest( @@ -525,7 +608,8 @@ impl service::users::Data for KeyValueDatabase { } let mut user_signing_key_key = prefix; - user_signing_key_key.extend_from_slice(user_signing_key_id.as_bytes()); + user_signing_key_key + .extend_from_slice(user_signing_key_id.as_bytes()); self.keyid_key.insert( &user_signing_key_key, @@ -551,32 +635,44 @@ impl service::users::Data for KeyValueDatabase { sender_id: &UserId, ) -> Result<()> { let mut key = target_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(key_id.as_bytes()); - let mut cross_signing_key: serde_json::Value = - serde_json::from_slice(&self.keyid_key.get(&key)?.ok_or(Error::BadRequest( + let mut cross_signing_key: serde_json::Value = serde_json::from_slice( + &self.keyid_key.get(&key)?.ok_or(Error::BadRequest( ErrorKind::InvalidParam, "Tried to sign nonexistent key.", - ))?) - .map_err(|_| Error::bad_database("key in keyid_key is invalid."))?; + ))?, + ) + .map_err(|_| Error::bad_database("key in keyid_key is invalid."))?; let signatures = cross_signing_key .get_mut("signatures") - .ok_or_else(|| Error::bad_database("key in keyid_key has no signatures field."))? + .ok_or_else(|| { + Error::bad_database("key in keyid_key has no signatures field.") + })? .as_object_mut() - .ok_or_else(|| Error::bad_database("key in keyid_key has invalid signatures field."))? + .ok_or_else(|| { + Error::bad_database( + "key in keyid_key has invalid signatures field.", + ) + })? .entry(sender_id.to_string()) .or_insert_with(|| serde_json::Map::new().into()); signatures .as_object_mut() - .ok_or_else(|| Error::bad_database("signatures in keyid_key for a user is invalid."))? + .ok_or_else(|| { + Error::bad_database( + "signatures in keyid_key for a user is invalid.", + ) + })? .insert(signature.0, signature.1.into()); self.keyid_key.insert( &key, - &serde_json::to_vec(&cross_signing_key).expect("CrossSigningKey::to_vec always works"), + &serde_json::to_vec(&cross_signing_key) + .expect("CrossSigningKey::to_vec always works"), )?; self.mark_device_key_update(target_id)?; @@ -591,7 +687,7 @@ impl service::users::Data for KeyValueDatabase { to: Option, ) -> Box> + 'a> { let mut prefix = user_or_room_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); let mut start = prefix.clone(); start.extend_from_slice(&(from + 1).to_be_bytes()); @@ -603,26 +699,39 @@ impl service::users::Data for KeyValueDatabase { .iter_from(&start, false) .take_while(move |(k, _)| { k.starts_with(&prefix) - && if let Some(current) = k.splitn(2, |&b| b == 0xff).nth(1) { + && if let Some(current) = + k.splitn(2, |&b| b == 0xFF).nth(1) + { if let Ok(c) = utils::u64_from_bytes(current) { c <= to } else { - warn!("BadDatabase: Could not parse keychangeid_userid bytes"); + warn!( + "BadDatabase: Could not parse \ + keychangeid_userid bytes" + ); false } } else { - warn!("BadDatabase: Could not parse keychangeid_userid"); + warn!( + "BadDatabase: Could not parse \ + keychangeid_userid" + ); false } }) .map(|(_, bytes)| { - UserId::parse(utils::string_from_bytes(&bytes).map_err(|_| { - Error::bad_database( - "User ID in devicekeychangeid_userid is invalid unicode.", - ) - })?) + UserId::parse(utils::string_from_bytes(&bytes).map_err( + |_| { + Error::bad_database( + "User ID in devicekeychangeid_userid is \ + invalid unicode.", + ) + }, + )?) .map_err(|_| { - Error::bad_database("User ID in devicekeychangeid_userid is invalid.") + Error::bad_database( + "User ID in devicekeychangeid_userid is invalid.", + ) }) }), ) @@ -647,14 +756,14 @@ impl service::users::Data for KeyValueDatabase { } let mut key = room_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(&count); self.keychangeid_userid.insert(&key, user_id.as_bytes())?; } let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(&count); self.keychangeid_userid.insert(&key, user_id.as_bytes())?; @@ -667,7 +776,7 @@ impl service::users::Data for KeyValueDatabase { device_id: &DeviceId, ) -> Result>> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(device_id.as_bytes()); self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| { @@ -683,11 +792,11 @@ impl service::users::Data for KeyValueDatabase { master_key: &Raw, ) -> Result<(Vec, CrossSigningKey)> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); - let master_key = master_key - .deserialize() - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid master key"))?; + let master_key = master_key.deserialize().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Invalid master key") + })?; let mut master_key_ids = master_key.keys.values(); let master_key_id = master_key_ids.next().ok_or(Error::BadRequest( ErrorKind::InvalidParam, @@ -712,8 +821,12 @@ impl service::users::Data for KeyValueDatabase { allowed_signatures: &dyn Fn(&UserId) -> bool, ) -> Result>> { self.keyid_key.get(key)?.map_or(Ok(None), |bytes| { - let mut cross_signing_key = serde_json::from_slice::(&bytes) - .map_err(|_| Error::bad_database("CrossSigningKey in db is invalid."))?; + let mut cross_signing_key = serde_json::from_slice::< + serde_json::Value, + >(&bytes) + .map_err(|_| { + Error::bad_database("CrossSigningKey in db is invalid.") + })?; clean_signatures( &mut cross_signing_key, sender_user, @@ -754,16 +867,20 @@ impl service::users::Data for KeyValueDatabase { }) } - fn get_user_signing_key(&self, user_id: &UserId) -> Result>> { - self.userid_usersigningkeyid - .get(user_id.as_bytes())? - .map_or(Ok(None), |key| { + fn get_user_signing_key( + &self, + user_id: &UserId, + ) -> Result>> { + self.userid_usersigningkeyid.get(user_id.as_bytes())?.map_or( + Ok(None), + |key| { self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| { Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { Error::bad_database("CrossSigningKey in db is invalid.") })?)) }) - }) + }, + ) } fn add_to_device_event( @@ -775,9 +892,9 @@ impl service::users::Data for KeyValueDatabase { content: serde_json::Value, ) -> Result<()> { let mut key = target_user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(target_device_id.as_bytes()); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); let mut json = serde_json::Map::new(); @@ -785,7 +902,8 @@ impl service::users::Data for KeyValueDatabase { json.insert("sender".to_owned(), sender.to_string().into()); json.insert("content".to_owned(), content); - let value = serde_json::to_vec(&json).expect("Map::to_vec always works"); + let value = + serde_json::to_vec(&json).expect("Map::to_vec always works"); self.todeviceid_events.insert(&key, &value)?; @@ -800,15 +918,14 @@ impl service::users::Data for KeyValueDatabase { let mut events = Vec::new(); let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(device_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); for (_, value) in self.todeviceid_events.scan_prefix(prefix) { - events.push( - serde_json::from_slice(&value) - .map_err(|_| Error::bad_database("Event in todeviceid_events is invalid."))?, - ); + events.push(serde_json::from_slice(&value).map_err(|_| { + Error::bad_database("Event in todeviceid_events is invalid.") + })?); } Ok(events) @@ -821,9 +938,9 @@ impl service::users::Data for KeyValueDatabase { until: u64, ) -> Result<()> { let mut prefix = user_id.as_bytes().to_vec(); - prefix.push(0xff); + prefix.push(0xFF); prefix.extend_from_slice(device_id.as_bytes()); - prefix.push(0xff); + prefix.push(0xFF); let mut last = prefix.clone(); last.extend_from_slice(&until.to_be_bytes()); @@ -836,8 +953,14 @@ impl service::users::Data for KeyValueDatabase { .map(|(key, _)| { Ok::<_, Error>(( key.clone(), - utils::u64_from_bytes(&key[key.len() - size_of::()..key.len()]) - .map_err(|_| Error::bad_database("ToDeviceId has invalid count bytes."))?, + utils::u64_from_bytes( + &key[key.len() - size_of::()..key.len()], + ) + .map_err(|_| { + Error::bad_database( + "ToDeviceId has invalid count bytes.", + ) + })?, )) }) .filter_map(Result::ok) @@ -856,7 +979,7 @@ impl service::users::Data for KeyValueDatabase { device: &Device, ) -> Result<()> { let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); assert!( @@ -864,12 +987,12 @@ impl service::users::Data for KeyValueDatabase { "this method should only be called with existing devices" ); - self.userid_devicelistversion - .increment(user_id.as_bytes())?; + self.userid_devicelistversion.increment(user_id.as_bytes())?; self.userdeviceid_metadata.insert( &userdeviceid, - &serde_json::to_vec(device).expect("Device::to_string always works"), + &serde_json::to_vec(device) + .expect("Device::to_string always works"), )?; Ok(()) @@ -882,26 +1005,32 @@ impl service::users::Data for KeyValueDatabase { device_id: &DeviceId, ) -> Result> { let mut userdeviceid = user_id.as_bytes().to_vec(); - userdeviceid.push(0xff); + userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); - self.userdeviceid_metadata - .get(&userdeviceid)? - .map_or(Ok(None), |bytes| { + self.userdeviceid_metadata.get(&userdeviceid)?.map_or( + Ok(None), + |bytes| { Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { - Error::bad_database("Metadata in userdeviceid_metadata is invalid.") + Error::bad_database( + "Metadata in userdeviceid_metadata is invalid.", + ) })?)) - }) + }, + ) } fn get_devicelist_version(&self, user_id: &UserId) -> Result> { - self.userid_devicelistversion - .get(user_id.as_bytes())? - .map_or(Ok(None), |bytes| { + self.userid_devicelistversion.get(user_id.as_bytes())?.map_or( + Ok(None), + |bytes| { utils::u64_from_bytes(&bytes) - .map_err(|_| Error::bad_database("Invalid devicelistversion in db.")) + .map_err(|_| { + Error::bad_database("Invalid devicelistversion in db.") + }) .map(Some) - }) + }, + ) } fn all_devices_metadata<'a>( @@ -909,25 +1038,29 @@ impl service::users::Data for KeyValueDatabase { user_id: &UserId, ) -> Box> + 'a> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); - Box::new( - self.userdeviceid_metadata - .scan_prefix(key) - .map(|(_, bytes)| { - serde_json::from_slice::(&bytes).map_err(|_| { - Error::bad_database("Device in userdeviceid_metadata is invalid.") - }) - }), - ) + Box::new(self.userdeviceid_metadata.scan_prefix(key).map( + |(_, bytes)| { + serde_json::from_slice::(&bytes).map_err(|_| { + Error::bad_database( + "Device in userdeviceid_metadata is invalid.", + ) + }) + }, + )) } /// Creates a new sync filter. Returns the filter id. - fn create_filter(&self, user_id: &UserId, filter: &FilterDefinition) -> Result { + fn create_filter( + &self, + user_id: &UserId, + filter: &FilterDefinition, + ) -> Result { let filter_id = utils::random_string(4); let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(filter_id.as_bytes()); self.userfilterid_filter.insert( @@ -938,9 +1071,13 @@ impl service::users::Data for KeyValueDatabase { Ok(filter_id) } - fn get_filter(&self, user_id: &UserId, filter_id: &str) -> Result> { + fn get_filter( + &self, + user_id: &UserId, + filter_id: &str, + ) -> Result> { let mut key = user_id.as_bytes().to_vec(); - key.push(0xff); + key.push(0xFF); key.extend_from_slice(filter_id.as_bytes()); let raw = self.userfilterid_filter.get(&key)?; @@ -956,9 +1093,12 @@ impl service::users::Data for KeyValueDatabase { /// Will only return with Some(username) if the password was not empty and the /// username could be successfully parsed. -/// If [`utils::string_from_bytes`] returns an error that username will be skipped -/// and the error will be logged. -fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option { +/// If [`utils::string_from_bytes`] returns an error that username will be +/// skipped and the error will be logged. +fn get_username_with_valid_password( + username: &[u8], + password: &[u8], +) -> Option { // A valid password is not empty if password.is_empty() { None @@ -967,7 +1107,8 @@ fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option< Ok(u) => Some(u), Err(e) => { warn!( - "Failed to parse username while calling get_local_users(): {}", + "Failed to parse username while calling \ + get_local_users(): {}", e.to_string() ); None diff --git a/src/main.rs b/src/main.rs index 1b44322f..407a01b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,9 @@ use axum::{ routing::{any, get, on, MethodFilter}, Router, }; -use axum_server::{bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle}; +use axum_server::{ + bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, +}; use figment::{ providers::{Env, Format, Toml}, Figment, @@ -50,16 +52,16 @@ use api::{client_server, server_server}; pub(crate) use config::Config; pub(crate) use database::KeyValueDatabase; pub(crate) use service::{pdu::PduEvent, Services}; -pub(crate) use utils::error::{Error, Result}; - #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] use tikv_jemallocator::Jemalloc; +pub(crate) use utils::error::{Error, Result}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -pub(crate) static SERVICES: RwLock> = RwLock::new(None); +pub(crate) static SERVICES: RwLock> = + RwLock::new(None); /// Convenient access to the global [`Services`] instance pub(crate) fn services() -> &'static Services { @@ -71,9 +73,9 @@ pub(crate) fn services() -> &'static Services { /// Returns the current version of the crate with extra info if supplied /// -/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string to -/// include it in parenthesis after the SemVer version. A common value are git -/// commit hashes. +/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string +/// to include it in parenthesis after the SemVer version. A common value are +/// git commit hashes. fn version() -> String { let cargo_pkg_version = env!("CARGO_PKG_VERSION"); @@ -91,7 +93,8 @@ async fn main() { let raw_config = Figment::new() .merge( Toml::file(Env::var("GRAPEVINE_CONFIG").expect( - "The GRAPEVINE_CONFIG env var needs to be set. Example: /etc/grapevine.toml", + "The GRAPEVINE_CONFIG env var needs to be set. Example: \ + /etc/grapevine.toml", )) .nested(), ) @@ -100,7 +103,10 @@ async fn main() { let config = match raw_config.extract::() { Ok(s) => s, Err(e) => { - eprintln!("It looks like your config is invalid. The following error occurred: {e}"); + eprintln!( + "It looks like your config is invalid. The following error \ + occurred: {e}" + ); std::process::exit(1); } }; @@ -108,7 +114,9 @@ async fn main() { config.warn_deprecated(); if config.allow_jaeger { - opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); + opentelemetry::global::set_text_map_propagator( + opentelemetry_jaeger::Propagator::new(), + ); let tracer = opentelemetry_jaeger::new_agent_pipeline() .with_auto_split_batch(true) .with_service_name("grapevine") @@ -120,7 +128,8 @@ async fn main() { Ok(s) => s, Err(e) => { eprintln!( - "It looks like your log config is invalid. The following error occurred: {e}" + "It looks like your log config is invalid. The following \ + error occurred: {e}" ); EnvFilter::try_new("warn").unwrap() } @@ -146,7 +155,10 @@ async fn main() { let filter_layer = match EnvFilter::try_new(&config.log) { Ok(s) => s, Err(e) => { - eprintln!("It looks like your config is invalid. The following error occured while parsing it: {e}"); + eprintln!( + "It looks like your config is invalid. The following \ + error occured while parsing it: {e}" + ); EnvFilter::try_new("warn").unwrap() } }; @@ -163,7 +175,8 @@ async fn main() { // * https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6 // * https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741 #[cfg(unix)] - maximize_fd_limit().expect("should be able to increase the soft limit to the hard limit"); + maximize_fd_limit() + .expect("should be able to increase the soft limit to the hard limit"); info!("Loading database"); if let Err(error) = KeyValueDatabase::load_or_create(config).await { @@ -190,17 +203,19 @@ async fn run_server() -> io::Result<()> { let middlewares = ServiceBuilder::new() .sensitive_headers([header::AUTHORIZATION]) .layer(axum::middleware::from_fn(spawn_task)) - .layer( - TraceLayer::new_for_http().make_span_with(|request: &http::Request<_>| { - let path = if let Some(path) = request.extensions().get::() { + .layer(TraceLayer::new_for_http().make_span_with( + |request: &http::Request<_>| { + let path = if let Some(path) = + request.extensions().get::() + { path.as_str() } else { request.uri().path() }; tracing::info_span!("http_request", %path) - }), - ) + }, + )) .layer(axum::middleware::from_fn(unrecognized_method)) .layer( CorsLayer::new() @@ -235,7 +250,8 @@ async fn run_server() -> io::Result<()> { match &config.tls { Some(tls) => { - let conf = RustlsConfig::from_pem_file(&tls.certs, &tls.key).await?; + let conf = + RustlsConfig::from_pem_file(&tls.certs, &tls.key).await?; let server = bind_rustls(addr, conf).handle(handle).serve(app); #[cfg(feature = "systemd")] @@ -411,9 +427,10 @@ fn routes(config: &Config) -> Router { .ruma_route(c2s::get_relating_events_route) .ruma_route(c2s::get_hierarchy_route); - // Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes - // share one Ruma request / response type pair with {get,send}_state_event_for_key_route. - // These two endpoints also allow trailing slashes. + // Ruma doesn't have support for multiple paths for a single endpoint yet, + // and these routes share one Ruma request / response type pair with + // {get,send}_state_event_for_key_route. These two endpoints also allow + // trailing slashes. let router = router .route( "/_matrix/client/r0/rooms/:room_id/state/:event_type", @@ -483,9 +500,7 @@ fn routes(config: &Config) -> Router { async fn shutdown_signal(handle: ServerHandle) { let ctrl_c = async { - signal::ctrl_c() - .await - .expect("failed to install Ctrl+C handler"); + signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); }; #[cfg(unix)] @@ -554,9 +569,9 @@ impl RouterExt for Router { } pub(crate) trait RumaHandler { - // Can't transform to a handler without boxing or relying on the nightly-only - // impl-trait-in-traits feature. Moving a small amount of extra logic into the trait - // allows bypassing both. + // Can't transform to a handler without boxing or relying on the + // nightly-only impl-trait-in-traits feature. Moving a small amount of + // extra logic into the trait allows bypassing both. fn add_to_router(self, router: Router) -> Router; } diff --git a/src/service.rs b/src/service.rs index 710edaca..65548616 100644 --- a/src/service.rs +++ b/src/service.rs @@ -4,10 +4,9 @@ use std::{ }; use lru_cache::LruCache; -use tokio::sync::{broadcast, Mutex}; +use tokio::sync::{broadcast, Mutex, RwLock}; use crate::{Config, Result}; -use tokio::sync::RwLock; pub(crate) mod account_data; pub(crate) mod admin; @@ -58,10 +57,14 @@ impl Services { ) -> Result { Ok(Self { appservice: appservice::Service::build(db)?, - pusher: pusher::Service { db }, + pusher: pusher::Service { + db, + }, rooms: rooms::Service { alias: db, - auth_chain: rooms::auth_chain::Service { db }, + auth_chain: rooms::auth_chain::Service { + db, + }, directory: db, edus: rooms::edus::Service { read_receipt: db, @@ -78,10 +81,14 @@ impl Services { }, metadata: db, outlier: db, - pdu_metadata: rooms::pdu_metadata::Service { db }, + pdu_metadata: rooms::pdu_metadata::Service { + db, + }, search: db, short: db, - state: rooms::state::Service { db }, + state: rooms::state::Service { + db, + }, state_accessor: rooms::state_accessor::Service { db, #[allow( @@ -101,7 +108,9 @@ impl Services { (100.0 * config.cache_capacity_modifier) as usize, )), }, - state_cache: rooms::state_cache::Service { db }, + state_cache: rooms::state_cache::Service { + db, + }, state_compressor: rooms::state_compressor::Service { db, #[allow( @@ -117,14 +126,18 @@ impl Services { db, lasttimelinecount_cache: Mutex::new(HashMap::new()), }, - threads: rooms::threads::Service { db }, + threads: rooms::threads::Service { + db, + }, spaces: rooms::spaces::Service { roomid_spacechunk_cache: Mutex::new(LruCache::new(200)), }, user: db, }, transaction_ids: db, - uiaa: uiaa::Service { db }, + uiaa: uiaa::Service { + db, + }, users: users::Service { db, connections: StdMutex::new(BTreeMap::new()), @@ -132,14 +145,18 @@ impl Services { account_data: db, admin: admin::Service::build(), key_backups: db, - media: media::Service { db }, + media: media::Service { + db, + }, sending: sending::Service::build(db, &config), globals: globals::Service::load(db, config)?, }) } + async fn memory_usage(&self) -> String { - let lazy_load_waiting = self.rooms.lazy_loading.lazy_load_waiting.lock().await.len(); + let lazy_load_waiting = + self.rooms.lazy_loading.lazy_load_waiting.lock().await.len(); let server_visibility_cache = self .rooms .state_accessor @@ -154,21 +171,12 @@ impl Services { .lock() .unwrap() .len(); - let stateinfo_cache = self - .rooms - .state_compressor - .stateinfo_cache - .lock() - .unwrap() - .len(); - let lasttimelinecount_cache = self - .rooms - .timeline - .lasttimelinecount_cache - .lock() - .await - .len(); - let roomid_spacechunk_cache = self.rooms.spaces.roomid_spacechunk_cache.lock().await.len(); + let stateinfo_cache = + self.rooms.state_compressor.stateinfo_cache.lock().unwrap().len(); + let lasttimelinecount_cache = + self.rooms.timeline.lasttimelinecount_cache.lock().await.len(); + let roomid_spacechunk_cache = + self.rooms.spaces.roomid_spacechunk_cache.lock().await.len(); format!( "\ @@ -177,18 +185,13 @@ server_visibility_cache: {server_visibility_cache} user_visibility_cache: {user_visibility_cache} stateinfo_cache: {stateinfo_cache} lasttimelinecount_cache: {lasttimelinecount_cache} -roomid_spacechunk_cache: {roomid_spacechunk_cache}\ - " +roomid_spacechunk_cache: {roomid_spacechunk_cache}" ) } + async fn clear_caches(&self, amount: u32) { if amount > 0 { - self.rooms - .lazy_loading - .lazy_load_waiting - .lock() - .await - .clear(); + self.rooms.lazy_loading.lazy_load_waiting.lock().await.clear(); } if amount > 1 { self.rooms @@ -207,28 +210,13 @@ roomid_spacechunk_cache: {roomid_spacechunk_cache}\ .clear(); } if amount > 3 { - self.rooms - .state_compressor - .stateinfo_cache - .lock() - .unwrap() - .clear(); + self.rooms.state_compressor.stateinfo_cache.lock().unwrap().clear(); } if amount > 4 { - self.rooms - .timeline - .lasttimelinecount_cache - .lock() - .await - .clear(); + self.rooms.timeline.lasttimelinecount_cache.lock().await.clear(); } if amount > 5 { - self.rooms - .spaces - .roomid_spacechunk_cache - .lock() - .await - .clear(); + self.rooms.spaces.roomid_spacechunk_cache.lock().await.clear(); } } } diff --git a/src/service/account_data/data.rs b/src/service/account_data/data.rs index 92a17cab..04d4bf76 100644 --- a/src/service/account_data/data.rs +++ b/src/service/account_data/data.rs @@ -1,14 +1,16 @@ use std::collections::HashMap; -use crate::Result; use ruma::{ events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, serde::Raw, RoomId, UserId, }; +use crate::Result; + pub(crate) trait Data: Send + Sync { - /// Places one event in the account data of the user and removes the previous entry. + /// Places one event in the account data of the user and removes the + /// previous entry. fn update( &self, room_id: Option<&RoomId>, diff --git a/src/service/admin.rs b/src/service/admin.rs index d591a514..87b645f1 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -16,7 +16,9 @@ use ruma::{ canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, - history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, + history_visibility::{ + HistoryVisibility, RoomHistoryVisibilityEventContent, + }, join_rules::{JoinRule, RoomJoinRulesEventContent}, member::{MembershipState, RoomMemberEventContent}, message::RoomMessageEventContent, @@ -26,19 +28,19 @@ use ruma::{ }, TimelineEventType, }, - EventId, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, RoomVersionId, ServerName, UserId, + EventId, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, RoomVersionId, + ServerName, UserId, }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; use tracing::warn; +use super::pdu::PduBuilder; use crate::{ api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH}, services, utils, Error, PduEvent, Result, }; -use super::pdu::PduBuilder; - #[cfg_attr(test, derive(Debug))] #[derive(Parser)] #[command(name = "@grapevine:server.name:", version = env!("CARGO_PKG_VERSION"))] @@ -46,11 +48,12 @@ enum AdminCommand { #[command(verbatim_doc_comment)] /// Register an appservice using its registration YAML /// - /// This command needs a YAML generated by an appservice (such as a bridge), - /// which must be provided in a Markdown code-block below the command. + /// This command needs a YAML generated by an appservice (such as a + /// bridge), which must be provided in a Markdown code-block below the + /// command. /// - /// Registering a new bridge using the ID of an existing bridge will replace - /// the old one. + /// Registering a new bridge using the ID of an existing bridge will + /// replace the old one. /// /// [commandbody]() /// # ``` @@ -95,8 +98,9 @@ enum AdminCommand { /// /// Users will not be removed from joined rooms by default. /// Can be overridden with --leave-rooms flag. - /// Removing a mass amount of users from a room may cause a significant amount of leave events. - /// The time to leave rooms may depend significantly on joined rooms and servers. + /// Removing a mass amount of users from a room may cause a significant + /// amount of leave events. The time to leave rooms may depend + /// significantly on joined rooms and servers. /// /// [commandbody]() /// # ``` @@ -138,11 +142,17 @@ enum AdminCommand { /// Print database memory usage statistics MemoryUsage, - /// Clears all of Grapevine's database caches with index smaller than the amount - ClearDatabaseCaches { amount: u32 }, + /// Clears all of Grapevine's database caches with index smaller than the + /// amount + ClearDatabaseCaches { + amount: u32, + }, - /// Clears all of Grapevine's service caches with index smaller than the amount - ClearServiceCaches { amount: u32 }, + /// Clears all of Grapevine's service caches with index smaller than the + /// amount + ClearServiceCaches { + amount: u32, + }, /// Show configuration values ShowConfig, @@ -162,9 +172,13 @@ enum AdminCommand { }, /// Disables incoming federation handling for a room. - DisableRoom { room_id: Box }, + DisableRoom { + room_id: Box, + }, /// Enables incoming federation handling for a room again. - EnableRoom { room_id: Box }, + EnableRoom { + room_id: Box, + }, /// Verify json signatures /// [commandbody]() @@ -267,31 +281,38 @@ impl Service { } pub(crate) fn process_message(&self, room_message: String) { - self.sender - .send(AdminRoomEvent::ProcessMessage(room_message)) - .unwrap(); + self.sender.send(AdminRoomEvent::ProcessMessage(room_message)).unwrap(); } - pub(crate) fn send_message(&self, message_content: RoomMessageEventContent) { - self.sender - .send(AdminRoomEvent::SendMessage(message_content)) - .unwrap(); + pub(crate) fn send_message( + &self, + message_content: RoomMessageEventContent, + ) { + self.sender.send(AdminRoomEvent::SendMessage(message_content)).unwrap(); } // Parse and process a message from the admin room - async fn process_admin_message(&self, room_message: String) -> RoomMessageEventContent { + async fn process_admin_message( + &self, + room_message: String, + ) -> RoomMessageEventContent { let mut lines = room_message.lines().filter(|l| !l.trim().is_empty()); - let command_line = lines.next().expect("each string has at least one line"); + let command_line = + lines.next().expect("each string has at least one line"); let body: Vec<_> = lines.collect(); let admin_command = match Self::parse_admin_command(command_line) { Ok(command) => command, Err(error) => { let server_name = services().globals.server_name(); - let message = error.replace("server.name", server_name.as_str()); + let message = + error.replace("server.name", server_name.as_str()); let html_message = Self::usage_to_html(&message, server_name); - return RoomMessageEventContent::text_html(message, html_message); + return RoomMessageEventContent::text_html( + message, + html_message, + ); } }; @@ -299,22 +320,28 @@ impl Service { Ok(reply_message) => reply_message, Err(error) => { let markdown_message = format!( - "Encountered an error while handling the command:\n\ - ```\n{error}\n```", + "Encountered an error while handling the \ + command:\n```\n{error}\n```", ); let html_message = format!( - "Encountered an error while handling the command:\n\ -
\n{error}\n
", + "Encountered an error while handling the \ + command:\n
\n{error}\n
", ); - RoomMessageEventContent::text_html(markdown_message, html_message) + RoomMessageEventContent::text_html( + markdown_message, + html_message, + ) } } } // Parse chat messages from the admin room into an AdminCommand object - fn parse_admin_command(command_line: &str) -> std::result::Result { - // Note: argv[0] is `@grapevine:servername:`, which is treated as the main command + fn parse_admin_command( + command_line: &str, + ) -> std::result::Result { + // Note: argv[0] is `@grapevine:servername:`, which is treated as the + // main command let mut argv: Vec<_> = command_line.split_whitespace().collect(); // Replace `help command` with `command --help` @@ -342,18 +369,26 @@ impl Service { ) -> Result { let reply_message_content = match command { AdminCommand::RegisterAppservice => { - if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```" + if body.len() > 2 + && body[0].trim() == "```" + && body.last().unwrap().trim() == "```" { let appservice_config = body[1..body.len() - 1].join("\n"); - let parsed_config = serde_yaml::from_str::(&appservice_config); + let parsed_config = serde_yaml::from_str::( + &appservice_config, + ); match parsed_config { - Ok(yaml) => match services().appservice.register_appservice(yaml).await { - Ok(id) => RoomMessageEventContent::text_plain(format!( - "Appservice registered with ID: {id}." - )), - Err(e) => RoomMessageEventContent::text_plain(format!( - "Failed to register appservice: {e}" - )), + Ok(yaml) => match services() + .appservice + .register_appservice(yaml) + .await + { + Ok(id) => RoomMessageEventContent::text_plain( + format!("Appservice registered with ID: {id}."), + ), + Err(e) => RoomMessageEventContent::text_plain( + format!("Failed to register appservice: {e}"), + ), }, Err(e) => RoomMessageEventContent::text_plain(format!( "Could not parse appservice config: {e}" @@ -361,7 +396,8 @@ impl Service { } } else { RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", + "Expected code block in command body. Add --help for \ + details.", ) } } @@ -372,7 +408,9 @@ impl Service { .unregister_appservice(&appservice_identifier) .await { - Ok(()) => RoomMessageEventContent::text_plain("Appservice unregistered."), + Ok(()) => RoomMessageEventContent::text_plain( + "Appservice unregistered.", + ), Err(e) => RoomMessageEventContent::text_plain(format!( "Failed to unregister appservice: {e}" )), @@ -407,17 +445,25 @@ impl Service { ); RoomMessageEventContent::text_plain(output) } - AdminCommand::ListLocalUsers => match services().users.list_local_users() { + AdminCommand::ListLocalUsers => match services() + .users + .list_local_users() + { Ok(users) => { - let mut msg: String = format!("Found {} local user account(s):\n", users.len()); + let mut msg: String = format!( + "Found {} local user account(s):\n", + users.len() + ); msg += &users.join("\n"); RoomMessageEventContent::text_plain(&msg) } Err(e) => RoomMessageEventContent::text_plain(e.to_string()), }, AdminCommand::IncomingFederation => { - let map = services().globals.roomid_federationhandletime.read().await; - let mut msg: String = format!("Handling {} incoming pdus:\n", map.len()); + let map = + services().globals.roomid_federationhandletime.read().await; + let mut msg: String = + format!("Handling {} incoming pdus:\n", map.len()); for (r, (e, i)) in map.iter() { let elapsed = i.elapsed(); @@ -431,17 +477,26 @@ impl Service { } RoomMessageEventContent::text_plain(&msg) } - AdminCommand::GetAuthChain { event_id } => { + AdminCommand::GetAuthChain { + event_id, + } => { let event_id = Arc::::from(event_id); - if let Some(event) = services().rooms.timeline.get_pdu_json(&event_id)? { + if let Some(event) = + services().rooms.timeline.get_pdu_json(&event_id)? + { let room_id_str = event .get("room_id") .and_then(|val| val.as_str()) - .ok_or_else(|| Error::bad_database("Invalid event in database"))?; + .ok_or_else(|| { + Error::bad_database("Invalid event in database") + })?; - let room_id = <&RoomId>::try_from(room_id_str).map_err(|_| { - Error::bad_database("Invalid room id field in event in database") - })?; + let room_id = + <&RoomId>::try_from(room_id_str).map_err(|_| { + Error::bad_database( + "Invalid room id field in event in database", + ) + })?; let start = Instant::now(); let count = services() .rooms @@ -458,29 +513,47 @@ impl Service { } } AdminCommand::ParsePdu => { - if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```" + if body.len() > 2 + && body[0].trim() == "```" + && body.last().unwrap().trim() == "```" { let string = body[1..body.len() - 1].join("\n"); match serde_json::from_str(&string) { Ok(value) => { - match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) { + match ruma::signatures::reference_hash( + &value, + &RoomVersionId::V6, + ) { Ok(hash) => { - let event_id = EventId::parse(format!("${hash}")); + let event_id = + EventId::parse(format!("${hash}")); match serde_json::from_value::( - serde_json::to_value(value).expect("value is json"), + serde_json::to_value(value) + .expect("value is json"), ) { - Ok(pdu) => RoomMessageEventContent::text_plain(format!( - "EventId: {event_id:?}\n{pdu:#?}" - )), - Err(e) => RoomMessageEventContent::text_plain(format!( - "EventId: {event_id:?}\nCould not parse event: {e}" - )), + Ok(pdu) => { + RoomMessageEventContent::text_plain( + format!( + "EventId: {event_id:?}\\ + n{pdu:#?}" + ), + ) + } + Err(e) => { + RoomMessageEventContent::text_plain( + format!( + "EventId: {event_id:?}\\ + nCould not parse event: \ + {e}" + ), + ) + } } } - Err(e) => RoomMessageEventContent::text_plain(format!( - "Could not parse PDU JSON: {e:?}" - )), + Err(e) => RoomMessageEventContent::text_plain( + format!("Could not parse PDU JSON: {e:?}"), + ), } } Err(e) => RoomMessageEventContent::text_plain(format!( @@ -488,10 +561,14 @@ impl Service { )), } } else { - RoomMessageEventContent::text_plain("Expected code block in command body.") + RoomMessageEventContent::text_plain( + "Expected code block in command body.", + ) } } - AdminCommand::GetPdu { event_id } => { + AdminCommand::GetPdu { + event_id, + } => { let mut outlier = false; let mut pdu_json = services() .rooms @@ -499,7 +576,8 @@ impl Service { .get_non_outlier_pdu_json(&event_id)?; if pdu_json.is_none() { outlier = true; - pdu_json = services().rooms.timeline.get_pdu_json(&event_id)?; + pdu_json = + services().rooms.timeline.get_pdu_json(&event_id)?; } match pdu_json { Some(json) => { @@ -516,7 +594,8 @@ impl Service { json_text ), format!( - "

{}

\n
{}\n
\n", + "

{}

\n
{}\n
\n", if outlier { "PDU is outlier" } else { @@ -526,7 +605,9 @@ impl Service { ), ) } - None => RoomMessageEventContent::text_plain("PDU not found."), + None => { + RoomMessageEventContent::text_plain("PDU not found.") + } } } AdminCommand::MemoryUsage => { @@ -537,30 +618,42 @@ impl Service { "Services:\n{response1}\n\nDatabase:\n{response2}" )) } - AdminCommand::ClearDatabaseCaches { amount } => { + AdminCommand::ClearDatabaseCaches { + amount, + } => { services().globals.db.clear_caches(amount); RoomMessageEventContent::text_plain("Done.") } - AdminCommand::ClearServiceCaches { amount } => { + AdminCommand::ClearServiceCaches { + amount, + } => { services().clear_caches(amount).await; RoomMessageEventContent::text_plain("Done.") } AdminCommand::ShowConfig => { // Construct and send the response - RoomMessageEventContent::text_plain(format!("{}", services().globals.config)) + RoomMessageEventContent::text_plain(format!( + "{}", + services().globals.config + )) } - AdminCommand::ResetPassword { username } => { + AdminCommand::ResetPassword { + username, + } => { let user_id = match UserId::parse_with_server_name( username.as_str().to_lowercase(), services().globals.server_name(), ) { Ok(id) => id, Err(e) => { - return Ok(RoomMessageEventContent::text_plain(format!( - "The supplied username is not a valid username: {e}" - ))) + return Ok(RoomMessageEventContent::text_plain( + format!( + "The supplied username is not a valid \ + username: {e}" + ), + )) } }; @@ -589,23 +682,29 @@ impl Service { )); } - let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH); + let new_password = + utils::random_string(AUTO_GEN_PASSWORD_LENGTH); match services() .users .set_password(&user_id, Some(new_password.as_str())) { Ok(()) => RoomMessageEventContent::text_plain(format!( - "Successfully reset the password for user {user_id}: {new_password}" + "Successfully reset the password for user {user_id}: \ + {new_password}" )), Err(e) => RoomMessageEventContent::text_plain(format!( "Couldn't reset the password for user {user_id}: {e}" )), } } - AdminCommand::CreateUser { username, password } => { - let password = - password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH)); + AdminCommand::CreateUser { + username, + password, + } => { + let password = password.unwrap_or_else(|| { + utils::random_string(AUTO_GEN_PASSWORD_LENGTH) + }); // Validate user id let user_id = match UserId::parse_with_server_name( username.as_str().to_lowercase(), @@ -613,9 +712,12 @@ impl Service { ) { Ok(id) => id, Err(e) => { - return Ok(RoomMessageEventContent::text_plain(format!( - "The supplied username is not a valid username: {e}" - ))) + return Ok(RoomMessageEventContent::text_plain( + format!( + "The supplied username is not a valid \ + username: {e}" + ), + )) } }; if user_id.is_historical() { @@ -647,24 +749,32 @@ impl Service { .into(), &serde_json::to_value(PushRulesEvent { content: PushRulesEventContent { - global: ruma::push::Ruleset::server_default(&user_id), + global: ruma::push::Ruleset::server_default( + &user_id, + ), }, }) .expect("to json value always works"), )?; - // we dont add a device since we're not the user, just the creator + // we dont add a device since we're not the user, just the + // creator // Inhibit login does not work for guests RoomMessageEventContent::text_plain(format!( - "Created user with user_id: {user_id} and password: {password}" + "Created user with user_id: {user_id} and password: \ + {password}" )) } - AdminCommand::DisableRoom { room_id } => { + AdminCommand::DisableRoom { + room_id, + } => { services().rooms.metadata.disable_room(&room_id, true)?; RoomMessageEventContent::text_plain("Room disabled.") } - AdminCommand::EnableRoom { room_id } => { + AdminCommand::EnableRoom { + room_id, + } => { services().rooms.metadata.disable_room(&room_id, false)?; RoomMessageEventContent::text_plain("Room enabled.") } @@ -677,13 +787,16 @@ impl Service { RoomMessageEventContent::text_plain(format!( "User {user_id} doesn't exist on this server" )) - } else if user_id.server_name() != services().globals.server_name() { + } else if user_id.server_name() + != services().globals.server_name() + { RoomMessageEventContent::text_plain(format!( "User {user_id} is not from this server" )) } else { RoomMessageEventContent::text_plain(format!( - "Making {user_id} leave all rooms before deactivation..." + "Making {user_id} leave all rooms before \ + deactivation..." )); services().users.deactivate_account(&user_id)?; @@ -697,10 +810,18 @@ impl Service { )) } } - AdminCommand::DeactivateAll { leave_rooms, force } => { - if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```" + AdminCommand::DeactivateAll { + leave_rooms, + force, + } => { + if body.len() > 2 + && body[0].trim() == "```" + && body.last().unwrap().trim() == "```" { - let users = body.clone().drain(1..body.len() - 1).collect::>(); + let users = body + .clone() + .drain(1..body.len() - 1) + .collect::>(); let mut user_ids = Vec::new(); let mut remote_ids = Vec::new(); @@ -710,7 +831,9 @@ impl Service { for &user in &users { match <&UserId>::try_from(user) { Ok(user_id) => { - if user_id.server_name() != services().globals.server_name() { + if user_id.server_name() + != services().globals.server_name() + { remote_ids.push(user_id); } else if !services().users.exists(user_id)? { non_existant_ids.push(user_id); @@ -727,39 +850,59 @@ impl Service { let mut markdown_message = String::new(); let mut html_message = String::new(); if !invalid_users.is_empty() { - markdown_message.push_str("The following user ids are not valid:\n```\n"); - html_message.push_str("The following user ids are not valid:\n
\n");
+                        markdown_message.push_str(
+                            "The following user ids are not valid:\n```\n",
+                        );
+                        html_message.push_str(
+                            "The following user ids are not valid:\n
\n",
+                        );
                         for invalid_user in invalid_users {
                             writeln!(markdown_message, "{invalid_user}")
-                                .expect("write to in-memory buffer should succeed");
-                            writeln!(html_message, "{invalid_user}")
-                                .expect("write to in-memory buffer should succeed");
+                                .expect(
+                                    "write to in-memory buffer should succeed",
+                                );
+                            writeln!(html_message, "{invalid_user}").expect(
+                                "write to in-memory buffer should succeed",
+                            );
                         }
                         markdown_message.push_str("```\n\n");
                         html_message.push_str("
\n\n"); } if !remote_ids.is_empty() { - markdown_message - .push_str("The following users are not from this server:\n```\n"); - html_message - .push_str("The following users are not from this server:\n
\n");
+                        markdown_message.push_str(
+                            "The following users are not from this \
+                             server:\n```\n",
+                        );
+                        html_message.push_str(
+                            "The following users are not from this \
+                             server:\n
\n",
+                        );
                         for remote_id in remote_ids {
-                            writeln!(markdown_message, "{remote_id}")
-                                .expect("write to in-memory buffer should succeed");
-                            writeln!(html_message, "{remote_id}")
-                                .expect("write to in-memory buffer should succeed");
+                            writeln!(markdown_message, "{remote_id}").expect(
+                                "write to in-memory buffer should succeed",
+                            );
+                            writeln!(html_message, "{remote_id}").expect(
+                                "write to in-memory buffer should succeed",
+                            );
                         }
                         markdown_message.push_str("```\n\n");
                         html_message.push_str("
\n\n"); } if !non_existant_ids.is_empty() { - markdown_message.push_str("The following users do not exist:\n```\n"); - html_message.push_str("The following users do not exist:\n
\n");
+                        markdown_message.push_str(
+                            "The following users do not exist:\n```\n",
+                        );
+                        html_message.push_str(
+                            "The following users do not exist:\n
\n",
+                        );
                         for non_existant_id in non_existant_ids {
                             writeln!(markdown_message, "{non_existant_id}")
-                                .expect("write to in-memory buffer should succeed");
-                            writeln!(html_message, "{non_existant_id}")
-                                .expect("write to in-memory buffer should succeed");
+                                .expect(
+                                    "write to in-memory buffer should succeed",
+                                );
+                            writeln!(html_message, "{non_existant_id}").expect(
+                                "write to in-memory buffer should succeed",
+                            );
                         }
                         markdown_message.push_str("```\n\n");
                         html_message.push_str("
\n\n"); @@ -775,21 +918,24 @@ impl Service { let mut admins = Vec::new(); if !force { - user_ids.retain(|&user_id| match services().users.is_admin(user_id) { - Ok(is_admin) => { - if is_admin { - admins.push(user_id.localpart()); - false - } else { - true + user_ids.retain(|&user_id| { + match services().users.is_admin(user_id) { + Ok(is_admin) => { + if is_admin { + admins.push(user_id.localpart()); + false + } else { + true + } } + Err(_) => false, } - Err(_) => false, }); } for &user_id in &user_ids { - if services().users.deactivate_account(user_id).is_ok() { + if services().users.deactivate_account(user_id).is_ok() + { deactivation_count += 1; } } @@ -807,16 +953,25 @@ impl Service { "Deactivated {deactivation_count} accounts." )) } else { - RoomMessageEventContent::text_plain(format!("Deactivated {} accounts.\nSkipped admin accounts: {:?}. Use --force to deactivate admin accounts", deactivation_count, admins.join(", "))) + RoomMessageEventContent::text_plain(format!( + "Deactivated {} accounts.\nSkipped admin \ + accounts: {:?}. Use --force to deactivate admin \ + accounts", + deactivation_count, + admins.join(", ") + )) } } else { RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", + "Expected code block in command body. Add --help for \ + details.", ) } } AdminCommand::SignJson => { - if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```" + if body.len() > 2 + && body[0].trim() == "```" + && body.last().unwrap().trim() == "```" { let string = body[1..body.len() - 1].join("\n"); match serde_json::from_str(&string) { @@ -827,20 +982,26 @@ impl Service { &mut value, ) .expect("our request json is what ruma expects"); - let json_text = serde_json::to_string_pretty(&value) - .expect("canonical json is valid json"); + let json_text = + serde_json::to_string_pretty(&value) + .expect("canonical json is valid json"); RoomMessageEventContent::text_plain(json_text) } - Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json: {e}")), + Err(e) => RoomMessageEventContent::text_plain(format!( + "Invalid json: {e}" + )), } } else { RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", + "Expected code block in command body. Add --help for \ + details.", ) } } AdminCommand::VerifyJson => { - if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```" + if body.len() > 2 + && body[0].trim() == "```" + && body.last().unwrap().trim() == "```" { let string = body[1..body.len() - 1].join("\n"); match serde_json::from_str(&string) { @@ -850,22 +1011,35 @@ impl Service { services() .rooms .event_handler - .fetch_required_signing_keys(&value, &pub_key_map) + .fetch_required_signing_keys( + &value, + &pub_key_map, + ) .await?; let pub_key_map = pub_key_map.read().await; - match ruma::signatures::verify_json(&pub_key_map, &value) { - Ok(()) => RoomMessageEventContent::text_plain("Signature correct"), - Err(e) => RoomMessageEventContent::text_plain(format!( - "Signature verification failed: {e}" - )), + match ruma::signatures::verify_json( + &pub_key_map, + &value, + ) { + Ok(()) => RoomMessageEventContent::text_plain( + "Signature correct", + ), + Err(e) => RoomMessageEventContent::text_plain( + format!( + "Signature verification failed: {e}" + ), + ), } } - Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json: {e}")), + Err(e) => RoomMessageEventContent::text_plain(format!( + "Invalid json: {e}" + )), } } else { RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", + "Expected code block in command body. Add --help for \ + details.", ) } } @@ -876,7 +1050,8 @@ impl Service { // Utility to turn clap's `--help` text to HTML. fn usage_to_html(text: &str, server_name: &ServerName) -> String { - // Replace `@grapevine:servername:-subcmdname` with `@grapevine:servername: subcmdname` + // Replace `@grapevine:servername:-subcmdname` with + // `@grapevine:servername: subcmdname` let localpart = if services().globals.config.conduit_compat { "conduit" } else { @@ -892,11 +1067,13 @@ impl Service { let text = text.replace("SUBCOMMAND", "COMMAND"); let text = text.replace("subcommand", "command"); - // Escape option names (e.g. ``) since they look like HTML tags + // Escape option names (e.g. ``) since they look like HTML + // tags let text = text.replace('<', "<").replace('>', ">"); // Italicize the first line (command name and version text) - let re = Regex::new("^(.*?)\n").expect("Regex compilation should not fail"); + let re = + Regex::new("^(.*?)\n").expect("Regex compilation should not fail"); let text = re.replace_all(&text, "$1\n"); // Unmerge wrapped lines @@ -911,8 +1088,8 @@ impl Service { .expect("Regex compilation should not fail"); let text = re.replace_all(&text, "$1: $4"); - // Look for a `[commandbody]()` tag. If it exists, use all lines below it that - // start with a `#` in the USAGE section. + // Look for a `[commandbody]()` tag. If it exists, use all lines below + // it that start with a `#` in the USAGE section. let mut text_lines: Vec<&str> = text.lines().collect(); let command_body = text_lines .iter() @@ -936,8 +1113,11 @@ impl Service { // This makes the usage of e.g. `register-appservice` more accurate let re = Regex::new("(?m)^USAGE:\n (.*?)\n\n") .expect("Regex compilation should not fail"); - re.replace_all(&text, "USAGE:\n
$1[nobr]\n[commandbodyblock]
") - .replace("[commandbodyblock]", &command_body) + re.replace_all( + &text, + "USAGE:\n
$1[nobr]\n[commandbodyblock]
", + ) + .replace("[commandbodyblock]", &command_body) }; // Add HTML line-breaks @@ -949,8 +1129,9 @@ impl Service { /// Create the admin room. /// - /// Users in this room are considered admins by grapevine, and the room can be - /// used to issue admin commands by talking to the server user inside it. + /// Users in this room are considered admins by grapevine, and the room can + /// be used to issue admin commands by talking to the server user inside + /// it. #[allow(clippy::too_many_lines)] pub(crate) async fn create_admin_room(&self) -> Result<()> { let room_id = RoomId::new(services().globals.server_name()); @@ -993,7 +1174,9 @@ impl Service { | RoomVersionId::V7 | RoomVersionId::V8 | RoomVersionId::V9 - | RoomVersionId::V10 => RoomCreateEventContent::new_v1(grapevine_user.clone()), + | RoomVersionId::V10 => { + RoomCreateEventContent::new_v1(grapevine_user.clone()) + } RoomVersionId::V11 => RoomCreateEventContent::new_v11(), _ => unreachable!("Validity of room version already checked"), }; @@ -1008,7 +1191,8 @@ impl Service { .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomCreate, - content: to_raw_value(&content).expect("event is valid, we just created it"), + content: to_raw_value(&content) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), redacts: None, @@ -1079,8 +1263,10 @@ impl Service { .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomJoinRules, - content: to_raw_value(&RoomJoinRulesEventContent::new(JoinRule::Invite)) - .expect("event is valid, we just created it"), + content: to_raw_value(&RoomJoinRulesEventContent::new( + JoinRule::Invite, + )) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), redacts: None, @@ -1098,9 +1284,11 @@ impl Service { .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomHistoryVisibility, - content: to_raw_value(&RoomHistoryVisibilityEventContent::new( - HistoryVisibility::Shared, - )) + content: to_raw_value( + &RoomHistoryVisibilityEventContent::new( + HistoryVisibility::Shared, + ), + ) .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), @@ -1134,15 +1322,18 @@ impl Service { .await?; // 5. Events implied by name and topic - let room_name = format!("{} Admin Room", services().globals.server_name()); + let room_name = + format!("{} Admin Room", services().globals.server_name()); services() .rooms .timeline .build_and_append_pdu( PduBuilder { event_type: TimelineEventType::RoomName, - content: to_raw_value(&RoomNameEventContent::new(room_name)) - .expect("event is valid, we just created it"), + content: to_raw_value(&RoomNameEventContent::new( + room_name, + )) + .expect("event is valid, we just created it"), unsigned: None, state_key: Some(String::new()), redacts: None, @@ -1160,7 +1351,10 @@ impl Service { PduBuilder { event_type: TimelineEventType::RoomTopic, content: to_raw_value(&RoomTopicEventContent { - topic: format!("Manage {}", services().globals.server_name()), + topic: format!( + "Manage {}", + services().globals.server_name() + ), }) .expect("event is valid, we just created it"), unsigned: None, @@ -1174,9 +1368,10 @@ impl Service { .await?; // 6. Room alias - let alias: OwnedRoomAliasId = format!("#admins:{}", services().globals.server_name()) - .try_into() - .expect("#admins:server_name is a valid alias name"); + let alias: OwnedRoomAliasId = + format!("#admins:{}", services().globals.server_name()) + .try_into() + .expect("#admins:server_name is a valid alias name"); services() .rooms @@ -1206,7 +1401,8 @@ impl Service { /// Gets the room ID of the admin room /// - /// Errors are propagated from the database, and will have None if there is no admin room + /// Errors are propagated from the database, and will have None if there is + /// no admin room // Allowed because this function uses `services()` #[allow(clippy::unused_self)] pub(crate) fn get_admin_room(&self) -> Result> { @@ -1215,10 +1411,7 @@ impl Service { .try_into() .expect("#admins:server_name is a valid alias name"); - services() - .rooms - .alias - .resolve_local_alias(&admin_room_alias) + services().rooms.alias.resolve_local_alias(&admin_room_alias) } /// Invite the user to the grapevine admin room. @@ -1356,11 +1549,13 @@ mod test { } fn get_help_inner(input: &str) { - let error = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) - .unwrap_err() - .to_string(); + let error = + AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) + .unwrap_err() + .to_string(); - // Search for a handful of keywords that suggest the help printed properly + // Search for a handful of keywords that suggest the help printed + // properly assert!(error.contains("Usage:")); assert!(error.contains("Commands:")); assert!(error.contains("Options:")); diff --git a/src/service/appservice.rs b/src/service/appservice.rs index 665d5a31..8abc7735 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -3,7 +3,6 @@ mod data; use std::collections::BTreeMap; pub(crate) use data::Data; - use futures_util::Future; use regex::RegexSet; use ruma::{ @@ -48,6 +47,8 @@ impl NamespaceRegex { } impl TryFrom> for NamespaceRegex { + type Error = regex::Error; + fn try_from(value: Vec) -> Result { let mut exclusive = vec![]; let mut non_exclusive = vec![]; @@ -73,8 +74,6 @@ impl TryFrom> for NamespaceRegex { }, }) } - - type Error = regex::Error; } /// Appservice registration combined with its compiled regular expressions. @@ -99,6 +98,8 @@ impl RegistrationInfo { } impl TryFrom for RegistrationInfo { + type Error = regex::Error; + fn try_from(value: Registration) -> Result { Ok(RegistrationInfo { users: value.namespaces.users.clone().try_into()?, @@ -107,8 +108,6 @@ impl TryFrom for RegistrationInfo { registration: value, }) } - - type Error = regex::Error; } pub(crate) struct Service { @@ -135,8 +134,12 @@ impl Service { registration_info: RwLock::new(registration_info), }) } + /// Registers an appservice and returns the ID to the caller. - pub(crate) async fn register_appservice(&self, yaml: Registration) -> Result { + pub(crate) async fn register_appservice( + &self, + yaml: Registration, + ) -> Result { //TODO: Check for collisions between exclusive appservice namespaces self.registration_info .write() @@ -151,19 +154,27 @@ impl Service { /// # Arguments /// /// * `service_name` - the name you send to register the service previously - pub(crate) async fn unregister_appservice(&self, service_name: &str) -> Result<()> { + pub(crate) async fn unregister_appservice( + &self, + service_name: &str, + ) -> Result<()> { services() .appservice .registration_info .write() .await .remove(service_name) - .ok_or_else(|| crate::Error::AdminCommand("Appservice not found"))?; + .ok_or_else(|| { + crate::Error::AdminCommand("Appservice not found") + })?; self.db.unregister_appservice(service_name) } - pub(crate) async fn get_registration(&self, id: &str) -> Option { + pub(crate) async fn get_registration( + &self, + id: &str, + ) -> Option { self.registration_info .read() .await @@ -173,15 +184,13 @@ impl Service { } pub(crate) async fn iter_ids(&self) -> Vec { - self.registration_info - .read() - .await - .keys() - .cloned() - .collect() + self.registration_info.read().await.keys().cloned().collect() } - pub(crate) async fn find_from_token(&self, token: &str) -> Option { + pub(crate) async fn find_from_token( + &self, + token: &str, + ) -> Option { self.read() .await .values() @@ -207,8 +216,12 @@ impl Service { pub(crate) fn read( &self, - ) -> impl Future>> - { + ) -> impl Future< + Output = tokio::sync::RwLockReadGuard< + '_, + BTreeMap, + >, + > { self.registration_info.read() } } diff --git a/src/service/appservice/data.rs b/src/service/appservice/data.rs index 92da3a73..84e2bd90 100644 --- a/src/service/appservice/data.rs +++ b/src/service/appservice/data.rs @@ -15,7 +15,9 @@ pub(crate) trait Data: Send + Sync { fn get_registration(&self, id: &str) -> Result>; - fn iter_ids<'a>(&'a self) -> Result> + 'a>>; + fn iter_ids<'a>( + &'a self, + ) -> Result> + 'a>>; fn all(&self) -> Result>; } diff --git a/src/service/globals.rs b/src/service/globals.rs index a72a5e06..87ad973e 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -1,26 +1,4 @@ mod data; -pub(crate) use data::Data; -use ruma::{ - serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName, - OwnedServerSigningKeyId, OwnedUserId, -}; - -use crate::api::server_server::FedDest; - -use crate::{services, Config, Error, Result}; -use futures_util::FutureExt; -use hyper::{ - client::connect::dns::{GaiResolver, Name}, - service::Service as HyperService, -}; -use reqwest::dns::{Addrs, Resolve, Resolving}; -use ruma::{ - api::{ - client::sync::sync_events, - federation::discovery::{ServerSigningKeys, VerifyKey}, - }, - DeviceId, RoomVersionId, ServerName, UserId, -}; use std::{ collections::{BTreeMap, HashMap}, error::Error as StdError, @@ -35,11 +13,29 @@ use std::{ }, time::{Duration, Instant}, }; + +use base64::{engine::general_purpose, Engine as _}; +pub(crate) use data::Data; +use futures_util::FutureExt; +use hyper::{ + client::connect::dns::{GaiResolver, Name}, + service::Service as HyperService, +}; +use reqwest::dns::{Addrs, Resolve, Resolving}; +use ruma::{ + api::{ + client::sync::sync_events, + federation::discovery::{ServerSigningKeys, VerifyKey}, + }, + serde::Base64, + DeviceId, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName, + OwnedServerSigningKeyId, OwnedUserId, RoomVersionId, ServerName, UserId, +}; use tokio::sync::{broadcast, watch::Receiver, Mutex, RwLock, Semaphore}; use tracing::{error, info}; use trust_dns_resolver::TokioAsyncResolver; -use base64::{engine::general_purpose, Engine as _}; +use crate::{api::server_server::FedDest, services, Config, Error, Result}; type WellKnownMap = HashMap; type TlsNameMap = HashMap, u16)>; @@ -66,27 +62,40 @@ pub(crate) struct Service { default_client: reqwest::Client, pub(crate) stable_room_versions: Vec, pub(crate) unstable_room_versions: Vec, - pub(crate) bad_event_ratelimiter: Arc>>, - pub(crate) bad_signature_ratelimiter: Arc, RateLimitState>>>, - pub(crate) bad_query_ratelimiter: Arc>>, - pub(crate) servername_ratelimiter: Arc>>>, - pub(crate) sync_receivers: RwLock>, - pub(crate) roomid_mutex_insert: RwLock>>>, + pub(crate) bad_event_ratelimiter: + Arc>>, + pub(crate) bad_signature_ratelimiter: + Arc, RateLimitState>>>, + pub(crate) bad_query_ratelimiter: + Arc>>, + pub(crate) servername_ratelimiter: + Arc>>>, + pub(crate) sync_receivers: + RwLock>, + pub(crate) roomid_mutex_insert: + RwLock>>>, pub(crate) roomid_mutex_state: RwLock>>>, // this lock will be held longer - pub(crate) roomid_mutex_federation: RwLock>>>, - pub(crate) roomid_federationhandletime: RwLock>, + pub(crate) roomid_mutex_federation: + RwLock>>>, + pub(crate) roomid_federationhandletime: + RwLock>, pub(crate) stateres_mutex: Arc>, pub(crate) rotate: RotationHandler, pub(crate) shutdown: AtomicBool, } -/// Handles "rotation" of long-polling requests. "Rotation" in this context is similar to "rotation" of log files and the like. +/// Handles "rotation" of long-polling requests. "Rotation" in this context is +/// similar to "rotation" of log files and the like. /// -/// This is utilized to have sync workers return early and release read locks on the database. -pub(crate) struct RotationHandler(broadcast::Sender<()>, broadcast::Receiver<()>); +/// This is utilized to have sync workers return early and release read locks on +/// the database. +pub(crate) struct RotationHandler( + broadcast::Sender<()>, + broadcast::Receiver<()>, +); impl RotationHandler { pub(crate) fn new() -> Self { @@ -136,7 +145,10 @@ impl Resolve for Resolver { .and_then(|(override_name, port)| { override_name.first().map(|first_name| { let x: Box + Send> = - Box::new(iter::once(SocketAddr::new(*first_name, *port))); + Box::new(iter::once(SocketAddr::new( + *first_name, + *port, + ))); let x: Resolving = Box::pin(future::ready(Ok(x))); x }) @@ -144,9 +156,11 @@ impl Resolve for Resolver { .unwrap_or_else(|| { let this = &mut self.inner.clone(); Box::pin(HyperService::::call(this, name).map(|result| { - result - .map(|addrs| -> Addrs { Box::new(addrs) }) - .map_err(|err| -> Box { Box::new(err) }) + result.map(|addrs| -> Addrs { Box::new(addrs) }).map_err( + |err| -> Box { + Box::new(err) + }, + ) })) }) } @@ -167,10 +181,9 @@ impl Service { let tls_name_override = Arc::new(StdRwLock::new(TlsNameMap::new())); - let jwt_decoding_key = config - .jwt_secret - .as_ref() - .map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes())); + let jwt_decoding_key = config.jwt_secret.as_ref().map(|secret| { + jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()) + }); let default_client = reqwest_client_builder(&config)?.build()?; let federation_client = reqwest_client_builder(&config)? @@ -187,20 +200,28 @@ impl Service { RoomVersionId::V11, ]; // Experimental, partially supported room versions - let unstable_room_versions = vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5]; + let unstable_room_versions = + vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5]; let mut s = Self { db, config, keypair: Arc::new(keypair), - dns_resolver: TokioAsyncResolver::tokio_from_system_conf().map_err(|e| { - error!( - "Failed to set up trust dns resolver with system config: {}", - e - ); - Error::bad_config("Failed to set up trust dns resolver with system config.") - })?, - actual_destination_cache: Arc::new(RwLock::new(WellKnownMap::new())), + dns_resolver: TokioAsyncResolver::tokio_from_system_conf() + .map_err(|e| { + error!( + "Failed to set up trust dns resolver with system \ + config: {}", + e + ); + Error::bad_config( + "Failed to set up trust dns resolver with system \ + config.", + ) + })?, + actual_destination_cache: Arc::new( + RwLock::new(WellKnownMap::new()), + ), tls_name_override, federation_client, default_client, @@ -223,12 +244,11 @@ impl Service { fs::create_dir_all(s.get_media_folder())?; - if !s - .supported_room_versions() - .contains(&s.config.default_room_version) + if !s.supported_room_versions().contains(&s.config.default_room_version) { error!(config=?s.config.default_room_version, fallback=?crate::config::default_default_room_version(), "Room version in config isn't supported, falling back to default version"); - s.config.default_room_version = crate::config::default_default_room_version(); + s.config.default_room_version = + crate::config::default_default_room_version(); }; Ok(s) @@ -261,7 +281,11 @@ impl Service { self.db.current_count() } - pub(crate) async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { + pub(crate) async fn watch( + &self, + user_id: &UserId, + device_id: &DeviceId, + ) -> Result<()> { self.db.watch(user_id, device_id).await } @@ -313,7 +337,9 @@ impl Service { &self.dns_resolver } - pub(crate) fn jwt_decoding_key(&self) -> Option<&jsonwebtoken::DecodingKey> { + pub(crate) fn jwt_decoding_key( + &self, + ) -> Option<&jsonwebtoken::DecodingKey> { self.jwt_decoding_key.as_ref() } @@ -353,7 +379,8 @@ impl Service { /// TODO: the key valid until timestamp is only honored in room version > 4 /// Remove the outdated keys and insert the new ones. /// - /// This doesn't actually check that the keys provided are newer than the old set. + /// This doesn't actually check that the keys provided are newer than the + /// old set. pub(crate) fn add_signing_key( &self, origin: &ServerName, @@ -362,7 +389,8 @@ impl Service { self.db.add_signing_key(origin, new_keys) } - /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server. + /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found + /// for the server. pub(crate) fn signing_keys_for( &self, origin: &ServerName, diff --git a/src/service/globals/data.rs b/src/service/globals/data.rs index 9d863ca9..e416530d 100644 --- a/src/service/globals/data.rs +++ b/src/service/globals/data.rs @@ -13,7 +13,8 @@ use crate::Result; pub(crate) trait Data: Send + Sync { fn next_count(&self) -> Result; fn current_count(&self) -> Result; - async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()>; + async fn watch(&self, user_id: &UserId, device_id: &DeviceId) + -> Result<()>; fn cleanup(&self) -> Result<()>; fn memory_usage(&self) -> String; fn clear_caches(&self, amount: u32); @@ -25,7 +26,8 @@ pub(crate) trait Data: Send + Sync { new_keys: ServerSigningKeys, ) -> Result>; - /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server. + /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found + /// for the server. fn signing_keys_for( &self, origin: &ServerName, diff --git a/src/service/key_backups/data.rs b/src/service/key_backups/data.rs index c99fe1d6..0b667192 100644 --- a/src/service/key_backups/data.rs +++ b/src/service/key_backups/data.rs @@ -1,12 +1,13 @@ use std::collections::BTreeMap; -use crate::Result; use ruma::{ api::client::backup::{BackupAlgorithm, KeyBackupData, RoomKeyBackup}, serde::Raw, OwnedRoomId, RoomId, UserId, }; +use crate::Result; + pub(crate) trait Data: Send + Sync { fn create_backup( &self, @@ -23,12 +24,21 @@ pub(crate) trait Data: Send + Sync { backup_metadata: &Raw, ) -> Result; - fn get_latest_backup_version(&self, user_id: &UserId) -> Result>; + fn get_latest_backup_version( + &self, + user_id: &UserId, + ) -> Result>; - fn get_latest_backup(&self, user_id: &UserId) - -> Result)>>; + fn get_latest_backup( + &self, + user_id: &UserId, + ) -> Result)>>; - fn get_backup(&self, user_id: &UserId, version: &str) -> Result>>; + fn get_backup( + &self, + user_id: &UserId, + version: &str, + ) -> Result>>; fn add_key( &self, @@ -66,7 +76,12 @@ pub(crate) trait Data: Send + Sync { fn delete_all_keys(&self, user_id: &UserId, version: &str) -> Result<()>; - fn delete_room_keys(&self, user_id: &UserId, version: &str, room_id: &RoomId) -> Result<()>; + fn delete_room_keys( + &self, + user_id: &UserId, + version: &str, + room_id: &RoomId, + ) -> Result<()>; fn delete_room_key( &self, diff --git a/src/service/media.rs b/src/service/media.rs index 76184374..899320fe 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -2,15 +2,14 @@ mod data; use std::io::Cursor; pub(crate) use data::Data; - -use crate::{services, Result}; use image::imageops::FilterType; - use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt, BufReader}, }; +use crate::{services, Result}; + pub(crate) struct FileMeta { pub(crate) content_disposition: Option, pub(crate) content_type: Option, @@ -31,9 +30,13 @@ impl Service { file: &[u8], ) -> Result<()> { // Width, Height = 0 if it's not a thumbnail - let key = self - .db - .create_file_metadata(mxc, 0, 0, content_disposition, content_type)?; + let key = self.db.create_file_metadata( + mxc, + 0, + 0, + content_disposition, + content_type, + )?; let path = services().globals.get_media_file(&key); let mut f = File::create(path).await?; @@ -52,9 +55,13 @@ impl Service { height: u32, file: &[u8], ) -> Result<()> { - let key = - self.db - .create_file_metadata(mxc, width, height, content_disposition, content_type)?; + let key = self.db.create_file_metadata( + mxc, + width, + height, + content_disposition, + content_type, + )?; let path = services().globals.get_media_file(&key); let mut f = File::create(path).await?; @@ -84,9 +91,12 @@ impl Service { } } - /// Returns width, height of the thumbnail and whether it should be cropped. Returns None when - /// the server should send the original file. - fn thumbnail_properties(width: u32, height: u32) -> Option<(u32, u32, bool)> { + /// Returns width, height of the thumbnail and whether it should be cropped. + /// Returns None when the server should send the original file. + fn thumbnail_properties( + width: u32, + height: u32, + ) -> Option<(u32, u32, bool)> { match (width, height) { (0..=32, 0..=32) => Some((32, 32, true)), (0..=96, 0..=96) => Some((96, 96, true)), @@ -102,11 +112,15 @@ impl Service { /// Here's an example on how it works: /// /// - Client requests an image with width=567, height=567 - /// - Server rounds that up to (800, 600), so it doesn't have to save too many thumbnails - /// - Server rounds that up again to (958, 600) to fix the aspect ratio (only for width,height>96) + /// - Server rounds that up to (800, 600), so it doesn't have to save too + /// many thumbnails + /// - Server rounds that up again to (958, 600) to fix the aspect ratio + /// (only for width,height>96) /// - Server creates the thumbnail and sends it to the user /// - /// For width,height <= 96 the server uses another thumbnailing algorithm which crops the image afterwards. + /// For width,height <= 96 the server uses another thumbnailing algorithm + /// which crops the image afterwards. + #[allow(clippy::too_many_lines)] pub(crate) async fn get_thumbnail( &self, mxc: String, @@ -154,7 +168,8 @@ impl Service { } else { let (exact_width, exact_height) = { // Copied from image::dynimage::resize_dimensions - let use_width = (u64::from(width) * u64::from(original_height)) + let use_width = (u64::from(width) + * u64::from(original_height)) <= (u64::from(original_width) * u64::from(height)); let intermediate = if use_width { u64::from(original_height) * u64::from(width) @@ -165,21 +180,31 @@ impl Service { }; if use_width { if intermediate <= u64::from(::std::u32::MAX) { - (width, intermediate.try_into().unwrap_or(u32::MAX)) + ( + width, + intermediate.try_into().unwrap_or(u32::MAX), + ) } else { ( - (u64::from(width) * u64::from(::std::u32::MAX) / intermediate) + (u64::from(width) + * u64::from(::std::u32::MAX) + / intermediate) .try_into() .unwrap_or(u32::MAX), ::std::u32::MAX, ) } } else if intermediate <= u64::from(::std::u32::MAX) { - (intermediate.try_into().unwrap_or(u32::MAX), height) + ( + intermediate.try_into().unwrap_or(u32::MAX), + height, + ) } else { ( ::std::u32::MAX, - (u64::from(height) * u64::from(::std::u32::MAX) / intermediate) + (u64::from(height) + * u64::from(::std::u32::MAX) + / intermediate) .try_into() .unwrap_or(u32::MAX), ) @@ -195,7 +220,8 @@ impl Service { image::ImageOutputFormat::Png, )?; - // Save thumbnail in database so we don't have to generate it again next time + // Save thumbnail in database so we don't have to generate it + // again next time let thumbnail_key = self.db.create_file_metadata( mxc, width, diff --git a/src/service/pdu.rs b/src/service/pdu.rs index 1de3d852..a5296ceb 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -1,24 +1,31 @@ -use crate::Error; +use std::{cmp::Ordering, collections::BTreeMap, sync::Arc}; + use ruma::{ canonical_json::redact_content_in_place, events::{ - room::{member::RoomMemberEventContent, redaction::RoomRedactionEventContent}, + room::{ + member::RoomMemberEventContent, + redaction::RoomRedactionEventContent, + }, space::child::HierarchySpaceChildEvent, - AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, - AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, TimelineEventType, + AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyStateEvent, + AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent, + AnyTimelineEvent, StateEvent, TimelineEventType, }, serde::Raw, - state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, - OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, RoomVersionId, UInt, UserId, + state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, + MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, + RoomVersionId, UInt, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::{ json, value::{to_raw_value, RawValue as RawJsonValue}, }; -use std::{cmp::Ordering, collections::BTreeMap, sync::Arc}; use tracing::warn; +use crate::Error; + /// Content hashes of a PDU. #[derive(Clone, Debug, Deserialize, Serialize)] pub(crate) struct EventHash { @@ -61,10 +68,18 @@ impl PduEvent { ) -> crate::Result<()> { self.unsigned = None; - let mut content = serde_json::from_str(self.content.get()) - .map_err(|_| Error::bad_database("PDU in db has invalid content."))?; - redact_content_in_place(&mut content, &room_version_id, self.kind.to_string()) - .map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?; + let mut content = + serde_json::from_str(self.content.get()).map_err(|_| { + Error::bad_database("PDU in db has invalid content.") + })?; + redact_content_in_place( + &mut content, + &room_version_id, + self.kind.to_string(), + ) + .map_err(|e| { + Error::Redaction(self.sender.server_name().to_owned(), e) + })?; self.unsigned = Some(to_raw_value(&json!({ "redacted_because": serde_json::to_value(reason).expect("to_value(PduEvent) always works") @@ -78,10 +93,12 @@ impl PduEvent { pub(crate) fn remove_transaction_id(&mut self) -> crate::Result<()> { if let Some(unsigned) = &self.unsigned { let mut unsigned: BTreeMap> = - serde_json::from_str(unsigned.get()) - .map_err(|_| Error::bad_database("Invalid unsigned in pdu event"))?; + serde_json::from_str(unsigned.get()).map_err(|_| { + Error::bad_database("Invalid unsigned in pdu event") + })?; unsigned.remove("transaction_id"); - self.unsigned = Some(to_raw_value(&unsigned).expect("unsigned is valid")); + self.unsigned = + Some(to_raw_value(&unsigned).expect("unsigned is valid")); } Ok(()) @@ -91,31 +108,45 @@ impl PduEvent { let mut unsigned: BTreeMap> = self .unsigned .as_ref() - .map_or_else(|| Ok(BTreeMap::new()), |u| serde_json::from_str(u.get())) - .map_err(|_| Error::bad_database("Invalid unsigned in pdu event"))?; + .map_or_else( + || Ok(BTreeMap::new()), + |u| serde_json::from_str(u.get()), + ) + .map_err(|_| { + Error::bad_database("Invalid unsigned in pdu event") + })?; unsigned.insert("age".to_owned(), to_raw_value(&1).unwrap()); - self.unsigned = Some(to_raw_value(&unsigned).expect("unsigned is valid")); + self.unsigned = + Some(to_raw_value(&unsigned).expect("unsigned is valid")); Ok(()) } - /// Copies the `redacts` property of the event to the `content` dict and vice-versa. + /// Copies the `redacts` property of the event to the `content` dict and + /// vice-versa. /// /// This follows the specification's /// [recommendation](https://spec.matrix.org/v1.10/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property): /// - /// > For backwards-compatibility with older clients, servers should add a redacts - /// > property to the top level of m.room.redaction events in when serving such events + /// > For backwards-compatibility with older clients, servers should add a + /// > redacts + /// > property to the top level of m.room.redaction events in when serving + /// > such events /// > over the Client-Server API. /// > - /// > For improved compatibility with newer clients, servers should add a redacts property - /// > to the content of m.room.redaction events in older room versions when serving + /// > For improved compatibility with newer clients, servers should add a + /// > redacts property + /// > to the content of m.room.redaction events in older room versions when + /// > serving /// > such events over the Client-Server API. - pub(crate) fn copy_redacts(&self) -> (Option>, Box) { + pub(crate) fn copy_redacts( + &self, + ) -> (Option>, Box) { if self.kind == TimelineEventType::RoomRedaction { - if let Ok(mut content) = - serde_json::from_str::(self.content.get()) + if let Ok(mut content) = serde_json::from_str::< + RoomRedactionEventContent, + >(self.content.get()) { if let Some(redacts) = content.redacts { return (Some(redacts.into()), self.content.clone()); @@ -123,7 +154,9 @@ impl PduEvent { content.redacts = Some(redacts.into()); return ( self.redacts.clone(), - to_raw_value(&content).expect("Must be valid, we only added redacts field"), + to_raw_value(&content).expect( + "Must be valid, we only added redacts field", + ), ); } } @@ -281,7 +314,9 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub(crate) fn to_stripped_spacechild_state_event(&self) -> Raw { + pub(crate) fn to_stripped_spacechild_state_event( + &self, + ) -> Raw { let json = json!({ "content": self.content, "type": self.kind, @@ -294,7 +329,9 @@ impl PduEvent { } #[tracing::instrument(skip(self))] - pub(crate) fn to_member_event(&self) -> Raw> { + pub(crate) fn to_member_event( + &self, + ) -> Raw> { let mut json = json!({ "content": self.content, "type": self.kind, @@ -318,16 +355,16 @@ impl PduEvent { pub(crate) fn convert_to_outgoing_federation_event( mut pdu_json: CanonicalJsonObject, ) -> Box { - if let Some(unsigned) = pdu_json - .get_mut("unsigned") - .and_then(|val| val.as_object_mut()) + if let Some(unsigned) = + pdu_json.get_mut("unsigned").and_then(|val| val.as_object_mut()) { unsigned.remove("transaction_id"); } pdu_json.remove("event_id"); - to_raw_value(&pdu_json).expect("CanonicalJson is valid serde_json::Value") + to_raw_value(&pdu_json) + .expect("CanonicalJson is valid serde_json::Value") } pub(crate) fn from_id_val( @@ -374,11 +411,15 @@ impl state_res::Event for PduEvent { self.state_key.as_deref() } - fn prev_events(&self) -> Box + '_> { + fn prev_events( + &self, + ) -> Box + '_> { Box::new(self.prev_events.iter()) } - fn auth_events(&self) -> Box + '_> { + fn auth_events( + &self, + ) -> Box + '_> { Box::new(self.auth_events.iter()) } @@ -408,15 +449,17 @@ impl Ord for PduEvent { /// Generates a correct eventId for the incoming pdu. /// -/// Returns a tuple of the new `EventId` and the PDU as a `BTreeMap`. +/// Returns a tuple of the new `EventId` and the PDU as a `BTreeMap`. pub(crate) fn gen_event_id_canonical_json( pdu: &RawJsonValue, room_version_id: &RoomVersionId, ) -> crate::Result<(OwnedEventId, CanonicalJsonObject)> { - let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { - warn!("Error parsing incoming event {:?}: {:?}", pdu, e); - Error::BadServerResponse("Invalid PDU in server response") - })?; + let value: CanonicalJsonObject = + serde_json::from_str(pdu.get()).map_err(|e| { + warn!("Error parsing incoming event {:?}: {:?}", pdu, e); + Error::BadServerResponse("Invalid PDU in server response") + })?; let event_id = format!( "${}", diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 90a3b90d..903f1a73 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -1,27 +1,34 @@ mod data; -pub(crate) use data::Data; -use ruma::{events::AnySyncTimelineEvent, push::PushConditionPowerLevelsCtx}; +use std::{fmt::Debug, mem}; -use crate::{services, Error, PduEvent, Result}; use bytes::BytesMut; +pub(crate) use data::Data; use ruma::{ api::{ client::push::{set_pusher, Pusher, PusherKind}, push_gateway::send_event_notification::{ self, - v1::{Device, Notification, NotificationCounts, NotificationPriority}, + v1::{ + Device, Notification, NotificationCounts, NotificationPriority, + }, }, IncomingResponse, MatrixVersion, OutgoingRequest, SendAccessToken, }, - events::{room::power_levels::RoomPowerLevelsEventContent, StateEventType, TimelineEventType}, - push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak}, + events::{ + room::power_levels::RoomPowerLevelsEventContent, AnySyncTimelineEvent, + StateEventType, TimelineEventType, + }, + push::{ + Action, PushConditionPowerLevelsCtx, PushConditionRoomCtx, PushFormat, + Ruleset, Tweak, + }, serde::Raw, uint, RoomId, UInt, UserId, }; - -use std::{fmt::Debug, mem}; use tracing::{info, warn}; +use crate::{services, Error, PduEvent, Result}; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } @@ -35,7 +42,11 @@ impl Service { self.db.set_pusher(sender, pusher) } - pub(crate) fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result> { + pub(crate) fn get_pusher( + &self, + sender: &UserId, + pushkey: &str, + ) -> Result> { self.db.get_pusher(sender, pushkey) } @@ -43,7 +54,10 @@ impl Service { self.db.get_pushers(sender) } - pub(crate) fn get_pushkeys(&self, sender: &UserId) -> Box>> { + pub(crate) fn get_pushkeys( + &self, + sender: &UserId, + ) -> Box>> { self.db.get_pushkeys(sender) } @@ -73,11 +87,8 @@ impl Service { let reqwest_request = reqwest::Request::try_from(http_request)?; let url = reqwest_request.url().clone(); - let response = services() - .globals - .default_client() - .execute(reqwest_request) - .await; + let response = + services().globals.default_client().execute(reqwest_request).await; match response { Ok(mut response) => { @@ -119,11 +130,16 @@ impl Service { "Push gateway returned invalid response bytes {}\n{}", destination, url ); - Error::BadServerResponse("Push gateway returned bad response.") + Error::BadServerResponse( + "Push gateway returned bad response.", + ) }) } Err(e) => { - warn!("Could not send request to pusher {}: {}", destination, e); + warn!( + "Could not send request to pusher {}: {}", + destination, e + ); Err(e.into()) } } @@ -146,8 +162,9 @@ impl Service { .state_accessor .room_state_get(&pdu.room_id, &StateEventType::RoomPowerLevels, "")? .map(|ev| { - serde_json::from_str(ev.content.get()) - .map_err(|_| Error::bad_database("invalid m.room.power_levels event")) + serde_json::from_str(ev.content.get()).map_err(|_| { + Error::bad_database("invalid m.room.power_levels event") + }) }) .transpose()? .unwrap_or_default(); @@ -228,11 +245,16 @@ impl Service { PusherKind::Http(http) => { // TODO: // Two problems with this - // 1. if "event_id_only" is the only format kind it seems we should never add more info + // 1. if "event_id_only" is the only format kind it seems we + // should never add more info // 2. can pusher/devices have conflicting formats - let event_id_only = http.format == Some(PushFormat::EventIdOnly); + let event_id_only = + http.format == Some(PushFormat::EventIdOnly); - let mut device = Device::new(pusher.ids.app_id.clone(), pusher.ids.pushkey.clone()); + let mut device = Device::new( + pusher.ids.app_id.clone(), + pusher.ids.pushkey.clone(), + ); device.data.default_payload = http.default_payload.clone(); device.data.format = http.format.clone(); @@ -251,32 +273,43 @@ impl Service { notifi.counts = NotificationCounts::new(unread, uint!(0)); if event.kind == TimelineEventType::RoomEncrypted - || tweaks - .iter() - .any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_))) + || tweaks.iter().any(|t| { + matches!(t, Tweak::Highlight(true) | Tweak::Sound(_)) + }) { notifi.prio = NotificationPriority::High; } if event_id_only { - self.send_request(&http.url, send_event_notification::v1::Request::new(notifi)) - .await?; + self.send_request( + &http.url, + send_event_notification::v1::Request::new(notifi), + ) + .await?; } else { notifi.sender = Some(event.sender.clone()); notifi.event_type = Some(event.kind.clone()); - notifi.content = serde_json::value::to_raw_value(&event.content).ok(); + notifi.content = + serde_json::value::to_raw_value(&event.content).ok(); if event.kind == TimelineEventType::RoomMember { - notifi.user_is_target = - event.state_key.as_deref() == Some(event.sender.as_str()); + notifi.user_is_target = event.state_key.as_deref() + == Some(event.sender.as_str()); } - notifi.sender_display_name = services().users.displayname(&event.sender)?; + notifi.sender_display_name = + services().users.displayname(&event.sender)?; - notifi.room_name = services().rooms.state_accessor.get_name(&event.room_id)?; + 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?; + self.send_request( + &http.url, + send_event_notification::v1::Request::new(notifi), + ) + .await?; } Ok(()) diff --git a/src/service/pusher/data.rs b/src/service/pusher/data.rs index b13fcc3b..9fcba933 100644 --- a/src/service/pusher/data.rs +++ b/src/service/pusher/data.rs @@ -1,16 +1,27 @@ -use crate::Result; use ruma::{ api::client::push::{set_pusher, Pusher}, UserId, }; -pub(crate) trait Data: Send + Sync { - fn set_pusher(&self, sender: &UserId, pusher: set_pusher::v3::PusherAction) -> Result<()>; +use crate::Result; - fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result>; +pub(crate) trait Data: Send + Sync { + fn set_pusher( + &self, + sender: &UserId, + pusher: set_pusher::v3::PusherAction, + ) -> Result<()>; + + fn get_pusher( + &self, + sender: &UserId, + pushkey: &str, + ) -> Result>; fn get_pushers(&self, sender: &UserId) -> Result>; - fn get_pushkeys<'a>(&'a self, sender: &UserId) - -> Box> + 'a>; + fn get_pushkeys<'a>( + &'a self, + sender: &UserId, + ) -> Box> + 'a>; } diff --git a/src/service/rooms/alias/data.rs b/src/service/rooms/alias/data.rs index f0f442d0..0e0e6f7c 100644 --- a/src/service/rooms/alias/data.rs +++ b/src/service/rooms/alias/data.rs @@ -1,6 +1,7 @@ -use crate::Result; use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { /// Creates or updates the alias to the given room id. fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()>; @@ -9,7 +10,10 @@ pub(crate) trait Data: Send + Sync { fn remove_alias(&self, alias: &RoomAliasId) -> Result<()>; /// Looks up the roomid for the given alias. - fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result>; + fn resolve_local_alias( + &self, + alias: &RoomAliasId, + ) -> Result>; /// Returns all local aliases that point to the given room fn local_aliases_for_room<'a>( diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 40f49713..2a601830 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -43,7 +43,8 @@ impl Service { let mut i = 0; for id in starting_events { - let short = services().rooms.short.get_or_create_shorteventid(&id)?; + let short = + services().rooms.short.get_or_create_shorteventid(&id)?; // I'm afraid to change this in case there is accidental reliance on // the truncation #[allow(clippy::as_conversions, clippy::cast_possible_truncation)] @@ -64,7 +65,8 @@ impl Service { continue; } - let chunk_key: Vec = chunk.iter().map(|(short, _)| short).copied().collect(); + let chunk_key: Vec = + chunk.iter().map(|(short, _)| short).copied().collect(); if let Some(cached) = services() .rooms .auth_chain @@ -90,11 +92,13 @@ impl Service { chunk_cache.extend(cached.iter().copied()); } else { misses2 += 1; - let auth_chain = Arc::new(self.get_auth_chain_inner(room_id, &event_id)?); - services() - .rooms - .auth_chain - .cache_auth_chain(vec![sevent_id], Arc::clone(&auth_chain))?; + let auth_chain = Arc::new( + self.get_auth_chain_inner(room_id, &event_id)?, + ); + services().rooms.auth_chain.cache_auth_chain( + vec![sevent_id], + Arc::clone(&auth_chain), + )?; debug!( event_id = ?event_id, chain_length = ?auth_chain.len(), @@ -129,13 +133,17 @@ impl Service { "Auth chain stats", ); - Ok(full_auth_chain - .into_iter() - .filter_map(move |sid| services().rooms.short.get_eventid_from_short(sid).ok())) + Ok(full_auth_chain.into_iter().filter_map(move |sid| { + services().rooms.short.get_eventid_from_short(sid).ok() + })) } #[tracing::instrument(skip(self, event_id))] - fn get_auth_chain_inner(&self, room_id: &RoomId, event_id: &EventId) -> Result> { + fn get_auth_chain_inner( + &self, + room_id: &RoomId, + event_id: &EventId, + ) -> Result> { let mut todo = vec![Arc::from(event_id)]; let mut found = HashSet::new(); @@ -143,7 +151,10 @@ impl Service { match services().rooms.timeline.get_pdu(&event_id) { Ok(Some(pdu)) => { if pdu.room_id != room_id { - return Err(Error::BadRequest(ErrorKind::Forbidden, "Evil event in db")); + return Err(Error::BadRequest( + ErrorKind::Forbidden, + "Evil event in db", + )); } for auth_event in &pdu.auth_events { let sauthevent = services() @@ -158,10 +169,17 @@ impl Service { } } Ok(None) => { - warn!(?event_id, "Could not find pdu mentioned in auth events"); + warn!( + ?event_id, + "Could not find pdu mentioned in auth events" + ); } Err(error) => { - error!(?event_id, ?error, "Could not load event in auth chain"); + error!( + ?event_id, + ?error, + "Could not load event in auth chain" + ); } } } diff --git a/src/service/rooms/auth_chain/data.rs b/src/service/rooms/auth_chain/data.rs index 7a865368..5ecaee34 100644 --- a/src/service/rooms/auth_chain/data.rs +++ b/src/service/rooms/auth_chain/data.rs @@ -1,11 +1,15 @@ -use crate::Result; use std::{collections::HashSet, sync::Arc}; +use crate::Result; + pub(crate) trait Data: Send + Sync { fn get_cached_eventid_authchain( &self, shorteventid: &[u64], ) -> Result>>>; - fn cache_auth_chain(&self, shorteventid: Vec, auth_chain: Arc>) - -> Result<()>; + fn cache_auth_chain( + &self, + shorteventid: Vec, + auth_chain: Arc>, + ) -> Result<()>; } diff --git a/src/service/rooms/directory/data.rs b/src/service/rooms/directory/data.rs index 9aefaf6c..f0bc3bf5 100644 --- a/src/service/rooms/directory/data.rs +++ b/src/service/rooms/directory/data.rs @@ -1,6 +1,7 @@ -use crate::Result; use ruma::{OwnedRoomId, RoomId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { /// Adds the room to the public room directory fn set_public(&self, room_id: &RoomId) -> Result<()>; @@ -12,5 +13,7 @@ pub(crate) trait Data: Send + Sync { fn is_public_room(&self, room_id: &RoomId) -> Result; /// Returns the unsorted public room directory - fn public_rooms<'a>(&'a self) -> Box> + 'a>; + fn public_rooms<'a>( + &'a self, + ) -> Box> + 'a>; } diff --git a/src/service/rooms/edus/read_receipt/data.rs b/src/service/rooms/edus/read_receipt/data.rs index 366e946c..9e468400 100644 --- a/src/service/rooms/edus/read_receipt/data.rs +++ b/src/service/rooms/edus/read_receipt/data.rs @@ -1,5 +1,8 @@ +use ruma::{ + events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId, +}; + use crate::Result; -use ruma::{events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId}; pub(crate) trait Data: Send + Sync { /// Replaces the previous read receipt. @@ -10,7 +13,8 @@ pub(crate) trait Data: Send + Sync { event: ReceiptEvent, ) -> Result<()>; - /// Returns an iterator over the most recent read receipts in a room that happened after the event with id `since`. + /// Returns an iterator over the most recent read receipts in a room that + /// happened after the event with id `since`. #[allow(clippy::type_complexity)] fn readreceipts_since<'a>( &'a self, @@ -27,11 +31,24 @@ pub(crate) trait Data: Send + Sync { >; /// Sets a private read marker at `count`. - fn private_read_set(&self, room_id: &RoomId, user_id: &UserId, count: u64) -> Result<()>; + fn private_read_set( + &self, + room_id: &RoomId, + user_id: &UserId, + count: u64, + ) -> Result<()>; /// Returns the private read marker. - fn private_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result>; + fn private_read_get( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result>; /// Returns the count of the last typing update in this room. - fn last_privateread_update(&self, user_id: &UserId, room_id: &RoomId) -> Result; + fn last_privateread_update( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result; } diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index b3838d8a..52761d69 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -1,8 +1,9 @@ +use std::collections::BTreeMap; + use ruma::{ events::{typing::TypingEventContent, SyncEphemeralRoomEvent}, OwnedRoomId, OwnedUserId, RoomId, UserId, }; -use std::collections::BTreeMap; use tokio::sync::{broadcast, RwLock}; use tracing::trace; @@ -10,15 +11,16 @@ use crate::{services, utils, Result}; pub(crate) struct Service { // u64 is unix timestamp of timeout - pub(crate) typing: RwLock>>, + pub(crate) typing: + RwLock>>, // timestamp of the last change to typing users pub(crate) last_typing_update: RwLock>, pub(crate) typing_update_sender: broadcast::Sender, } impl Service { - /// Sets a user as typing until the timeout timestamp is reached or `roomtyping_remove` is - /// called. + /// Sets a user as typing until the timeout timestamp is reached or + /// `roomtyping_remove` is called. pub(crate) async fn typing_add( &self, user_id: &UserId, @@ -36,13 +38,20 @@ impl Service { .await .insert(room_id.to_owned(), services().globals.next_count()?); if self.typing_update_sender.send(room_id.to_owned()).is_err() { - trace!("receiver found what it was looking for and is no longer interested"); + trace!( + "receiver found what it was looking for and is no longer \ + interested" + ); } Ok(()) } /// Removes a user from typing before the timeout is reached. - pub(crate) async fn typing_remove(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> { + pub(crate) async fn typing_remove( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result<()> { self.typing .write() .await @@ -54,7 +63,10 @@ impl Service { .await .insert(room_id.to_owned(), services().globals.next_count()?); if self.typing_update_sender.send(room_id.to_owned()).is_err() { - trace!("receiver found what it was looking for and is no longer interested"); + trace!( + "receiver found what it was looking for and is no longer \ + interested" + ); } Ok(()) } @@ -97,14 +109,20 @@ impl Service { .await .insert(room_id.to_owned(), services().globals.next_count()?); if self.typing_update_sender.send(room_id.to_owned()).is_err() { - trace!("receiver found what it was looking for and is no longer interested"); + trace!( + "receiver found what it was looking for and is no longer \ + interested" + ); } } Ok(()) } /// Returns the count of the last typing update in this room. - pub(crate) async fn last_typing_update(&self, room_id: &RoomId) -> Result { + pub(crate) async fn last_typing_update( + &self, + room_id: &RoomId, + ) -> Result { self.typings_maintain(room_id).await?; Ok(self .last_typing_update diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 8ff88088..48c40be1 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -24,7 +24,8 @@ use ruma::{ }, events::{ room::{ - create::RoomCreateEventContent, redaction::RoomRedactionEventContent, + create::RoomCreateEventContent, + redaction::RoomRedactionEventContent, server_acl::RoomServerAclEventContent, }, StateEventType, TimelineEventType, @@ -32,16 +33,16 @@ use ruma::{ int, serde::Base64, state_res::{self, RoomVersion, StateMap}, - uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, - OwnedServerName, OwnedServerSigningKeyId, RoomId, RoomVersionId, ServerName, + uint, CanonicalJsonObject, CanonicalJsonValue, EventId, + MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedServerSigningKeyId, + RoomId, RoomVersionId, ServerName, }; use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; -use crate::{service::pdu, services, Error, PduEvent, Result}; - use super::state_compressor::CompressedStateEvent; +use crate::{service::pdu, services, Error, PduEvent, Result}; pub(crate) struct Service; @@ -52,24 +53,29 @@ impl Service { /// 1.1. Remove unsigned field /// 2. Check signatures, otherwise drop /// 3. Check content hash, redact if doesn't match - /// 4. Fetch any missing auth events doing all checks listed here starting at 1. These are not - /// timeline events - /// 5. Reject "due to auth events" if can't get all the auth events or some of the auth events are - /// also rejected "due to auth events" - /// 6. Reject "due to auth events" if the event doesn't pass auth based on the auth events + /// 4. Fetch any missing auth events doing all checks listed here starting + /// at 1. These are not timeline events + /// 5. Reject "due to auth events" if can't get all the auth events or some + /// of the auth events are also rejected "due to auth events" + /// 6. Reject "due to auth events" if the event doesn't pass auth based on + /// the auth events /// 7. Persist this event as an outlier /// 8. If not timeline event: stop - /// 9. Fetch any missing prev events doing all checks listed here starting at 1. These are timeline - /// events - /// 10. Fetch missing state and auth chain events by calling /state_ids at backwards extremities - /// doing all the checks in this list starting at 1. These are not timeline events + /// 9. Fetch any missing prev events doing all checks listed here starting + /// at 1. These are timeline events + /// 10. Fetch missing state and auth chain events by calling /state_ids at + /// backwards extremities doing all the checks in this list starting at + /// 1. These are not timeline events /// 11. Check the auth of the event passes based on the state of the event - /// 12. Ensure that the state is derived from the previous current state (i.e. we calculated by - /// doing state res where one of the inputs was a previously trusted set of state, don't just - /// trust a set of state we got from a remote) + /// 12. Ensure that the state is derived from the previous current state + /// (i.e. we calculated by doing state res where one of the inputs was a + /// previously trusted set of state, don't just trust a set of state we + /// got from a remote) /// 13. Use state resolution to find new room state - /// 14. Check if the event passes auth based on the "current state" of the room, if not soft fail it - // We use some AsyncRecursiveType hacks here so we can call this async funtion recursively + /// 14. Check if the event passes auth based on the "current state" of the + /// room, if not soft fail it + // We use some AsyncRecursiveType hacks here so we can call this async + // funtion recursively #[tracing::instrument(skip(self, value, is_timeline_event, pub_key_map))] pub(crate) async fn handle_incoming_pdu<'a>( &self, @@ -106,7 +112,9 @@ impl Service { .rooms .state_accessor .room_state_get(room_id, &StateEventType::RoomCreate, "")? - .ok_or_else(|| Error::bad_database("Failed to find create event in db."))?; + .ok_or_else(|| { + Error::bad_database("Failed to find create event in db.") + })?; let create_event_content: RoomCreateEventContent = serde_json::from_str(create_event.content.get()).map_err(|e| { @@ -115,11 +123,10 @@ impl Service { })?; let room_version_id = &create_event_content.room_version; - let first_pdu_in_room = services() - .rooms - .timeline - .first_pdu_in_room(room_id)? - .ok_or_else(|| Error::bad_database("Failed to find first pdu in db."))?; + let first_pdu_in_room = + services().rooms.timeline.first_pdu_in_room(room_id)?.ok_or_else( + || Error::bad_database("Failed to find first pdu in db."), + )?; let (incoming_pdu, val) = self .handle_outlier_pdu( @@ -144,7 +151,8 @@ impl Service { return Ok(None); } - // 9. Fetch any missing prev events doing all checks listed here starting at 1. These are timeline events + // 9. Fetch any missing prev events doing all checks listed here + // starting at 1. These are timeline events let (sorted_prev_events, mut eventid_info) = self .fetch_unknown_prev_events( origin, @@ -163,7 +171,8 @@ impl Service { if services().rooms.metadata.is_disabled(room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, - "Federation of this room is currently disabled on this server.", + "Federation of this room is currently disabled on this \ + server.", )); } @@ -175,7 +184,8 @@ impl Service { .get(&*prev_id) { // Exponential backoff - let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries); + let mut min_elapsed_duration = + Duration::from_secs(5 * 60) * (*tries) * (*tries); if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { min_elapsed_duration = Duration::from_secs(60 * 60 * 24); } @@ -217,7 +227,10 @@ impl Service { .roomid_federationhandletime .write() .await - .insert(room_id.to_owned(), ((*prev_id).to_owned(), start_time)); + .insert( + room_id.to_owned(), + ((*prev_id).to_owned(), start_time), + ); if let Err(e) = self .upgrade_outlier_to_timeline_pdu( @@ -305,32 +318,40 @@ impl Service { mut value: BTreeMap, auth_events_known: bool, pub_key_map: &'a RwLock>>, - ) -> AsyncRecursiveType<'a, Result<(Arc, BTreeMap)>> { + ) -> AsyncRecursiveType< + 'a, + Result<(Arc, BTreeMap)>, + > { Box::pin(async move { // 1.1. Remove unsigned field value.remove("unsigned"); // TODO: For RoomVersion6 we must check that Raw<..> is canonical do we anywhere?: https://matrix.org/docs/spec/rooms/v6#canonical-json - // We go through all the signatures we see on the value and fetch the corresponding signing - // keys - self.fetch_required_signing_keys(&value, pub_key_map) - .await?; + // We go through all the signatures we see on the value and fetch + // the corresponding signing keys + self.fetch_required_signing_keys(&value, pub_key_map).await?; // 2. Check signatures, otherwise drop // 3. check content hash, redact if doesn't match let create_event_content: RoomCreateEventContent = - serde_json::from_str(create_event.content.get()).map_err(|e| { - error!("Invalid create event: {}", e); - Error::BadDatabase("Invalid create event in db") - })?; + serde_json::from_str(create_event.content.get()).map_err( + |e| { + error!("Invalid create event: {}", e); + Error::BadDatabase("Invalid create event in db") + }, + )?; let room_version_id = &create_event_content.room_version; - let room_version = - RoomVersion::new(room_version_id).expect("room version is supported"); + let room_version = RoomVersion::new(room_version_id) + .expect("room version is supported"); let guard = pub_key_map.read().await; - let mut val = match ruma::signatures::verify_event(&guard, &value, room_version_id) { + let mut val = match ruma::signatures::verify_event( + &guard, + &value, + room_version_id, + ) { Err(e) => { // Drop warn!("Dropping bad event {}: {}", event_id, e,); @@ -342,15 +363,25 @@ impl Service { Ok(ruma::signatures::Verified::Signatures) => { // Redact warn!("Calculated hash does not match: {}", event_id); - let Ok(obj) = ruma::canonical_json::redact(value, room_version_id, None) else { + let Ok(obj) = ruma::canonical_json::redact( + value, + room_version_id, + None, + ) else { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Redaction failed", )); }; - // Skip the PDU if it is redacted and we already have it as an outlier event - if services().rooms.timeline.get_pdu_json(event_id)?.is_some() { + // Skip the PDU if it is redacted and we already have it as + // an outlier event + if services() + .rooms + .timeline + .get_pdu_json(event_id)? + .is_some() + { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Event was redacted and we already knew about it", @@ -364,23 +395,28 @@ impl Service { drop(guard); - // Now that we have checked the signature and hashes we can add the eventID and convert - // to our PduEvent type + // Now that we have checked the signature and hashes we can add the + // eventID and convert to our PduEvent type val.insert( "event_id".to_owned(), CanonicalJsonValue::String(event_id.as_str().to_owned()), ); let incoming_pdu = serde_json::from_value::( - serde_json::to_value(&val).expect("CanonicalJsonObj is a valid JsonValue"), + serde_json::to_value(&val) + .expect("CanonicalJsonObj is a valid JsonValue"), ) .map_err(|_| Error::bad_database("Event is not a valid PDU."))?; Self::check_room_id(room_id, &incoming_pdu)?; if !auth_events_known { - // 4. fetch any missing auth events doing all checks listed here starting at 1. These are not timeline events - // 5. Reject "due to auth events" if can't get all the auth events or some of the auth events are also rejected "due to auth events" - // NOTE: Step 5 is not applied anymore because it failed too often + // 4. fetch any missing auth events doing all checks listed here + // starting at 1. These are not timeline events + // 5. Reject "due to auth events" if can't get all the auth + // events or some of the auth events are also rejected "due + // to auth events" + // NOTE: Step 5 is not applied anymore because it failed too + // often debug!(event_id = ?incoming_pdu.event_id, "Fetching auth events"); self.fetch_and_handle_outliers( origin, @@ -397,7 +433,8 @@ impl Service { .await; } - // 6. Reject "due to auth events" if the event doesn't pass auth based on the auth events + // 6. Reject "due to auth events" if the event doesn't pass auth + // based on the auth events debug!( "Auth check for {} based on auth events", incoming_pdu.event_id @@ -406,7 +443,8 @@ impl Service { // Build map of auth events let mut auth_events = HashMap::new(); for id in &incoming_pdu.auth_events { - let Some(auth_event) = services().rooms.timeline.get_pdu(id)? else { + let Some(auth_event) = services().rooms.timeline.get_pdu(id)? + else { warn!("Could not find auth event {}", id); continue; }; @@ -426,7 +464,8 @@ impl Service { hash_map::Entry::Occupied(_) => { return Err(Error::BadRequest( ErrorKind::InvalidParam, - "Auth event's type and state_key combination exists multiple times.", + "Auth event's type and state_key combination \ + exists multiple times.", )); } } @@ -450,8 +489,9 @@ impl Service { None::, |k, s| auth_events.get(&(k.to_string().into(), s.to_owned())), ) - .map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed"))? - { + .map_err(|_e| { + Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed") + })? { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Auth check failed", @@ -472,7 +512,13 @@ impl Service { }) } - #[tracing::instrument(skip(self, incoming_pdu, val, create_event, pub_key_map))] + #[tracing::instrument(skip( + self, + incoming_pdu, + val, + create_event, + pub_key_map + ))] pub(crate) async fn upgrade_outlier_to_timeline_pdu( &self, incoming_pdu: Arc, @@ -483,7 +529,9 @@ impl Service { pub_key_map: &RwLock>>, ) -> Result>> { // Skip the PDU if we already have it as a timeline event - if let Ok(Some(pduid)) = services().rooms.timeline.get_pdu_id(&incoming_pdu.event_id) { + if let Ok(Some(pduid)) = + services().rooms.timeline.get_pdu_id(&incoming_pdu.event_id) + { return Ok(Some(pduid)); } @@ -507,13 +555,16 @@ impl Service { })?; let room_version_id = &create_event_content.room_version; - let room_version = RoomVersion::new(room_version_id).expect("room version is supported"); + let room_version = RoomVersion::new(room_version_id) + .expect("room version is supported"); - // 10. Fetch missing state and auth chain events by calling /state_ids at backwards extremities - // doing all the checks in this list starting at 1. These are not timeline events. + // 10. Fetch missing state and auth chain events by calling /state_ids + // at backwards extremities doing all the checks in this list + // starting at 1. These are not timeline events. - // TODO: if we know the prev_events of the incoming event we can avoid the request and build - // the state from a known point and resolve if > 1 prev_event + // TODO: if we know the prev_events of the incoming event we can avoid + // the request and build the state from a known point and + // resolve if > 1 prev_event debug!("Requesting state at event"); let mut state_at_incoming_event = None; @@ -546,14 +597,17 @@ impl Service { .ok() .flatten() .ok_or_else(|| { - Error::bad_database("Could not find prev event, but we know the state.") + Error::bad_database( + "Could not find prev event, but we know the state.", + ) })?; if let Some(state_key) = &prev_pdu.state_key { - let shortstatekey = services().rooms.short.get_or_create_shortstatekey( - &prev_pdu.kind.to_string().into(), - state_key, - )?; + let shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &prev_pdu.kind.to_string().into(), + state_key, + )?; state.insert(shortstatekey, Arc::from(prev_event)); // Now it's the state after the pdu @@ -567,7 +621,9 @@ impl Service { let mut okay = true; for prev_eventid in &incoming_pdu.prev_events { - let Ok(Some(prev_event)) = services().rooms.timeline.get_pdu(prev_eventid) else { + let Ok(Some(prev_event)) = + services().rooms.timeline.get_pdu(prev_eventid) + else { okay = false; break; }; @@ -585,8 +641,10 @@ impl Service { } if okay { - let mut fork_states = Vec::with_capacity(extremity_sstatehashes.len()); - let mut auth_chain_sets = Vec::with_capacity(extremity_sstatehashes.len()); + let mut fork_states = + Vec::with_capacity(extremity_sstatehashes.len()); + let mut auth_chain_sets = + Vec::with_capacity(extremity_sstatehashes.len()); for (sstatehash, prev_event) in extremity_sstatehashes { let mut leaf_state: HashMap<_, _> = services() @@ -596,23 +654,34 @@ impl Service { .await?; if let Some(state_key) = &prev_event.state_key { - let shortstatekey = services().rooms.short.get_or_create_shortstatekey( - &prev_event.kind.to_string().into(), - state_key, - )?; - leaf_state.insert(shortstatekey, Arc::from(&*prev_event.event_id)); + let shortstatekey = services() + .rooms + .short + .get_or_create_shortstatekey( + &prev_event.kind.to_string().into(), + state_key, + )?; + leaf_state.insert( + shortstatekey, + Arc::from(&*prev_event.event_id), + ); // Now it's the state after the pdu } let mut state = StateMap::with_capacity(leaf_state.len()); - let mut starting_events = Vec::with_capacity(leaf_state.len()); + let mut starting_events = + Vec::with_capacity(leaf_state.len()); for (k, id) in leaf_state { - if let Ok((ty, st_key)) = services().rooms.short.get_statekey_from_short(k) + if let Ok((ty, st_key)) = + services().rooms.short.get_statekey_from_short(k) { // FIXME: Undo .to_string().into() when StateMap // is updated to use StateEventType - state.insert((ty.to_string().into(), st_key), id.clone()); + state.insert( + (ty.to_string().into(), st_key), + id.clone(), + ); } else { warn!("Failed to get_statekey_from_short."); } @@ -633,14 +702,18 @@ impl Service { let lock = services().globals.stateres_mutex.lock(); - let result = - state_res::resolve(room_version_id, &fork_states, auth_chain_sets, |id| { + let result = state_res::resolve( + room_version_id, + &fork_states, + auth_chain_sets, + |id| { let res = services().rooms.timeline.get_pdu(id); if let Err(e) = &res { error!("LOOK AT ME Failed to fetch event: {}", e); } res.ok().flatten() - }); + }, + ); drop(lock); state_at_incoming_event = match result { @@ -648,8 +721,10 @@ impl Service { new_state .into_iter() .map(|((event_type, state_key), event_id)| { - let shortstatekey = - services().rooms.short.get_or_create_shortstatekey( + let shortstatekey = services() + .rooms + .short + .get_or_create_shortstatekey( &event_type.to_string().into(), &state_key, )?; @@ -658,7 +733,12 @@ impl Service { .collect::>()?, ), Err(e) => { - warn!("State resolution on prev events failed, either an event could not be found or deserialization: {}", e); + warn!( + "State resolution on prev events failed, either \ + an event could not be found or deserialization: \ + {}", + e + ); None } } @@ -667,8 +747,9 @@ impl Service { if state_at_incoming_event.is_none() { debug!("Calling /state_ids"); - // Call /state_ids to find out what the state at this pdu is. We trust the server's - // response to some extend, but we still do a lot of checks on the events + // Call /state_ids to find out what the state at this pdu is. We + // trust the server's response to some extend, but we + // still do a lot of checks on the events match services() .sending .send_federation_request( @@ -700,22 +781,31 @@ impl Service { let mut state: HashMap<_, Arc> = HashMap::new(); for (pdu, _) in state_vec { - let state_key = pdu.state_key.clone().ok_or_else(|| { - Error::bad_database("Found non-state pdu in state events.") - })?; + let state_key = + pdu.state_key.clone().ok_or_else(|| { + Error::bad_database( + "Found non-state pdu in state events.", + ) + })?; - let shortstatekey = services().rooms.short.get_or_create_shortstatekey( - &pdu.kind.to_string().into(), - &state_key, - )?; + let shortstatekey = services() + .rooms + .short + .get_or_create_shortstatekey( + &pdu.kind.to_string().into(), + &state_key, + )?; match state.entry(shortstatekey) { hash_map::Entry::Vacant(v) => { v.insert(Arc::from(&*pdu.event_id)); } - hash_map::Entry::Occupied(_) => return Err( - Error::bad_database("State event's type and state_key combination exists multiple times."), - ), + hash_map::Entry::Occupied(_) => { + return Err(Error::bad_database( + "State event's type and state_key \ + combination exists multiple times.", + )) + } } } @@ -726,7 +816,9 @@ impl Service { .get_shortstatekey(&StateEventType::RoomCreate, "")? .expect("Room exists"); - if state.get(&create_shortstatekey) != Some(&create_event.event_id) { + if state.get(&create_shortstatekey) + != Some(&create_event.event_id) + { return Err(Error::bad_database( "Incoming event refers to wrong create event.", )); @@ -745,7 +837,8 @@ impl Service { state_at_incoming_event.expect("we always set this to some above"); debug!("Starting auth check"); - // 11. Check the auth of the event passes based on the state of the event + // 11. Check the auth of the event passes based on the state of the + // event let check_result = state_res::event_auth::auth_check( &room_version, &incoming_pdu, @@ -758,11 +851,22 @@ impl Service { .get_shortstatekey(&k.to_string().into(), s) .ok() .flatten() - .and_then(|shortstatekey| state_at_incoming_event.get(&shortstatekey)) - .and_then(|event_id| services().rooms.timeline.get_pdu(event_id).ok().flatten()) + .and_then(|shortstatekey| { + state_at_incoming_event.get(&shortstatekey) + }) + .and_then(|event_id| { + services() + .rooms + .timeline + .get_pdu(event_id) + .ok() + .flatten() + }) }, ) - .map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed."))?; + .map_err(|_e| { + Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") + })?; if !check_result { return Err(Error::bad_database( @@ -786,51 +890,57 @@ impl Service { None::, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) - .map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed."))? - || incoming_pdu.kind == TimelineEventType::RoomRedaction - && match room_version_id { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - if let Some(redact_id) = &incoming_pdu.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - true, - )? - } else { - false - } + .map_err(|_e| { + Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") + })? || incoming_pdu.kind + == TimelineEventType::RoomRedaction + && match room_version_id { + RoomVersionId::V1 + | RoomVersionId::V2 + | RoomVersionId::V3 + | RoomVersionId::V4 + | RoomVersionId::V5 + | RoomVersionId::V6 + | RoomVersionId::V7 + | RoomVersionId::V8 + | RoomVersionId::V9 + | RoomVersionId::V10 => { + if let Some(redact_id) = &incoming_pdu.redacts { + !services().rooms.state_accessor.user_can_redact( + redact_id, + &incoming_pdu.sender, + &incoming_pdu.room_id, + true, + )? + } else { + false } - RoomVersionId::V11 => { - let content = serde_json::from_str::( - incoming_pdu.content.get(), - ) - .map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?; + } + RoomVersionId::V11 => { + let content = serde_json::from_str::< + RoomRedactionEventContent, + >( + incoming_pdu.content.get() + ) + .map_err(|_| { + Error::bad_database("Invalid content in redaction pdu.") + })?; - if let Some(redact_id) = &content.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - true, - )? - } else { - false - } + if let Some(redact_id) = &content.redacts { + !services().rooms.state_accessor.user_can_redact( + redact_id, + &incoming_pdu.sender, + &incoming_pdu.room_id, + true, + )? + } else { + false } - _ => { - unreachable!("Validity of room version already checked") - } - }; + } + _ => { + unreachable!("Validity of room version already checked") + } + }; // 13. Use state resolution to find new room state @@ -846,12 +956,15 @@ impl Service { ); let state_lock = mutex_state.lock().await; - // Now we calculate the set of extremities this room has after the incoming event has been - // applied. We start with the previous extremities (aka leaves) + // Now we calculate the set of extremities this room has after the + // incoming event has been applied. We start with the previous + // extremities (aka leaves) debug!("Calculating extremities"); - let mut extremities = services().rooms.state.get_forward_extremities(room_id)?; + let mut extremities = + services().rooms.state.get_forward_extremities(room_id)?; - // Remove any forward extremities that are referenced by this incoming event's prev_events + // Remove any forward extremities that are referenced by this incoming + // event's prev_events for prev_event in &incoming_pdu.prev_events { if extremities.contains(prev_event) { extremities.remove(prev_event); @@ -861,10 +974,7 @@ impl Service { // Only keep those extremities were not referenced yet extremities.retain(|id| { !matches!( - services() - .rooms - .pdu_metadata - .is_event_referenced(room_id, id), + services().rooms.pdu_metadata.is_event_referenced(room_id, id), Ok(true) ) }); @@ -888,12 +998,14 @@ impl Service { // We also add state after incoming event to the fork states let mut state_after = state_at_incoming_event.clone(); if let Some(state_key) = &incoming_pdu.state_key { - let shortstatekey = services().rooms.short.get_or_create_shortstatekey( - &incoming_pdu.kind.to_string().into(), - state_key, - )?; + let shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &incoming_pdu.kind.to_string().into(), + state_key, + )?; - state_after.insert(shortstatekey, Arc::from(&*incoming_pdu.event_id)); + state_after + .insert(shortstatekey, Arc::from(&*incoming_pdu.event_id)); } let new_room_state = self @@ -915,7 +1027,8 @@ impl Service { .await?; } - // 14. Check if the event passes auth based on the "current state" of the room, if not soft fail it + // 14. Check if the event passes auth based on the "current state" of + // the room, if not soft fail it debug!("Starting soft fail auth check"); if soft_fail { @@ -932,7 +1045,8 @@ impl Service { ) .await?; - // Soft fail, we keep the event as an outlier but don't add it to the timeline + // Soft fail, we keep the event as an outlier but don't add it to + // the timeline warn!("Event was soft failed: {:?}", incoming_pdu); services() .rooms @@ -998,7 +1112,10 @@ impl Service { services() .rooms .auth_chain - .get_auth_chain(room_id, state.iter().map(|(_, id)| id.clone()).collect()) + .get_auth_chain( + room_id, + state.iter().map(|(_, id)| id.clone()).collect(), + ) .await? .collect(), ); @@ -1015,7 +1132,9 @@ impl Service { .rooms .short .get_statekey_from_short(k) - .map(|(ty, st_key)| ((ty.to_string().into(), st_key), id)) + .map(|(ty, st_key)| { + ((ty.to_string().into(), st_key), id) + }) .ok() }) .collect::>() @@ -1033,11 +1152,15 @@ impl Service { }; let lock = services().globals.stateres_mutex.lock(); - let Ok(state) = - state_res::resolve(room_version_id, &fork_states, auth_chain_sets, fetch_event) - else { + let Ok(state) = state_res::resolve( + room_version_id, + &fork_states, + auth_chain_sets, + fetch_event, + ) else { return Err(Error::bad_database( - "State resolution failed, either an event could not be found or deserialization", + "State resolution failed, either an event could not be found \ + or deserialization", )); }; @@ -1048,10 +1171,11 @@ impl Service { let new_room_state = state .into_iter() .map(|((event_type, state_key), event_id)| { - let shortstatekey = services() - .rooms - .short - .get_or_create_shortstatekey(&event_type.to_string().into(), &state_key)?; + let shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &event_type.to_string().into(), + &state_key, + )?; services() .rooms .state_compressor @@ -1081,8 +1205,10 @@ impl Service { room_id: &'a RoomId, room_version_id: &'a RoomVersionId, pub_key_map: &'a RwLock>>, - ) -> AsyncRecursiveType<'a, Vec<(Arc, Option>)>> - { + ) -> AsyncRecursiveType< + 'a, + Vec<(Arc, Option>)>, + > { Box::pin(async move { let back_off = |id| async move { match services() @@ -1106,15 +1232,17 @@ impl Service { // a. Look in the main timeline (pduid_pdu tree) // b. Look at outlier pdu tree // (get_pdu_json checks both) - if let Ok(Some(local_pdu)) = services().rooms.timeline.get_pdu(id) { + if let Ok(Some(local_pdu)) = + services().rooms.timeline.get_pdu(id) + { trace!("Found {} in db", id); pdus.push((local_pdu, None)); continue; } // c. Ask origin server over federation - // We also handle its auth chain here so we don't get a stack overflow in - // handle_outlier_pdu. + // We also handle its auth chain here so we don't get a stack + // overflow in handle_outlier_pdu. let mut todo_auth_events = vec![Arc::clone(id)]; let mut events_in_reverse_order = Vec::new(); let mut events_all = HashSet::new(); @@ -1130,8 +1258,11 @@ impl Service { // Exponential backoff let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries); - if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { - min_elapsed_duration = Duration::from_secs(60 * 60 * 24); + if min_elapsed_duration + > Duration::from_secs(60 * 60 * 24) + { + min_elapsed_duration = + Duration::from_secs(60 * 60 * 24); } if time.elapsed() < min_elapsed_duration { @@ -1149,7 +1280,9 @@ impl Service { tokio::task::yield_now().await; } - if let Ok(Some(_)) = services().rooms.timeline.get_pdu(&next_id) { + if let Ok(Some(_)) = + services().rooms.timeline.get_pdu(&next_id) + { trace!("Found {} in db", next_id); continue; } @@ -1167,24 +1300,30 @@ impl Service { { info!("Got {} over federation", next_id); let Ok((calculated_event_id, value)) = - pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) + pdu::gen_event_id_canonical_json( + &res.pdu, + room_version_id, + ) else { back_off((*next_id).to_owned()).await; continue; }; if calculated_event_id != *next_id { - warn!("Server didn't return event id we requested: requested: {}, we got {}. Event: {:?}", - next_id, calculated_event_id, &res.pdu); + warn!( + "Server didn't return event id we requested: \ + requested: {}, we got {}. Event: {:?}", + next_id, calculated_event_id, &res.pdu + ); } if let Some(auth_events) = value.get("auth_events").and_then(|c| c.as_array()) { for auth_event in auth_events { - if let Ok(auth_event) = - serde_json::from_value(auth_event.clone().into()) - { + if let Ok(auth_event) = serde_json::from_value( + auth_event.clone().into(), + ) { let a: Arc = auth_event; todo_auth_events.push(a); } else { @@ -1214,8 +1353,11 @@ impl Service { // Exponential backoff let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries); - if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { - min_elapsed_duration = Duration::from_secs(60 * 60 * 24); + if min_elapsed_duration + > Duration::from_secs(60 * 60 * 24) + { + min_elapsed_duration = + Duration::from_secs(60 * 60 * 24); } if time.elapsed() < min_elapsed_duration { @@ -1242,7 +1384,10 @@ impl Service { } } Err(e) => { - warn!("Authentication of event {} failed: {:?}", next_id, e); + warn!( + "Authentication of event {} failed: {:?}", + next_id, e + ); back_off((**next_id).to_owned()).await; } } @@ -1262,17 +1407,19 @@ impl Service { initial_set: Vec>, ) -> Result<( Vec>, - HashMap, (Arc, BTreeMap)>, + HashMap< + Arc, + (Arc, BTreeMap), + >, )> { let mut graph: HashMap, _> = HashMap::new(); let mut eventid_info = HashMap::new(); let mut todo_outlier_stack: Vec> = initial_set; - let first_pdu_in_room = services() - .rooms - .timeline - .first_pdu_in_room(room_id)? - .ok_or_else(|| Error::bad_database("Failed to find first pdu in db."))?; + let first_pdu_in_room = + services().rooms.timeline.first_pdu_in_room(room_id)?.ok_or_else( + || Error::bad_database("Failed to find first pdu in db."), + )?; let mut amount = 0; @@ -1306,7 +1453,8 @@ impl Service { .ok() .flatten() }) { - if pdu.origin_server_ts > first_pdu_in_room.origin_server_ts { + if pdu.origin_server_ts > first_pdu_in_room.origin_server_ts + { amount += 1; for prev_prev in &pdu.prev_events { if !graph.contains_key(prev_prev) { @@ -1334,20 +1482,22 @@ impl Service { } } - let sorted = state_res::lexicographical_topological_sort(&graph, |event_id| { - // This return value is the key used for sorting events, - // events are then sorted by power level, time, - // and lexically by event_id. - Ok(( - int!(0), - MilliSecondsSinceUnixEpoch( - eventid_info - .get(event_id) - .map_or_else(|| uint!(0), |info| info.0.origin_server_ts), - ), - )) - }) - .map_err(|_| Error::bad_database("Error sorting prev events"))?; + let sorted = + state_res::lexicographical_topological_sort(&graph, |event_id| { + // This return value is the key used for sorting events, + // events are then sorted by power level, time, + // and lexically by event_id. + Ok(( + int!(0), + MilliSecondsSinceUnixEpoch( + eventid_info.get(event_id).map_or_else( + || uint!(0), + |info| info.0.origin_server_ts, + ), + ), + )) + }) + .map_err(|_| Error::bad_database("Error sorting prev events"))?; Ok((sorted, eventid_info)) } @@ -1368,20 +1518,23 @@ impl Service { "Invalid signatures object in server response pdu.", ))?; - // We go through all the signatures we see on the value and fetch the corresponding signing - // keys + // We go through all the signatures we see on the value and fetch the + // corresponding signing keys for (signature_server, signature) in signatures { - let signature_object = signature.as_object().ok_or(Error::BadServerResponse( - "Invalid signatures content object in server response pdu.", - ))?; + let signature_object = + signature.as_object().ok_or(Error::BadServerResponse( + "Invalid signatures content object in server response pdu.", + ))?; - let signature_ids = signature_object.keys().cloned().collect::>(); + let signature_ids = + signature_object.keys().cloned().collect::>(); let fetch_res = self .fetch_signing_keys( signature_server.as_str().try_into().map_err(|_| { Error::BadServerResponse( - "Invalid servername in signatures of server response pdu.", + "Invalid servername in signatures of server \ + response pdu.", ) })?, signature_ids, @@ -1389,32 +1542,40 @@ impl Service { .await; let Ok(keys) = fetch_res else { - warn!("Signature verification failed: Could not fetch signing key.",); + warn!( + "Signature verification failed: Could not fetch signing \ + key.", + ); continue; }; - pub_key_map - .write() - .await - .insert(signature_server.clone(), keys); + pub_key_map.write().await.insert(signature_server.clone(), keys); } Ok(()) } - // Gets a list of servers for which we don't have the signing key yet. We go over - // the PDUs and either cache the key or add it to the list that needs to be retrieved. + // Gets a list of servers for which we don't have the signing key yet. We go + // over the PDUs and either cache the key or add it to the list that + // needs to be retrieved. async fn get_server_keys_from_cache( &self, pdu: &RawJsonValue, - servers: &mut BTreeMap>, + servers: &mut BTreeMap< + OwnedServerName, + BTreeMap, + >, room_version: &RoomVersionId, - pub_key_map: &mut RwLockWriteGuard<'_, BTreeMap>>, + pub_key_map: &mut RwLockWriteGuard< + '_, + BTreeMap>, + >, ) -> Result<()> { - let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| { - error!("Invalid PDU in server response: {:?}: {:?}", pdu, e); - Error::BadServerResponse("Invalid PDU in server response") - })?; + let value: CanonicalJsonObject = serde_json::from_str(pdu.get()) + .map_err(|e| { + error!("Invalid PDU in server response: {:?}: {:?}", pdu, e); + Error::BadServerResponse("Invalid PDU in server response") + })?; let event_id = format!( "${}", @@ -1424,22 +1585,21 @@ impl Service { let event_id = <&EventId>::try_from(event_id.as_str()) .expect("ruma's reference hashes are valid event ids"); - if let Some((time, tries)) = services() - .globals - .bad_event_ratelimiter - .read() - .await - .get(event_id) + if let Some((time, tries)) = + services().globals.bad_event_ratelimiter.read().await.get(event_id) { // Exponential backoff - let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries); + let mut min_elapsed_duration = + Duration::from_secs(30) * (*tries) * (*tries); if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { min_elapsed_duration = Duration::from_secs(60 * 60 * 24); } if time.elapsed() < min_elapsed_duration { debug!("Backing off from {}", event_id); - return Err(Error::BadServerResponse("bad event, still backing off")); + return Err(Error::BadServerResponse( + "bad event, still backing off", + )); } } @@ -1454,21 +1614,29 @@ impl Service { ))?; for (signature_server, signature) in signatures { - let signature_object = signature.as_object().ok_or(Error::BadServerResponse( - "Invalid signatures content object in server response pdu.", - ))?; + let signature_object = + signature.as_object().ok_or(Error::BadServerResponse( + "Invalid signatures content object in server response pdu.", + ))?; - let signature_ids = signature_object.keys().cloned().collect::>(); + let signature_ids = + signature_object.keys().cloned().collect::>(); let contains_all_ids = |keys: &BTreeMap| { signature_ids.iter().all(|id| keys.contains_key(id)) }; - let origin = <&ServerName>::try_from(signature_server.as_str()).map_err(|_| { - Error::BadServerResponse("Invalid servername in signatures of server response pdu.") - })?; + let origin = <&ServerName>::try_from(signature_server.as_str()) + .map_err(|_| { + Error::BadServerResponse( + "Invalid servername in signatures of server response \ + pdu.", + ) + })?; - if servers.contains_key(origin) || pub_key_map.contains_key(origin.as_str()) { + if servers.contains_key(origin) + || pub_key_map.contains_key(origin.as_str()) + { continue; } @@ -1492,6 +1660,7 @@ impl Service { Ok(()) } + #[allow(clippy::too_many_lines)] pub(crate) async fn fetch_join_signing_keys( &self, event: &create_join_event::v2::Response, @@ -1515,7 +1684,12 @@ impl Service { .chain(&event.room_state.auth_chain) { if let Err(error) = self - .get_server_keys_from_cache(pdu, &mut servers, room_version, &mut pkm) + .get_server_keys_from_cache( + pdu, + &mut servers, + room_version, + &mut pkm, + ) .await { debug!(%error, "failed to get server keys from cache"); @@ -1549,7 +1723,8 @@ impl Service { Ok(key) => key, Err(e) => { warn!( - "Received error {} while fetching keys from trusted server {}", + "Received error {} while fetching keys from \ + trusted server {}", e, server ); warn!("{}", k.into_json()); @@ -1584,7 +1759,10 @@ impl Service { ( services() .sending - .send_federation_request(&server, get_server_keys::v2::Request::new()) + .send_federation_request( + &server, + get_server_keys::v2::Request::new(), + ) .await, server, ) @@ -1602,7 +1780,10 @@ impl Service { .into_iter() .map(|(k, v)| (k.to_string(), v.key)) .collect(); - pub_key_map.write().await.insert(origin.to_string(), result); + pub_key_map + .write() + .await + .insert(origin.to_string(), result); } } info!("Done handling result"); @@ -1616,7 +1797,11 @@ impl Service { /// Returns Ok if the acl allows the server // Allowed because this function uses `services()` #[allow(clippy::unused_self)] - pub(crate) fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> { + pub(crate) fn acl_check( + &self, + server_name: &ServerName, + room_id: &RoomId, + ) -> Result<()> { let Some(acl_event) = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomServerAcl, @@ -1626,9 +1811,9 @@ impl Service { return Ok(()); }; - let Ok(acl_event_content) = - serde_json::from_str::(acl_event.content.get()) - else { + let Ok(acl_event_content) = serde_json::from_str::< + RoomServerAclEventContent, + >(acl_event.content.get()) else { warn!("Invalid ACL event"); return Ok(()); }; @@ -1652,16 +1837,17 @@ impl Service { } } - /// Search the DB for the signing keys of the given server, if we don't have them - /// fetch them from the server and save to our DB. + /// Search the DB for the signing keys of the given server, if we don't have + /// them fetch them from the server and save to our DB. #[tracing::instrument(skip_all)] pub(crate) async fn fetch_signing_keys( &self, origin: &ServerName, signature_ids: Vec, ) -> Result> { - let contains_all_ids = - |keys: &BTreeMap| signature_ids.iter().all(|id| keys.contains_key(id)); + let contains_all_ids = |keys: &BTreeMap| { + signature_ids.iter().all(|id| keys.contains_key(id)) + }; let permit = services() .globals @@ -1674,7 +1860,8 @@ impl Service { let permit = if let Some(p) = permit { p } else { - let mut write = services().globals.servername_ratelimiter.write().await; + let mut write = + services().globals.servername_ratelimiter.write().await; let s = Arc::clone( write .entry(origin.to_owned()) @@ -1696,7 +1883,9 @@ impl Service { hash_map::Entry::Vacant(e) => { e.insert((Instant::now(), 1)); } - hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1), + hash_map::Entry::Occupied(mut e) => { + *e.get_mut() = (Instant::now(), e.get().1 + 1); + } } }; @@ -1708,14 +1897,17 @@ impl Service { .get(&signature_ids) { // Exponential backoff - let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries); + let mut min_elapsed_duration = + Duration::from_secs(30) * (*tries) * (*tries); if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { min_elapsed_duration = Duration::from_secs(60 * 60 * 24); } if time.elapsed() < min_elapsed_duration { debug!("Backing off from {:?}", signature_ids); - return Err(Error::BadServerResponse("bad signature, still backing off")); + return Err(Error::BadServerResponse( + "bad signature, still backing off", + )); } } @@ -1736,14 +1928,15 @@ impl Service { if let Some(server_key) = services() .sending - .send_federation_request(origin, get_server_keys::v2::Request::new()) + .send_federation_request( + origin, + get_server_keys::v2::Request::new(), + ) .await .ok() .and_then(|resp| resp.server_key.deserialize().ok()) { - services() - .globals - .add_signing_key(origin, server_key.clone())?; + services().globals.add_signing_key(origin, server_key.clone())?; result.extend( server_key @@ -1814,9 +2007,7 @@ impl Service { back_off(signature_ids).await; warn!("Failed to find public key for server: {}", origin); - Err(Error::BadServerResponse( - "Failed to find public key for server", - )) + Err(Error::BadServerResponse("Failed to find public key for server")) } fn check_room_id(room_id: &RoomId, pdu: &PduEvent) -> Result<()> { diff --git a/src/service/rooms/lazy_loading.rs b/src/service/rooms/lazy_loading.rs index 54a10480..10e0fff1 100644 --- a/src/service/rooms/lazy_loading.rs +++ b/src/service/rooms/lazy_loading.rs @@ -5,16 +5,19 @@ pub(crate) use data::Data; use ruma::{DeviceId, OwnedDeviceId, OwnedRoomId, OwnedUserId, RoomId, UserId}; use tokio::sync::Mutex; -use crate::Result; - use super::timeline::PduCount; +use crate::Result; pub(crate) struct Service { pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub(crate) lazy_load_waiting: - Mutex>>, + pub(crate) lazy_load_waiting: Mutex< + HashMap< + (OwnedUserId, OwnedDeviceId, OwnedRoomId, PduCount), + HashSet, + >, + >, } impl Service { @@ -26,8 +29,7 @@ impl Service { room_id: &RoomId, ll_user: &UserId, ) -> Result { - self.db - .lazy_load_was_sent_before(user_id, device_id, room_id, ll_user) + self.db.lazy_load_was_sent_before(user_id, device_id, room_id, ll_user) } #[tracing::instrument(skip(self))] diff --git a/src/service/rooms/lazy_loading/data.rs b/src/service/rooms/lazy_loading/data.rs index 92f694a3..95bf83d8 100644 --- a/src/service/rooms/lazy_loading/data.rs +++ b/src/service/rooms/lazy_loading/data.rs @@ -1,6 +1,7 @@ -use crate::Result; use ruma::{DeviceId, RoomId, UserId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { fn lazy_load_was_sent_before( &self, diff --git a/src/service/rooms/metadata/data.rs b/src/service/rooms/metadata/data.rs index 48317458..81dd44ff 100644 --- a/src/service/rooms/metadata/data.rs +++ b/src/service/rooms/metadata/data.rs @@ -1,10 +1,13 @@ -use crate::Result; use ruma::{OwnedRoomId, RoomId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { /// Checks if a room exists. fn exists(&self, room_id: &RoomId) -> Result; - fn iter_ids<'a>(&'a self) -> Box> + 'a>; + fn iter_ids<'a>( + &'a self, + ) -> Box> + 'a>; fn is_disabled(&self, room_id: &RoomId) -> Result; fn disable_room(&self, room_id: &RoomId, disabled: bool) -> Result<()>; } diff --git a/src/service/rooms/outlier/data.rs b/src/service/rooms/outlier/data.rs index 8956b491..3357c5c9 100644 --- a/src/service/rooms/outlier/data.rs +++ b/src/service/rooms/outlier/data.rs @@ -4,8 +4,15 @@ use crate::{PduEvent, Result}; pub(crate) trait Data: Send + Sync { /// Returns the pdu from the outlier tree. - fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result>; + fn get_outlier_pdu_json( + &self, + event_id: &EventId, + ) -> Result>; fn get_outlier_pdu(&self, event_id: &EventId) -> Result>; /// Append the PDU as an outlier. - fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()>; + fn add_pdu_outlier( + &self, + event_id: &EventId, + pdu: &CanonicalJsonObject, + ) -> Result<()>; } diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 5a778f6b..d8acc698 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -9,9 +9,8 @@ use ruma::{ }; use serde::Deserialize; -use crate::{services, PduEvent, Result}; - use super::timeline::PduCount; +use crate::{services, PduEvent, Result}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -29,9 +28,15 @@ struct ExtractRelatesToEventId { impl Service { #[tracing::instrument(skip(self, from, to))] - pub(crate) fn add_relation(&self, from: PduCount, to: PduCount) -> Result<()> { + pub(crate) fn add_relation( + &self, + from: PduCount, + to: PduCount, + ) -> Result<()> { match (from, to) { - (PduCount::Normal(f), PduCount::Normal(t)) => self.db.add_relation(f, t), + (PduCount::Normal(f), PduCount::Normal(t)) => { + self.db.add_relation(f, t) + } _ => { // TODO: Relations with backfilled pdus @@ -42,6 +47,7 @@ impl Service { #[allow( clippy::too_many_arguments, + clippy::too_many_lines, // Allowed because this function uses `services()` clippy::unused_self, )] @@ -68,15 +74,17 @@ impl Service { .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { - filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t) - && if let Ok(content) = - serde_json::from_str::( - pdu.content.get(), - ) - { - filter_rel_type - .as_ref() - .map_or(true, |r| &&content.relates_to.rel_type == r) + filter_event_type + .as_ref() + .map_or(true, |t| &&pdu.kind == t) + && if let Ok(content) = serde_json::from_str::< + ExtractRelatesToEventId, + >( + pdu.content.get() + ) { + filter_rel_type.as_ref().map_or(true, |r| { + &&content.relates_to.rel_type == r + }) } else { false } @@ -88,13 +96,18 @@ impl Service { services() .rooms .state_accessor - .user_can_see_event(sender_user, room_id, &pdu.event_id) + .user_can_see_event( + sender_user, + room_id, + &pdu.event_id, + ) .unwrap_or(false) }) .take_while(|&(k, _)| Some(k) != to) .collect(); - next_token = events_after.last().map(|(count, _)| count).copied(); + next_token = + events_after.last().map(|(count, _)| count).copied(); // Reversed because relations are always most recent first let events_after: Vec<_> = events_after @@ -116,15 +129,17 @@ impl Service { .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { - filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t) - && if let Ok(content) = - serde_json::from_str::( - pdu.content.get(), - ) - { - filter_rel_type - .as_ref() - .map_or(true, |r| &&content.relates_to.rel_type == r) + filter_event_type + .as_ref() + .map_or(true, |t| &&pdu.kind == t) + && if let Ok(content) = serde_json::from_str::< + ExtractRelatesToEventId, + >( + pdu.content.get() + ) { + filter_rel_type.as_ref().map_or(true, |r| { + &&content.relates_to.rel_type == r + }) } else { false } @@ -136,13 +151,18 @@ impl Service { services() .rooms .state_accessor - .user_can_see_event(sender_user, room_id, &pdu.event_id) + .user_can_see_event( + sender_user, + room_id, + &pdu.event_id, + ) .unwrap_or(false) }) .take_while(|&(k, _)| Some(k) != to) .collect(); - next_token = events_before.last().map(|(count, _)| count).copied(); + next_token = + events_before.last().map(|(count, _)| count).copied(); let events_before: Vec<_> = events_before .into_iter() @@ -165,7 +185,8 @@ impl Service { target: &'a EventId, until: PduCount, ) -> Result> + 'a> { - let room_id = services().rooms.short.get_or_create_shortroomid(room_id)?; + let room_id = + services().rooms.short.get_or_create_shortroomid(room_id)?; let target = match services().rooms.timeline.get_pdu_count(target)? { Some(PduCount::Normal(c)) => c, // TODO: Support backfilled relations @@ -185,17 +206,27 @@ impl Service { } #[tracing::instrument(skip(self))] - pub(crate) fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result { + pub(crate) fn is_event_referenced( + &self, + room_id: &RoomId, + event_id: &EventId, + ) -> Result { self.db.is_event_referenced(room_id, event_id) } #[tracing::instrument(skip(self))] - pub(crate) fn mark_event_soft_failed(&self, event_id: &EventId) -> Result<()> { + pub(crate) fn mark_event_soft_failed( + &self, + event_id: &EventId, + ) -> Result<()> { self.db.mark_event_soft_failed(event_id) } #[tracing::instrument(skip(self))] - pub(crate) fn is_event_soft_failed(&self, event_id: &EventId) -> Result { + pub(crate) fn is_event_soft_failed( + &self, + event_id: &EventId, + ) -> Result { self.db.is_event_soft_failed(event_id) } } diff --git a/src/service/rooms/pdu_metadata/data.rs b/src/service/rooms/pdu_metadata/data.rs index 4e28474a..9aace01f 100644 --- a/src/service/rooms/pdu_metadata/data.rs +++ b/src/service/rooms/pdu_metadata/data.rs @@ -1,8 +1,9 @@ use std::sync::Arc; -use crate::{service::rooms::timeline::PduCount, PduEvent, Result}; use ruma::{EventId, RoomId, UserId}; +use crate::{service::rooms::timeline::PduCount, PduEvent, Result}; + pub(crate) trait Data: Send + Sync { fn add_relation(&self, from: u64, to: u64) -> Result<()>; #[allow(clippy::type_complexity)] @@ -13,8 +14,16 @@ pub(crate) trait Data: Send + Sync { target: u64, until: PduCount, ) -> Result> + 'a>>; - fn mark_as_referenced(&self, room_id: &RoomId, event_ids: &[Arc]) -> Result<()>; - fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result; + fn mark_as_referenced( + &self, + room_id: &RoomId, + event_ids: &[Arc], + ) -> Result<()>; + fn is_event_referenced( + &self, + room_id: &RoomId, + event_id: &EventId, + ) -> Result; fn mark_event_soft_failed(&self, event_id: &EventId) -> Result<()>; fn is_event_soft_failed(&self, event_id: &EventId) -> Result; } diff --git a/src/service/rooms/search/data.rs b/src/service/rooms/search/data.rs index 9e68fe62..903a85e6 100644 --- a/src/service/rooms/search/data.rs +++ b/src/service/rooms/search/data.rs @@ -1,8 +1,14 @@ -use crate::Result; use ruma::RoomId; +use crate::Result; + pub(crate) trait Data: Send + Sync { - fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()>; + fn index_pdu( + &self, + shortroomid: u64, + pdu_id: &[u8], + message_body: &str, + ) -> Result<()>; #[allow(clippy::type_complexity)] fn search_pdus<'a>( diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index 295f11a2..dcde51c3 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -1,8 +1,9 @@ use std::sync::Arc; -use crate::Result; use ruma::{events::StateEventType, EventId, RoomId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result; @@ -18,12 +19,19 @@ pub(crate) trait Data: Send + Sync { state_key: &str, ) -> Result; - fn get_eventid_from_short(&self, shorteventid: u64) -> Result>; + fn get_eventid_from_short(&self, shorteventid: u64) + -> Result>; - fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)>; + fn get_statekey_from_short( + &self, + shortstatekey: u64, + ) -> Result<(StateEventType, String)>; /// Returns `(shortstatehash, already_existed)` - fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)>; + fn get_or_create_shortstatehash( + &self, + state_hash: &[u8], + ) -> Result<(u64, bool)>; fn get_shortroomid(&self, room_id: &RoomId) -> Result>; diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 7e97dbd4..854d6d44 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -15,8 +15,12 @@ use ruma::{ canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, - history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, - join_rules::{self, AllowRule, JoinRule, RoomJoinRulesEventContent}, + history_visibility::{ + HistoryVisibility, RoomHistoryVisibilityEventContent, + }, + join_rules::{ + self, AllowRule, JoinRule, RoomJoinRulesEventContent, + }, topic::RoomTopicEventContent, }, space::child::SpaceChildEventContent, @@ -26,7 +30,6 @@ use ruma::{ OwnedRoomId, RoomId, UserId, }; use tokio::sync::Mutex; - use tracing::{debug, error, warn}; use crate::{services, Error, PduEvent, Result}; @@ -42,7 +45,8 @@ pub(crate) struct CachedSpaceChunk { } pub(crate) struct Service { - pub(crate) roomid_spacechunk_cache: Mutex>>, + pub(crate) roomid_spacechunk_cache: + Mutex>>, } impl Service { @@ -86,9 +90,11 @@ impl Service { { if let Some(cached) = cached { let allowed = match &cached.join_rule { - CachedJoinRule::Full(f) => { - self.handle_join_rule(f, sender_user, ¤t_room)? - } + CachedJoinRule::Full(f) => self.handle_join_rule( + f, + sender_user, + ¤t_room, + )?, }; if allowed { if left_to_skip > 0 { @@ -104,10 +110,8 @@ impl Service { continue; } - if let Some(current_shortstatehash) = services() - .rooms - .state - .get_room_shortstatehash(¤t_room)? + if let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(¤t_room)? { let state = services() .rooms @@ -124,16 +128,21 @@ impl Service { continue; } - let pdu = services() - .rooms - .timeline - .get_pdu(&id)? - .ok_or_else(|| Error::bad_database("Event in space state not found"))?; + let pdu = + services().rooms.timeline.get_pdu(&id)?.ok_or_else( + || { + Error::bad_database( + "Event in space state not found", + ) + }, + )?; - if serde_json::from_str::(pdu.content.get()) - .ok() - .map(|c| c.via) - .map_or(true, |v| v.is_empty()) + if serde_json::from_str::( + pdu.content.get(), + ) + .ok() + .map(|c| c.via) + .map_or(true, |v| v.is_empty()) { continue; } @@ -147,7 +156,11 @@ impl Service { // TODO: Sort children children_ids.reverse(); - let chunk = self.get_room_chunk(sender_user, ¤t_room, children_pdus); + let chunk = self.get_room_chunk( + sender_user, + ¤t_room, + children_pdus, + ); if let Ok(chunk) = chunk { if left_to_skip > 0 { left_to_skip -= 1; @@ -157,13 +170,24 @@ impl Service { let join_rule = services() .rooms .state_accessor - .room_state_get(¤t_room, &StateEventType::RoomJoinRules, "")? + .room_state_get( + ¤t_room, + &StateEventType::RoomJoinRules, + "", + )? .map(|s| { serde_json::from_str(s.content.get()) .map(|c: RoomJoinRulesEventContent| c.join_rule) .map_err(|e| { - error!("Invalid room join rule event in database: {}", e); - Error::BadDatabase("Invalid room join rule event in database.") + error!( + "Invalid room join rule event in \ + database: {}", + e + ); + Error::BadDatabase( + "Invalid room join rule event in \ + database.", + ) }) }) .transpose()? @@ -205,7 +229,10 @@ impl Service { ) .await { - warn!("Got response from {server} for /hierarchy\n{response:?}"); + warn!( + "Got response from {server} for \ + /hierarchy\n{response:?}" + ); let chunk = SpaceHierarchyRoomsChunk { canonical_alias: response.room.canonical_alias, name: response.room.name, @@ -250,9 +277,17 @@ impl Service { }) } SpaceRoomJoinRule::Public => JoinRule::Public, - _ => return Err(Error::BadServerResponse("Unknown join rule")), + _ => { + return Err(Error::BadServerResponse( + "Unknown join rule", + )) + } }; - if self.handle_join_rule(&join_rule, sender_user, ¤t_room)? { + if self.handle_join_rule( + &join_rule, + sender_user, + ¤t_room, + )? { if left_to_skip > 0 { left_to_skip -= 1; } else { @@ -301,12 +336,18 @@ impl Service { canonical_alias: services() .rooms .state_accessor - .room_state_get(room_id, &StateEventType::RoomCanonicalAlias, "")? + .room_state_get( + room_id, + &StateEventType::RoomCanonicalAlias, + "", + )? .map_or(Ok(None), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomCanonicalAliasEventContent| c.alias) .map_err(|_| { - Error::bad_database("Invalid canonical alias event in database.") + Error::bad_database( + "Invalid canonical alias event in database.", + ) }) })?, name: services().rooms.state_accessor.get_name(room_id)?, @@ -329,22 +370,34 @@ impl Service { serde_json::from_str(s.content.get()) .map(|c: RoomTopicEventContent| Some(c.topic)) .map_err(|_| { - error!("Invalid room topic event in database for room {}", room_id); - Error::bad_database("Invalid room topic event in database.") + error!( + "Invalid room topic event in database for \ + room {}", + room_id + ); + Error::bad_database( + "Invalid room topic event in database.", + ) }) })?, world_readable: services() .rooms .state_accessor - .room_state_get(room_id, &StateEventType::RoomHistoryVisibility, "")? + .room_state_get( + room_id, + &StateEventType::RoomHistoryVisibility, + "", + )? .map_or(Ok(false), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomHistoryVisibilityEventContent| { - c.history_visibility == HistoryVisibility::WorldReadable + c.history_visibility + == HistoryVisibility::WorldReadable }) .map_err(|_| { Error::bad_database( - "Invalid room history visibility event in database.", + "Invalid room history visibility event in \ + database.", ) }) })?, @@ -358,7 +411,9 @@ impl Service { c.guest_access == GuestAccess::CanJoin }) .map_err(|_| { - Error::bad_database("Invalid room guest access event in database.") + Error::bad_database( + "Invalid room guest access event in database.", + ) }) })?, avatar_url: services() @@ -368,7 +423,11 @@ impl Service { .map(|s| { serde_json::from_str(s.content.get()) .map(|c: RoomAvatarEventContent| c.url) - .map_err(|_| Error::bad_database("Invalid room avatar event in database.")) + .map_err(|_| { + Error::bad_database( + "Invalid room avatar event in database.", + ) + }) }) .transpose()? .flatten(), @@ -376,13 +435,23 @@ impl Service { let join_rule = services() .rooms .state_accessor - .room_state_get(room_id, &StateEventType::RoomJoinRules, "")? + .room_state_get( + room_id, + &StateEventType::RoomJoinRules, + "", + )? .map(|s| { serde_json::from_str(s.content.get()) .map(|c: RoomJoinRulesEventContent| c.join_rule) .map_err(|e| { - error!("Invalid room join rule event in database: {}", e); - Error::BadDatabase("Invalid room join rule event in database.") + error!( + "Invalid room join rule event in \ + database: {}", + e + ); + Error::BadDatabase( + "Invalid room join rule event in database.", + ) }) }) .transpose()? @@ -404,9 +473,14 @@ impl Service { .state_accessor .room_state_get(room_id, &StateEventType::RoomCreate, "")? .map(|s| { - serde_json::from_str::(s.content.get()).map_err(|e| { + serde_json::from_str::( + s.content.get(), + ) + .map_err(|e| { error!("Invalid room create event in database: {}", e); - Error::BadDatabase("Invalid room create event in database.") + Error::BadDatabase( + "Invalid room create event in database.", + ) }) }) .transpose()? @@ -424,7 +498,9 @@ impl Service { JoinRule::Knock => Ok(SpaceRoomJoinRule::Knock), JoinRule::Private => Ok(SpaceRoomJoinRule::Private), JoinRule::Restricted(_) => Ok(SpaceRoomJoinRule::Restricted), - JoinRule::KnockRestricted(_) => Ok(SpaceRoomJoinRule::KnockRestricted), + JoinRule::KnockRestricted(_) => { + Ok(SpaceRoomJoinRule::KnockRestricted) + } JoinRule::Public => Ok(SpaceRoomJoinRule::Public), _ => Err(Error::BadServerResponse("Unknown join rule")), } @@ -440,10 +516,9 @@ impl Service { ) -> Result { let allowed = match join_rule { SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::Public => true, - SpaceRoomJoinRule::Invite => services() - .rooms - .state_cache - .is_joined(sender_user, room_id)?, + SpaceRoomJoinRule::Invite => { + services().rooms.state_cache.is_joined(sender_user, room_id)? + } _ => false, }; diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 0d4f91a0..07e5fc5e 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -19,9 +19,8 @@ use serde::Deserialize; use tokio::sync::MutexGuard; use tracing::warn; -use crate::{services, utils::calculate_hash, Error, PduEvent, Result}; - use super::state_compressor::CompressedStateEvent; +use crate::{services, utils::calculate_hash, Error, PduEvent, Result}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -46,12 +45,15 @@ impl Service { .ok() .map(|(_, id)| id) }) { - let Some(pdu) = services().rooms.timeline.get_pdu_json(&event_id)? else { + let Some(pdu) = + services().rooms.timeline.get_pdu_json(&event_id)? + else { continue; }; let pdu: PduEvent = match serde_json::from_str( - &serde_json::to_string(&pdu).expect("CanonicalJsonObj can be serialized to JSON"), + &serde_json::to_string(&pdu) + .expect("CanonicalJsonObj can be serialized to JSON"), ) { Ok(pdu) => pdu, Err(_) => continue, @@ -65,7 +67,9 @@ impl Service { } let membership = - match serde_json::from_str::(pdu.content.get()) { + match serde_json::from_str::( + pdu.content.get(), + ) { Ok(e) => e.membership, Err(_) => continue, }; @@ -102,8 +106,7 @@ impl Service { services().rooms.state_cache.update_joined_count(room_id)?; - self.db - .set_room_state(room_id, shortstatehash, state_lock)?; + self.db.set_room_state(room_id, shortstatehash, state_lock)?; Ok(()) } @@ -119,24 +122,18 @@ impl Service { room_id: &RoomId, state_ids_compressed: Arc>, ) -> Result { - let shorteventid = services() - .rooms - .short - .get_or_create_shorteventid(event_id)?; + let shorteventid = + services().rooms.short.get_or_create_shorteventid(event_id)?; - let previous_shortstatehash = self.db.get_room_shortstatehash(room_id)?; + let previous_shortstatehash = + self.db.get_room_shortstatehash(room_id)?; let state_hash = calculate_hash( - &state_ids_compressed - .iter() - .map(|s| &s[..]) - .collect::>(), + &state_ids_compressed.iter().map(|s| &s[..]).collect::>(), ); - let (shortstatehash, already_existed) = services() - .rooms - .short - .get_or_create_shortstatehash(&state_hash)?; + let (shortstatehash, already_existed) = + services().rooms.short.get_or_create_shortstatehash(&state_hash)?; if !already_existed { let states_parents = previous_shortstatehash.map_or_else( @@ -192,7 +189,8 @@ impl Service { .short .get_or_create_shorteventid(&new_pdu.event_id)?; - let previous_shortstatehash = self.get_room_shortstatehash(&new_pdu.room_id)?; + let previous_shortstatehash = + self.get_room_shortstatehash(&new_pdu.room_id)?; if let Some(p) = previous_shortstatehash { self.db.set_event_state(shorteventid, p)?; @@ -209,10 +207,11 @@ impl Service { }, )?; - let shortstatekey = services() - .rooms - .short - .get_or_create_shortstatekey(&new_pdu.kind.to_string().into(), state_key)?; + let shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &new_pdu.kind.to_string().into(), + state_key, + )?; let new = services() .rooms @@ -222,9 +221,9 @@ impl Service { let replaces = states_parents .last() .map(|info| { - info.1 - .iter() - .find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes())) + info.1.iter().find(|bytes| { + bytes.starts_with(&shortstatekey.to_be_bytes()) + }) }) .unwrap_or_default(); @@ -253,7 +252,8 @@ impl Service { Ok(shortstatehash) } else { - Ok(previous_shortstatehash.expect("first event in room must be a state event")) + Ok(previous_shortstatehash + .expect("first event in room must be a state event")) } } @@ -325,7 +325,10 @@ impl Service { /// Returns the room's version. #[tracing::instrument(skip(self))] - pub(crate) fn get_room_version(&self, room_id: &RoomId) -> Result { + pub(crate) fn get_room_version( + &self, + room_id: &RoomId, + ) -> Result { let create_event = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomCreate, @@ -341,12 +344,20 @@ impl Service { }) }) .transpose()? - .ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "No create event found"))?; + .ok_or_else(|| { + Error::BadRequest( + ErrorKind::InvalidParam, + "No create event found", + ) + })?; Ok(create_event_content.room_version) } - pub(crate) fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result> { + pub(crate) fn get_room_shortstatehash( + &self, + room_id: &RoomId, + ) -> Result> { self.db.get_room_shortstatehash(room_id) } @@ -364,8 +375,7 @@ impl Service { // Take mutex guard to make sure users get the room state mutex state_lock: &MutexGuard<'_, ()>, ) -> Result<()> { - self.db - .set_forward_extremities(room_id, event_ids, state_lock) + self.db.set_forward_extremities(room_id, event_ids, state_lock) } /// This fetches auth events from the current state. @@ -378,12 +388,15 @@ impl Service { state_key: Option<&str>, content: &serde_json::value::RawValue, ) -> Result>> { - let Some(shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else { + let Some(shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + else { return Ok(HashMap::new()); }; - let auth_events = state_res::auth_types_for_event(kind, sender, state_key, content) - .expect("content is a valid JSON object"); + let auth_events = + state_res::auth_types_for_event(kind, sender, state_key, content) + .expect("content is a valid JSON object"); let mut sauthevents = auth_events .into_iter() @@ -391,7 +404,10 @@ impl Service { services() .rooms .short - .get_shortstatekey(&event_type.to_string().into(), &state_key) + .get_shortstatekey( + &event_type.to_string().into(), + &state_key, + ) .ok() .flatten() .map(|s| (s, (event_type, state_key))) diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index 8e267760..2ed50def 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -1,8 +1,10 @@ -use crate::Result; -use ruma::{EventId, OwnedEventId, RoomId}; use std::{collections::HashSet, sync::Arc}; + +use ruma::{EventId, OwnedEventId, RoomId}; use tokio::sync::MutexGuard; +use crate::Result; + pub(crate) trait Data: Send + Sync { /// Returns the last state hash key added to the db for the given room. fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result>; @@ -17,10 +19,17 @@ pub(crate) trait Data: Send + Sync { ) -> Result<()>; /// Associates a state with an event. - fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()>; + fn set_event_state( + &self, + shorteventid: u64, + shortstatehash: u64, + ) -> Result<()>; /// Returns all events we would send as the `prev_events` of the next event. - fn get_forward_extremities(&self, room_id: &RoomId) -> Result>>; + fn get_forward_extremities( + &self, + room_id: &RoomId, + ) -> Result>>; /// Replace the forward extremities of the room. fn set_forward_extremities( diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 420ad76f..d980b3bf 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -10,7 +10,9 @@ use ruma::{ events::{ room::{ avatar::RoomAvatarEventContent, - history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, + history_visibility::{ + HistoryVisibility, RoomHistoryVisibilityEventContent, + }, member::{MembershipState, RoomMemberEventContent}, name::RoomNameEventContent, power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, @@ -18,7 +20,8 @@ use ruma::{ StateEventType, }, state_res::Event, - EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, + EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, + UserId, }; use serde_json::value::to_raw_value; use tokio::sync::MutexGuard; @@ -28,7 +31,8 @@ use crate::{service::pdu::PduBuilder, services, Error, PduEvent, Result}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, - pub(crate) server_visibility_cache: Mutex>, + pub(crate) server_visibility_cache: + Mutex>, pub(crate) user_visibility_cache: Mutex>, } @@ -50,7 +54,8 @@ impl Service { self.db.state_full(shortstatehash).await } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). #[tracing::instrument(skip(self))] pub(crate) fn state_get_id( &self, @@ -61,7 +66,8 @@ impl Service { self.db.state_get_id(shortstatehash, event_type, state_key) } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). pub(crate) fn state_get( &self, shortstatehash: u64, @@ -72,7 +78,11 @@ impl Service { } /// Get membership for given user in state - fn user_membership(&self, shortstatehash: u64, user_id: &UserId) -> Result { + fn user_membership( + &self, + shortstatehash: u64, + user_id: &UserId, + ) -> Result { self.state_get( shortstatehash, &StateEventType::RoomMember, @@ -81,7 +91,11 @@ impl Service { .map_or(Ok(MembershipState::Leave), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomMemberEventContent| c.membership) - .map_err(|_| Error::bad_database("Invalid room membership event in database.")) + .map_err(|_| { + Error::bad_database( + "Invalid room membership event in database.", + ) + }) }) } @@ -123,12 +137,20 @@ impl Service { } let history_visibility = self - .state_get(shortstatehash, &StateEventType::RoomHistoryVisibility, "")? + .state_get( + shortstatehash, + &StateEventType::RoomHistoryVisibility, + "", + )? .map_or(Ok(HistoryVisibility::Shared), |s| { serde_json::from_str(s.content.get()) - .map(|c: RoomHistoryVisibilityEventContent| c.history_visibility) + .map(|c: RoomHistoryVisibilityEventContent| { + c.history_visibility + }) .map_err(|_| { - Error::bad_database("Invalid history visibility event in database.") + Error::bad_database( + "Invalid history visibility event in database.", + ) }) })?; @@ -140,14 +162,20 @@ impl Service { .filter(|member| member.server_name() == origin); let visibility = match history_visibility { - HistoryVisibility::WorldReadable | HistoryVisibility::Shared => true, + HistoryVisibility::WorldReadable | HistoryVisibility::Shared => { + true + } HistoryVisibility::Invited => { - // Allow if any member on requesting server was AT LEAST invited, else deny - current_server_members.any(|member| self.user_was_invited(shortstatehash, &member)) + // Allow if any member on requesting server was AT LEAST + // invited, else deny + current_server_members.any(|member| { + self.user_was_invited(shortstatehash, &member) + }) } HistoryVisibility::Joined => { // Allow if any member on requested server was joined, else deny - current_server_members.any(|member| self.user_was_joined(shortstatehash, &member)) + current_server_members + .any(|member| self.user_was_joined(shortstatehash, &member)) } _ => { error!("Unknown history visibility {history_visibility}"); @@ -185,15 +213,24 @@ impl Service { return Ok(*visibility); } - let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?; + let currently_member = + services().rooms.state_cache.is_joined(user_id, room_id)?; let history_visibility = self - .state_get(shortstatehash, &StateEventType::RoomHistoryVisibility, "")? + .state_get( + shortstatehash, + &StateEventType::RoomHistoryVisibility, + "", + )? .map_or(Ok(HistoryVisibility::Shared), |s| { serde_json::from_str(s.content.get()) - .map(|c: RoomHistoryVisibilityEventContent| c.history_visibility) + .map(|c: RoomHistoryVisibilityEventContent| { + c.history_visibility + }) .map_err(|_| { - Error::bad_database("Invalid history visibility event in database.") + Error::bad_database( + "Invalid history visibility event in database.", + ) }) })?; @@ -201,7 +238,8 @@ impl Service { HistoryVisibility::WorldReadable => true, HistoryVisibility::Shared => currently_member, HistoryVisibility::Invited => { - // Allow if any member on requesting server was AT LEAST invited, else deny + // Allow if any member on requesting server was AT LEAST + // invited, else deny self.user_was_invited(shortstatehash, user_id) } HistoryVisibility::Joined => { @@ -230,23 +268,36 @@ impl Service { user_id: &UserId, room_id: &RoomId, ) -> Result { - let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?; + let currently_member = + services().rooms.state_cache.is_joined(user_id, room_id)?; let history_visibility = self - .room_state_get(room_id, &StateEventType::RoomHistoryVisibility, "")? + .room_state_get( + room_id, + &StateEventType::RoomHistoryVisibility, + "", + )? .map_or(Ok(HistoryVisibility::Shared), |s| { serde_json::from_str(s.content.get()) - .map(|c: RoomHistoryVisibilityEventContent| c.history_visibility) + .map(|c: RoomHistoryVisibilityEventContent| { + c.history_visibility + }) .map_err(|_| { - Error::bad_database("Invalid history visibility event in database.") + Error::bad_database( + "Invalid history visibility event in database.", + ) }) })?; - Ok(currently_member || history_visibility == HistoryVisibility::WorldReadable) + Ok(currently_member + || history_visibility == HistoryVisibility::WorldReadable) } /// Returns the state hash for this pdu. - pub(crate) fn pdu_shortstatehash(&self, event_id: &EventId) -> Result> { + pub(crate) fn pdu_shortstatehash( + &self, + event_id: &EventId, + ) -> Result> { self.db.pdu_shortstatehash(event_id) } @@ -259,7 +310,8 @@ impl Service { self.db.room_state_full(room_id).await } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). #[tracing::instrument(skip(self))] pub(crate) fn room_state_get_id( &self, @@ -270,7 +322,8 @@ impl Service { self.db.room_state_get_id(room_id, event_type, state_key) } - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). #[tracing::instrument(skip(self))] pub(crate) fn room_state_get( &self, @@ -282,26 +335,39 @@ impl Service { } pub(crate) fn get_name(&self, room_id: &RoomId) -> Result> { - self.room_state_get(room_id, &StateEventType::RoomName, "")? - .map_or(Ok(None), |s| { + self.room_state_get(room_id, &StateEventType::RoomName, "")?.map_or( + Ok(None), + |s| { serde_json::from_str(s.content.get()) .map(|c: RoomNameEventContent| Some(c.name)) .map_err(|e| { error!( - "Invalid room name event in database for room {}. {}", + "Invalid room name event in database for room {}. \ + {}", room_id, e ); - Error::bad_database("Invalid room name event in database.") + Error::bad_database( + "Invalid room name event in database.", + ) }) - }) + }, + ) } - pub(crate) fn get_avatar(&self, room_id: &RoomId) -> Result> { - self.room_state_get(room_id, &StateEventType::RoomAvatar, "")? - .map_or(Ok(JsOption::Undefined), |s| { - serde_json::from_str(s.content.get()) - .map_err(|_| Error::bad_database("Invalid room avatar event in database.")) - }) + pub(crate) fn get_avatar( + &self, + room_id: &RoomId, + ) -> Result> { + self.room_state_get(room_id, &StateEventType::RoomAvatar, "")?.map_or( + Ok(JsOption::Undefined), + |s| { + serde_json::from_str(s.content.get()).map_err(|_| { + Error::bad_database( + "Invalid room avatar event in database.", + ) + }) + }, + ) } // Allowed because this function uses `services()` @@ -313,8 +379,9 @@ impl Service { target_user: &UserId, state_lock: &MutexGuard<'_, ()>, ) -> bool { - let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite)) - .expect("Event content always serializes"); + let content = + to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite)) + .expect("Event content always serializes"); let new_event = PduBuilder { event_type: ruma::events::TimelineEventType::RoomMember, @@ -336,18 +403,23 @@ impl Service { room_id: &RoomId, user_id: &UserId, ) -> Result> { - self.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())? - .map_or(Ok(None), |s| { - serde_json::from_str(s.content.get()) - .map_err(|_| Error::bad_database("Invalid room member event in database.")) + self.room_state_get( + room_id, + &StateEventType::RoomMember, + user_id.as_str(), + )? + .map_or(Ok(None), |s| { + serde_json::from_str(s.content.get()).map_err(|_| { + Error::bad_database("Invalid room member event in database.") }) + }) } /// Checks if a given user can redact a given event /// - /// If `federation` is `true`, it allows redaction events from any user of the same server - /// as the original event sender, [as required by room versions >= - /// v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions) + /// If `federation` is `true`, it allows redaction events from any user of + /// the same server as the original event sender, [as required by room + /// versions >= v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions) pub(crate) fn user_can_redact( &self, redacts: &EventId, @@ -359,18 +431,23 @@ impl Service { .map_or_else( // Falling back on m.room.create to judge power levels || { - if let Some(pdu) = - self.room_state_get(room_id, &StateEventType::RoomCreate, "")? - { + if let Some(pdu) = self.room_state_get( + room_id, + &StateEventType::RoomCreate, + "", + )? { Ok(pdu.sender == sender - || if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) { + || if let Ok(Some(pdu)) = + services().rooms.timeline.get_pdu(redacts) + { pdu.sender == sender } else { false }) } else { Err(Error::bad_database( - "No m.room.power_levels or m.room.create events in database for room", + "No m.room.power_levels or m.room.create events \ + in database for room", )) } }, @@ -380,11 +457,14 @@ impl Service { .map(|e: RoomPowerLevels| { e.user_can_redact_event_of_other(sender) || e.user_can_redact_own_event(sender) - && if let Ok(Some(pdu)) = - services().rooms.timeline.get_pdu(redacts) + && if let Ok(Some(pdu)) = services() + .rooms + .timeline + .get_pdu(redacts) { if federation { - pdu.sender().server_name() == sender.server_name() + pdu.sender().server_name() + == sender.server_name() } else { pdu.sender == sender } @@ -393,7 +473,9 @@ impl Service { } }) .map_err(|_| { - Error::bad_database("Invalid m.room.power_levels event in database") + Error::bad_database( + "Invalid m.room.power_levels event in database", + ) }) }, ) diff --git a/src/service/rooms/state_accessor/data.rs b/src/service/rooms/state_accessor/data.rs index a57cdb9d..68214f1d 100644 --- a/src/service/rooms/state_accessor/data.rs +++ b/src/service/rooms/state_accessor/data.rs @@ -9,14 +9,18 @@ use crate::{PduEvent, Result}; pub(crate) trait Data: Send + Sync { /// Builds a StateMap by iterating over all keys that start /// with state_hash, this gives the full state for the given state_hash. - async fn state_full_ids(&self, shortstatehash: u64) -> Result>>; + async fn state_full_ids( + &self, + shortstatehash: u64, + ) -> Result>>; async fn state_full( &self, shortstatehash: u64, ) -> Result>>; - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn state_get_id( &self, shortstatehash: u64, @@ -24,7 +28,8 @@ pub(crate) trait Data: Send + Sync { state_key: &str, ) -> Result>>; - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn state_get( &self, shortstatehash: u64, @@ -41,7 +46,8 @@ pub(crate) trait Data: Send + Sync { room_id: &RoomId, ) -> Result>>; - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn room_state_get_id( &self, room_id: &RoomId, @@ -49,7 +55,8 @@ pub(crate) trait Data: Send + Sync { state_key: &str, ) -> Result>>; - /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`). + /// Returns a single PDU from `room_id` with key (`event_type`, + /// `state_key`). fn room_state_get( &self, room_id: &RoomId, diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 9b15cd88..bf4ce632 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -2,7 +2,6 @@ mod data; use std::{collections::HashSet, sync::Arc}; pub(crate) use data::Data; - use ruma::{ events::{ direct::DirectEvent, @@ -34,7 +33,8 @@ impl Service { last_state: Option>>, update_joined_count: bool, ) -> Result<()> { - // Keep track what remote users exist by adding them as "deactivated" users + // Keep track what remote users exist by adding them as "deactivated" + // users if user_id.server_name() != services().globals.server_name() { services().users.create(user_id, None)?; // TODO: displayname, avatar url @@ -51,17 +51,26 @@ impl Service { if let Some(predecessor) = services() .rooms .state_accessor - .room_state_get(room_id, &StateEventType::RoomCreate, "")? - .and_then(|create| serde_json::from_str(create.content.get()).ok()) - .and_then(|content: RoomCreateEventContent| content.predecessor) + .room_state_get( + room_id, + &StateEventType::RoomCreate, + "", + )? + .and_then(|create| { + serde_json::from_str(create.content.get()).ok() + }) + .and_then(|content: RoomCreateEventContent| { + content.predecessor + }) { - // Copy user settings from predecessor to the current room: + // Copy user settings from predecessor to the current + // room: // - Push rules // // TODO: finish this once push rules are implemented. // - // let mut push_rules_event_content: PushRulesEvent = account_data - // .get( + // let mut push_rules_event_content: PushRulesEvent = + // account_data .get( // None, // user_id, // EventType::PushRules, @@ -90,8 +99,13 @@ impl Service { )? .map(|event| { serde_json::from_str(event.get()).map_err(|e| { - warn!("Invalid account data event in db: {e:?}"); - Error::BadDatabase("Invalid account data event in db.") + warn!( + "Invalid account data event in db: \ + {e:?}" + ); + Error::BadDatabase( + "Invalid account data event in db.", + ) }) }) { @@ -112,20 +126,32 @@ impl Service { .get( None, user_id, - GlobalAccountDataEventType::Direct.to_string().into(), + GlobalAccountDataEventType::Direct + .to_string() + .into(), )? .map(|event| { - serde_json::from_str::(event.get()).map_err(|e| { - warn!("Invalid account data event in db: {e:?}"); - Error::BadDatabase("Invalid account data event in db.") - }) + serde_json::from_str::(event.get()) + .map_err(|e| { + warn!( + "Invalid account data event in \ + db: {e:?}" + ); + Error::BadDatabase( + "Invalid account data event in db.", + ) + }) }) { let mut direct_event = direct_event?; let mut room_ids_updated = false; - for room_ids in direct_event.content.0.values_mut() { - if room_ids.iter().any(|r| r == &predecessor.room_id) { + for room_ids in direct_event.content.0.values_mut() + { + if room_ids + .iter() + .any(|r| r == &predecessor.room_id) + { room_ids.push(room_id.to_owned()); room_ids_updated = true; } @@ -135,7 +161,9 @@ impl Service { services().account_data.update( None, user_id, - GlobalAccountDataEventType::Direct.to_string().into(), + GlobalAccountDataEventType::Direct + .to_string() + .into(), &serde_json::to_value(&direct_event) .expect("to json always works"), )?; @@ -160,9 +188,14 @@ impl Service { .into(), )? .map(|event| { - serde_json::from_str::(event.get()).map_err(|e| { + serde_json::from_str::( + event.get(), + ) + .map_err(|e| { warn!("Invalid account data event in db: {e:?}"); - Error::BadDatabase("Invalid account data event in db.") + Error::BadDatabase( + "Invalid account data event in db.", + ) }) }) .transpose()? @@ -199,7 +232,10 @@ impl Service { } #[tracing::instrument(skip(self, room_id))] - pub(crate) fn get_our_real_users(&self, room_id: &RoomId) -> Result>> { + pub(crate) fn get_our_real_users( + &self, + room_id: &RoomId, + ) -> Result>> { self.db.get_our_real_users(room_id) } @@ -214,7 +250,11 @@ impl Service { /// Makes a user forget a room. #[tracing::instrument(skip(self))] - pub(crate) fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()> { + pub(crate) fn forget( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result<()> { self.db.forget(room_id, user_id) } @@ -228,11 +268,16 @@ impl Service { } #[tracing::instrument(skip(self))] - pub(crate) fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result { + pub(crate) fn server_in_room( + &self, + server: &ServerName, + room_id: &RoomId, + ) -> Result { self.db.server_in_room(server, room_id) } - /// Returns an iterator of all rooms a server participates in (as far as we know). + /// Returns an iterator of all rooms a server participates in (as far as we + /// know). #[tracing::instrument(skip(self))] pub(crate) fn server_rooms<'a>( &'a self, @@ -251,12 +296,18 @@ impl Service { } #[tracing::instrument(skip(self))] - pub(crate) fn room_joined_count(&self, room_id: &RoomId) -> Result> { + pub(crate) fn room_joined_count( + &self, + room_id: &RoomId, + ) -> Result> { self.db.room_joined_count(room_id) } #[tracing::instrument(skip(self))] - pub(crate) fn room_invited_count(&self, room_id: &RoomId) -> Result> { + pub(crate) fn room_invited_count( + &self, + room_id: &RoomId, + ) -> Result> { self.db.room_invited_count(room_id) } @@ -288,7 +339,11 @@ impl Service { } #[tracing::instrument(skip(self))] - pub(crate) fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result> { + pub(crate) fn get_left_count( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { self.db.get_left_count(room_id, user_id) } @@ -306,7 +361,9 @@ impl Service { pub(crate) fn rooms_invited<'a>( &'a self, user_id: &UserId, - ) -> impl Iterator>)>> + 'a { + ) -> impl Iterator< + Item = Result<(OwnedRoomId, Vec>)>, + > + 'a { self.db.rooms_invited(user_id) } @@ -333,27 +390,44 @@ impl Service { pub(crate) fn rooms_left<'a>( &'a self, user_id: &UserId, - ) -> impl Iterator>)>> + 'a { + ) -> impl Iterator>)>> + 'a + { self.db.rooms_left(user_id) } #[tracing::instrument(skip(self))] - pub(crate) fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn once_joined( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { self.db.once_joined(user_id, room_id) } #[tracing::instrument(skip(self))] - pub(crate) fn is_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn is_joined( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { self.db.is_joined(user_id, room_id) } #[tracing::instrument(skip(self))] - pub(crate) fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn is_invited( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { self.db.is_invited(user_id, room_id) } #[tracing::instrument(skip(self))] - pub(crate) fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> Result { + pub(crate) fn is_left( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result { self.db.is_left(user_id, room_id) } } diff --git a/src/service/rooms/state_cache/data.rs b/src/service/rooms/state_cache/data.rs index c92ec848..94791c85 100644 --- a/src/service/rooms/state_cache/data.rs +++ b/src/service/rooms/state_cache/data.rs @@ -1,14 +1,19 @@ use std::{collections::HashSet, sync::Arc}; -use crate::{service::appservice::RegistrationInfo, Result}; use ruma::{ events::{AnyStrippedStateEvent, AnySyncStateEvent}, serde::Raw, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, }; +use crate::{service::appservice::RegistrationInfo, Result}; + pub(crate) trait Data: Send + Sync { - fn mark_as_once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; + fn mark_as_once_joined( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result<()>; fn mark_as_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; fn mark_as_invited( &self, @@ -20,9 +25,16 @@ pub(crate) trait Data: Send + Sync { fn update_joined_count(&self, room_id: &RoomId) -> Result<()>; - fn get_our_real_users(&self, room_id: &RoomId) -> Result>>; + fn get_our_real_users( + &self, + room_id: &RoomId, + ) -> Result>>; - fn appservice_in_room(&self, room_id: &RoomId, appservice: &RegistrationInfo) -> Result; + fn appservice_in_room( + &self, + room_id: &RoomId, + appservice: &RegistrationInfo, + ) -> Result; /// Makes a user forget a room. fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()>; @@ -33,9 +45,14 @@ pub(crate) trait Data: Send + Sync { room_id: &RoomId, ) -> Box> + 'a>; - fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result; + fn server_in_room( + &self, + server: &ServerName, + room_id: &RoomId, + ) -> Result; - /// Returns an iterator of all rooms a server participates in (as far as we know). + /// Returns an iterator of all rooms a server participates in (as far as we + /// know). fn server_rooms<'a>( &'a self, server: &ServerName, @@ -63,9 +80,17 @@ pub(crate) trait Data: Send + Sync { room_id: &RoomId, ) -> Box> + 'a>; - fn get_invite_count(&self, room_id: &RoomId, user_id: &UserId) -> Result>; + fn get_invite_count( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result>; - fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result>; + fn get_left_count( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result>; /// Returns an iterator over all rooms this user joined. fn rooms_joined<'a>( @@ -78,7 +103,11 @@ pub(crate) trait Data: Send + Sync { fn rooms_invited<'a>( &'a self, user_id: &UserId, - ) -> Box>)>> + 'a>; + ) -> Box< + dyn Iterator< + Item = Result<(OwnedRoomId, Vec>)>, + > + 'a, + >; fn invite_state( &self, @@ -97,7 +126,10 @@ pub(crate) trait Data: Send + Sync { fn rooms_left<'a>( &'a self, user_id: &UserId, - ) -> Box>)>> + 'a>; + ) -> Box< + dyn Iterator>)>> + + 'a, + >; fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result; diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 13f0912a..6e5fedf0 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -9,9 +9,8 @@ pub(crate) use data::Data; use lru_cache::LruCache; use ruma::{EventId, RoomId}; -use crate::{services, utils, Result}; - use self::data::StateDiff; +use crate::{services, utils, Result}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -37,7 +36,8 @@ pub(crate) struct Service { pub(crate) type CompressedStateEvent = [u8; 2 * size_of::()]; impl Service { - /// Returns a stack with info on shortstatehash, full state, added diff and removed diff for the selected shortstatehash and each parent layer. + /// Returns a stack with info on shortstatehash, full state, added diff and + /// removed diff for the selected shortstatehash and each parent layer. #[allow(clippy::type_complexity)] #[tracing::instrument(skip(self))] pub(crate) fn load_shortstatehash_info( @@ -55,11 +55,8 @@ impl Service { Arc>, )>, > { - if let Some(r) = self - .stateinfo_cache - .lock() - .unwrap() - .get_mut(&shortstatehash) + if let Some(r) = + self.stateinfo_cache.lock().unwrap().get_mut(&shortstatehash) { return Ok(r.clone()); } @@ -79,7 +76,12 @@ impl Service { state.remove(r); } - response.push((shortstatehash, Arc::new(state), added, Arc::new(removed))); + response.push(( + shortstatehash, + Arc::new(state), + added, + Arc::new(removed), + )); self.stateinfo_cache .lock() @@ -88,7 +90,8 @@ impl Service { Ok(response) } else { - let response = vec![(shortstatehash, added.clone(), added, removed)]; + let response = + vec![(shortstatehash, added.clone(), added, removed)]; self.stateinfo_cache .lock() .unwrap() @@ -132,19 +135,24 @@ impl Service { )) } - /// Creates a new shortstatehash that often is just a diff to an already existing - /// shortstatehash and therefore very efficient. + /// Creates a new shortstatehash that often is just a diff to an already + /// existing shortstatehash and therefore very efficient. /// - /// There are multiple layers of diffs. The bottom layer 0 always contains the full state. Layer - /// 1 contains diffs to states of layer 0, layer 2 diffs to layer 1 and so on. If layer n > 0 - /// grows too big, it will be combined with layer n-1 to create a new diff on layer n-1 that's - /// based on layer n-2. If that layer is also too big, it will recursively fix above layers too. + /// There are multiple layers of diffs. The bottom layer 0 always contains + /// the full state. Layer 1 contains diffs to states of layer 0, layer 2 + /// diffs to layer 1 and so on. If layer n > 0 grows too big, it will be + /// combined with layer n-1 to create a new diff on layer n-1 that's + /// based on layer n-2. If that layer is also too big, it will recursively + /// fix above layers too. /// /// * `shortstatehash` - Shortstatehash of this state /// * `statediffnew` - Added to base. Each vec is shortstatekey+shorteventid - /// * `statediffremoved` - Removed from base. Each vec is shortstatekey+shorteventid - /// * `diff_to_sibling` - Approximately how much the diff grows each time for this layer - /// * `parent_states` - A stack with info on shortstatehash, full state, added diff and removed diff for each parent layer + /// * `statediffremoved` - Removed from base. Each vec is + /// shortstatekey+shorteventid + /// * `diff_to_sibling` - Approximately how much the diff grows each time + /// for this layer + /// * `parent_states` - A stack with info on shortstatehash, full state, + /// added diff and removed diff for each parent layer #[allow(clippy::type_complexity)] #[tracing::instrument(skip( self, @@ -185,7 +193,8 @@ impl Service { // It was not added in the parent and we removed it parent_removed.insert(*removed); } - // Else it was added in the parent and we removed it again. We can forget this change + // Else it was added in the parent and we removed it again. We + // can forget this change } for new in statediffnew.iter() { @@ -193,7 +202,8 @@ impl Service { // It was not touched in the parent and we added it parent_new.insert(*new); } - // Else it was removed in the parent and we added it again. We can forget this change + // Else it was removed in the parent and we added it again. We + // can forget this change } self.save_state_from_diff( @@ -238,7 +248,8 @@ impl Service { // It was not added in the parent and we removed it parent_removed.insert(*removed); } - // Else it was added in the parent and we removed it again. We can forget this change + // Else it was added in the parent and we removed it again. We + // can forget this change } for new in statediffnew.iter() { @@ -246,7 +257,8 @@ impl Service { // It was not touched in the parent and we added it parent_new.insert(*new); } - // Else it was removed in the parent and we added it again. We can forget this change + // Else it was removed in the parent and we added it again. We + // can forget this change } self.save_state_from_diff( @@ -271,7 +283,8 @@ impl Service { Ok(()) } - /// Returns the new shortstatehash, and the state diff from the previous room state + /// Returns the new shortstatehash, and the state diff from the previous + /// room state #[allow(clippy::type_complexity)] pub(crate) fn save_state( &self, @@ -282,7 +295,8 @@ impl Service { Arc>, Arc>, )> { - let previous_shortstatehash = services().rooms.state.get_room_shortstatehash(room_id)?; + let previous_shortstatehash = + services().rooms.state.get_room_shortstatehash(room_id)?; let state_hash = utils::calculate_hash( &new_state_ids_compressed @@ -291,10 +305,8 @@ impl Service { .collect::>(), ); - let (new_shortstatehash, already_existed) = services() - .rooms - .short - .get_or_create_shortstatehash(&state_hash)?; + let (new_shortstatehash, already_existed) = + services().rooms.short.get_or_create_shortstatehash(&state_hash)?; if Some(new_shortstatehash) == previous_shortstatehash { return Ok(( @@ -304,26 +316,28 @@ impl Service { )); } - let states_parents = previous_shortstatehash - .map_or_else(|| Ok(Vec::new()), |p| self.load_shortstatehash_info(p))?; + let states_parents = previous_shortstatehash.map_or_else( + || Ok(Vec::new()), + |p| self.load_shortstatehash_info(p), + )?; - let (statediffnew, statediffremoved) = if let Some(parent_stateinfo) = states_parents.last() - { - let statediffnew: HashSet<_> = new_state_ids_compressed - .difference(&parent_stateinfo.1) - .copied() - .collect(); + let (statediffnew, statediffremoved) = + if let Some(parent_stateinfo) = states_parents.last() { + let statediffnew: HashSet<_> = new_state_ids_compressed + .difference(&parent_stateinfo.1) + .copied() + .collect(); - let statediffremoved: HashSet<_> = parent_stateinfo - .1 - .difference(&new_state_ids_compressed) - .copied() - .collect(); + let statediffremoved: HashSet<_> = parent_stateinfo + .1 + .difference(&new_state_ids_compressed) + .copied() + .collect(); - (Arc::new(statediffnew), Arc::new(statediffremoved)) - } else { - (new_state_ids_compressed, Arc::new(HashSet::new())) - }; + (Arc::new(statediffnew), Arc::new(statediffremoved)) + } else { + (new_state_ids_compressed, Arc::new(HashSet::new())) + }; if !already_existed { self.save_state_from_diff( diff --git a/src/service/rooms/state_compressor/data.rs b/src/service/rooms/state_compressor/data.rs index 3000b4ad..3d9ffc19 100644 --- a/src/service/rooms/state_compressor/data.rs +++ b/src/service/rooms/state_compressor/data.rs @@ -11,5 +11,9 @@ pub(crate) struct StateDiff { pub(crate) trait Data: Send + Sync { fn get_statediff(&self, shortstatehash: u64) -> Result; - fn save_statediff(&self, shortstatehash: u64, diff: StateDiff) -> Result<()>; + fn save_statediff( + &self, + shortstatehash: u64, + diff: StateDiff, + ) -> Result<()>; } diff --git a/src/service/rooms/threads.rs b/src/service/rooms/threads.rs index 99f28c8d..c2e9ed11 100644 --- a/src/service/rooms/threads.rs +++ b/src/service/rooms/threads.rs @@ -6,7 +6,6 @@ use ruma::{ events::relation::BundledThread, uint, CanonicalJsonObject, CanonicalJsonValue, EventId, RoomId, UserId, }; - use serde_json::json; use crate::{services, Error, PduEvent, Result}; @@ -26,51 +25,64 @@ impl Service { self.db.threads_until(user_id, room_id, until, include) } - pub(crate) fn add_to_thread(&self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> { - let root_id = &services() - .rooms - .timeline - .get_pdu_id(root_event_id)? - .ok_or_else(|| { - Error::BadRequest( - ErrorKind::InvalidParam, - "Invalid event id in thread message", - ) - })?; + pub(crate) fn add_to_thread( + &self, + root_event_id: &EventId, + pdu: &PduEvent, + ) -> Result<()> { + let root_id = + &services().rooms.timeline.get_pdu_id(root_event_id)?.ok_or_else( + || { + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid event id in thread message", + ) + }, + )?; - let root_pdu = services() - .rooms - .timeline - .get_pdu_from_id(root_id)? - .ok_or_else(|| { - Error::BadRequest(ErrorKind::InvalidParam, "Thread root pdu not found") - })?; + let root_pdu = + services().rooms.timeline.get_pdu_from_id(root_id)?.ok_or_else( + || { + Error::BadRequest( + ErrorKind::InvalidParam, + "Thread root pdu not found", + ) + }, + )?; let mut root_pdu_json = services() .rooms .timeline .get_pdu_json_from_id(root_id)? .ok_or_else(|| { - Error::BadRequest(ErrorKind::InvalidParam, "Thread root pdu not found") + Error::BadRequest( + ErrorKind::InvalidParam, + "Thread root pdu not found", + ) })?; - if let CanonicalJsonValue::Object(unsigned) = root_pdu_json - .entry("unsigned".to_owned()) - .or_insert_with(|| CanonicalJsonValue::Object(CanonicalJsonObject::default())) + if let CanonicalJsonValue::Object(unsigned) = + root_pdu_json.entry("unsigned".to_owned()).or_insert_with(|| { + CanonicalJsonValue::Object(CanonicalJsonObject::default()) + }) { if let Some(mut relations) = unsigned .get("m.relations") .and_then(|r| r.as_object()) .and_then(|r| r.get("m.thread")) .and_then(|relations| { - serde_json::from_value::(relations.clone().into()).ok() + serde_json::from_value::( + relations.clone().into(), + ) + .ok() }) { // Thread already existed relations.count += uint!(1); relations.latest_event = pdu.to_message_like_event(); - let content = serde_json::to_value(relations).expect("to_value always works"); + let content = serde_json::to_value(relations) + .expect("to_value always works"); unsigned.insert( "m.relations".to_owned(), @@ -86,7 +98,8 @@ impl Service { current_user_participated: true, }; - let content = serde_json::to_value(relations).expect("to_value always works"); + let content = serde_json::to_value(relations) + .expect("to_value always works"); unsigned.insert( "m.relations".to_owned(), @@ -96,10 +109,11 @@ impl Service { ); } - services() - .rooms - .timeline - .replace_pdu(root_id, &root_pdu_json, &root_pdu)?; + services().rooms.timeline.replace_pdu( + root_id, + &root_pdu_json, + &root_pdu, + )?; } let mut users = Vec::new(); diff --git a/src/service/rooms/threads/data.rs b/src/service/rooms/threads/data.rs index 42596e9b..8a1607db 100644 --- a/src/service/rooms/threads/data.rs +++ b/src/service/rooms/threads/data.rs @@ -1,5 +1,9 @@ +use ruma::{ + api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, + UserId, +}; + use crate::{PduEvent, Result}; -use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId}; pub(crate) trait Data: Send + Sync { #[allow(clippy::type_complexity)] @@ -11,6 +15,13 @@ pub(crate) trait Data: Send + Sync { include: &'a IncludeThreads, ) -> Result> + 'a>>; - fn update_participants(&self, root_id: &[u8], participants: &[OwnedUserId]) -> Result<()>; - fn get_participants(&self, root_id: &[u8]) -> Result>>; + fn update_participants( + &self, + root_id: &[u8], + participants: &[OwnedUserId], + ) -> Result<()>; + fn get_participants( + &self, + root_id: &[u8], + ) -> Result>>; } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 42b802bc..1cc479fc 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -7,29 +7,31 @@ use std::{ }; pub(crate) use data::Data; - use ruma::{ api::{client::error::ErrorKind, federation}, canonical_json::to_canonical_value, events::{ push_rules::PushRulesEvent, room::{ - create::RoomCreateEventContent, encrypted::Relation, member::MembershipState, - power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent, + create::RoomCreateEventContent, encrypted::Relation, + member::MembershipState, power_levels::RoomPowerLevelsEventContent, + redaction::RoomRedactionEventContent, }, GlobalAccountDataEventType, StateEventType, TimelineEventType, }, push::{Action, Ruleset, Tweak}, serde::Base64, state_res::{self, Event, RoomVersion}, - uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, - OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, + uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, + OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId, + ServerName, UserId, }; use serde::Deserialize; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::{Mutex, MutexGuard, RwLock}; use tracing::{error, info, warn}; +use super::state_compressor::CompressedStateEvent; use crate::{ api::server_server, service::{ @@ -39,8 +41,6 @@ use crate::{ services, utils, Error, PduEvent, Result, }; -use super::state_compressor::CompressedStateEvent; - #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] pub(crate) enum PduCount { Backfilled(u64), @@ -48,8 +48,8 @@ pub(crate) enum PduCount { } impl PduCount { - pub(crate) const MIN: Self = Self::Backfilled(u64::MAX); pub(crate) const MAX: Self = Self::Normal(u64::MAX); + pub(crate) const MIN: Self = Self::Backfilled(u64::MAX); pub(crate) fn try_from_string(token: &str) -> Result { if let Some(stripped) = token.strip_prefix('-') { @@ -57,7 +57,12 @@ impl PduCount { } else { token.parse().map(PduCount::Normal) } - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid pagination token.")) + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid pagination token.", + ) + }) } pub(crate) fn stringify(&self) -> String { @@ -93,7 +98,10 @@ pub(crate) struct Service { impl Service { #[tracing::instrument(skip(self))] - pub(crate) fn first_pdu_in_room(&self, room_id: &RoomId) -> Result>> { + pub(crate) fn first_pdu_in_room( + &self, + room_id: &RoomId, + ) -> Result>> { self.all_pdus(user_id!("@doesntmatter:grapevine"), room_id)? .next() .map(|o| o.map(|(_, p)| Arc::new(p))) @@ -110,12 +118,18 @@ impl Service { } /// Returns the `count` of this pdu's id. - pub(crate) fn get_pdu_count(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_pdu_count( + &self, + event_id: &EventId, + ) -> Result> { self.db.get_pdu_count(event_id) } /// Returns the json of a pdu. - pub(crate) fn get_pdu_json(&self, event_id: &EventId) -> Result> { + pub(crate) fn get_pdu_json( + &self, + event_id: &EventId, + ) -> Result> { self.db.get_pdu_json(event_id) } @@ -128,21 +142,30 @@ impl Service { } /// Returns the pdu's id. - pub(crate) fn get_pdu_id(&self, event_id: &EventId) -> Result>> { + pub(crate) fn get_pdu_id( + &self, + event_id: &EventId, + ) -> Result>> { self.db.get_pdu_id(event_id) } /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. - pub(crate) fn get_pdu(&self, event_id: &EventId) -> Result>> { + pub(crate) fn get_pdu( + &self, + event_id: &EventId, + ) -> Result>> { self.db.get_pdu(event_id) } /// Returns the pdu. /// /// This does __NOT__ check the outliers `Tree`. - pub(crate) fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result> { + pub(crate) fn get_pdu_from_id( + &self, + pdu_id: &[u8], + ) -> Result> { self.db.get_pdu_from_id(pdu_id) } @@ -167,8 +190,8 @@ impl Service { /// Creates a new persisted data unit and adds it to a room. /// - /// By this point the incoming event should be fully authenticated, no auth happens - /// in `append_pdu`. + /// By this point the incoming event should be fully authenticated, no auth + /// happens in `append_pdu`. /// /// Returns pdu id #[tracing::instrument(skip(self, pdu, pdu_json, leaves))] @@ -186,13 +209,15 @@ impl Service { .get_shortroomid(&pdu.room_id)? .expect("room exists"); - // Make unsigned fields correct. This is not properly documented in the spec, but state - // events need to have previous content in the unsigned field, so clients can easily - // interpret things like membership changes + // Make unsigned fields correct. This is not properly documented in the + // spec, but state events need to have previous content in the + // unsigned field, so clients can easily interpret things like + // membership changes if let Some(state_key) = &pdu.state_key { - if let CanonicalJsonValue::Object(unsigned) = pdu_json - .entry("unsigned".to_owned()) - .or_insert_with(|| CanonicalJsonValue::Object(CanonicalJsonObject::default())) + if let CanonicalJsonValue::Object(unsigned) = + pdu_json.entry("unsigned".to_owned()).or_insert_with(|| { + CanonicalJsonValue::Object(CanonicalJsonObject::default()) + }) { if let Some(shortstatehash) = services() .rooms @@ -203,14 +228,20 @@ impl Service { if let Some(prev_state) = services() .rooms .state_accessor - .state_get(shortstatehash, &pdu.kind.to_string().into(), state_key) + .state_get( + shortstatehash, + &pdu.kind.to_string().into(), + state_key, + ) .unwrap() { unsigned.insert( "prev_content".to_owned(), CanonicalJsonValue::Object( - utils::to_canonical_object(prev_state.content.clone()) - .expect("event is valid, we just created it"), + utils::to_canonical_object( + prev_state.content.clone(), + ) + .expect("event is valid, we just created it"), ), ); } @@ -225,10 +256,11 @@ impl Service { .rooms .pdu_metadata .mark_as_referenced(&pdu.room_id, &pdu.prev_events)?; - services() - .rooms - .state - .set_forward_extremities(&pdu.room_id, leaves, state_lock)?; + services().rooms.state.set_forward_extremities( + &pdu.room_id, + leaves, + state_lock, + )?; let mutex_insert = Arc::clone( services() @@ -242,13 +274,13 @@ impl Service { let insert_lock = mutex_insert.lock().await; let count1 = services().globals.next_count()?; - // Mark as read first so the sending client doesn't get a notification even if appending - // fails - services() - .rooms - .edus - .read_receipt - .private_read_set(&pdu.room_id, &pdu.sender, count1)?; + // Mark as read first so the sending client doesn't get a notification + // even if appending fails + services().rooms.edus.read_receipt.private_read_set( + &pdu.room_id, + &pdu.sender, + count1, + )?; services() .rooms .user @@ -269,8 +301,9 @@ impl Service { .state_accessor .room_state_get(&pdu.room_id, &StateEventType::RoomPowerLevels, "")? .map(|ev| { - serde_json::from_str(ev.content.get()) - .map_err(|_| Error::bad_database("invalid m.room.power_levels event")) + serde_json::from_str(ev.content.get()).map_err(|_| { + Error::bad_database("invalid m.room.power_levels event") + }) }) .transpose()? .unwrap_or_default(); @@ -280,10 +313,8 @@ impl Service { let mut notifies = Vec::new(); let mut highlights = Vec::new(); - let mut push_target = services() - .rooms - .state_cache - .get_our_real_users(&pdu.room_id)?; + let mut push_target = + services().rooms.state_cache.get_our_real_users(&pdu.room_id)?; if pdu.kind == TimelineEventType::RoomMember { if let Some(state_key) = &pdu.state_key { @@ -312,8 +343,13 @@ impl Service { GlobalAccountDataEventType::PushRules.to_string().into(), )? .map(|event| { - serde_json::from_str::(event.get()) - .map_err(|_| Error::bad_database("Invalid push rules event in db.")) + serde_json::from_str::(event.get()).map_err( + |_| { + Error::bad_database( + "Invalid push rules event in db.", + ) + }, + ) }) .transpose()? .map_or_else( @@ -353,12 +389,16 @@ impl Service { } } - self.db - .increment_notification_counts(&pdu.room_id, notifies, highlights)?; + self.db.increment_notification_counts( + &pdu.room_id, + notifies, + highlights, + )?; match pdu.kind { TimelineEventType::RoomRedaction => { - let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; + let room_version_id = + services().rooms.state.get_room_version(&pdu.room_id)?; match room_version_id { RoomVersionId::V1 | RoomVersionId::V2 @@ -383,10 +423,14 @@ impl Service { } RoomVersionId::V11 => { let content = - serde_json::from_str::(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; + serde_json::from_str::( + pdu.content.get(), + ) + .map_err(|_| { + Error::bad_database( + "Invalid content in redaction pdu.", + ) + })?; if let Some(redact_id) = &content.redacts { if services().rooms.state_accessor.user_can_redact( redact_id, @@ -398,7 +442,9 @@ impl Service { } } } - _ => unreachable!("Validity of room version already checked"), + _ => { + unreachable!("Validity of room version already checked") + } }; } TimelineEventType::SpaceChild => { @@ -423,19 +469,27 @@ impl Service { let target_user_id = UserId::parse(state_key.clone()) .expect("This state_key was previously validated"); - let content = serde_json::from_str::(pdu.content.get()) - .map_err(|_| Error::bad_database("Invalid content in pdu."))?; + let content = serde_json::from_str::( + pdu.content.get(), + ) + .map_err(|_| { + Error::bad_database("Invalid content in pdu.") + })?; let invite_state = match content.membership { MembershipState::Invite => { - let state = services().rooms.state.calculate_invite_state(pdu)?; + let state = services() + .rooms + .state + .calculate_invite_state(pdu)?; Some(state) } _ => None, }; - // Update our membership info, we do this here incase a user is invited - // and immediately leaves we need the DB to record the invite event for auth + // Update our membership info, we do this here incase a user + // is invited and immediately leaves we + // need the DB to record the invite event for auth services().rooms.state_cache.update_membership( &pdu.room_id, &target_user_id, @@ -452,14 +506,18 @@ impl Service { body: Option, } - let content = serde_json::from_str::(pdu.content.get()) - .map_err(|_| Error::bad_database("Invalid content in pdu."))?; + let content = + serde_json::from_str::(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid content in pdu.") + })?; if let Some(body) = content.body { - services() - .rooms - .search - .index_pdu(shortroomid, &pdu_id, &body)?; + services().rooms.search.index_pdu( + shortroomid, + &pdu_id, + &body, + )?; let server_user = format!( "@{}:{}", @@ -471,18 +529,25 @@ impl Service { services().globals.server_name() ); - let to_grapevine = body.starts_with(&format!("{server_user}: ")) + let to_grapevine = body + .starts_with(&format!("{server_user}: ")) || body.starts_with(&format!("{server_user} ")) || body == format!("{server_user}:") || body == server_user; - // This will evaluate to false if the emergency password is set up so that - // the administrator can execute commands as grapevine + // This will evaluate to false if the emergency password is + // set up so that the administrator can + // execute commands as grapevine let from_grapevine = pdu.sender == server_user && services().globals.emergency_password().is_none(); - if let Some(admin_room) = services().admin.get_admin_room()? { - if to_grapevine && !from_grapevine && admin_room == pdu.room_id { + if let Some(admin_room) = + services().admin.get_admin_room()? + { + if to_grapevine + && !from_grapevine + && admin_room == pdu.room_id + { services().admin.process_message(body); } } @@ -493,7 +558,9 @@ impl Service { // Update Relationships - if let Ok(content) = serde_json::from_str::(pdu.content.get()) { + if let Ok(content) = + serde_json::from_str::(pdu.content.get()) + { if let Some(related_pducount) = services() .rooms .timeline @@ -506,9 +573,13 @@ impl Service { } } - if let Ok(content) = serde_json::from_str::(pdu.content.get()) { + if let Ok(content) = + serde_json::from_str::(pdu.content.get()) + { match content.relates_to { - Relation::Reply { in_reply_to } => { + Relation::Reply { + in_reply_to, + } => { // We need to do it again here, because replies don't have // event_id as a top level field if let Some(related_pducount) = services() @@ -516,10 +587,10 @@ impl Service { .timeline .get_pdu_count(&in_reply_to.event_id)? { - services() - .rooms - .pdu_metadata - .add_relation(PduCount::Normal(count2), related_pducount)?; + services().rooms.pdu_metadata.add_relation( + PduCount::Normal(count2), + related_pducount, + )?; } } Relation::Thread(thread) => { @@ -539,21 +610,24 @@ impl Service { .state_cache .appservice_in_room(&pdu.room_id, appservice)? { - services() - .sending - .send_pdu_appservice(appservice.registration.id.clone(), pdu_id.clone())?; + services().sending.send_pdu_appservice( + appservice.registration.id.clone(), + pdu_id.clone(), + )?; continue; } - // If the RoomMember event has a non-empty state_key, it is targeted at someone. - // If it is our appservice user, we send this PDU to it. + // If the RoomMember event has a non-empty state_key, it is targeted + // at someone. If it is our appservice user, we send + // this PDU to it. if pdu.kind == TimelineEventType::RoomMember { - if let Some(state_key_uid) = &pdu - .state_key - .as_ref() - .and_then(|state_key| UserId::parse(state_key.as_str()).ok()) + if let Some(state_key_uid) = + &pdu.state_key.as_ref().and_then(|state_key| { + UserId::parse(state_key.as_str()).ok() + }) { - let appservice_uid = appservice.registration.sender_localpart.as_str(); + let appservice_uid = + appservice.registration.sender_localpart.as_str(); if state_key_uid == appservice_uid { services().sending.send_pdu_appservice( appservice.registration.id.clone(), @@ -567,10 +641,9 @@ impl Service { let matching_users = |users: &NamespaceRegex| { appservice.users.is_match(pdu.sender.as_str()) || pdu.kind == TimelineEventType::RoomMember - && pdu - .state_key - .as_ref() - .map_or(false, |state_key| users.is_match(state_key)) + && pdu.state_key.as_ref().map_or(false, |state_key| { + users.is_match(state_key) + }) }; let matching_aliases = |aliases: &NamespaceRegex| { services() @@ -585,9 +658,10 @@ impl Service { || appservice.rooms.is_match(pdu.room_id.as_str()) || matching_users(&appservice.users) { - services() - .sending - .send_pdu_appservice(appservice.registration.id.clone(), pdu_id.clone())?; + services().sending.send_pdu_appservice( + appservice.registration.id.clone(), + pdu_id.clone(), + )?; } } @@ -620,13 +694,13 @@ impl Service { .collect(); // If there was no create event yet, assume we are creating a room - let room_version_id = services() - .rooms - .state - .get_room_version(room_id) - .or_else(|_| { + let room_version_id = + services().rooms.state.get_room_version(room_id).or_else(|_| { if event_type == TimelineEventType::RoomCreate { - let content = serde_json::from_str::(content.get()) + let content = + serde_json::from_str::( + content.get(), + ) .expect("Invalid content in RoomCreate pdu."); Ok(content.room_version) } else { @@ -637,7 +711,8 @@ impl Service { } })?; - let room_version = RoomVersion::new(&room_version_id).expect("room version is supported"); + let room_version = RoomVersion::new(&room_version_id) + .expect("room version is supported"); let auth_events = services().rooms.state.get_auth_events( room_id, @@ -658,18 +733,22 @@ impl Service { let mut unsigned = unsigned.unwrap_or_default(); if let Some(state_key) = &state_key { - if let Some(prev_pdu) = services().rooms.state_accessor.room_state_get( - room_id, - &event_type.to_string().into(), - state_key, - )? { + if let Some(prev_pdu) = + services().rooms.state_accessor.room_state_get( + room_id, + &event_type.to_string().into(), + state_key, + )? + { unsigned.insert( "prev_content".to_owned(), - serde_json::from_str(prev_pdu.content.get()).expect("string is valid json"), + serde_json::from_str(prev_pdu.content.get()) + .expect("string is valid json"), ); unsigned.insert( "prev_sender".to_owned(), - serde_json::to_value(&prev_pdu.sender).expect("UserId::to_value always works"), + serde_json::to_value(&prev_pdu.sender) + .expect("UserId::to_value always works"), ); } } @@ -694,7 +773,9 @@ impl Service { unsigned: if unsigned.is_empty() { None } else { - Some(to_raw_value(&unsigned).expect("to_raw_value always works")) + Some( + to_raw_value(&unsigned).expect("to_raw_value always works"), + ) }, hashes: EventHash { sha256: "aaa".to_owned(), @@ -722,8 +803,8 @@ impl Service { } // Hash and sign - let mut pdu_json = - utils::to_canonical_object(&pdu).expect("event is valid, we just created it"); + let mut pdu_json = utils::to_canonical_object(&pdu) + .expect("event is valid, we just created it"); pdu_json.remove("event_id"); @@ -769,16 +850,15 @@ impl Service { ); // Generate short event id - let _shorteventid = services() - .rooms - .short - .get_or_create_shorteventid(&pdu.event_id)?; + let _shorteventid = + services().rooms.short.get_or_create_shorteventid(&pdu.event_id)?; Ok((pdu, pdu_json)) } - /// Creates a new persisted data unit and adds it to a room. This function takes a - /// roomid_mutex_state, meaning that only this function is able to mutate the room state. + /// Creates a new persisted data unit and adds it to a room. This function + /// takes a roomid_mutex_state, meaning that only this function is able + /// to mutate the room state. #[tracing::instrument(skip(self, state_lock))] pub(crate) async fn build_and_append_pdu( &self, @@ -788,8 +868,12 @@ impl Service { // Take mutex guard to make sure users get the room state mutex state_lock: &MutexGuard<'_, ()>, ) -> Result> { - let (pdu, pdu_json) = - self.create_hash_and_sign_event(pdu_builder, sender, room_id, state_lock)?; + let (pdu, pdu_json) = self.create_hash_and_sign_event( + pdu_builder, + sender, + room_id, + state_lock, + )?; if let Some(admin_room) = services().admin.get_admin_room()? { if admin_room == room_id { @@ -820,15 +904,24 @@ impl Service { "grapevine" }, ); - let content = serde_json::from_str::(pdu.content.get()) - .map_err(|_| Error::bad_database("Invalid content in pdu."))?; + let content = + serde_json::from_str::( + pdu.content.get(), + ) + .map_err(|_| { + Error::bad_database("Invalid content in pdu.") + })?; if content.membership == MembershipState::Leave { if target == server_user { - warn!("Grapevine user cannot leave from admins room"); + warn!( + "Grapevine user cannot leave from admins \ + room" + ); return Err(Error::BadRequest( ErrorKind::Forbidden, - "Grapevine user cannot leave from admins room.", + "Grapevine user cannot leave from admins \ + room.", )); } @@ -841,7 +934,9 @@ impl Service { .filter(|m| m != target) .count(); if count < 2 { - warn!("Last admin cannot leave from admins room"); + warn!( + "Last admin cannot leave from admins room" + ); return Err(Error::BadRequest( ErrorKind::Forbidden, "Last admin cannot leave from admins room.", @@ -849,12 +944,18 @@ impl Service { } } - if content.membership == MembershipState::Ban && pdu.state_key().is_some() { + if content.membership == MembershipState::Ban + && pdu.state_key().is_some() + { if target == server_user { - warn!("Grapevine user cannot be banned in admins room"); + warn!( + "Grapevine user cannot be banned in \ + admins room" + ); return Err(Error::BadRequest( ErrorKind::Forbidden, - "Grapevine user cannot be banned in admins room.", + "Grapevine user cannot be banned in \ + admins room.", )); } @@ -867,10 +968,14 @@ impl Service { .filter(|m| m != target) .count(); if count < 2 { - warn!("Last admin cannot be banned in admins room"); + warn!( + "Last admin cannot be banned in admins \ + room" + ); return Err(Error::BadRequest( ErrorKind::Forbidden, - "Last admin cannot be banned in admins room.", + "Last admin cannot be banned in admins \ + room.", )); } } @@ -880,7 +985,8 @@ impl Service { } } - // If redaction event is not authorized, do not append it to the timeline + // If redaction event is not authorized, do not append it to the + // timeline if pdu.kind == TimelineEventType::RoomRedaction { match services().rooms.state.get_room_version(&pdu.room_id)? { RoomVersionId::V1 @@ -908,11 +1014,12 @@ impl Service { }; } RoomVersionId::V11 => { - let content = - serde_json::from_str::(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; + let content = serde_json::from_str::< + RoomRedactionEventContent, + >(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid content in redaction pdu.") + })?; if let Some(redact_id) = &content.redacts { if !services().rooms.state_accessor.user_can_redact( @@ -937,27 +1044,30 @@ impl Service { } } - // We append to state before appending the pdu, so we don't have a moment in time with the - // pdu without it's state. This is okay because append_pdu can't fail. + // We append to state before appending the pdu, so we don't have a + // moment in time with the pdu without it's state. This is okay + // because append_pdu can't fail. let statehashid = services().rooms.state.append_to_state(&pdu)?; let pdu_id = self .append_pdu( &pdu, pdu_json, - // Since this PDU references all pdu_leaves we can update the leaves - // of the room + // Since this PDU references all pdu_leaves we can update the + // leaves of the room vec![(*pdu.event_id).to_owned()], state_lock, ) .await?; - // We set the room state after inserting the pdu, so that we never have a moment in time - // where events in the current room state do not exist - services() - .rooms - .state - .set_room_state(room_id, statehashid, state_lock)?; + // We set the room state after inserting the pdu, so that we never have + // a moment in time where events in the current room state do + // not exist + services().rooms.state.set_room_state( + room_id, + statehashid, + state_lock, + )?; let mut servers: HashSet = services() .rooms @@ -966,7 +1076,8 @@ impl Service { .filter_map(Result::ok) .collect(); - // In case we are kicking or banning a user, we need to inform their server of the change + // In case we are kicking or banning a user, we need to inform their + // server of the change if pdu.kind == TimelineEventType::RoomMember { if let Some(state_key_uid) = &pdu .state_key @@ -977,7 +1088,8 @@ impl Service { } } - // Remove our server from the server list since it will be added to it by room_servers() and/or the if statement above + // Remove our server from the server list since it will be added to it + // by room_servers() and/or the if statement above servers.remove(services().globals.server_name()); services().sending.send_pdu(servers.into_iter(), &pdu_id)?; @@ -985,8 +1097,8 @@ impl Service { Ok(pdu.event_id) } - /// Append the incoming event setting the state snapshot to the state from the - /// server that sent the event. + /// Append the incoming event setting the state snapshot to the state from + /// the server that sent the event. #[tracing::instrument(skip_all)] pub(crate) async fn append_incoming_pdu( &self, @@ -998,8 +1110,9 @@ impl Service { // Take mutex guard to make sure users get the room state mutex state_lock: &MutexGuard<'_, ()>, ) -> Result>> { - // We append to state before appending the pdu, so we don't have a moment in time with the - // pdu without it's state. This is okay because append_pdu can't fail. + // We append to state before appending the pdu, so we don't have a + // moment in time with the pdu without it's state. This is okay + // because append_pdu can't fail. services().rooms.state.set_event_state( &pdu.event_id, &pdu.room_id, @@ -1037,8 +1150,9 @@ impl Service { self.pdus_after(user_id, room_id, PduCount::MIN) } - /// Returns an iterator over all events and their tokens in a room that happened before the - /// event with id `until` in reverse-chronological order. + /// Returns an iterator over all events and their tokens in a room that + /// happened before the event with id `until` in reverse-chronological + /// order. #[tracing::instrument(skip(self))] pub(crate) fn pdus_until<'a>( &'a self, @@ -1049,8 +1163,8 @@ impl Service { self.db.pdus_until(user_id, room_id, until) } - /// Returns an iterator over all events and their token in a room that happened after the event - /// with id `from` in chronological order. + /// Returns an iterator over all events and their token in a room that + /// happened after the event with id `from` in chronological order. #[tracing::instrument(skip(self))] pub(crate) fn pdus_after<'a>( &'a self, @@ -1063,13 +1177,18 @@ impl Service { /// Replace a PDU with the redacted form. #[tracing::instrument(skip(self, reason))] - pub(crate) fn redact_pdu(&self, event_id: &EventId, reason: &PduEvent) -> Result<()> { + pub(crate) fn redact_pdu( + &self, + event_id: &EventId, + reason: &PduEvent, + ) -> Result<()> { // TODO: Don't reserialize, keep original json if let Some(pdu_id) = self.get_pdu_id(event_id)? { - let mut pdu = self - .get_pdu_from_id(&pdu_id)? - .ok_or_else(|| Error::bad_database("PDU ID points to invalid PDU."))?; - let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; + let mut pdu = self.get_pdu_from_id(&pdu_id)?.ok_or_else(|| { + Error::bad_database("PDU ID points to invalid PDU.") + })?; + let room_version_id = + services().rooms.state.get_room_version(&pdu.room_id)?; pdu.redact(room_version_id, reason)?; self.replace_pdu( &pdu_id, @@ -1102,8 +1221,9 @@ impl Service { .state_accessor .room_state_get(room_id, &StateEventType::RoomPowerLevels, "")? .map(|ev| { - serde_json::from_str(ev.content.get()) - .map_err(|_| Error::bad_database("invalid m.room.power_levels event")) + serde_json::from_str(ev.content.get()).map_err(|_| { + Error::bad_database("invalid m.room.power_levels event") + }) }) .transpose()? .unwrap_or_default(); @@ -1133,7 +1253,9 @@ impl Service { Ok(response) => { let pub_key_map = RwLock::new(BTreeMap::new()); for pdu in response.pdus { - if let Err(e) = self.backfill_pdu(backfill_server, pdu, &pub_key_map).await + if let Err(e) = self + .backfill_pdu(backfill_server, pdu, &pub_key_map) + .await { warn!("Failed to add backfilled pdu: {e}"); } @@ -1157,7 +1279,8 @@ impl Service { pdu: Box, pub_key_map: &RwLock>>, ) -> Result<()> { - let (event_id, value, room_id) = server_server::parse_incoming_pdu(&pdu)?; + let (event_id, value, room_id) = + server_server::parse_incoming_pdu(&pdu)?; // Lock so we cannot backfill the same pdu twice at the same time let mutex = Arc::clone( @@ -1180,7 +1303,14 @@ impl Service { services() .rooms .event_handler - .handle_incoming_pdu(origin, &event_id, &room_id, value, false, pub_key_map) + .handle_incoming_pdu( + origin, + &event_id, + &room_id, + value, + false, + pub_key_map, + ) .await?; let value = self.get_pdu_json(&event_id)?.expect("We just created it"); @@ -1219,14 +1349,18 @@ impl Service { body: Option, } - let content = serde_json::from_str::(pdu.content.get()) - .map_err(|_| Error::bad_database("Invalid content in pdu."))?; + let content = + serde_json::from_str::(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid content in pdu.") + })?; if let Some(body) = content.body { - services() - .rooms - .search - .index_pdu(shortroomid, &pdu_id, &body)?; + services().rooms.search.index_pdu( + shortroomid, + &pdu_id, + &body, + )?; } } drop(mutex_lock); diff --git a/src/service/rooms/timeline/data.rs b/src/service/rooms/timeline/data.rs index 025e3ef0..5ea5ae03 100644 --- a/src/service/rooms/timeline/data.rs +++ b/src/service/rooms/timeline/data.rs @@ -2,21 +2,30 @@ use std::sync::Arc; use ruma::{CanonicalJsonObject, EventId, OwnedUserId, RoomId, UserId}; +use super::PduCount; use crate::{PduEvent, Result}; -use super::PduCount; - pub(crate) trait Data: Send + Sync { - fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result; + fn last_timeline_count( + &self, + sender_user: &UserId, + room_id: &RoomId, + ) -> Result; /// Returns the `count` of this pdu's id. fn get_pdu_count(&self, event_id: &EventId) -> Result>; /// Returns the json of a pdu. - fn get_pdu_json(&self, event_id: &EventId) -> Result>; + fn get_pdu_json( + &self, + event_id: &EventId, + ) -> Result>; /// Returns the json of a pdu. - fn get_non_outlier_pdu_json(&self, event_id: &EventId) -> Result>; + fn get_non_outlier_pdu_json( + &self, + event_id: &EventId, + ) -> Result>; /// Returns the pdu's id. fn get_pdu_id(&self, event_id: &EventId) -> Result>>; @@ -24,7 +33,10 @@ pub(crate) trait Data: Send + Sync { /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. - fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result>; + fn get_non_outlier_pdu( + &self, + event_id: &EventId, + ) -> Result>; /// Returns the pdu. /// @@ -37,7 +49,10 @@ pub(crate) trait Data: Send + Sync { fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result>; /// Returns the pdu as a `BTreeMap`. - fn get_pdu_json_from_id(&self, pdu_id: &[u8]) -> Result>; + fn get_pdu_json_from_id( + &self, + pdu_id: &[u8], + ) -> Result>; /// Adds a new pdu to the timeline fn append_pdu( @@ -64,8 +79,9 @@ pub(crate) trait Data: Send + Sync { pdu: &PduEvent, ) -> Result<()>; - /// Returns an iterator over all events and their tokens in a room that happened before the - /// event with id `until` in reverse-chronological order. + /// Returns an iterator over all events and their tokens in a room that + /// happened before the event with id `until` in reverse-chronological + /// order. #[allow(clippy::type_complexity)] fn pdus_until<'a>( &'a self, @@ -74,8 +90,8 @@ pub(crate) trait Data: Send + Sync { until: PduCount, ) -> Result> + 'a>>; - /// Returns an iterator over all events in a room that happened after the event with id `from` - /// in chronological order. + /// Returns an iterator over all events in a room that happened after the + /// event with id `from` in chronological order. #[allow(clippy::type_complexity)] fn pdus_after<'a>( &'a self, diff --git a/src/service/rooms/user/data.rs b/src/service/rooms/user/data.rs index 6b5f1cde..bfcb8f70 100644 --- a/src/service/rooms/user/data.rs +++ b/src/service/rooms/user/data.rs @@ -1,15 +1,32 @@ -use crate::Result; use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { - fn reset_notification_counts(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; + fn reset_notification_counts( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result<()>; - fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result; + fn notification_count( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result; - fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result; + fn highlight_count( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result; // Returns the count at which the last reset_notification_counts was called - fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result; + fn last_notification_read( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result; fn associate_token_shortstatehash( &self, @@ -18,7 +35,11 @@ pub(crate) trait Data: Send + Sync { shortstatehash: u64, ) -> Result<()>; - fn get_token_shortstatehash(&self, room_id: &RoomId, token: u64) -> Result>; + fn get_token_shortstatehash( + &self, + room_id: &RoomId, + token: u64, + ) -> Result>; fn get_shared_rooms<'a>( &'a self, diff --git a/src/service/sending.rs b/src/service/sending.rs index 22216faf..1cef2ab2 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -1,7 +1,5 @@ mod data; -pub(crate) use data::Data; - use std::{ collections::{BTreeMap, HashMap, HashSet}, fmt::Debug, @@ -9,34 +7,29 @@ use std::{ time::{Duration, Instant}, }; -use crate::{ - api::{appservice_server, server_server}, - services, - utils::calculate_hash, - Config, Error, PduEvent, Result, -}; +use base64::{engine::general_purpose, Engine as _}; +pub(crate) use data::Data; use federation::transactions::send_transaction_message; use futures_util::{stream::FuturesUnordered, StreamExt}; - -use base64::{engine::general_purpose, Engine as _}; - use ruma::{ api::{ appservice::{self, Registration}, federation::{ self, transactions::edu::{ - DeviceListUpdateContent, Edu, ReceiptContent, ReceiptData, ReceiptMap, + DeviceListUpdateContent, Edu, ReceiptContent, ReceiptData, + ReceiptMap, }, }, OutgoingRequest, }, device_id, events::{ - push_rules::PushRulesEvent, receipt::ReceiptType, AnySyncEphemeralRoomEvent, - GlobalAccountDataEventType, + push_rules::PushRulesEvent, receipt::ReceiptType, + AnySyncEphemeralRoomEvent, GlobalAccountDataEventType, }, - push, uint, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedUserId, ServerName, UInt, UserId, + push, uint, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedUserId, + ServerName, UInt, UserId, }; use tokio::{ select, @@ -44,6 +37,13 @@ use tokio::{ }; use tracing::{debug, error, warn}; +use crate::{ + api::{appservice_server, server_server}, + services, + utils::calculate_hash, + Config, Error, PduEvent, Result, +}; + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum OutgoingKind { Appservice(String), @@ -64,7 +64,7 @@ impl OutgoingKind { OutgoingKind::Push(user, pushkey) => { let mut p = b"$".to_vec(); p.extend_from_slice(user.as_bytes()); - p.push(0xff); + p.push(0xFF); p.extend_from_slice(pushkey.as_bytes()); p } @@ -74,7 +74,7 @@ impl OutgoingKind { p } }; - prefix.push(0xff); + prefix.push(0xFF); prefix } @@ -93,8 +93,11 @@ pub(crate) struct Service { /// The state for a given state hash. pub(super) maximum_requests: Arc, - pub(crate) sender: mpsc::UnboundedSender<(OutgoingKind, SendingEventType, Vec)>, - receiver: Mutex)>>, + pub(crate) sender: + mpsc::UnboundedSender<(OutgoingKind, SendingEventType, Vec)>, + receiver: Mutex< + mpsc::UnboundedReceiver<(OutgoingKind, SendingEventType, Vec)>, + >, } enum TransactionStatus { @@ -112,7 +115,9 @@ impl Service { db, sender, receiver: Mutex::new(receiver), - maximum_requests: Arc::new(Semaphore::new(config.max_concurrent_requests.into())), + maximum_requests: Arc::new(Semaphore::new( + config.max_concurrent_requests.into(), + )), }) } @@ -129,15 +134,18 @@ impl Service { let mut futures = FuturesUnordered::new(); - let mut current_transaction_status = HashMap::::new(); + let mut current_transaction_status = + HashMap::::new(); // Retry requests we could not finish yet - let mut initial_transactions = HashMap::>::new(); + let mut initial_transactions = + HashMap::>::new(); - for (key, outgoing_kind, event) in self.db.active_requests().filter_map(Result::ok) { - let entry = initial_transactions - .entry(outgoing_kind.clone()) - .or_default(); + for (key, outgoing_kind, event) in + self.db.active_requests().filter_map(Result::ok) + { + let entry = + initial_transactions.entry(outgoing_kind.clone()).or_default(); if entry.len() > 30 { warn!( @@ -152,77 +160,90 @@ impl Service { } for (outgoing_kind, events) in initial_transactions { - current_transaction_status.insert(outgoing_kind.clone(), TransactionStatus::Running); + current_transaction_status + .insert(outgoing_kind.clone(), TransactionStatus::Running); futures.push(Self::handle_events(outgoing_kind.clone(), events)); } - let handle_futures = |response, - current_transaction_status: &mut HashMap<_, _>, - futures: &mut FuturesUnordered<_>| { - match response { - Ok(outgoing_kind) => { - self.db.delete_all_active_requests_for(&outgoing_kind)?; + let handle_futures = + |response, + current_transaction_status: &mut HashMap<_, _>, + futures: &mut FuturesUnordered<_>| { + match response { + Ok(outgoing_kind) => { + self.db + .delete_all_active_requests_for(&outgoing_kind)?; - // Find events that have been added since starting the - // last request - let new_events = self - .db - .queued_requests(&outgoing_kind) - .filter_map(Result::ok) - .take(30) - .collect::>(); + // Find events that have been added since starting the + // last request + let new_events = self + .db + .queued_requests(&outgoing_kind) + .filter_map(Result::ok) + .take(30) + .collect::>(); - if new_events.is_empty() { - current_transaction_status.remove(&outgoing_kind); - } else { - // Insert pdus we found - self.db.mark_as_active(&new_events)?; + if new_events.is_empty() { + current_transaction_status.remove(&outgoing_kind); + } else { + // Insert pdus we found + self.db.mark_as_active(&new_events)?; - futures.push(Self::handle_events( - outgoing_kind.clone(), - new_events.into_iter().map(|(event, _)| event).collect(), - )); + futures.push(Self::handle_events( + outgoing_kind.clone(), + new_events + .into_iter() + .map(|(event, _)| event) + .collect(), + )); + } } - } - Err((outgoing_kind, _)) => { - current_transaction_status - .entry(outgoing_kind) - .and_modify(|e| { - *e = match e { - TransactionStatus::Running => { - TransactionStatus::Failed(1, Instant::now()) - } - TransactionStatus::Retrying(n) => { - TransactionStatus::Failed(*n + 1, Instant::now()) - } - TransactionStatus::Failed(..) => { - error!( - "Request that was not even \ + Err((outgoing_kind, _)) => { + current_transaction_status + .entry(outgoing_kind) + .and_modify(|e| { + *e = match e { + TransactionStatus::Running => { + TransactionStatus::Failed( + 1, + Instant::now(), + ) + } + TransactionStatus::Retrying(n) => { + TransactionStatus::Failed( + *n + 1, + Instant::now(), + ) + } + TransactionStatus::Failed(..) => { + error!( + "Request that was not even \ running failed?!" - ); - return; + ); + return; + } } - } - }); - } + }); + } + }; + + Result::<_>::Ok(()) }; - Result::<_>::Ok(()) - }; - - let handle_receiver = |outgoing_kind, - event, - key, - current_transaction_status: &mut HashMap<_, _>, - futures: &mut FuturesUnordered<_>| { - if let Ok(Some(events)) = self.select_events( - &outgoing_kind, - vec![(event, key)], - current_transaction_status, - ) { - futures.push(Self::handle_events(outgoing_kind, events)); - } - }; + let handle_receiver = + |outgoing_kind, + event, + key, + current_transaction_status: &mut HashMap<_, _>, + futures: &mut FuturesUnordered<_>| { + if let Ok(Some(events)) = self.select_events( + &outgoing_kind, + vec![(event, key)], + current_transaction_status, + ) { + futures.push(Self::handle_events(outgoing_kind, events)); + } + }; loop { select! { @@ -244,13 +265,21 @@ impl Service { } } - #[tracing::instrument(skip(self, outgoing_kind, new_events, current_transaction_status))] + #[tracing::instrument(skip( + self, + outgoing_kind, + new_events, + current_transaction_status + ))] fn select_events( &self, outgoing_kind: &OutgoingKind, // Events we want to send: event and full key new_events: Vec<(SendingEventType, Vec)>, - current_transaction_status: &mut HashMap, + current_transaction_status: &mut HashMap< + OutgoingKind, + TransactionStatus, + >, ) -> Result>> { let mut retry = false; let mut allow = true; @@ -264,10 +293,14 @@ impl Service { allow = false; } TransactionStatus::Failed(tries, time) => { - // Fail if a request has failed recently (exponential backoff) - let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries); - if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { - min_elapsed_duration = Duration::from_secs(60 * 60 * 24); + // Fail if a request has failed recently (exponential + // backoff) + let mut min_elapsed_duration = + Duration::from_secs(30) * (*tries) * (*tries); + if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) + { + min_elapsed_duration = + Duration::from_secs(60 * 60 * 24); } if time.elapsed() < min_elapsed_duration { @@ -302,8 +335,12 @@ impl Service { } if let OutgoingKind::Normal(server_name) = outgoing_kind { - if let Ok((select_edus, last_count)) = self.select_edus(server_name) { - events.extend(select_edus.into_iter().map(SendingEventType::Edu)); + if let Ok((select_edus, last_count)) = + self.select_edus(server_name) + { + events.extend( + select_edus.into_iter().map(SendingEventType::Edu), + ); self.db.set_latest_educount(server_name, last_count)?; } @@ -314,14 +351,19 @@ impl Service { } #[tracing::instrument(skip(self, server_name))] - pub(crate) fn select_edus(&self, server_name: &ServerName) -> Result<(Vec>, u64)> { + pub(crate) fn select_edus( + &self, + server_name: &ServerName, + ) -> Result<(Vec>, u64)> { // u64: count of last edu let since = self.db.get_latest_educount(server_name)?; let mut events = Vec::new(); let mut max_edu_count = since; let mut device_list_changes = HashSet::new(); - 'outer: for room_id in services().rooms.state_cache.server_rooms(server_name) { + 'outer: for room_id in + services().rooms.state_cache.server_rooms(server_name) + { let room_id = room_id?; // Look for device list updates in this room device_list_changes.extend( @@ -329,7 +371,10 @@ impl Service { .users .keys_changed(room_id.as_ref(), since, None) .filter_map(Result::ok) - .filter(|user_id| user_id.server_name() == services().globals.server_name()), + .filter(|user_id| { + user_id.server_name() + == services().globals.server_name() + }), ); // Look for read receipts in this room @@ -349,44 +394,57 @@ impl Service { continue; } - let event: AnySyncEphemeralRoomEvent = - serde_json::from_str(read_receipt.json().get()) - .map_err(|_| Error::bad_database("Invalid edu event in read_receipts."))?; - let federation_event = if let AnySyncEphemeralRoomEvent::Receipt(r) = event { - let mut read = BTreeMap::new(); + let event: AnySyncEphemeralRoomEvent = serde_json::from_str( + read_receipt.json().get(), + ) + .map_err(|_| { + Error::bad_database("Invalid edu event in read_receipts.") + })?; + let federation_event = + if let AnySyncEphemeralRoomEvent::Receipt(r) = event { + let mut read = BTreeMap::new(); - let (event_id, mut receipt) = r - .content - .0 - .into_iter() - .next() - .expect("we only use one event per read receipt"); - let receipt = receipt - .remove(&ReceiptType::Read) - .expect("our read receipts always set this") - .remove(&user_id) - .expect("our read receipts always have the user here"); + let (event_id, mut receipt) = + r.content.0.into_iter().next().expect( + "we only use one event per read receipt", + ); + let receipt = receipt + .remove(&ReceiptType::Read) + .expect("our read receipts always set this") + .remove(&user_id) + .expect( + "our read receipts always have the user here", + ); - read.insert( - user_id, - ReceiptData { - data: receipt.clone(), - event_ids: vec![event_id.clone()], - }, - ); + read.insert( + user_id, + ReceiptData { + data: receipt.clone(), + event_ids: vec![event_id.clone()], + }, + ); - let receipt_map = ReceiptMap { read }; + let receipt_map = ReceiptMap { + read, + }; - let mut receipts = BTreeMap::new(); - receipts.insert(room_id.clone(), receipt_map); + let mut receipts = BTreeMap::new(); + receipts.insert(room_id.clone(), receipt_map); - Edu::Receipt(ReceiptContent { receipts }) - } else { - Error::bad_database("Invalid event type in read_receipts"); - continue; - }; + Edu::Receipt(ReceiptContent { + receipts, + }) + } else { + Error::bad_database( + "Invalid event type in read_receipts", + ); + continue; + }; - events.push(serde_json::to_vec(&federation_event).expect("json can be serialized")); + events.push( + serde_json::to_vec(&federation_event) + .expect("json can be serialized"), + ); if events.len() >= 20 { break 'outer; @@ -407,7 +465,9 @@ impl Service { keys: None, }); - events.push(serde_json::to_vec(&edu).expect("json can be serialized")); + events.push( + serde_json::to_vec(&edu).expect("json can be serialized"), + ); } Ok((events, max_edu_count)) @@ -422,7 +482,8 @@ impl Service { ) -> Result<()> { let outgoing_kind = OutgoingKind::Push(user.to_owned(), pushkey); let event = SendingEventType::Pdu(pdu_id.to_owned()); - let keys = self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; + let keys = + self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; self.sender .send((outgoing_kind, event, keys.into_iter().next().unwrap())) .unwrap(); @@ -446,15 +507,10 @@ impl Service { }) .collect::>(); let keys = self.db.queue_requests( - &requests - .iter() - .map(|(o, e)| (o, e.clone())) - .collect::>(), + &requests.iter().map(|(o, e)| (o, e.clone())).collect::>(), )?; for ((outgoing_kind, event), key) in requests.into_iter().zip(keys) { - self.sender - .send((outgoing_kind.clone(), event, key)) - .unwrap(); + self.sender.send((outgoing_kind.clone(), event, key)).unwrap(); } Ok(()) @@ -469,7 +525,8 @@ impl Service { ) -> Result<()> { let outgoing_kind = OutgoingKind::Normal(server.to_owned()); let event = SendingEventType::Edu(serialized); - let keys = self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; + let keys = + self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; self.sender .send((outgoing_kind, event, keys.into_iter().next().unwrap())) .unwrap(); @@ -478,10 +535,15 @@ impl Service { } #[tracing::instrument(skip(self))] - pub(crate) fn send_pdu_appservice(&self, appservice_id: String, pdu_id: Vec) -> Result<()> { + pub(crate) fn send_pdu_appservice( + &self, + appservice_id: String, + pdu_id: Vec, + ) -> Result<()> { let outgoing_kind = OutgoingKind::Appservice(appservice_id); let event = SendingEventType::Pdu(pdu_id); - let keys = self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; + let keys = + self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; self.sender .send((outgoing_kind, event, keys.into_iter().next().unwrap())) .unwrap(); @@ -493,8 +555,9 @@ impl Service { /// Used for instance after we remove an appservice registration #[tracing::instrument(skip(self))] pub(crate) fn cleanup_events(&self, appservice_id: String) -> Result<()> { - self.db - .delete_all_requests_for(&OutgoingKind::Appservice(appservice_id))?; + self.db.delete_all_requests_for(&OutgoingKind::Appservice( + appservice_id, + ))?; Ok(()) } @@ -511,18 +574,24 @@ impl Service { for event in &events { match event { SendingEventType::Pdu(pdu_id) => { - pdu_jsons.push(services().rooms.timeline - .get_pdu_from_id(pdu_id) - .map_err(|e| (kind.clone(), e))? - .ok_or_else(|| { - ( - kind.clone(), - Error::bad_database( - "[Appservice] Event in servernameevent_data not found in db.", - ), - ) - })? - .to_room_event()); + pdu_jsons.push( + services() + .rooms + .timeline + .get_pdu_from_id(pdu_id) + .map_err(|e| (kind.clone(), e))? + .ok_or_else(|| { + ( + kind.clone(), + Error::bad_database( + "[Appservice] Event in \ + servernameevent_data not \ + found in db.", + ), + ) + })? + .to_room_event(), + ); } SendingEventType::Edu(_) => { // Appservices don't need EDUs (?) @@ -530,7 +599,8 @@ impl Service { } } - let permit = services().sending.maximum_requests.acquire().await; + let permit = + services().sending.maximum_requests.acquire().await; let response = match appservice_server::send_request( services() @@ -541,20 +611,24 @@ impl Service { ( kind.clone(), Error::bad_database( - "[Appservice] Could not load registration from db.", + "[Appservice] Could not load registration \ + from db.", ), ) })?, appservice::event::push_events::v1::Request { events: pdu_jsons, - txn_id: (&*general_purpose::URL_SAFE_NO_PAD.encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) | SendingEventType::Pdu(b) => &**b, - }) - .collect::>(), - ))) + txn_id: (&*general_purpose::URL_SAFE_NO_PAD.encode( + calculate_hash( + &events + .iter() + .map(|e| match e { + SendingEventType::Edu(b) + | SendingEventType::Pdu(b) => &**b, + }) + .collect::>(), + ), + )) .into(), }, ) @@ -575,7 +649,8 @@ impl Service { match event { SendingEventType::Pdu(pdu_id) => { pdus.push( - services().rooms + services() + .rooms .timeline .get_pdu_from_id(pdu_id) .map_err(|e| (kind.clone(), e))? @@ -583,7 +658,9 @@ impl Service { ( kind.clone(), Error::bad_database( - "[Push] Event in servernamevent_datas not found in db.", + "[Push] Event in \ + servernamevent_datas not \ + found in db.", ), ) })?, @@ -595,10 +672,13 @@ impl Service { } for pdu in pdus { - // Redacted events are not notification targets (we don't send push for them) + // Redacted events are not notification targets (we don't + // send push for them) if let Some(unsigned) = &pdu.unsigned { if let Ok(unsigned) = - serde_json::from_str::(unsigned.get()) + serde_json::from_str::( + unsigned.get(), + ) { if unsigned.get("redacted_because").is_some() { continue; @@ -609,7 +689,15 @@ impl Service { let Some(pusher) = services() .pusher .get_pusher(userid, pushkey) - .map_err(|e| (OutgoingKind::Push(userid.clone(), pushkey.clone()), e))? + .map_err(|e| { + ( + OutgoingKind::Push( + userid.clone(), + pushkey.clone(), + ), + e, + ) + })? else { continue; }; @@ -619,10 +707,15 @@ impl Service { .get( None, userid, - GlobalAccountDataEventType::PushRules.to_string().into(), + GlobalAccountDataEventType::PushRules + .to_string() + .into(), ) .unwrap_or_default() - .and_then(|event| serde_json::from_str::(event.get()).ok()) + .and_then(|event| { + serde_json::from_str::(event.get()) + .ok() + }) .map_or_else( || push::Ruleset::server_default(userid), |ev: PushRulesEvent| ev.content.global, @@ -636,11 +729,18 @@ impl Service { .try_into() .expect("notification count can't go that high"); - let permit = services().sending.maximum_requests.acquire().await; + let permit = + services().sending.maximum_requests.acquire().await; let _response = services() .pusher - .send_push_notice(userid, unread, &pusher, rules_for_user, &pdu) + .send_push_notice( + userid, + unread, + &pusher, + rules_for_user, + &pdu, + ) .await .map(|_response| kind.clone()) .map_err(|e| (kind.clone(), e)); @@ -656,22 +756,39 @@ impl Service { for event in &events { match event { SendingEventType::Pdu(pdu_id) => { - // TODO: check room version and remove event_id if needed - let raw = PduEvent::convert_to_outgoing_federation_event( - services().rooms - .timeline - .get_pdu_json_from_id(pdu_id) - .map_err(|e| (OutgoingKind::Normal(server.clone()), e))? - .ok_or_else(|| { - error!("event not found: {server} {pdu_id:?}"); - ( - OutgoingKind::Normal(server.clone()), - Error::bad_database( - "[Normal] Event in servernamevent_datas not found in db.", - ), - ) - })?, - ); + // TODO: check room version and remove event_id if + // needed + let raw = + PduEvent::convert_to_outgoing_federation_event( + services() + .rooms + .timeline + .get_pdu_json_from_id(pdu_id) + .map_err(|e| { + ( + OutgoingKind::Normal( + server.clone(), + ), + e, + ) + })? + .ok_or_else(|| { + error!( + "event not found: {server} \ + {pdu_id:?}" + ); + ( + OutgoingKind::Normal( + server.clone(), + ), + Error::bad_database( + "[Normal] Event in \ + servernamevent_datas not \ + found in db.", + ), + ) + })?, + ); pdu_jsons.push(raw); } SendingEventType::Edu(edu) => { @@ -682,7 +799,8 @@ impl Service { } } - let permit = services().sending.maximum_requests.acquire().await; + let permit = + services().sending.maximum_requests.acquire().await; let response = server_server::send_request( server, @@ -691,16 +809,16 @@ impl Service { pdus: pdu_jsons, edus: edu_jsons, origin_server_ts: MilliSecondsSinceUnixEpoch::now(), - transaction_id: (&*general_purpose::URL_SAFE_NO_PAD.encode( - calculate_hash( + transaction_id: (&*general_purpose::URL_SAFE_NO_PAD + .encode(calculate_hash( &events .iter() .map(|e| match e { - SendingEventType::Edu(b) | SendingEventType::Pdu(b) => &**b, + SendingEventType::Edu(b) + | SendingEventType::Pdu(b) => &**b, }) .collect::>(), - ), - )) + ))) .into(), }, ) @@ -750,7 +868,8 @@ impl Service { /// Sends a request to an appservice /// - /// Only returns None if there is no url specified in the appservice registration file + /// Only returns None if there is no url specified in the appservice + /// registration file #[tracing::instrument(skip(self, registration, request))] pub(crate) async fn send_appservice_request( &self, @@ -761,7 +880,8 @@ impl Service { T: Debug, { let permit = self.maximum_requests.acquire().await; - let response = appservice_server::send_request(registration, request).await; + let response = + appservice_server::send_request(registration, request).await; drop(permit); response diff --git a/src/service/sending/data.rs b/src/service/sending/data.rs index f2351534..b250c07c 100644 --- a/src/service/sending/data.rs +++ b/src/service/sending/data.rs @@ -1,21 +1,29 @@ use ruma::ServerName; -use crate::Result; - use super::{OutgoingKind, SendingEventType}; +use crate::Result; pub(crate) trait Data: Send + Sync { #[allow(clippy::type_complexity)] fn active_requests<'a>( &'a self, - ) -> Box, OutgoingKind, SendingEventType)>> + 'a>; + ) -> Box< + dyn Iterator, OutgoingKind, SendingEventType)>> + + 'a, + >; fn active_requests_for<'a>( &'a self, outgoing_kind: &OutgoingKind, ) -> Box, SendingEventType)>> + 'a>; fn delete_active_request(&self, key: Vec) -> Result<()>; - fn delete_all_active_requests_for(&self, outgoing_kind: &OutgoingKind) -> Result<()>; - fn delete_all_requests_for(&self, outgoing_kind: &OutgoingKind) -> Result<()>; + fn delete_all_active_requests_for( + &self, + outgoing_kind: &OutgoingKind, + ) -> Result<()>; + fn delete_all_requests_for( + &self, + outgoing_kind: &OutgoingKind, + ) -> Result<()>; fn queue_requests( &self, requests: &[(&OutgoingKind, SendingEventType)], @@ -24,7 +32,14 @@ pub(crate) trait Data: Send + Sync { &'a self, outgoing_kind: &OutgoingKind, ) -> Box)>> + 'a>; - fn mark_as_active(&self, events: &[(SendingEventType, Vec)]) -> Result<()>; - fn set_latest_educount(&self, server_name: &ServerName, educount: u64) -> Result<()>; + fn mark_as_active( + &self, + events: &[(SendingEventType, Vec)], + ) -> Result<()>; + fn set_latest_educount( + &self, + server_name: &ServerName, + educount: u64, + ) -> Result<()>; fn get_latest_educount(&self, server_name: &ServerName) -> Result; } diff --git a/src/service/transaction_ids/data.rs b/src/service/transaction_ids/data.rs index 6964abcd..e7012956 100644 --- a/src/service/transaction_ids/data.rs +++ b/src/service/transaction_ids/data.rs @@ -1,6 +1,7 @@ -use crate::Result; use ruma::{DeviceId, TransactionId, UserId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { fn add_txnid( &self, diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 1cb026d9..61e4e252 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -1,7 +1,6 @@ mod data; pub(crate) use data::Data; - use ruma::{ api::client::{ error::ErrorKind, @@ -11,7 +10,9 @@ use ruma::{ }; use tracing::error; -use crate::{api::client_server::SESSION_ID_LENGTH, services, utils, Error, Result}; +use crate::{ + api::client_server::SESSION_ID_LENGTH, services, utils, Error, Result, +}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -29,7 +30,8 @@ impl Service { self.db.set_uiaa_request( user_id, device_id, - // TODO: better session error handling (why is it optional in ruma?) + // TODO: better session error handling (why is it optional in + // ruma?) uiaainfo.session.as_ref().expect("session should be set"), json_body, )?; @@ -64,7 +66,8 @@ impl Service { password, .. }) => { - let UserIdentifier::UserIdOrLocalpart(username) = identifier else { + let UserIdentifier::UserIdOrLocalpart(username) = identifier + else { return Err(Error::BadRequest( ErrorKind::Unrecognized, "Identifier type not recognized.", @@ -75,18 +78,26 @@ impl Service { username.clone(), services().globals.server_name(), ) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "User ID is invalid."))?; + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "User ID is invalid.", + ) + })?; // Check if password is correct if let Some(hash) = services().users.password_hash(&user_id)? { let hash_matches = - argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false); + argon2::verify_encoded(&hash, password.as_bytes()) + .unwrap_or(false); if !hash_matches { - uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { - kind: ErrorKind::Forbidden, - message: "Invalid username or password.".to_owned(), - }); + uiaainfo.auth_error = + Some(ruma::api::client::error::StandardErrorBody { + kind: ErrorKind::Forbidden, + message: "Invalid username or password." + .to_owned(), + }); return Ok((false, uiaainfo)); } } @@ -95,13 +106,16 @@ impl Service { uiaainfo.completed.push(AuthType::Password); } AuthData::RegistrationToken(t) => { - if Some(t.token.trim()) == services().globals.config.registration_token.as_deref() { + if Some(t.token.trim()) + == services().globals.config.registration_token.as_deref() + { uiaainfo.completed.push(AuthType::RegistrationToken); } else { - uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { - kind: ErrorKind::Forbidden, - message: "Invalid registration token.".to_owned(), - }); + uiaainfo.auth_error = + Some(ruma::api::client::error::StandardErrorBody { + kind: ErrorKind::Forbidden, + message: "Invalid registration token.".to_owned(), + }); return Ok((false, uiaainfo)); } } diff --git a/src/service/uiaa/data.rs b/src/service/uiaa/data.rs index 9a63fbb8..c8f41793 100644 --- a/src/service/uiaa/data.rs +++ b/src/service/uiaa/data.rs @@ -1,6 +1,7 @@ -use crate::Result; use ruma::{api::client::uiaa::UiaaInfo, CanonicalJsonValue, DeviceId, UserId}; +use crate::Result; + pub(crate) trait Data: Send + Sync { fn set_uiaa_request( &self, diff --git a/src/service/users.rs b/src/service/users.rs index b69c98b0..5fd8cfbd 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -19,8 +19,8 @@ use ruma::{ encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::AnyToDeviceEvent, serde::Raw, - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedMxcUri, - OwnedRoomId, OwnedUserId, RoomAliasId, UInt, UserId, + DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, + OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, UInt, UserId, }; use crate::{services, Error, Result}; @@ -36,8 +36,12 @@ pub(crate) struct SlidingSyncCache { pub(crate) struct Service { pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub(crate) connections: - Mutex>>>, + pub(crate) connections: Mutex< + BTreeMap< + (OwnedUserId, OwnedDeviceId, String), + Arc>, + >, + >, } impl Service { @@ -52,10 +56,7 @@ impl Service { device_id: OwnedDeviceId, conn_id: String, ) { - self.connections - .lock() - .unwrap() - .remove(&(user_id, device_id, conn_id)); + self.connections.lock().unwrap().remove(&(user_id, device_id, conn_id)); } #[allow(clippy::too_many_lines)] @@ -71,16 +72,14 @@ impl Service { let mut cache = self.connections.lock().unwrap(); let cached = Arc::clone( - cache - .entry((user_id, device_id, conn_id)) - .or_insert_with(|| { - Arc::new(Mutex::new(SlidingSyncCache { - lists: BTreeMap::new(), - subscriptions: BTreeMap::new(), - known_rooms: BTreeMap::new(), - extensions: ExtensionsConfig::default(), - })) - }), + cache.entry((user_id, device_id, conn_id)).or_insert_with(|| { + Arc::new(Mutex::new(SlidingSyncCache { + lists: BTreeMap::new(), + subscriptions: BTreeMap::new(), + known_rooms: BTreeMap::new(), + extensions: ExtensionsConfig::default(), + })) + }), ); let cached = &mut cached.lock().unwrap(); drop(cache); @@ -104,19 +103,22 @@ impl Service { .or(cached_list.include_old_rooms.clone()); match (&mut list.filters, cached_list.filters.clone()) { (Some(list_filters), Some(cached_filters)) => { - list_filters.is_dm = list_filters.is_dm.or(cached_filters.is_dm); + list_filters.is_dm = + list_filters.is_dm.or(cached_filters.is_dm); if list_filters.spaces.is_empty() { list_filters.spaces = cached_filters.spaces; } - list_filters.is_encrypted = - list_filters.is_encrypted.or(cached_filters.is_encrypted); + list_filters.is_encrypted = list_filters + .is_encrypted + .or(cached_filters.is_encrypted); list_filters.is_invite = list_filters.is_invite.or(cached_filters.is_invite); if list_filters.room_types.is_empty() { list_filters.room_types = cached_filters.room_types; } if list_filters.not_room_types.is_empty() { - list_filters.not_room_types = cached_filters.not_room_types; + list_filters.not_room_types = + cached_filters.not_room_types; } list_filters.room_name_like = list_filters .room_name_like @@ -129,12 +131,17 @@ impl Service { list_filters.not_tags = cached_filters.not_tags; } } - (_, Some(cached_filters)) => list.filters = Some(cached_filters), - (Some(list_filters), _) => list.filters = Some(list_filters.clone()), - (_, _) => {} + (_, Some(cached_filters)) => { + list.filters = Some(cached_filters); + } + (Some(list_filters), _) => { + list.filters = Some(list_filters.clone()); + } + (..) => {} } if list.bump_event_types.is_empty() { - list.bump_event_types = cached_list.bump_event_types.clone(); + list.bump_event_types = + cached_list.bump_event_types.clone(); }; } cached.lists.insert(list_id.clone(), list.clone()); @@ -147,17 +154,11 @@ impl Service { .map(|(k, v)| (k.clone(), v.clone())), ); request.room_subscriptions.extend( - cached - .subscriptions - .iter() - .map(|(k, v)| (k.clone(), v.clone())), + cached.subscriptions.iter().map(|(k, v)| (k.clone(), v.clone())), ); - request.extensions.e2ee.enabled = request - .extensions - .e2ee - .enabled - .or(cached.extensions.e2ee.enabled); + request.extensions.e2ee.enabled = + request.extensions.e2ee.enabled.or(cached.extensions.e2ee.enabled); request.extensions.to_device.enabled = request .extensions @@ -197,16 +198,14 @@ impl Service { ) { let mut cache = self.connections.lock().unwrap(); let cached = Arc::clone( - cache - .entry((user_id, device_id, conn_id)) - .or_insert_with(|| { - Arc::new(Mutex::new(SlidingSyncCache { - lists: BTreeMap::new(), - subscriptions: BTreeMap::new(), - known_rooms: BTreeMap::new(), - extensions: ExtensionsConfig::default(), - })) - }), + cache.entry((user_id, device_id, conn_id)).or_insert_with(|| { + Arc::new(Mutex::new(SlidingSyncCache { + lists: BTreeMap::new(), + subscriptions: BTreeMap::new(), + known_rooms: BTreeMap::new(), + extensions: ExtensionsConfig::default(), + })) + }), ); let cached = &mut cached.lock().unwrap(); drop(cache); @@ -225,25 +224,20 @@ impl Service { ) { let mut cache = self.connections.lock().unwrap(); let cached = Arc::clone( - cache - .entry((user_id, device_id, conn_id)) - .or_insert_with(|| { - Arc::new(Mutex::new(SlidingSyncCache { - lists: BTreeMap::new(), - subscriptions: BTreeMap::new(), - known_rooms: BTreeMap::new(), - extensions: ExtensionsConfig::default(), - })) - }), + cache.entry((user_id, device_id, conn_id)).or_insert_with(|| { + Arc::new(Mutex::new(SlidingSyncCache { + lists: BTreeMap::new(), + subscriptions: BTreeMap::new(), + known_rooms: BTreeMap::new(), + extensions: ExtensionsConfig::default(), + })) + }), ); let cached = &mut cached.lock().unwrap(); drop(cache); - for (roomid, lastsince) in cached - .known_rooms - .entry(list_id.clone()) - .or_default() - .iter_mut() + for (roomid, lastsince) in + cached.known_rooms.entry(list_id.clone()).or_default().iter_mut() { if !new_cached_rooms.contains(roomid) { *lastsince = 0; @@ -264,23 +258,28 @@ impl Service { // Allowed because this function uses `services()` #[allow(clippy::unused_self)] pub(crate) fn is_admin(&self, user_id: &UserId) -> Result { - let admin_room_alias_id = - RoomAliasId::parse(format!("#admins:{}", services().globals.server_name())) - .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias."))?; + let admin_room_alias_id = RoomAliasId::parse(format!( + "#admins:{}", + services().globals.server_name() + )) + .map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias.") + })?; let admin_room_id = services() .rooms .alias .resolve_local_alias(&admin_room_alias_id)? .unwrap(); - services() - .rooms - .state_cache - .is_joined(user_id, &admin_room_id) + services().rooms.state_cache.is_joined(user_id, &admin_room_id) } /// Create a new user account on this homeserver. - pub(crate) fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { + pub(crate) fn create( + &self, + user_id: &UserId, + password: Option<&str>, + ) -> Result<()> { self.db.set_password(user_id, password)?; Ok(()) } @@ -291,38 +290,55 @@ impl Service { } /// Find out which user an access token belongs to. - pub(crate) fn find_from_token(&self, token: &str) -> Result> { + pub(crate) fn find_from_token( + &self, + token: &str, + ) -> Result> { self.db.find_from_token(token) } /// Returns an iterator over all users on this homeserver. - pub(crate) fn iter(&self) -> impl Iterator> + '_ { + pub(crate) fn iter( + &self, + ) -> impl Iterator> + '_ { self.db.iter() } /// Returns a list of local users as list of usernames. /// - /// A user account is considered `local` if the length of it's password is greater then zero. + /// A user account is considered `local` if the length of it's password is + /// greater then zero. pub(crate) fn list_local_users(&self) -> Result> { self.db.list_local_users() } /// Returns the password hash for the given user. - pub(crate) fn password_hash(&self, user_id: &UserId) -> Result> { + pub(crate) fn password_hash( + &self, + user_id: &UserId, + ) -> Result> { self.db.password_hash(user_id) } /// Hash and set the user's password to the Argon2 hash - pub(crate) fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { + pub(crate) fn set_password( + &self, + user_id: &UserId, + password: Option<&str>, + ) -> Result<()> { self.db.set_password(user_id, password) } /// Returns the displayname of a user on this homeserver. - pub(crate) fn displayname(&self, user_id: &UserId) -> Result> { + pub(crate) fn displayname( + &self, + user_id: &UserId, + ) -> Result> { self.db.displayname(user_id) } - /// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change. + /// Sets a new displayname or removes it if displayname is None. You still + /// need to nofify all rooms of this change. pub(crate) fn set_displayname( &self, user_id: &UserId, @@ -332,7 +348,10 @@ impl Service { } /// Get the `avatar_url` of a user. - pub(crate) fn avatar_url(&self, user_id: &UserId) -> Result> { + pub(crate) fn avatar_url( + &self, + user_id: &UserId, + ) -> Result> { self.db.avatar_url(user_id) } @@ -351,7 +370,11 @@ impl Service { } /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. - pub(crate) fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { + pub(crate) fn set_blurhash( + &self, + user_id: &UserId, + blurhash: Option, + ) -> Result<()> { self.db.set_blurhash(user_id, blurhash) } @@ -363,12 +386,20 @@ impl Service { token: &str, initial_device_display_name: Option, ) -> Result<()> { - self.db - .create_device(user_id, device_id, token, initial_device_display_name) + self.db.create_device( + user_id, + device_id, + token, + initial_device_display_name, + ) } /// Removes a device from a user. - pub(crate) fn remove_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> { + pub(crate) fn remove_device( + &self, + user_id: &UserId, + device_id: &DeviceId, + ) -> Result<()> { self.db.remove_device(user_id, device_id) } @@ -397,8 +428,12 @@ impl Service { one_time_key_key: &DeviceKeyId, one_time_key_value: &Raw, ) -> Result<()> { - self.db - .add_one_time_key(user_id, device_id, one_time_key_key, one_time_key_value) + self.db.add_one_time_key( + user_id, + device_id, + one_time_key_key, + one_time_key_value, + ) } pub(crate) fn take_one_time_key( @@ -463,7 +498,10 @@ impl Service { self.db.keys_changed(user_or_room_id, from, to) } - pub(crate) fn mark_device_key_update(&self, user_id: &UserId) -> Result<()> { + pub(crate) fn mark_device_key_update( + &self, + user_id: &UserId, + ) -> Result<()> { self.db.mark_device_key_update(user_id) } @@ -490,8 +528,7 @@ impl Service { user_id: &UserId, allowed_signatures: &dyn Fn(&UserId) -> bool, ) -> Result>> { - self.db - .get_key(key, sender_user, user_id, allowed_signatures) + self.db.get_key(key, sender_user, user_id, allowed_signatures) } pub(crate) fn get_master_key( @@ -500,8 +537,7 @@ impl Service { user_id: &UserId, allowed_signatures: &dyn Fn(&UserId) -> bool, ) -> Result>> { - self.db - .get_master_key(sender_user, user_id, allowed_signatures) + self.db.get_master_key(sender_user, user_id, allowed_signatures) } pub(crate) fn get_self_signing_key( @@ -510,8 +546,7 @@ impl Service { user_id: &UserId, allowed_signatures: &dyn Fn(&UserId) -> bool, ) -> Result>> { - self.db - .get_self_signing_key(sender_user, user_id, allowed_signatures) + self.db.get_self_signing_key(sender_user, user_id, allowed_signatures) } pub(crate) fn get_user_signing_key( @@ -573,7 +608,10 @@ impl Service { self.db.get_device_metadata(user_id, device_id) } - pub(crate) fn get_devicelist_version(&self, user_id: &UserId) -> Result> { + pub(crate) fn get_devicelist_version( + &self, + user_id: &UserId, + ) -> Result> { self.db.get_devicelist_version(user_id) } @@ -591,9 +629,10 @@ impl Service { self.remove_device(user_id, &device_id?)?; } - // Set the password to "" to indicate a deactivated account. Hashes will never result in an - // empty string, so the user will not be able to log in again. Systems like changing the - // password without logging in should check if the account is deactivated. + // Set the password to "" to indicate a deactivated account. Hashes will + // never result in an empty string, so the user will not be able + // to log in again. Systems like changing the password without + // logging in should check if the account is deactivated. self.db.set_password(user_id, None)?; // TODO: Unhook 3PID @@ -625,19 +664,23 @@ pub(crate) fn clean_signatures bool>( user_id: &UserId, allowed_signatures: F, ) -> Result<(), Error> { - if let Some(signatures) = cross_signing_key - .get_mut("signatures") - .and_then(|v| v.as_object_mut()) + if let Some(signatures) = + cross_signing_key.get_mut("signatures").and_then(|v| v.as_object_mut()) { - // Don't allocate for the full size of the current signatures, but require - // at most one resize if nothing is dropped + // Don't allocate for the full size of the current signatures, but + // require at most one resize if nothing is dropped let new_capacity = signatures.len() / 2; - for (user, signature) in - mem::replace(signatures, serde_json::Map::with_capacity(new_capacity)) - { - let sid = <&UserId>::try_from(user.as_str()) - .map_err(|_| Error::bad_database("Invalid user ID in database."))?; - if sender_user == Some(user_id) || sid == user_id || allowed_signatures(sid) { + for (user, signature) in mem::replace( + signatures, + serde_json::Map::with_capacity(new_capacity), + ) { + let sid = <&UserId>::try_from(user.as_str()).map_err(|_| { + Error::bad_database("Invalid user ID in database.") + })?; + if sender_user == Some(user_id) + || sid == user_id + || allowed_signatures(sid) + { signatures.insert(user, signature); } } diff --git a/src/service/users/data.rs b/src/service/users/data.rs index 3a7af883..ca6442e3 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -1,13 +1,15 @@ -use crate::Result; +use std::collections::BTreeMap; + use ruma::{ api::client::{device::Device, filter::FilterDefinition}, encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::AnyToDeviceEvent, serde::Raw, - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedMxcUri, - OwnedUserId, UInt, UserId, + DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, + OwnedMxcUri, OwnedUserId, UInt, UserId, }; -use std::collections::BTreeMap; + +use crate::Result; pub(crate) trait Data: Send + Sync { /// Check if a user has an account on this homeserver. @@ -20,39 +22,61 @@ pub(crate) trait Data: Send + Sync { fn count(&self) -> Result; /// Find out which user an access token belongs to. - fn find_from_token(&self, token: &str) -> Result>; + fn find_from_token( + &self, + token: &str, + ) -> Result>; /// Returns an iterator over all users on this homeserver. - fn iter<'a>(&'a self) -> Box> + 'a>; + fn iter<'a>(&'a self) + -> Box> + 'a>; /// Returns a list of local users as list of usernames. /// - /// A user account is considered `local` if the length of it's password is greater then zero. + /// A user account is considered `local` if the length of it's password is + /// greater then zero. fn list_local_users(&self) -> Result>; /// Returns the password hash for the given user. fn password_hash(&self, user_id: &UserId) -> Result>; /// Hash and set the user's password to the Argon2 hash - fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()>; + fn set_password( + &self, + user_id: &UserId, + password: Option<&str>, + ) -> Result<()>; /// Returns the displayname of a user on this homeserver. fn displayname(&self, user_id: &UserId) -> Result>; - /// Sets a new `displayname` or removes it if `displayname` is `None`. You still need to nofify all rooms of this change. - fn set_displayname(&self, user_id: &UserId, displayname: Option) -> Result<()>; + /// Sets a new `displayname` or removes it if `displayname` is `None`. You + /// still need to nofify all rooms of this change. + fn set_displayname( + &self, + user_id: &UserId, + displayname: Option, + ) -> Result<()>; /// Get the `avatar_url` of a user. fn avatar_url(&self, user_id: &UserId) -> Result>; /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. - fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()>; + fn set_avatar_url( + &self, + user_id: &UserId, + avatar_url: Option, + ) -> Result<()>; /// Get the `blurhash` of a user. fn blurhash(&self, user_id: &UserId) -> Result>; /// Sets a new `avatar_url` or removes it if `avatar_url` is `None`. - fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()>; + fn set_blurhash( + &self, + user_id: &UserId, + blurhash: Option, + ) -> Result<()>; /// Adds a new device to a user. fn create_device( @@ -64,7 +88,11 @@ pub(crate) trait Data: Send + Sync { ) -> Result<()>; /// Removes a device from a user. - fn remove_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()>; + fn remove_device( + &self, + user_id: &UserId, + device_id: &DeviceId, + ) -> Result<()>; /// Returns an iterator over all device ids of this user. fn all_device_ids<'a>( @@ -73,7 +101,12 @@ pub(crate) trait Data: Send + Sync { ) -> Box> + 'a>; /// Replaces the access token of one device. - fn set_token(&self, user_id: &UserId, device_id: &DeviceId, token: &str) -> Result<()>; + fn set_token( + &self, + user_id: &UserId, + device_id: &DeviceId, + token: &str, + ) -> Result<()>; fn add_one_time_key( &self, @@ -165,7 +198,10 @@ pub(crate) trait Data: Send + Sync { allowed_signatures: &dyn Fn(&UserId) -> bool, ) -> Result>>; - fn get_user_signing_key(&self, user_id: &UserId) -> Result>>; + fn get_user_signing_key( + &self, + user_id: &UserId, + ) -> Result>>; fn add_to_device_event( &self, @@ -197,8 +233,11 @@ pub(crate) trait Data: Send + Sync { ) -> Result<()>; /// Get device metadata. - fn get_device_metadata(&self, user_id: &UserId, device_id: &DeviceId) - -> Result>; + fn get_device_metadata( + &self, + user_id: &UserId, + device_id: &DeviceId, + ) -> Result>; fn get_devicelist_version(&self, user_id: &UserId) -> Result>; @@ -208,7 +247,15 @@ pub(crate) trait Data: Send + Sync { ) -> Box> + 'a>; /// Creates a new sync filter. Returns the filter id. - fn create_filter(&self, user_id: &UserId, filter: &FilterDefinition) -> Result; + fn create_filter( + &self, + user_id: &UserId, + filter: &FilterDefinition, + ) -> Result; - fn get_filter(&self, user_id: &UserId, filter_id: &str) -> Result>; + fn get_filter( + &self, + user_id: &UserId, + filter_id: &str, + ) -> Result>; } diff --git a/src/utils.rs b/src/utils.rs index 194f0eef..0b40acbb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,14 +1,17 @@ pub(crate) mod error; +use std::{ + cmp, + str::FromStr, + time::{SystemTime, UNIX_EPOCH}, +}; + use argon2::{Config, Variant}; use cmp::Ordering; use rand::prelude::*; use ring::digest; -use ruma::{canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject}; -use std::{ - cmp, - str::FromStr, - time::{SystemTime, UNIX_EPOCH}, +use ruma::{ + canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, }; // Hopefully we have a better chat protocol in 530 years @@ -36,7 +39,7 @@ pub(crate) fn increment(old: Option<&[u8]>) -> Vec { pub(crate) fn generate_keypair() -> Vec { let mut value = random_string(8).as_bytes().to_vec(); - value.push(0xff); + value.push(0xFF); value.extend_from_slice( &ruma::signatures::Ed25519KeyPair::generate() .expect("Ed25519KeyPair generation always works (?)"), @@ -45,13 +48,17 @@ pub(crate) fn generate_keypair() -> Vec { } /// Parses the bytes into an u64. -pub(crate) fn u64_from_bytes(bytes: &[u8]) -> Result { +pub(crate) fn u64_from_bytes( + bytes: &[u8], +) -> Result { let array: [u8; 8] = bytes.try_into()?; Ok(u64::from_be_bytes(array)) } /// Parses the bytes into a string. -pub(crate) fn string_from_bytes(bytes: &[u8]) -> Result { +pub(crate) fn string_from_bytes( + bytes: &[u8], +) -> Result { String::from_utf8(bytes.to_vec()) } @@ -64,7 +71,9 @@ pub(crate) fn random_string(length: usize) -> String { } /// Calculate a new hash for the given password -pub(crate) fn calculate_password_hash(password: &str) -> Result { +pub(crate) fn calculate_password_hash( + password: &str, +) -> Result { let hashing_config = Config { variant: Variant::Argon2id, ..Default::default() @@ -77,7 +86,7 @@ pub(crate) fn calculate_password_hash(password: &str) -> Result Vec { // We only hash the pdu's event ids, not the whole pdu - let bytes = keys.join(&0xff); + let bytes = keys.join(&0xFF); let hash = digest::digest(&digest::SHA256, &bytes); hash.as_ref().to_owned() } @@ -92,7 +101,8 @@ where F: Fn(&[u8], &[u8]) -> Ordering, { let first_iterator = iterators.next()?; - let mut other_iterators = iterators.map(Iterator::peekable).collect::>(); + let mut other_iterators = + iterators.map(Iterator::peekable).collect::>(); Some(first_iterator.filter(move |target| { other_iterators.iter_mut().all(|it| { @@ -113,7 +123,8 @@ where })) } -/// Fallible conversion from any value that implements `Serialize` to a `CanonicalJsonObject`. +/// Fallible conversion from any value that implements `Serialize` to a +/// `CanonicalJsonObject`. /// /// `value` must serialize to an `serde_json::Value::Object`. pub(crate) fn to_canonical_object( @@ -138,11 +149,18 @@ pub(crate) fn deserialize_from_str< deserializer: D, ) -> Result { struct Visitor, E>(std::marker::PhantomData); - impl, Err: std::fmt::Display> serde::de::Visitor<'_> for Visitor { + impl, Err: std::fmt::Display> serde::de::Visitor<'_> + for Visitor + { type Value = T; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + + fn expecting( + &self, + formatter: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { write!(formatter, "a parsable string") } + fn visit_str(self, v: &str) -> Result where E: serde::de::Error, diff --git a/src/utils/error.rs b/src/utils/error.rs index c0655de5..581975ff 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -19,13 +19,19 @@ pub(crate) type Result = std::result::Result; #[allow(clippy::error_impl_error)] pub(crate) enum Error { #[cfg(feature = "sqlite")] - #[error("There was a problem with the connection to the sqlite database: {source}")] + #[error( + "There was a problem with the connection to the sqlite database: \ + {source}" + )] Sqlite { #[from] source: rusqlite::Error, }, #[cfg(feature = "rocksdb")] - #[error("There was a problem with the connection to the rocksdb database: {source}")] + #[error( + "There was a problem with the connection to the rocksdb database: \ + {source}" + )] RocksDb { #[from] source: rocksdb::Error, @@ -91,9 +97,10 @@ impl Error { pub(crate) fn to_response(&self) -> RumaResponse { use ErrorKind::{ - Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, - ThreepidAuthFailed, ThreepidDenied, TooLarge, Unauthorized, Unknown, UnknownToken, - Unrecognized, UserDeactivated, WrongRoomKeysVersion, + Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, + NotFound, ThreepidAuthFailed, ThreepidDenied, TooLarge, + Unauthorized, Unknown, UnknownToken, Unrecognized, UserDeactivated, + WrongRoomKeysVersion, }; if let Self::Uiaa(uiaainfo) = self { @@ -115,15 +122,23 @@ impl Error { Self::BadRequest(kind, _) => ( kind.clone(), match kind { - WrongRoomKeysVersion { .. } + WrongRoomKeysVersion { + .. + } | Forbidden | GuestAccessForbidden | ThreepidAuthFailed | UserDeactivated | ThreepidDenied => StatusCode::FORBIDDEN, - Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED, + Unauthorized + | UnknownToken { + .. + } + | MissingToken => StatusCode::UNAUTHORIZED, NotFound | Unrecognized => StatusCode::NOT_FOUND, - LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS, + LimitExceeded { + .. + } => StatusCode::TOO_MANY_REQUESTS, TooLarge => StatusCode::PAYLOAD_TOO_LARGE, _ => StatusCode::BAD_REQUEST, }, @@ -135,7 +150,10 @@ impl Error { info!("Returning an error: {}: {}", status_code, message); RumaResponse(UiaaResponse::MatrixError(RumaError { - body: ErrorBody::Standard { kind, message }, + body: ErrorBody::Standard { + kind, + message, + }, status_code, })) } @@ -146,12 +164,22 @@ impl Error { match self { #[cfg(feature = "sqlite")] - Self::Sqlite { .. } => db_error, + Self::Sqlite { + .. + } => db_error, #[cfg(feature = "rocksdb")] - Self::RocksDb { .. } => db_error, - Self::Io { .. } => db_error, - Self::BadConfig { .. } => db_error, - Self::BadDatabase { .. } => db_error, + Self::RocksDb { + .. + } => db_error, + Self::Io { + .. + } => db_error, + Self::BadConfig { + .. + } => db_error, + Self::BadDatabase { + .. + } => db_error, _ => self.to_string(), } } From 5cb2551422898af8b3ba02d5e79fe27c2e2adec9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 01:42:48 -0700 Subject: [PATCH 115/617] enable `error_on_line_overflow` and fix errors These required some manual intervention. --- rustfmt.toml | 1 + src/api/client_server/report.rs | 107 +++++++++++++++-------------- src/database/abstraction/sqlite.rs | 6 +- src/main.rs | 12 +++- src/service/admin.rs | 4 +- src/service/rooms/state_cache.rs | 5 +- 6 files changed, 75 insertions(+), 60 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 58355ad5..a0729a75 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,6 +1,7 @@ edition = "2021" condense_wildcard_suffixes = true +error_on_line_overflow = true format_code_in_doc_comments = true format_macro_bodies = true format_macro_matchers = true diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index 7bcf3f25..d9242f62 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -35,58 +35,61 @@ pub(crate) async fn report_event_route( )); }; - services() - .admin - .send_message(message::RoomMessageEventContent::text_html( - format!( - "Report received from: {}\n\nEvent ID: {:?}\nRoom ID: {:?}\nSent \ - By: {:?}\n\nReport Score: {:?}\nReport Reason: {:?}", - sender_user, pdu.event_id, pdu.room_id, pdu.sender, body.score, body.reason - ), - format!( - r#" -
- - Report received from: - {0:?} - -
    -
  • - Event Info -
      -
    • - Event ID: - {1:?} - 🔗 -
    • -
    • - Room ID: - {2:?} -
    • -
    • - Sent By: - {3:?} -
    • -
    -
  • -
  • - Report Info -
      -
    • Report Score: {4:?}
    • -
    • Report Reason: {5}
    • -
    -
  • -
-
- "#, - sender_user, - pdu.event_id, - pdu.room_id, - pdu.sender, - body.score, - html_escape::encode_safe(body.reason.as_deref().unwrap_or("")) - ), - )); + services().admin.send_message(message::RoomMessageEventContent::text_html( + format!( + "Report received from: {}\n\nEvent ID: {:?}\nRoom ID: {:?}\nSent \ + By: {:?}\n\nReport Score: {:?}\nReport Reason: {:?}", + sender_user, + pdu.event_id, + pdu.room_id, + pdu.sender, + body.score, + body.reason + ), + format!( + r#" +
+ + Report received from: + {0:?} + +
    +
  • + Event Info +
      +
    • + Event ID: + {1:?} + 🔗 +
    • +
    • + Room ID: + {2:?} +
    • +
    • + Sent By: + {3:?} +
    • +
    +
  • +
  • + Report Info +
      +
    • Report Score: {4:?}
    • +
    • Report Reason: {5}
    • +
    +
  • +
+
+ "#, + sender_user, + pdu.event_id, + pdu.room_id, + pdu.sender, + body.score, + html_escape::encode_safe(body.reason.as_deref().unwrap_or("")) + ), + )); Ok(report_content::v3::Response {}) } diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index f73ba509..1ddfff54 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -15,8 +15,10 @@ use super::{watchers::Watchers, KeyValueDatabaseEngine, KvTree}; use crate::{database::Config, Result}; thread_local! { - static READ_CONNECTION: RefCell> = RefCell::new(None); - static READ_CONNECTION_ITERATOR: RefCell> = RefCell::new(None); + static READ_CONNECTION: RefCell> = + RefCell::new(None); + static READ_CONNECTION_ITERATOR: RefCell> = + RefCell::new(None); } struct PreparedStatementIterator<'a> { diff --git a/src/main.rs b/src/main.rs index 407a01b0..cccc7ba4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -595,9 +595,15 @@ macro_rules! impl_ruma_handler { for path in meta.history.all_paths() { let handler = self.clone(); - router = router.route(path, on(method_filter, |$( $ty: $ty, )* req| async move { - handler($($ty,)* req).await.map(RumaResponse) - })) + router = router.route( + path, + on( + method_filter, + |$( $ty: $ty, )* req| async move { + handler($($ty,)* req).await.map(RumaResponse) + } + ) + ) } router diff --git a/src/service/admin.rs b/src/service/admin.rs index 87b645f1..562d0ec3 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -243,7 +243,9 @@ impl Service { Some(event) = receiver.recv() => { let message_content = match event { AdminRoomEvent::SendMessage(content) => content, - AdminRoomEvent::ProcessMessage(room_message) => self.process_admin_message(room_message).await + AdminRoomEvent::ProcessMessage(room_message) => { + self.process_admin_message(room_message).await + } }; let mutex_state = Arc::clone( diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index bf4ce632..deaf1cc8 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -21,6 +21,8 @@ pub(crate) struct Service { pub(crate) db: &'static dyn Data, } +type RoomsLeft = (OwnedRoomId, Vec>); + impl Service { /// Update current membership data. #[tracing::instrument(skip(self, last_state))] @@ -390,8 +392,7 @@ impl Service { pub(crate) fn rooms_left<'a>( &'a self, user_id: &UserId, - ) -> impl Iterator>)>> + 'a - { + ) -> impl Iterator> + 'a { self.db.rooms_left(user_id) } From 146465693ecb6c1adb7edcb40e9f3759c350dd1f Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 16 May 2024 21:02:05 -0700 Subject: [PATCH 116/617] remove sync response cache This cache can serve invalid responses, and has an extremely low hit rate. It serves invalid responses because because it's only keyed off the `since` parameter, but many of the other request parameters also affect the response or it's side effects. This will become worse once we implement filtering, because there will be a wider space of parameters with different responses. This problem is fixable, but not worth it because of the low hit rate. The low hit rate is because normal clients will always issue the next sync request with `since` set to the `prev_batch` value of the previous response. The only time we expect to see multiple requests with the same `since` is when the response is empty, but we don't cache empty responses. This was confirmed experimentally by logging cache hits and misses over 15 minutes with a wide variety of clients. This test was run on matrix.computer.surgery, which has only a few active users, but a large volume of sync traffic from many rooms. Over the test period, we had 3 hits and 5309 misses. All hits occurred in the first minute, so I suspect that they had something to do with client recovery from an offline state. The clients that were connected during the test are: - element web - schildichat web - iamb - gomuks - nheko - fractal - fluffychat web - fluffychat android - cinny web - element android - element X android Fixes: #2 --- src/api/client_server/sync.rs | 120 ++-------------------------------- src/service/globals.rs | 20 ++---- 2 files changed, 10 insertions(+), 130 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 71ec8497..bad7e304 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1,5 +1,5 @@ use std::{ - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + collections::{BTreeMap, BTreeSet, HashMap, HashSet}, sync::Arc, time::Duration, }; @@ -23,11 +23,9 @@ use ruma::{ room::member::{MembershipState, RoomMemberEventContent}, StateEventType, TimelineEventType, }, - uint, DeviceId, EventId, JsOption, OwnedDeviceId, OwnedUserId, RoomId, - UInt, UserId, + uint, DeviceId, EventId, JsOption, OwnedUserId, RoomId, UInt, UserId, }; -use tokio::sync::watch::Sender; -use tracing::{debug, error, info}; +use tracing::{debug, error}; use crate::{ service::{pdu::EventHash, rooms::timeline::PduCount}, @@ -73,10 +71,7 @@ use crate::{ /// For left rooms: /// - If the user left after `since`: `prev_batch` token, empty state (TODO: /// subset of the state at the point of the leave) -/// -/// - Sync is handled in an async task, multiple requests from the same device -/// with the same -/// `since` will be cached +#[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_route( body: Ruma, ) -> Result> { @@ -84,109 +79,6 @@ pub(crate) async fn sync_events_route( let sender_device = body.sender_device.expect("user is authenticated"); let body = body.body; - let mut rx = match services() - .globals - .sync_receivers - .write() - .await - .entry((sender_user.clone(), sender_device.clone())) - { - Entry::Vacant(v) => { - let (tx, rx) = tokio::sync::watch::channel(None); - - v.insert((body.since.clone(), rx.clone())); - - tokio::spawn(sync_helper_wrapper( - sender_user.clone(), - sender_device.clone(), - body, - tx, - )); - - rx - } - Entry::Occupied(mut o) => { - if o.get().0 == body.since { - o.get().1.clone() - } else { - let (tx, rx) = tokio::sync::watch::channel(None); - - o.insert((body.since.clone(), rx.clone())); - - info!("Sync started for {sender_user}"); - - tokio::spawn(sync_helper_wrapper( - sender_user.clone(), - sender_device.clone(), - body, - tx, - )); - - rx - } - } - }; - - let we_have_to_wait = rx.borrow().is_none(); - if we_have_to_wait { - if let Err(e) = rx.changed().await { - error!("Error waiting for sync: {}", e); - } - } - - let result = match rx - .borrow() - .as_ref() - .expect("When sync channel changes it's always set to some") - { - Ok(response) => Ok(response.clone()), - Err(error) => Err(error.to_response()), - }; - - result -} - -async fn sync_helper_wrapper( - sender_user: OwnedUserId, - sender_device: OwnedDeviceId, - body: sync_events::v3::Request, - tx: Sender>>, -) { - let since = body.since.clone(); - - let r = sync_helper(sender_user.clone(), sender_device.clone(), body).await; - - if let Ok((_, caching_allowed)) = r { - if !caching_allowed { - match services() - .globals - .sync_receivers - .write() - .await - .entry((sender_user, sender_device)) - { - Entry::Occupied(o) => { - // Only remove if the device didn't start a different /sync - // already - if o.get().0 == since { - o.remove(); - } - } - Entry::Vacant(_) => {} - } - } - } - - tx.send(Some(r.map(|(r, _)| r))).expect("receiver should not be dropped"); -} - -#[allow(clippy::too_many_lines)] -async fn sync_helper( - sender_user: OwnedUserId, - sender_device: OwnedDeviceId, - body: sync_events::v3::Request, - // bool = caching allowed -) -> Result<(sync_events::v3::Response, bool), Error> { // Setup watchers, so if there's no response, we can wait for them let watcher = services().globals.watch(&sender_user, &sender_device); @@ -565,10 +457,10 @@ async fn sync_helper( Ok(x) => x.expect("watcher should succeed"), Err(error) => debug!(%error, "timed out"), }; - Ok((response, false)) + Ok(response) } else { // Only cache if we made progress - Ok((response, since != next_batch)) + Ok(response) } } diff --git a/src/service/globals.rs b/src/service/globals.rs index 87ad973e..a97f08a9 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -23,15 +23,12 @@ use hyper::{ }; use reqwest::dns::{Addrs, Resolve, Resolving}; use ruma::{ - api::{ - client::sync::sync_events, - federation::discovery::{ServerSigningKeys, VerifyKey}, - }, + api::federation::discovery::{ServerSigningKeys, VerifyKey}, serde::Base64, - DeviceId, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName, - OwnedServerSigningKeyId, OwnedUserId, RoomVersionId, ServerName, UserId, + DeviceId, OwnedEventId, OwnedRoomId, OwnedServerName, + OwnedServerSigningKeyId, RoomVersionId, ServerName, UserId, }; -use tokio::sync::{broadcast, watch::Receiver, Mutex, RwLock, Semaphore}; +use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use tracing::{error, info}; use trust_dns_resolver::TokioAsyncResolver; @@ -41,12 +38,6 @@ type WellKnownMap = HashMap; type TlsNameMap = HashMap, u16)>; // Time if last failed try, number of failed tries type RateLimitState = (Instant, u32); -type SyncHandle = ( - // since - Option, - // rx - Receiver>>, -); pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -70,8 +61,6 @@ pub(crate) struct Service { Arc>>, pub(crate) servername_ratelimiter: Arc>>>, - pub(crate) sync_receivers: - RwLock>, pub(crate) roomid_mutex_insert: RwLock>>>, pub(crate) roomid_mutex_state: RwLock>>>, @@ -237,7 +226,6 @@ impl Service { roomid_mutex_federation: RwLock::new(HashMap::new()), roomid_federationhandletime: RwLock::new(HashMap::new()), stateres_mutex: Arc::new(Mutex::new(())), - sync_receivers: RwLock::new(HashMap::new()), rotate: RotationHandler::new(), shutdown: AtomicBool::new(false), }; From e7b8f78867d00da065f3841e63067512c8652b44 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 20:56:36 -0700 Subject: [PATCH 117/617] update rocksdb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'rocksdb': 'github:facebook/rocksdb/bcf88d48ce8aa8b536aee4dd305533b3b83cf435' (2024-04-16) → 'github:facebook/rocksdb/6f7cabeac80a3a6150be2c8a8369fcecb107bf43' (2024-04-22) --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- flake.lock | 8 ++++---- flake.nix | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f01f20b..23f938e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2120,9 +2120,9 @@ dependencies = [ [[package]] name = "rust-librocksdb-sys" -version = "0.20.0+9.1.0" +version = "0.21.0+9.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b14f4848d8574c074bb26445b43e63735d802ef2fc5cc40c1b015134baee0c" +checksum = "75cb7b9cd5ce3b3ce0757ceab2240f7471826780b8700845c0cfd418cb7e398d" dependencies = [ "bindgen", "bzip2-sys", @@ -2136,9 +2136,9 @@ dependencies = [ [[package]] name = "rust-rocksdb" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36eae38b1d3d0018e273191f791343bd3eb030d7da63aaa20350e41c0182881" +checksum = "2bcfb31b5bf2e3274686ebfdf9a946e9a327a3bc54adc7e5cda9f4fdcc4b55f1" dependencies = [ "libc", "rust-librocksdb-sys", diff --git a/Cargo.toml b/Cargo.toml index 671a4e6a..5b37280d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,7 +110,7 @@ rand = "0.8.5" regex = "1.8.1" reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } ring = "0.17.7" -rocksdb = { package = "rust-rocksdb", version = "0.24.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +rocksdb = { package = "rust-rocksdb", version = "0.25.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", rev = "5495b85aa311c2805302edb0a7de40399e22b397", 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" ] } rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } rust-argon2 = "1.0.0" diff --git a/flake.lock b/flake.lock index 396bd2f2..c7dd9319 100644 --- a/flake.lock +++ b/flake.lock @@ -221,16 +221,16 @@ "rocksdb": { "flake": false, "locked": { - "lastModified": 1713310517, - "narHash": "sha256-vRPyrXkXVVhP56n5FVYef8zbIsnnanQSpElmQLZ7mh8=", + "lastModified": 1713810944, + "narHash": "sha256-/Xf0bzNJPclH9IP80QNaABfhj4IAR5LycYET18VFCXc=", "owner": "facebook", "repo": "rocksdb", - "rev": "bcf88d48ce8aa8b536aee4dd305533b3b83cf435", + "rev": "6f7cabeac80a3a6150be2c8a8369fcecb107bf43", "type": "github" }, "original": { "owner": "facebook", - "ref": "v9.1.0", + "ref": "v9.1.1", "repo": "rocksdb", "type": "github" } diff --git a/flake.nix b/flake.nix index 8021b3de..dc042ad0 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,7 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.0"; flake = false; }; + rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.1"; flake = false; }; }; outputs = inputs: From 93a2bf9c93e0358adb61207a52c01f2616916a21 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 21:09:42 -0700 Subject: [PATCH 118/617] change `FedDest::{into -> to}_*` They don't need to take ownership of `self`. --- src/api/server_server.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 5ae6af46..3de1d3aa 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -94,14 +94,14 @@ pub(crate) enum FedDest { } impl FedDest { - fn into_https_string(self) -> String { + fn to_https_string(&self) -> String { match self { Self::Literal(addr) => format!("https://{addr}"), Self::Named(host, port) => format!("https://{host}{port}"), } } - fn into_uri_string(self) -> String { + fn to_uri_string(&self) -> String { match self { Self::Literal(addr) => addr.to_string(), Self::Named(host, port) => format!("{host}{port}"), @@ -162,10 +162,10 @@ where let result = find_actual_destination(destination).await; - (result.0, result.1.into_uri_string()) + (result.0, result.1.to_uri_string()) }; - let actual_destination_str = actual_destination.clone().into_https_string(); + let actual_destination_str = actual_destination.to_https_string(); let mut http_request = request .try_into_http_request::>( @@ -378,7 +378,7 @@ async fn find_actual_destination( { debug!("3: A .well-known file is available"); hostname = add_port_to_hostname(&delegated_hostname) - .into_uri_string(); + .to_uri_string(); if let Some(host_and_port) = get_ip_with_port(&delegated_hostname) { From 87ac0e2a38dfe62d2b6d3acd3a6596700dc125e6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 16 May 2024 21:36:28 -0700 Subject: [PATCH 119/617] don't log that federation is disabled This mostly just spams the logs with useless information when doing cursed local testing. --- src/api/server_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 3de1d3aa..64fe253d 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -134,7 +134,7 @@ where T: Debug, { if !services().globals.allow_federation() { - return Err(Error::bad_config("Federation is disabled.")); + return Err(Error::BadConfig("Federation is disabled.")); } if destination == services().globals.server_name() { From 230ebd3884940d1e4bc084c21d63652a853b09ca Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 18 May 2024 18:31:36 -0700 Subject: [PATCH 120/617] don't automatically wrap in `RumaResponse` This allows us to use the `ruma_route` convenience function even when we need to add our own hacks into the responses, thus making us less reliant on Ruma. --- src/api/client_server/account.rs | 48 +++++----- src/api/client_server/alias.rs | 14 +-- src/api/client_server/backup.rs | 82 ++++++++--------- src/api/client_server/capabilities.rs | 8 +- src/api/client_server/config.rs | 22 ++--- src/api/client_server/context.rs | 6 +- src/api/client_server/device.rs | 26 +++--- src/api/client_server/directory.rs | 21 +++-- src/api/client_server/filter.rs | 12 +-- src/api/client_server/keys.rs | 32 +++---- src/api/client_server/media.rs | 43 +++++---- src/api/client_server/membership.rs | 59 ++++++------ src/api/client_server/message.rs | 16 ++-- src/api/client_server/profile.rs | 42 +++++---- src/api/client_server/push.rs | 52 +++++------ src/api/client_server/read_marker.rs | 9 +- src/api/client_server/redact.rs | 8 +- src/api/client_server/relations.rs | 54 ++++++----- src/api/client_server/report.rs | 6 +- src/api/client_server/room.rs | 24 ++--- src/api/client_server/search.rs | 8 +- src/api/client_server/session.rs | 22 ++--- src/api/client_server/space.rs | 5 +- src/api/client_server/state.rs | 18 ++-- src/api/client_server/sync.rs | 14 +-- src/api/client_server/tag.rs | 16 ++-- src/api/client_server/thirdparty.rs | 8 +- src/api/client_server/threads.rs | 8 +- src/api/client_server/to_device.rs | 8 +- src/api/client_server/typing.rs | 6 +- src/api/client_server/unversioned.rs | 6 +- src/api/client_server/user_directory.rs | 8 +- src/api/client_server/voip.rs | 8 +- src/api/server_server.rs | 116 ++++++++++++------------ src/main.rs | 8 +- 35 files changed, 438 insertions(+), 405 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 4d529b68..52e6c5fc 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -19,7 +19,9 @@ use ruma::{ use tracing::{info, warn}; use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH}; -use crate::{api::client_server, services, utils, Error, Result, Ruma}; +use crate::{ + api::client_server, services, utils, Error, Result, Ruma, RumaResponse, +}; const RANDOM_USER_ID_LENGTH: usize = 10; @@ -36,7 +38,7 @@ const RANDOM_USER_ID_LENGTH: usize = 10; /// invalid when trying to register pub(crate) async fn get_register_available_route( body: Ruma, -) -> Result { +) -> Result> { // Validate user id let user_id = UserId::parse_with_server_name( body.username.to_lowercase(), @@ -63,9 +65,9 @@ pub(crate) async fn get_register_available_route( // TODO add check for appservice namespaces // If no if check is true we have an username that's available to be used. - Ok(get_username_availability::v3::Response { + Ok(RumaResponse(get_username_availability::v3::Response { available: true, - }) + })) } /// # `POST /_matrix/client/r0/register` @@ -88,7 +90,7 @@ pub(crate) async fn get_register_available_route( #[allow(clippy::too_many_lines)] pub(crate) async fn register_route( body: Ruma, -) -> Result { +) -> Result> { if !services().globals.allow_registration() && body.appservice_info.is_none() { @@ -248,13 +250,13 @@ pub(crate) async fn register_route( // Inhibit login does not work for guests if !is_guest && body.inhibit_login { - return Ok(register::v3::Response { + return Ok(RumaResponse(register::v3::Response { access_token: None, user_id, device_id: None, refresh_token: None, expires_in: None, - }); + })); } // Generate new device id if the user didn't specify one @@ -300,13 +302,13 @@ pub(crate) async fn register_route( } } - Ok(register::v3::Response { + Ok(RumaResponse(register::v3::Response { access_token: Some(token), user_id, device_id: Some(device_id), refresh_token: None, expires_in: None, - }) + })) } /// # `POST /_matrix/client/r0/account/password` @@ -328,7 +330,7 @@ pub(crate) async fn register_route( /// - Triggers device list updates pub(crate) async fn change_password_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -381,7 +383,7 @@ pub(crate) async fn change_password_route( format!("User {sender_user} changed their password."), )); - Ok(change_password::v3::Response {}) + Ok(RumaResponse(change_password::v3::Response {})) } /// # `GET _matrix/client/r0/account/whoami` @@ -391,16 +393,16 @@ pub(crate) async fn change_password_route( /// Note: Also works for Application Services pub(crate) async fn whoami_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device_id = body.sender_device.as_ref().cloned(); - Ok(whoami::v3::Response { + Ok(RumaResponse(whoami::v3::Response { user_id: sender_user.clone(), device_id, is_guest: services().users.is_deactivated(sender_user)? && body.appservice_info.is_none(), - }) + })) } /// # `POST /_matrix/client/r0/account/deactivate` @@ -416,7 +418,7 @@ pub(crate) async fn whoami_route( /// - Removes ability to log in again pub(crate) async fn deactivate_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -461,9 +463,9 @@ pub(crate) async fn deactivate_route( format!("User {sender_user} deactivated their account."), )); - Ok(deactivate::v3::Response { + Ok(RumaResponse(deactivate::v3::Response { id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport, - }) + })) } /// # `GET _matrix/client/v3/account/3pid` @@ -473,11 +475,11 @@ pub(crate) async fn deactivate_route( /// - Currently always returns empty list pub(crate) async fn third_party_route( body: Ruma, -) -> Result { +) -> Result> { let _sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(get_3pids::v3::Response::new(Vec::new())) + Ok(RumaResponse(get_3pids::v3::Response::new(Vec::new()))) } /// # `POST /_matrix/client/v3/account/3pid/email/requestToken` @@ -489,7 +491,8 @@ pub(crate) async fn third_party_route( /// as a contact option. pub(crate) async fn request_3pid_management_token_via_email_route( _body: Ruma, -) -> Result { +) -> Result> +{ Err(Error::BadRequest( ErrorKind::ThreepidDenied, "Third party identifier is not allowed", @@ -503,9 +506,12 @@ pub(crate) async fn request_3pid_management_token_via_email_route( /// /// - 403 signals that The homeserver does not allow the third party identifier /// as a contact option. +#[rustfmt::skip] pub(crate) async fn request_3pid_management_token_via_msisdn_route( _body: Ruma, -) -> Result { +) +-> Result> +{ Err(Error::BadRequest( ErrorKind::ThreepidDenied, "Third party identifier is not allowed", diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 5d1a0c30..41db0740 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -11,14 +11,14 @@ use ruma::{ OwnedRoomAliasId, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}` /// /// Creates a new room alias on this server. pub(crate) async fn create_alias_route( body: Ruma, -) -> Result { +) -> Result> { if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -46,7 +46,7 @@ pub(crate) async fn create_alias_route( services().rooms.alias.set_alias(&body.room_alias, &body.room_id)?; - Ok(create_alias::v3::Response::new()) + Ok(RumaResponse(create_alias::v3::Response::new())) } /// # `DELETE /_matrix/client/r0/directory/room/{roomAlias}` @@ -57,7 +57,7 @@ pub(crate) async fn create_alias_route( /// - TODO: Update canonical alias event pub(crate) async fn delete_alias_route( body: Ruma, -) -> Result { +) -> Result> { if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -83,7 +83,7 @@ pub(crate) async fn delete_alias_route( // TODO: update alt_aliases? - Ok(delete_alias::v3::Response::new()) + Ok(RumaResponse(delete_alias::v3::Response::new())) } /// # `GET /_matrix/client/r0/directory/room/{roomAlias}` @@ -93,8 +93,8 @@ pub(crate) async fn delete_alias_route( /// - TODO: Suggest more servers to join via pub(crate) async fn get_alias_route( body: Ruma, -) -> Result { - get_alias_helper(body.body.room_alias).await +) -> Result> { + get_alias_helper(body.body.room_alias).await.map(RumaResponse) } pub(crate) async fn get_alias_helper( diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index 4cfffcc1..161f0fd2 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -9,21 +9,21 @@ use ruma::api::client::{ error::ErrorKind, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `POST /_matrix/client/r0/room_keys/version` /// /// Creates a new backup. pub(crate) async fn create_backup_version_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let version = services().key_backups.create_backup(sender_user, &body.algorithm)?; - Ok(create_backup_version::v3::Response { + Ok(RumaResponse(create_backup_version::v3::Response { version, - }) + })) } /// # `PUT /_matrix/client/r0/room_keys/version/{version}` @@ -32,7 +32,7 @@ pub(crate) async fn create_backup_version_route( /// modified. pub(crate) async fn update_backup_version_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.update_backup( sender_user, @@ -40,7 +40,7 @@ pub(crate) async fn update_backup_version_route( &body.algorithm, )?; - Ok(update_backup_version::v3::Response {}) + Ok(RumaResponse(update_backup_version::v3::Response {})) } /// # `GET /_matrix/client/r0/room_keys/version` @@ -48,7 +48,7 @@ pub(crate) async fn update_backup_version_route( /// Get information about the latest backup version. pub(crate) async fn get_latest_backup_info_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let (version, algorithm) = services() @@ -59,7 +59,7 @@ pub(crate) async fn get_latest_backup_info_route( "Key backup does not exist.", ))?; - Ok(get_latest_backup_info::v3::Response { + Ok(RumaResponse(get_latest_backup_info::v3::Response { algorithm, count: services() .key_backups @@ -68,7 +68,7 @@ pub(crate) async fn get_latest_backup_info_route( .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &version)?, version, - }) + })) } /// # `GET /_matrix/client/r0/room_keys/version` @@ -76,7 +76,7 @@ pub(crate) async fn get_latest_backup_info_route( /// Get information about an existing backup. pub(crate) async fn get_backup_info_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let algorithm = services() .key_backups @@ -86,7 +86,7 @@ pub(crate) async fn get_backup_info_route( "Key backup does not exist.", ))?; - Ok(get_backup_info::v3::Response { + Ok(RumaResponse(get_backup_info::v3::Response { algorithm, count: services() .key_backups @@ -95,7 +95,7 @@ pub(crate) async fn get_backup_info_route( .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, version: body.version.clone(), - }) + })) } /// # `DELETE /_matrix/client/r0/room_keys/version/{version}` @@ -106,12 +106,12 @@ pub(crate) async fn get_backup_info_route( /// to the backup pub(crate) async fn delete_backup_version_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_backup(sender_user, &body.version)?; - Ok(delete_backup_version::v3::Response {}) + Ok(RumaResponse(delete_backup_version::v3::Response {})) } /// # `PUT /_matrix/client/r0/room_keys/keys` @@ -124,7 +124,7 @@ pub(crate) async fn delete_backup_version_route( /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if Some(&body.version) @@ -152,14 +152,14 @@ pub(crate) async fn add_backup_keys_route( } } - Ok(add_backup_keys::v3::Response { + Ok(RumaResponse(add_backup_keys::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, - }) + })) } /// # `PUT /_matrix/client/r0/room_keys/keys/{roomId}` @@ -172,7 +172,7 @@ pub(crate) async fn add_backup_keys_route( /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_room_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if Some(&body.version) @@ -198,14 +198,14 @@ pub(crate) async fn add_backup_keys_for_room_route( )?; } - Ok(add_backup_keys_for_room::v3::Response { + Ok(RumaResponse(add_backup_keys_for_room::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, - }) + })) } /// # `PUT /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}` @@ -218,7 +218,7 @@ pub(crate) async fn add_backup_keys_for_room_route( /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_session_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if Some(&body.version) @@ -242,14 +242,14 @@ pub(crate) async fn add_backup_keys_for_session_route( &body.session_data, )?; - Ok(add_backup_keys_for_session::v3::Response { + Ok(RumaResponse(add_backup_keys_for_session::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, - }) + })) } /// # `GET /_matrix/client/r0/room_keys/keys` @@ -257,14 +257,14 @@ pub(crate) async fn add_backup_keys_for_session_route( /// Retrieves all keys from the backup. pub(crate) async fn get_backup_keys_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let rooms = services().key_backups.get_all(sender_user, &body.version)?; - Ok(get_backup_keys::v3::Response { + Ok(RumaResponse(get_backup_keys::v3::Response { rooms, - }) + })) } /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}` @@ -272,7 +272,7 @@ pub(crate) async fn get_backup_keys_route( /// Retrieves all keys from the backup for a given room. pub(crate) async fn get_backup_keys_for_room_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sessions = services().key_backups.get_room( @@ -281,9 +281,9 @@ pub(crate) async fn get_backup_keys_for_room_route( &body.room_id, )?; - Ok(get_backup_keys_for_room::v3::Response { + Ok(RumaResponse(get_backup_keys_for_room::v3::Response { sessions, - }) + })) } /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}` @@ -291,7 +291,7 @@ pub(crate) async fn get_backup_keys_for_room_route( /// Retrieves a key from the backup. pub(crate) async fn get_backup_keys_for_session_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let key_data = services() @@ -307,9 +307,9 @@ pub(crate) async fn get_backup_keys_for_session_route( "Backup key not found for this user's session.", ))?; - Ok(get_backup_keys_for_session::v3::Response { + Ok(RumaResponse(get_backup_keys_for_session::v3::Response { key_data, - }) + })) } /// # `DELETE /_matrix/client/r0/room_keys/keys` @@ -317,19 +317,19 @@ pub(crate) async fn get_backup_keys_for_session_route( /// Delete the keys from the backup. pub(crate) async fn delete_backup_keys_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_all_keys(sender_user, &body.version)?; - Ok(delete_backup_keys::v3::Response { + Ok(RumaResponse(delete_backup_keys::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, - }) + })) } /// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}` @@ -337,7 +337,7 @@ pub(crate) async fn delete_backup_keys_route( /// Delete the keys from the backup for a given room. pub(crate) async fn delete_backup_keys_for_room_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_room_keys( @@ -346,14 +346,14 @@ pub(crate) async fn delete_backup_keys_for_room_route( &body.room_id, )?; - Ok(delete_backup_keys_for_room::v3::Response { + Ok(RumaResponse(delete_backup_keys_for_room::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, - }) + })) } /// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}` @@ -361,7 +361,7 @@ pub(crate) async fn delete_backup_keys_for_room_route( /// Delete a key from the backup. pub(crate) async fn delete_backup_keys_for_session_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_room_key( @@ -371,12 +371,12 @@ pub(crate) async fn delete_backup_keys_for_session_route( &body.session_id, )?; - Ok(delete_backup_keys_for_session::v3::Response { + Ok(RumaResponse(delete_backup_keys_for_session::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? .try_into() .expect("count should fit in UInt"), etag: services().key_backups.get_etag(sender_user, &body.version)?, - }) + })) } diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index 61152bf0..410096bd 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -4,7 +4,7 @@ use ruma::api::client::discovery::get_capabilities::{ self, Capabilities, RoomVersionStability, RoomVersionsCapability, }; -use crate::{services, Result, Ruma}; +use crate::{services, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/capabilities` /// @@ -12,7 +12,7 @@ use crate::{services, Result, Ruma}; /// of this server. pub(crate) async fn get_capabilities_route( _body: Ruma, -) -> Result { +) -> Result> { let mut available = BTreeMap::new(); for room_version in &services().globals.unstable_room_versions { available.insert(room_version.clone(), RoomVersionStability::Unstable); @@ -27,7 +27,7 @@ pub(crate) async fn get_capabilities_route( available, }; - Ok(get_capabilities::v3::Response { + Ok(RumaResponse(get_capabilities::v3::Response { capabilities, - }) + })) } diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index d4265078..2a0bcda8 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -14,14 +14,14 @@ use ruma::{ use serde::Deserialize; use serde_json::{json, value::RawValue as RawJsonValue}; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}` /// /// Sets some account data for the sender user. pub(crate) async fn set_global_account_data_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let data: serde_json::Value = serde_json::from_str(body.data.json().get()) @@ -41,7 +41,7 @@ pub(crate) async fn set_global_account_data_route( }), )?; - Ok(set_global_account_data::v3::Response {}) + Ok(RumaResponse(set_global_account_data::v3::Response {})) } /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}` @@ -49,7 +49,7 @@ pub(crate) async fn set_global_account_data_route( /// Sets some room account data for the sender user. pub(crate) async fn set_room_account_data_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let data: serde_json::Value = serde_json::from_str(body.data.json().get()) @@ -69,7 +69,7 @@ pub(crate) async fn set_room_account_data_route( }), )?; - Ok(set_room_account_data::v3::Response {}) + Ok(RumaResponse(set_room_account_data::v3::Response {})) } /// # `GET /_matrix/client/r0/user/{userId}/account_data/{type}` @@ -77,7 +77,7 @@ pub(crate) async fn set_room_account_data_route( /// Gets some account data for the sender user. pub(crate) async fn get_global_account_data_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event: Box = services() @@ -92,9 +92,9 @@ pub(crate) async fn get_global_account_data_route( })? .content; - Ok(get_global_account_data::v3::Response { + Ok(RumaResponse(get_global_account_data::v3::Response { account_data, - }) + })) } /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}` @@ -102,7 +102,7 @@ pub(crate) async fn get_global_account_data_route( /// Gets some room account data for the sender user. pub(crate) async fn get_room_account_data_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event: Box = services() @@ -117,9 +117,9 @@ pub(crate) async fn get_room_account_data_route( })? .content; - Ok(get_room_account_data::v3::Response { + Ok(RumaResponse(get_room_account_data::v3::Response { account_data, - }) + })) } #[derive(Deserialize)] diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 0ada0bbc..73a7453b 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -9,7 +9,7 @@ use ruma::{ }; use tracing::error; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/rooms/{roomId}/context` /// @@ -21,7 +21,7 @@ use crate::{services, Error, Result, Ruma}; #[allow(clippy::too_many_lines)] pub(crate) async fn get_context_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -187,5 +187,5 @@ pub(crate) async fn get_context_route( state, }; - Ok(resp) + Ok(RumaResponse(resp)) } diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 59ae4710..b8cd3edc 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -8,14 +8,14 @@ use ruma::api::client::{ }; use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Result, Ruma}; +use crate::{services, utils, Error, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/devices` /// /// Get metadata on all devices of the sender user. pub(crate) async fn get_devices_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let devices: Vec = services() @@ -24,9 +24,9 @@ pub(crate) async fn get_devices_route( .filter_map(Result::ok) .collect(); - Ok(get_devices::v3::Response { + Ok(RumaResponse(get_devices::v3::Response { devices, - }) + })) } /// # `GET /_matrix/client/r0/devices/{deviceId}` @@ -34,7 +34,7 @@ pub(crate) async fn get_devices_route( /// Get metadata on a single device of the sender user. pub(crate) async fn get_device_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device = services() @@ -42,9 +42,9 @@ pub(crate) async fn get_device_route( .get_device_metadata(sender_user, &body.body.device_id)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?; - Ok(get_device::v3::Response { + Ok(RumaResponse(get_device::v3::Response { device, - }) + })) } /// # `PUT /_matrix/client/r0/devices/{deviceId}` @@ -52,7 +52,7 @@ pub(crate) async fn get_device_route( /// Updates the metadata on a given device of the sender user. pub(crate) async fn update_device_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let mut device = services() @@ -68,7 +68,7 @@ pub(crate) async fn update_device_route( &device, )?; - Ok(update_device::v3::Response {}) + Ok(RumaResponse(update_device::v3::Response {})) } /// # `DELETE /_matrix/client/r0/devices/{deviceId}` @@ -83,7 +83,7 @@ pub(crate) async fn update_device_route( /// - Triggers device list updates pub(crate) async fn delete_device_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -120,7 +120,7 @@ pub(crate) async fn delete_device_route( services().users.remove_device(sender_user, &body.device_id)?; - Ok(delete_device::v3::Response {}) + Ok(RumaResponse(delete_device::v3::Response {})) } /// # `PUT /_matrix/client/r0/devices/{deviceId}` @@ -137,7 +137,7 @@ pub(crate) async fn delete_device_route( /// - Triggers device list updates pub(crate) async fn delete_devices_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -176,5 +176,5 @@ pub(crate) async fn delete_devices_route( services().users.remove_device(sender_user, device_id)?; } - Ok(delete_devices::v3::Response {}) + Ok(RumaResponse(delete_devices::v3::Response {})) } diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 6776818d..2ce36036 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -29,7 +29,7 @@ use ruma::{ }; use tracing::{error, info, warn}; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `POST /_matrix/client/r0/publicRooms` /// @@ -38,7 +38,7 @@ use crate::{services, Error, Result, Ruma}; /// - Rooms are ordered by the number of joined members pub(crate) async fn get_public_rooms_filtered_route( body: Ruma, -) -> Result { +) -> Result> { get_public_rooms_filtered_helper( body.server.as_deref(), body.limit, @@ -47,6 +47,7 @@ pub(crate) async fn get_public_rooms_filtered_route( &body.room_network, ) .await + .map(RumaResponse) } /// # `GET /_matrix/client/r0/publicRooms` @@ -56,7 +57,7 @@ pub(crate) async fn get_public_rooms_filtered_route( /// - Rooms are ordered by the number of joined members pub(crate) async fn get_public_rooms_route( body: Ruma, -) -> Result { +) -> Result> { let response = get_public_rooms_filtered_helper( body.server.as_deref(), body.limit, @@ -66,12 +67,12 @@ pub(crate) async fn get_public_rooms_route( ) .await?; - Ok(get_public_rooms::v3::Response { + Ok(RumaResponse(get_public_rooms::v3::Response { chunk: response.chunk, prev_batch: response.prev_batch, next_batch: response.next_batch, total_room_count_estimate: response.total_room_count_estimate, - }) + })) } /// # `PUT /_matrix/client/r0/directory/list/room/{roomId}` @@ -81,7 +82,7 @@ pub(crate) async fn get_public_rooms_route( /// - TODO: Access control checks pub(crate) async fn set_room_visibility_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services().rooms.metadata.exists(&body.room_id)? { @@ -105,7 +106,7 @@ pub(crate) async fn set_room_visibility_route( } } - Ok(set_room_visibility::v3::Response {}) + Ok(RumaResponse(set_room_visibility::v3::Response {})) } /// # `GET /_matrix/client/r0/directory/list/room/{roomId}` @@ -113,13 +114,13 @@ pub(crate) async fn set_room_visibility_route( /// Gets the visibility of a given room in the room directory. pub(crate) async fn get_room_visibility_route( body: Ruma, -) -> Result { +) -> Result> { if !services().rooms.metadata.exists(&body.room_id)? { // Return 404 if the room doesn't exist return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found")); } - Ok(get_room_visibility::v3::Response { + Ok(RumaResponse(get_room_visibility::v3::Response { visibility: if services() .rooms .directory @@ -129,7 +130,7 @@ pub(crate) async fn get_room_visibility_route( } else { room::Visibility::Private }, - }) + })) } #[allow(clippy::too_many_lines)] diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index fc2f2a1c..197e3ff8 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -3,7 +3,7 @@ use ruma::api::client::{ filter::{create_filter, get_filter}, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}` /// @@ -12,7 +12,7 @@ use crate::{services, Error, Result, Ruma}; /// - A user can only access their own filters pub(crate) async fn get_filter_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? @@ -23,7 +23,7 @@ pub(crate) async fn get_filter_route( )); }; - Ok(get_filter::v3::Response::new(filter)) + Ok(RumaResponse(get_filter::v3::Response::new(filter))) } /// # `PUT /_matrix/client/r0/user/{userId}/filter` @@ -31,9 +31,9 @@ pub(crate) async fn get_filter_route( /// Creates a new filter to be used by other endpoints. pub(crate) async fn create_filter_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(create_filter::v3::Response::new( + Ok(RumaResponse(create_filter::v3::Response::new( services().users.create_filter(sender_user, &body.filter)?, - )) + ))) } diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 4e68e81a..614840a1 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -23,7 +23,7 @@ use serde_json::json; use tracing::debug; use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Result, Ruma}; +use crate::{services, utils, Error, Result, Ruma, RumaResponse}; /// # `POST /_matrix/client/r0/keys/upload` /// @@ -34,7 +34,7 @@ use crate::{services, utils, Error, Result, Ruma}; /// existing keys?) pub(crate) async fn upload_keys_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -64,11 +64,11 @@ pub(crate) async fn upload_keys_route( } } - Ok(upload_keys::v3::Response { + Ok(RumaResponse(upload_keys::v3::Response { one_time_key_counts: services() .users .count_one_time_keys(sender_user, sender_device)?, - }) + })) } /// # `POST /_matrix/client/r0/keys/query` @@ -81,7 +81,7 @@ pub(crate) async fn upload_keys_route( /// allowed to see pub(crate) async fn get_keys_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let response = get_keys_helper(Some(sender_user), &body.device_keys, |u| { @@ -89,7 +89,7 @@ pub(crate) async fn get_keys_route( }) .await?; - Ok(response) + Ok(RumaResponse(response)) } /// # `POST /_matrix/client/r0/keys/claim` @@ -97,10 +97,10 @@ pub(crate) async fn get_keys_route( /// Claims one-time keys pub(crate) async fn claim_keys_route( body: Ruma, -) -> Result { +) -> Result> { let response = claim_keys_helper(&body.one_time_keys).await?; - Ok(response) + Ok(RumaResponse(response)) } /// # `POST /_matrix/client/r0/keys/device_signing/upload` @@ -110,7 +110,7 @@ pub(crate) async fn claim_keys_route( /// - Requires UIAA to verify password pub(crate) async fn upload_signing_keys_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -156,7 +156,7 @@ pub(crate) async fn upload_signing_keys_route( )?; } - Ok(upload_signing_keys::v3::Response {}) + Ok(RumaResponse(upload_signing_keys::v3::Response {})) } /// # `POST /_matrix/client/r0/keys/signatures/upload` @@ -164,7 +164,7 @@ pub(crate) async fn upload_signing_keys_route( /// Uploads end-to-end key signatures from the sender user. pub(crate) async fn upload_signatures_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); for (user_id, keys) in &body.signed_keys { @@ -213,10 +213,10 @@ pub(crate) async fn upload_signatures_route( } } - Ok(upload_signatures::v3::Response { + Ok(RumaResponse(upload_signatures::v3::Response { // TODO: integrate failures: BTreeMap::new(), - }) + })) } /// # `POST /_matrix/client/r0/keys/changes` @@ -227,7 +227,7 @@ pub(crate) async fn upload_signatures_route( /// - TODO: left users pub(crate) async fn get_key_changes_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let mut device_list_updates = HashSet::new(); @@ -277,11 +277,11 @@ pub(crate) async fn get_key_changes_route( .filter_map(Result::ok), ); } - Ok(get_key_changes::v3::Response { + Ok(RumaResponse(get_key_changes::v3::Response { changed: device_list_updates.into_iter().collect(), // TODO left: Vec::new(), - }) + })) } #[allow(clippy::too_many_lines)] diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index e4b90ed3..16b72429 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -8,7 +8,10 @@ use ruma::api::client::{ }, }; -use crate::{service::media::FileMeta, services, utils, Error, Result, Ruma}; +use crate::{ + service::media::FileMeta, services, utils, Error, Result, Ruma, + RumaResponse, +}; const MXC_LENGTH: usize = 32; @@ -17,10 +20,10 @@ const MXC_LENGTH: usize = 32; /// Returns max upload size. pub(crate) async fn get_media_config_route( _body: Ruma, -) -> Result { - Ok(get_media_config::v3::Response { +) -> Result> { + Ok(RumaResponse(get_media_config::v3::Response { upload_size: services().globals.max_request_size().into(), - }) + })) } /// # `POST /_matrix/media/r0/upload` @@ -31,7 +34,7 @@ pub(crate) async fn get_media_config_route( /// - Media will be saved in the media/ directory pub(crate) async fn create_content_route( body: Ruma, -) -> Result { +) -> Result> { let mxc = format!( "mxc://{}/{}", services().globals.server_name(), @@ -51,10 +54,10 @@ pub(crate) async fn create_content_route( ) .await?; - Ok(create_content::v3::Response { + Ok(RumaResponse(create_content::v3::Response { content_uri: mxc.into(), blurhash: None, - }) + })) } pub(crate) async fn get_remote_content( @@ -96,7 +99,7 @@ pub(crate) async fn get_remote_content( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_route( body: Ruma, -) -> Result { +) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -105,19 +108,19 @@ pub(crate) async fn get_content_route( file, }) = services().media.get(mxc.clone()).await? { - Ok(get_content::v3::Response { + Ok(RumaResponse(get_content::v3::Response { file, content_type, content_disposition, cross_origin_resource_policy: Some("cross-origin".to_owned()), - }) + })) } else if &*body.server_name != services().globals.server_name() && body.allow_remote { let remote_content_response = get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(remote_content_response) + Ok(RumaResponse(remote_content_response)) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } @@ -130,7 +133,7 @@ pub(crate) async fn get_content_route( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_as_filename_route( body: Ruma, -) -> Result { +) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -139,7 +142,7 @@ pub(crate) async fn get_content_as_filename_route( .. }) = services().media.get(mxc.clone()).await? { - Ok(get_content_as_filename::v3::Response { + Ok(RumaResponse(get_content_as_filename::v3::Response { file, content_type, content_disposition: Some(format!( @@ -147,7 +150,7 @@ pub(crate) async fn get_content_as_filename_route( body.filename )), cross_origin_resource_policy: Some("cross-origin".to_owned()), - }) + })) } else if &*body.server_name != services().globals.server_name() && body.allow_remote { @@ -155,7 +158,7 @@ pub(crate) async fn get_content_as_filename_route( get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(get_content_as_filename::v3::Response { + Ok(RumaResponse(get_content_as_filename::v3::Response { content_disposition: Some(format!( "inline: filename={}", body.filename @@ -163,7 +166,7 @@ pub(crate) async fn get_content_as_filename_route( content_type: remote_content_response.content_type, file: remote_content_response.file, cross_origin_resource_policy: Some("cross-origin".to_owned()), - }) + })) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } @@ -176,7 +179,7 @@ pub(crate) async fn get_content_as_filename_route( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_thumbnail_route( body: Ruma, -) -> Result { +) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -196,11 +199,11 @@ pub(crate) async fn get_content_thumbnail_route( ) .await? { - Ok(get_content_thumbnail::v3::Response { + Ok(RumaResponse(get_content_thumbnail::v3::Response { file, content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), - }) + })) } else if &*body.server_name != services().globals.server_name() && body.allow_remote { @@ -233,7 +236,7 @@ pub(crate) async fn get_content_thumbnail_route( ) .await?; - Ok(get_thumbnail_response) + Ok(RumaResponse(get_thumbnail_response)) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index fa044f27..d99b1d5c 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -36,7 +36,7 @@ use tracing::{debug, error, info, warn}; use super::get_alias_helper; use crate::{ service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Result, Ruma, + services, utils, Error, PduEvent, Result, Ruma, RumaResponse, }; /// # `POST /_matrix/client/r0/rooms/{roomId}/join` @@ -49,7 +49,7 @@ use crate::{ /// federation pub(crate) async fn join_room_by_id_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // There is no body.server_name for /roomId/join @@ -83,6 +83,7 @@ pub(crate) async fn join_room_by_id_route( body.third_party_signed.as_ref(), ) .await + .map(RumaResponse) } /// # `POST /_matrix/client/r0/join/{roomIdOrAlias}` @@ -95,7 +96,7 @@ pub(crate) async fn join_room_by_id_route( /// federation pub(crate) async fn join_room_by_id_or_alias_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_deref().expect("user is authenticated"); let body = body.body; @@ -147,9 +148,9 @@ pub(crate) async fn join_room_by_id_or_alias_route( ) .await?; - Ok(join_room_by_id_or_alias::v3::Response { + Ok(RumaResponse(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id, - }) + })) } /// # `POST /_matrix/client/r0/rooms/{roomId}/leave` @@ -159,12 +160,12 @@ pub(crate) async fn join_room_by_id_or_alias_route( /// - This should always work if the user is currently joined. pub(crate) async fn leave_room_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); leave_room(sender_user, &body.room_id, body.reason.clone()).await?; - Ok(leave_room::v3::Response::new()) + Ok(RumaResponse(leave_room::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/invite` @@ -172,7 +173,7 @@ pub(crate) async fn leave_room_route( /// Tries to send an invite event into the room. pub(crate) async fn invite_user_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let invite_user::v3::InvitationRecipient::UserId { @@ -187,7 +188,7 @@ pub(crate) async fn invite_user_route( false, ) .await?; - Ok(invite_user::v3::Response {}) + Ok(RumaResponse(invite_user::v3::Response {})) } else { Err(Error::BadRequest(ErrorKind::NotFound, "User not found.")) } @@ -198,13 +199,13 @@ pub(crate) async fn invite_user_route( /// Tries to send a kick event into the room. pub(crate) async fn kick_user_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(true) = services().rooms.state_cache.is_left(sender_user, &body.room_id) { - return Ok(kick_user::v3::Response {}); + return Ok(RumaResponse(kick_user::v3::Response {})); } let mut event: RoomMemberEventContent = serde_json::from_str( @@ -259,7 +260,7 @@ pub(crate) async fn kick_user_route( drop(state_lock); - Ok(kick_user::v3::Response::new()) + Ok(RumaResponse(kick_user::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/ban` @@ -267,14 +268,14 @@ pub(crate) async fn kick_user_route( /// Tries to send a ban event into the room. pub(crate) async fn ban_user_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(Some(membership_event)) = services().rooms.state_accessor.get_member(&body.room_id, sender_user) { if membership_event.membership == MembershipState::Ban { - return Ok(ban_user::v3::Response {}); + return Ok(RumaResponse(ban_user::v3::Response {})); } } @@ -343,7 +344,7 @@ pub(crate) async fn ban_user_route( drop(state_lock); - Ok(ban_user::v3::Response::new()) + Ok(RumaResponse(ban_user::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/unban` @@ -351,14 +352,14 @@ pub(crate) async fn ban_user_route( /// Tries to send an unban event into the room. pub(crate) async fn unban_user_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(Some(membership_event)) = services().rooms.state_accessor.get_member(&body.room_id, sender_user) { if membership_event.membership != MembershipState::Ban { - return Ok(unban_user::v3::Response {}); + return Ok(RumaResponse(unban_user::v3::Response {})); } } @@ -414,7 +415,7 @@ pub(crate) async fn unban_user_route( drop(state_lock); - Ok(unban_user::v3::Response::new()) + Ok(RumaResponse(unban_user::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/forget` @@ -428,12 +429,12 @@ pub(crate) async fn unban_user_route( /// forgotten, so this has to be called from every device pub(crate) async fn forget_room_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().rooms.state_cache.forget(&body.room_id, sender_user)?; - Ok(forget_room::v3::Response::new()) + Ok(RumaResponse(forget_room::v3::Response::new())) } /// # `POST /_matrix/client/r0/joined_rooms` @@ -441,17 +442,17 @@ pub(crate) async fn forget_room_route( /// Lists all rooms the user has joined. pub(crate) async fn joined_rooms_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(joined_rooms::v3::Response { + Ok(RumaResponse(joined_rooms::v3::Response { joined_rooms: services() .rooms .state_cache .rooms_joined(sender_user) .filter_map(Result::ok) .collect(), - }) + })) } /// # `POST /_matrix/client/r0/rooms/{roomId}/members` @@ -462,7 +463,7 @@ pub(crate) async fn joined_rooms_route( /// - Only works if the user is currently joined pub(crate) async fn get_member_events_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -476,7 +477,7 @@ pub(crate) async fn get_member_events_route( )); } - Ok(get_member_events::v3::Response { + Ok(RumaResponse(get_member_events::v3::Response { chunk: services() .rooms .state_accessor @@ -486,7 +487,7 @@ pub(crate) async fn get_member_events_route( .filter(|(key, _)| key.0 == StateEventType::RoomMember) .map(|(_, pdu)| pdu.to_member_event()) .collect(), - }) + })) } /// # `POST /_matrix/client/r0/rooms/{roomId}/joined_members` @@ -497,7 +498,7 @@ pub(crate) async fn get_member_events_route( /// - TODO: An appservice just needs a puppet joined pub(crate) async fn joined_members_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -530,9 +531,9 @@ pub(crate) async fn joined_members_route( ); } - Ok(joined_members::v3::Response { + Ok(RumaResponse(joined_members::v3::Response { joined, - }) + })) } #[allow(clippy::too_many_lines)] diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 202b6c05..ef7b8947 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -14,7 +14,7 @@ use ruma::{ use crate::{ service::{pdu::PduBuilder, rooms::timeline::PduCount}, - services, utils, Error, Result, Ruma, + services, utils, Error, Result, Ruma, RumaResponse, }; /// # `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}` @@ -28,7 +28,7 @@ use crate::{ /// allowed pub(crate) async fn send_message_event_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); @@ -77,9 +77,9 @@ pub(crate) async fn send_message_event_route( .map_err(|_| { Error::bad_database("Invalid event id in txnid data.") })?; - return Ok(send_message_event::v3::Response { + return Ok(RumaResponse(send_message_event::v3::Response { event_id, - }); + })); } let mut unsigned = BTreeMap::new(); @@ -118,7 +118,9 @@ pub(crate) async fn send_message_event_route( drop(state_lock); - Ok(send_message_event::v3::Response::new((*event_id).to_owned())) + Ok(RumaResponse(send_message_event::v3::Response::new( + (*event_id).to_owned(), + ))) } /// # `GET /_matrix/client/r0/rooms/{roomId}/messages` @@ -131,7 +133,7 @@ pub(crate) async fn send_message_event_route( #[allow(clippy::too_many_lines)] pub(crate) async fn get_message_events_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -308,5 +310,5 @@ pub(crate) async fn get_message_events_route( } */ - Ok(resp) + Ok(RumaResponse(resp)) } diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index 4bcfc5af..f05f5b14 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -18,7 +18,9 @@ use ruma::{ use serde_json::value::to_raw_value; use tracing::warn; -use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma}; +use crate::{ + service::pdu::PduBuilder, services, Error, Result, Ruma, RumaResponse, +}; /// # `PUT /_matrix/client/r0/profile/{userId}/displayname` /// @@ -27,7 +29,7 @@ use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma}; /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_displayname_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().users.set_displayname(sender_user, body.displayname.clone())?; @@ -107,7 +109,7 @@ pub(crate) async fn set_displayname_route( } } - Ok(set_display_name::v3::Response {}) + Ok(RumaResponse(set_display_name::v3::Response {})) } /// # `GET /_matrix/client/r0/profile/{userId}/displayname` @@ -117,7 +119,7 @@ pub(crate) async fn set_displayname_route( /// - If user is on another server: Fetches displayname over federation pub(crate) async fn get_displayname_route( body: Ruma, -) -> Result { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() .sending @@ -130,14 +132,14 @@ pub(crate) async fn get_displayname_route( ) .await?; - return Ok(get_display_name::v3::Response { + return Ok(RumaResponse(get_display_name::v3::Response { displayname: response.displayname, - }); + })); } - Ok(get_display_name::v3::Response { + Ok(RumaResponse(get_display_name::v3::Response { displayname: services().users.displayname(&body.user_id)?, - }) + })) } /// # `PUT /_matrix/client/r0/profile/{userId}/avatar_url` @@ -147,7 +149,7 @@ pub(crate) async fn get_displayname_route( /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_avatar_url_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().users.set_avatar_url(sender_user, body.avatar_url.clone())?; @@ -229,7 +231,7 @@ pub(crate) async fn set_avatar_url_route( }; } - Ok(set_avatar_url::v3::Response {}) + Ok(RumaResponse(set_avatar_url::v3::Response {})) } /// # `GET /_matrix/client/r0/profile/{userId}/avatar_url` @@ -240,7 +242,7 @@ pub(crate) async fn set_avatar_url_route( /// federation pub(crate) async fn get_avatar_url_route( body: Ruma, -) -> Result { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() .sending @@ -253,16 +255,16 @@ pub(crate) async fn get_avatar_url_route( ) .await?; - return Ok(get_avatar_url::v3::Response { + return Ok(RumaResponse(get_avatar_url::v3::Response { avatar_url: response.avatar_url, blurhash: response.blurhash, - }); + })); } - Ok(get_avatar_url::v3::Response { + Ok(RumaResponse(get_avatar_url::v3::Response { avatar_url: services().users.avatar_url(&body.user_id)?, blurhash: services().users.blurhash(&body.user_id)?, - }) + })) } /// # `GET /_matrix/client/r0/profile/{userId}` @@ -272,7 +274,7 @@ pub(crate) async fn get_avatar_url_route( /// - If user is on another server: Fetches profile over federation pub(crate) async fn get_profile_route( body: Ruma, -) -> Result { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() .sending @@ -285,11 +287,11 @@ pub(crate) async fn get_profile_route( ) .await?; - return Ok(get_profile::v3::Response { + return Ok(RumaResponse(get_profile::v3::Response { displayname: response.displayname, avatar_url: response.avatar_url, blurhash: response.blurhash, - }); + })); } if !services().users.exists(&body.user_id)? { @@ -300,9 +302,9 @@ pub(crate) async fn get_profile_route( )); } - Ok(get_profile::v3::Response { + Ok(RumaResponse(get_profile::v3::Response { avatar_url: services().users.avatar_url(&body.user_id)?, blurhash: services().users.blurhash(&body.user_id)?, displayname: services().users.displayname(&body.user_id)?, - }) + })) } diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 9e101707..e000a3c5 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -11,14 +11,14 @@ use ruma::{ push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/pushrules` /// /// Retrieves the push rules event for this user. pub(crate) async fn get_pushrules_all_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services() @@ -37,9 +37,9 @@ pub(crate) async fn get_pushrules_all_route( .map_err(|_| Error::bad_database("Invalid account data event in db."))? .content; - Ok(get_pushrules_all::v3::Response { + Ok(RumaResponse(get_pushrules_all::v3::Response { global: account_data.global, - }) + })) } /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}` @@ -47,7 +47,7 @@ pub(crate) async fn get_pushrules_all_route( /// Retrieves a single specified push rule for this user. pub(crate) async fn get_pushrule_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services() @@ -72,9 +72,9 @@ pub(crate) async fn get_pushrule_route( .map(Into::into); if let Some(rule) = rule { - Ok(get_pushrule::v3::Response { + Ok(RumaResponse(get_pushrule::v3::Response { rule, - }) + })) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found.")) } @@ -85,7 +85,7 @@ pub(crate) async fn get_pushrule_route( /// Creates a single specified push rule for this user. pub(crate) async fn set_pushrule_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; @@ -157,7 +157,7 @@ pub(crate) async fn set_pushrule_route( .expect("to json value always works"), )?; - Ok(set_pushrule::v3::Response {}) + Ok(RumaResponse(set_pushrule::v3::Response {})) } /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions` @@ -165,7 +165,7 @@ pub(crate) async fn set_pushrule_route( /// Gets the actions of a single specified push rule for this user. pub(crate) async fn get_pushrule_actions_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -200,9 +200,9 @@ pub(crate) async fn get_pushrule_actions_route( "Push rule not found.", ))?; - Ok(get_pushrule_actions::v3::Response { + Ok(RumaResponse(get_pushrule_actions::v3::Response { actions, - }) + })) } /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions` @@ -210,7 +210,7 @@ pub(crate) async fn get_pushrule_actions_route( /// Sets the actions of a single specified push rule for this user. pub(crate) async fn set_pushrule_actions_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -257,7 +257,7 @@ pub(crate) async fn set_pushrule_actions_route( .expect("to json value always works"), )?; - Ok(set_pushrule_actions::v3::Response {}) + Ok(RumaResponse(set_pushrule_actions::v3::Response {})) } /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled` @@ -265,7 +265,7 @@ pub(crate) async fn set_pushrule_actions_route( /// Gets the enabled status of a single specified push rule for this user. pub(crate) async fn get_pushrule_enabled_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -301,9 +301,9 @@ pub(crate) async fn get_pushrule_enabled_route( "Push rule not found.", ))?; - Ok(get_pushrule_enabled::v3::Response { + Ok(RumaResponse(get_pushrule_enabled::v3::Response { enabled, - }) + })) } /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled` @@ -311,7 +311,7 @@ pub(crate) async fn get_pushrule_enabled_route( /// Sets the enabled status of a single specified push rule for this user. pub(crate) async fn set_pushrule_enabled_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -358,7 +358,7 @@ pub(crate) async fn set_pushrule_enabled_route( .expect("to json value always works"), )?; - Ok(set_pushrule_enabled::v3::Response {}) + Ok(RumaResponse(set_pushrule_enabled::v3::Response {})) } /// # `DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}` @@ -366,7 +366,7 @@ pub(crate) async fn set_pushrule_enabled_route( /// Deletes a single specified push rule for this user. pub(crate) async fn delete_pushrule_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -418,7 +418,7 @@ pub(crate) async fn delete_pushrule_route( .expect("to json value always works"), )?; - Ok(delete_pushrule::v3::Response {}) + Ok(RumaResponse(delete_pushrule::v3::Response {})) } /// # `GET /_matrix/client/r0/pushers` @@ -426,12 +426,12 @@ pub(crate) async fn delete_pushrule_route( /// Gets all currently active pushers for the sender user. pub(crate) async fn get_pushers_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(get_pushers::v3::Response { + Ok(RumaResponse(get_pushers::v3::Response { pushers: services().pusher.get_pushers(sender_user)?, - }) + })) } /// # `POST /_matrix/client/r0/pushers/set` @@ -441,10 +441,10 @@ pub(crate) async fn get_pushers_route( /// - TODO: Handle `append` pub(crate) async fn set_pushers_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().pusher.set_pusher(sender_user, body.action.clone())?; - Ok(set_pusher::v3::Response::default()) + Ok(RumaResponse(set_pusher::v3::Response::default())) } diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index 5fdfc7de..9d0643e8 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -13,6 +13,7 @@ use ruma::{ use crate::{ service::rooms::timeline::PduCount, services, Error, Result, Ruma, + RumaResponse, }; /// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers` @@ -24,7 +25,7 @@ use crate::{ /// EDU pub(crate) async fn set_read_marker_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Some(fully_read) = &body.fully_read { @@ -97,7 +98,7 @@ pub(crate) async fn set_read_marker_route( )?; } - Ok(set_read_marker::v3::Response {}) + Ok(RumaResponse(set_read_marker::v3::Response {})) } /// # `POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}` @@ -105,7 +106,7 @@ pub(crate) async fn set_read_marker_route( /// Sets private read marker and public read receipt EDU. pub(crate) async fn create_receipt_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if matches!( @@ -187,5 +188,5 @@ pub(crate) async fn create_receipt_route( _ => return Err(Error::bad_database("Unsupported receipt type")), } - Ok(create_receipt::v3::Response {}) + Ok(RumaResponse(create_receipt::v3::Response {})) } diff --git a/src/api/client_server/redact.rs b/src/api/client_server/redact.rs index 65683d81..0114aa29 100644 --- a/src/api/client_server/redact.rs +++ b/src/api/client_server/redact.rs @@ -6,7 +6,7 @@ use ruma::{ }; use serde_json::value::to_raw_value; -use crate::{service::pdu::PduBuilder, services, Result, Ruma}; +use crate::{service::pdu::PduBuilder, services, Result, Ruma, RumaResponse}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}` /// @@ -15,7 +15,7 @@ use crate::{service::pdu::PduBuilder, services, Result, Ruma}; /// - TODO: Handle txn id pub(crate) async fn redact_event_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; @@ -54,7 +54,7 @@ pub(crate) async fn redact_event_route( drop(state_lock); let event_id = (*event_id).to_owned(); - Ok(redact_event::v3::Response { + Ok(RumaResponse(redact_event::v3::Response { event_id, - }) + })) } diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 475adb9a..d2068bc2 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -6,12 +6,18 @@ use ruma::{ uint, }; -use crate::{service::rooms::timeline::PduCount, services, Result, Ruma}; +use crate::{ + service::rooms::timeline::PduCount, services, Result, Ruma, RumaResponse, +}; /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}` pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( body: Ruma, -) -> Result { +) -> Result< + RumaResponse< + get_relating_events_with_rel_type_and_event_type::v1::Response, + >, +> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let from = match body.from.clone() { @@ -44,17 +50,19 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( limit, )?; - Ok(get_relating_events_with_rel_type_and_event_type::v1::Response { - chunk: res.chunk, - next_batch: res.next_batch, - prev_batch: res.prev_batch, - }) + Ok(RumaResponse( + get_relating_events_with_rel_type_and_event_type::v1::Response { + chunk: res.chunk, + next_batch: res.next_batch, + prev_batch: res.prev_batch, + }, + )) } /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}` pub(crate) async fn get_relating_events_with_rel_type_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let from = match body.from.clone() { @@ -87,17 +95,17 @@ pub(crate) async fn get_relating_events_with_rel_type_route( limit, )?; - Ok(get_relating_events_with_rel_type::v1::Response { + Ok(RumaResponse(get_relating_events_with_rel_type::v1::Response { chunk: res.chunk, next_batch: res.next_batch, prev_batch: res.prev_batch, - }) + })) } /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}` pub(crate) async fn get_relating_events_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let from = match body.from.clone() { @@ -119,14 +127,18 @@ pub(crate) async fn get_relating_events_route( .try_into() .expect("0-100 should fit in usize"); - services().rooms.pdu_metadata.paginate_relations_with_filter( - sender_user, - &body.room_id, - &body.event_id, - None, - None, - from, - to, - limit, - ) + services() + .rooms + .pdu_metadata + .paginate_relations_with_filter( + sender_user, + &body.room_id, + &body.event_id, + None, + None, + from, + to, + limit, + ) + .map(RumaResponse) } diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index d9242f62..9dfd1a31 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -4,14 +4,14 @@ use ruma::{ int, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `POST /_matrix/client/r0/rooms/{roomId}/report/{eventId}` /// /// Reports an inappropriate event to homeserver admins pub(crate) async fn report_event_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else { @@ -91,5 +91,5 @@ pub(crate) async fn report_event_route( ), )); - Ok(report_content::v3::Response {}) + Ok(RumaResponse(report_content::v3::Response {})) } diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index b0a17e6a..f177bebf 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -31,7 +31,7 @@ use tracing::{info, warn}; use crate::{ api::client_server::invite_helper, service::pdu::PduBuilder, services, - Error, Result, Ruma, + Error, Result, Ruma, RumaResponse, }; /// # `POST /_matrix/client/r0/createRoom` @@ -53,7 +53,7 @@ use crate::{ #[allow(clippy::too_many_lines)] pub(crate) async fn create_room_route( body: Ruma, -) -> Result { +) -> Result> { use create_room::v3::RoomPreset; let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -538,7 +538,7 @@ pub(crate) async fn create_room_route( info!("{} created a room", sender_user); - Ok(create_room::v3::Response::new(room_id)) + Ok(RumaResponse(create_room::v3::Response::new(room_id))) } /// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}` @@ -549,7 +549,7 @@ pub(crate) async fn create_room_route( /// visibility) pub(crate) async fn get_room_event_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().rooms.timeline.get_pdu(&body.event_id)?.ok_or_else( @@ -573,9 +573,9 @@ pub(crate) async fn get_room_event_route( let mut event = (*event).clone(); event.add_age()?; - Ok(get_room_event::v3::Response { + Ok(RumaResponse(get_room_event::v3::Response { event: event.to_room_event(), - }) + })) } /// # `GET /_matrix/client/r0/rooms/{roomId}/aliases` @@ -586,7 +586,7 @@ pub(crate) async fn get_room_event_route( /// user to call it if `history_visibility` is world readable pub(crate) async fn get_room_aliases_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services().rooms.state_cache.is_joined(sender_user, &body.room_id)? { @@ -596,14 +596,14 @@ pub(crate) async fn get_room_aliases_route( )); } - Ok(aliases::v3::Response { + Ok(RumaResponse(aliases::v3::Response { aliases: services() .rooms .alias .local_aliases_for_room(&body.room_id) .filter_map(Result::ok) .collect(), - }) + })) } /// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade` @@ -619,7 +619,7 @@ pub(crate) async fn get_room_aliases_route( #[allow(clippy::too_many_lines)] pub(crate) async fn upgrade_room_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services().globals.supported_room_versions().contains(&body.new_version) @@ -914,7 +914,7 @@ pub(crate) async fn upgrade_room_route( drop(state_lock); // Return the replacement room id - Ok(upgrade_room::v3::Response { + Ok(RumaResponse(upgrade_room::v3::Response { replacement_room, - }) + })) } diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 340eb839..afe44597 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -14,7 +14,7 @@ use ruma::{ uint, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `POST /_matrix/client/r0/search` /// @@ -25,7 +25,7 @@ use crate::{services, Error, Result, Ruma}; #[allow(clippy::too_many_lines)] pub(crate) async fn search_events_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let search_criteria = body.search_categories.room_events.as_ref().unwrap(); @@ -136,7 +136,7 @@ pub(crate) async fn search_events_route( Some((skip + limit).to_string()) }; - Ok(search_events::v3::Response::new(ResultCategories { + Ok(RumaResponse(search_events::v3::Response::new(ResultCategories { room_events: ResultRoomEvents { count: None, // TODO @@ -151,5 +151,5 @@ pub(crate) async fn search_events_route( .map(str::to_lowercase) .collect(), }, - })) + }))) } diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 3894a05f..13c86896 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -16,7 +16,7 @@ use serde::Deserialize; use tracing::{info, warn}; use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; -use crate::{services, utils, Error, Result, Ruma}; +use crate::{services, utils, Error, Result, Ruma, RumaResponse}; #[derive(Debug, Deserialize)] struct Claims { @@ -29,13 +29,13 @@ struct Claims { /// the `type` field when logging in. pub(crate) async fn get_login_types_route( _body: Ruma, -) -> Result { - Ok(get_login_types::v3::Response::new(vec![ +) -> Result> { + Ok(RumaResponse(get_login_types::v3::Response::new(vec![ get_login_types::v3::LoginType::Password(PasswordLoginType::default()), get_login_types::v3::LoginType::ApplicationService( ApplicationServiceLoginType::default(), ), - ])) + ]))) } /// # `POST /_matrix/client/r0/login` @@ -54,7 +54,7 @@ pub(crate) async fn get_login_types_route( #[allow(clippy::too_many_lines)] pub(crate) async fn login_route( body: Ruma, -) -> Result { +) -> Result> { // To allow deprecated login methods #![allow(deprecated)] // Validate login method @@ -256,7 +256,7 @@ pub(crate) async fn login_route( // Homeservers are still required to send the `home_server` field #[allow(deprecated)] - Ok(login::v3::Response { + Ok(RumaResponse(login::v3::Response { user_id, access_token: token, home_server: Some(services().globals.server_name().to_owned()), @@ -264,7 +264,7 @@ pub(crate) async fn login_route( well_known: None, refresh_token: None, expires_in: None, - }) + })) } /// # `POST /_matrix/client/r0/logout` @@ -278,7 +278,7 @@ pub(crate) async fn login_route( /// - Triggers device list updates pub(crate) async fn logout_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -294,7 +294,7 @@ pub(crate) async fn logout_route( services().users.remove_device(sender_user, sender_device)?; - Ok(logout::v3::Response::new()) + Ok(RumaResponse(logout::v3::Response::new())) } /// # `POST /_matrix/client/r0/logout/all` @@ -311,7 +311,7 @@ pub(crate) async fn logout_route( /// /_matrix/client/r0/logout`](logout_route) from each device of this user. pub(crate) async fn logout_all_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Some(info) = &body.appservice_info { @@ -332,5 +332,5 @@ pub(crate) async fn logout_all_route( services().users.remove_device(sender_user, &device_id)?; } - Ok(logout_all::v3::Response::new()) + Ok(RumaResponse(logout_all::v3::Response::new())) } diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index a9b1af35..77396ed6 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,6 +1,6 @@ use ruma::{api::client::space::get_hierarchy, uint}; -use crate::{services, Result, Ruma}; +use crate::{services, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy` /// @@ -8,7 +8,7 @@ use crate::{services, Result, Ruma}; /// of a given space. pub(crate) async fn get_hierarchy_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let skip = @@ -40,4 +40,5 @@ pub(crate) async fn get_hierarchy_route( body.suggested_only, ) .await + .map(RumaResponse) } diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index ace26880..31b770d5 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -28,7 +28,7 @@ use crate::{ /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_key_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event_id = send_state_event_for_key_helper( @@ -42,9 +42,9 @@ pub(crate) async fn send_state_event_for_key_route( .await?; let event_id = (*event_id).to_owned(); - Ok(send_state_event::v3::Response { + Ok(RumaResponse(send_state_event::v3::Response { event_id, - }) + })) } /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}` @@ -94,7 +94,7 @@ pub(crate) async fn send_state_event_for_empty_key_route( /// readable pub(crate) async fn get_state_events_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -108,7 +108,7 @@ pub(crate) async fn get_state_events_route( )); } - Ok(get_state_events::v3::Response { + Ok(RumaResponse(get_state_events::v3::Response { room_state: services() .rooms .state_accessor @@ -117,7 +117,7 @@ pub(crate) async fn get_state_events_route( .values() .map(|pdu| pdu.to_state_event()) .collect(), - }) + })) } /// # `GET /_matrix/client/r0/rooms/{roomid}/state/{eventType}/{stateKey}` @@ -128,7 +128,7 @@ pub(crate) async fn get_state_events_route( /// readable pub(crate) async fn get_state_events_for_key_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -154,11 +154,11 @@ pub(crate) async fn get_state_events_for_key_route( Error::BadRequest(ErrorKind::NotFound, "State event not found.") })?; - Ok(get_state_events_for_key::v3::Response { + Ok(RumaResponse(get_state_events_for_key::v3::Response { content: serde_json::from_str(event.content.get()).map_err(|_| { Error::bad_database("Invalid event content in database") })?, - }) + })) } /// # `GET /_matrix/client/r0/rooms/{roomid}/state/{eventType}` diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index bad7e304..a7176a3d 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -74,7 +74,8 @@ use crate::{ #[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_route( body: Ruma, -) -> Result> { +) -> Result, RumaResponse> +{ let sender_user = body.sender_user.expect("user is authenticated"); let sender_device = body.sender_device.expect("user is authenticated"); let body = body.body; @@ -457,10 +458,10 @@ pub(crate) async fn sync_events_route( Ok(x) => x.expect("watcher should succeed"), Err(error) => debug!(%error, "timed out"), }; - Ok(response) + Ok(RumaResponse(response)) } else { // Only cache if we made progress - Ok(response) + Ok(RumaResponse(response)) } } @@ -1127,7 +1128,8 @@ fn share_encrypted_room( #[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_v4_route( body: Ruma, -) -> Result> { +) -> Result, RumaResponse> +{ let sender_user = body.sender_user.expect("user is authenticated"); let sender_device = body.sender_device.expect("user is authenticated"); let mut body = body.body; @@ -1700,7 +1702,7 @@ pub(crate) async fn sync_events_v4_route( }; } - Ok(sync_events::v4::Response { + Ok(RumaResponse(sync_events::v4::Response { initial: globalsince == 0, txn_id: body.txn_id.clone(), pos: next_batch.to_string(), @@ -1763,5 +1765,5 @@ pub(crate) async fn sync_events_v4_route( }, }, delta_token: None, - }) + })) } diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 22738af8..81ba5f9a 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -8,7 +8,7 @@ use ruma::{ }, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}` /// @@ -17,7 +17,7 @@ use crate::{services, Error, Result, Ruma}; /// - Inserts the tag into the tag event of the room account data. pub(crate) async fn update_tag_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( @@ -53,7 +53,7 @@ pub(crate) async fn update_tag_route( &serde_json::to_value(tags_event).expect("to json value always works"), )?; - Ok(create_tag::v3::Response {}) + Ok(RumaResponse(create_tag::v3::Response {})) } /// # `DELETE /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}` @@ -63,7 +63,7 @@ pub(crate) async fn update_tag_route( /// - Removes the tag from the tag event of the room account data. pub(crate) async fn delete_tag_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( @@ -96,7 +96,7 @@ pub(crate) async fn delete_tag_route( &serde_json::to_value(tags_event).expect("to json value always works"), )?; - Ok(delete_tag::v3::Response {}) + Ok(RumaResponse(delete_tag::v3::Response {})) } /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags` @@ -106,7 +106,7 @@ pub(crate) async fn delete_tag_route( /// - Gets the tag event of the room account data. pub(crate) async fn get_tags_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( @@ -130,7 +130,7 @@ pub(crate) async fn get_tags_route( }, )?; - Ok(get_tags::v3::Response { + Ok(RumaResponse(get_tags::v3::Response { tags: tags_event.content.tags, - }) + })) } diff --git a/src/api/client_server/thirdparty.rs b/src/api/client_server/thirdparty.rs index 8cb77e4b..f93fc1f1 100644 --- a/src/api/client_server/thirdparty.rs +++ b/src/api/client_server/thirdparty.rs @@ -2,16 +2,16 @@ use std::collections::BTreeMap; use ruma::api::client::thirdparty::get_protocols; -use crate::{Result, Ruma}; +use crate::{Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/thirdparty/protocols` /// /// TODO: Fetches all metadata about protocols supported by the homeserver. pub(crate) async fn get_protocols_route( _body: Ruma, -) -> Result { +) -> Result> { // TODO - Ok(get_protocols::v3::Response { + Ok(RumaResponse(get_protocols::v3::Response { protocols: BTreeMap::new(), - }) + })) } diff --git a/src/api/client_server/threads.rs b/src/api/client_server/threads.rs index 9beb8aa2..fdc1de01 100644 --- a/src/api/client_server/threads.rs +++ b/src/api/client_server/threads.rs @@ -1,11 +1,11 @@ use ruma::api::client::{error::ErrorKind, threads::get_threads}; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/r0/rooms/{roomId}/threads` pub(crate) async fn get_threads_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // Use limit or else 10, with maximum 100 @@ -36,11 +36,11 @@ pub(crate) async fn get_threads_route( let next_batch = threads.last().map(|(count, _)| count.to_string()); - Ok(get_threads::v1::Response { + Ok(RumaResponse(get_threads::v1::Response { chunk: threads .into_iter() .map(|(_, pdu)| pdu.to_room_event()) .collect(), next_batch, - }) + })) } diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index c29c41de..0fb36c59 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -8,14 +8,14 @@ use ruma::{ to_device::DeviceIdOrAllDevices, }; -use crate::{services, Error, Result, Ruma}; +use crate::{services, Error, Result, Ruma, RumaResponse}; /// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}` /// /// Send a to-device event to a set of client devices. pub(crate) async fn send_event_to_device_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); @@ -25,7 +25,7 @@ pub(crate) async fn send_event_to_device_route( .existing_txnid(sender_user, sender_device, &body.txn_id)? .is_some() { - return Ok(send_event_to_device::v3::Response {}); + return Ok(RumaResponse(send_event_to_device::v3::Response {})); } for (target_user_id, map) in &body.messages { @@ -103,5 +103,5 @@ pub(crate) async fn send_event_to_device_route( &[], )?; - Ok(send_event_to_device::v3::Response {}) + Ok(RumaResponse(send_event_to_device::v3::Response {})) } diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index 1e7b1b68..eddcfa48 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -1,13 +1,13 @@ use ruma::api::client::{error::ErrorKind, typing::create_typing_event}; -use crate::{services, utils, Error, Result, Ruma}; +use crate::{services, utils, Error, Result, Ruma, RumaResponse}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}` /// /// Sets the typing state of the sender user. pub(crate) async fn create_typing_event_route( body: Ruma, -) -> Result { +) -> Result> { use create_typing_event::v3::Typing; let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -40,5 +40,5 @@ pub(crate) async fn create_typing_event_route( .await?; } - Ok(create_typing_event::v3::Response {}) + Ok(RumaResponse(create_typing_event::v3::Response {})) } diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 9d3dab29..0ad806a6 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, iter::FromIterator}; use ruma::api::client::discovery::get_supported_versions; -use crate::{Result, Ruma}; +use crate::{Result, Ruma, RumaResponse}; /// # `GET /_matrix/client/versions` /// @@ -18,7 +18,7 @@ use crate::{Result, Ruma}; /// should avoid using unstable features in their stable releases pub(crate) async fn get_supported_versions_route( _body: Ruma, -) -> Result { +) -> Result> { let resp = get_supported_versions::Response { versions: vec![ "r0.5.0".to_owned(), @@ -35,5 +35,5 @@ pub(crate) async fn get_supported_versions_route( )]), }; - Ok(resp) + Ok(RumaResponse(resp)) } diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index fdac5c1b..2667e927 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -6,7 +6,7 @@ use ruma::{ }, }; -use crate::{services, Result, Ruma}; +use crate::{services, Result, Ruma, RumaResponse}; /// # `POST /_matrix/client/r0/user_directory/search` /// @@ -17,7 +17,7 @@ use crate::{services, Result, Ruma}; /// and don't share a room with the sender pub(crate) async fn search_users_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let limit = body.limit.try_into().unwrap_or(usize::MAX); @@ -99,8 +99,8 @@ pub(crate) async fn search_users_route( let results = users.by_ref().take(limit).collect(); let limited = users.next().is_some(); - Ok(search_users::v3::Response { + Ok(RumaResponse(search_users::v3::Response { results, limited, - }) + })) } diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index 1cd80d27..9c29c2d1 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -5,7 +5,7 @@ use hmac::{Hmac, Mac}; use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch}; use sha1::Sha1; -use crate::{services, Result, Ruma}; +use crate::{services, Result, Ruma, RumaResponse}; type HmacSha1 = Hmac; @@ -14,7 +14,7 @@ type HmacSha1 = Hmac; /// TODO: Returns information about the recommended turn server. pub(crate) async fn turn_server_route( body: Ruma, -) -> Result { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let turn_secret = services().globals.turn_secret().clone(); @@ -43,10 +43,10 @@ pub(crate) async fn turn_server_route( (username, password) }; - Ok(get_turn_server_info::v3::Response { + Ok(RumaResponse(get_turn_server_info::v3::Response { username, password, uris: services().globals.turn_uris().to_vec(), ttl: Duration::from_secs(services().globals.turn_ttl()), - }) + })) } diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 64fe253d..e66d9ab2 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -66,7 +66,7 @@ use tracing::{debug, error, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Result, Ruma, + services, utils, Error, PduEvent, Result, Ruma, RumaResponse, }; /// Wraps either an literal IP address plus port, or a hostname plus complement @@ -554,13 +554,13 @@ async fn request_well_known(destination: &str) -> Option { /// Get version information on this server. pub(crate) async fn get_server_version_route( _body: Ruma, -) -> Result { - Ok(get_server_version::v1::Response { +) -> Result> { + Ok(RumaResponse(get_server_version::v1::Response { server: Some(get_server_version::v1::Server { name: Some(env!("CARGO_PKG_NAME").to_owned()), version: Some(crate::version()), }), - }) + })) } /// # `GET /_matrix/key/v2/server` @@ -631,7 +631,7 @@ pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { /// Lists the public rooms on this server. pub(crate) async fn get_public_rooms_filtered_route( body: Ruma, -) -> Result { +) -> Result> { let response = client_server::get_public_rooms_filtered_helper( None, body.limit, @@ -641,12 +641,12 @@ pub(crate) async fn get_public_rooms_filtered_route( ) .await?; - Ok(get_public_rooms_filtered::v1::Response { + Ok(RumaResponse(get_public_rooms_filtered::v1::Response { chunk: response.chunk, prev_batch: response.prev_batch, next_batch: response.next_batch, total_room_count_estimate: response.total_room_count_estimate, - }) + })) } /// # `GET /_matrix/federation/v1/publicRooms` @@ -654,7 +654,7 @@ pub(crate) async fn get_public_rooms_filtered_route( /// Lists the public rooms on this server. pub(crate) async fn get_public_rooms_route( body: Ruma, -) -> Result { +) -> Result> { let response = client_server::get_public_rooms_filtered_helper( None, body.limit, @@ -664,12 +664,12 @@ pub(crate) async fn get_public_rooms_route( ) .await?; - Ok(get_public_rooms::v1::Response { + Ok(RumaResponse(get_public_rooms::v1::Response { chunk: response.chunk, prev_batch: response.prev_batch, next_batch: response.next_batch, total_room_count_estimate: response.total_room_count_estimate, - }) + })) } pub(crate) fn parse_incoming_pdu( @@ -709,7 +709,7 @@ pub(crate) fn parse_incoming_pdu( #[allow(clippy::too_many_lines)] pub(crate) async fn send_transaction_message_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -976,12 +976,12 @@ pub(crate) async fn send_transaction_message_route( } } - Ok(send_transaction_message::v1::Response { + Ok(RumaResponse(send_transaction_message::v1::Response { pdus: resolved_map .into_iter() .map(|(e, r)| (e, r.map_err(|e| e.sanitized_error()))) .collect(), - }) + })) } /// # `GET /_matrix/federation/v1/event/{eventId}` @@ -992,7 +992,7 @@ pub(crate) async fn send_transaction_message_route( /// room pub(crate) async fn get_event_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1035,11 +1035,11 @@ pub(crate) async fn get_event_route( )); } - Ok(get_event::v1::Response { + Ok(RumaResponse(get_event::v1::Response { origin: services().globals.server_name().to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch::now(), pdu: PduEvent::convert_to_outgoing_federation_event(event), - }) + })) } /// # `GET /_matrix/federation/v1/backfill/` @@ -1048,7 +1048,7 @@ pub(crate) async fn get_event_route( /// history visibility allows. pub(crate) async fn get_backfill_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1106,11 +1106,11 @@ pub(crate) async fn get_backfill_route( .map(PduEvent::convert_to_outgoing_federation_event) .collect(); - Ok(get_backfill::v1::Response { + Ok(RumaResponse(get_backfill::v1::Response { origin: services().globals.server_name().to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch::now(), pdus: events, - }) + })) } /// # `POST /_matrix/federation/v1/get_missing_events/{roomId}` @@ -1118,7 +1118,7 @@ pub(crate) async fn get_backfill_route( /// Retrieves events that the sender is missing. pub(crate) async fn get_missing_events_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1208,9 +1208,9 @@ pub(crate) async fn get_missing_events_route( i += 1; } - Ok(get_missing_events::v1::Response { + Ok(RumaResponse(get_missing_events::v1::Response { events, - }) + })) } /// # `GET /_matrix/federation/v1/event_auth/{roomId}/{eventId}` @@ -1220,7 +1220,7 @@ pub(crate) async fn get_missing_events_route( /// - This does not include the event itself pub(crate) async fn get_event_authorization_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1263,14 +1263,14 @@ pub(crate) async fn get_event_authorization_route( .get_auth_chain(room_id, vec![Arc::from(&*body.event_id)]) .await?; - Ok(get_event_authorization::v1::Response { + Ok(RumaResponse(get_event_authorization::v1::Response { auth_chain: auth_chain_ids .filter_map(|id| { services().rooms.timeline.get_pdu_json(&id).ok()? }) .map(PduEvent::convert_to_outgoing_federation_event) .collect(), - }) + })) } /// # `GET /_matrix/federation/v1/state/{roomId}` @@ -1278,7 +1278,7 @@ pub(crate) async fn get_event_authorization_route( /// Retrieves the current state of the room. pub(crate) async fn get_room_state_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1326,7 +1326,7 @@ pub(crate) async fn get_room_state_route( .get_auth_chain(&body.room_id, vec![Arc::from(&*body.event_id)]) .await?; - Ok(get_room_state::v1::Response { + Ok(RumaResponse(get_room_state::v1::Response { auth_chain: auth_chain_ids .filter_map(|id| { if let Some(json) = @@ -1340,7 +1340,7 @@ pub(crate) async fn get_room_state_route( }) .collect(), pdus, - }) + })) } /// # `GET /_matrix/federation/v1/state_ids/{roomId}` @@ -1348,7 +1348,7 @@ pub(crate) async fn get_room_state_route( /// Retrieves the current state of the room. pub(crate) async fn get_room_state_ids_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1392,10 +1392,10 @@ pub(crate) async fn get_room_state_ids_route( .get_auth_chain(&body.room_id, vec![Arc::from(&*body.event_id)]) .await?; - Ok(get_room_state_ids::v1::Response { + Ok(RumaResponse(get_room_state_ids::v1::Response { auth_chain_ids: auth_chain_ids.map(|id| (*id).to_owned()).collect(), pdu_ids, - }) + })) } /// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}` @@ -1403,7 +1403,7 @@ pub(crate) async fn get_room_state_ids_route( /// Creates a join template. pub(crate) async fn create_join_event_template_route( body: Ruma, -) -> Result { +) -> Result> { if !services().rooms.metadata.exists(&body.room_id)? { return Err(Error::BadRequest( ErrorKind::NotFound, @@ -1504,11 +1504,11 @@ pub(crate) async fn create_join_event_template_route( pdu_json.remove("event_id"); - Ok(prepare_join_event::v1::Response { + Ok(RumaResponse(prepare_join_event::v1::Response { room_version: Some(room_version_id), event: to_raw_value(&pdu_json) .expect("CanonicalJson can be serialized to JSON"), - }) + })) } #[allow(clippy::too_many_lines)] @@ -1661,16 +1661,16 @@ async fn create_join_event( /// Submits a signed join event. pub(crate) async fn create_join_event_v1_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); let room_state = create_join_event(sender_servername, &body.room_id, &body.pdu).await?; - Ok(create_join_event::v1::Response { + Ok(RumaResponse(create_join_event::v1::Response { room_state, - }) + })) } /// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}` @@ -1678,7 +1678,7 @@ pub(crate) async fn create_join_event_v1_route( /// Submits a signed join event. pub(crate) async fn create_join_event_v2_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1695,9 +1695,9 @@ pub(crate) async fn create_join_event_v2_route( servers_in_room: None, }; - Ok(create_join_event::v2::Response { + Ok(RumaResponse(create_join_event::v2::Response { room_state, - }) + })) } /// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}` @@ -1706,7 +1706,7 @@ pub(crate) async fn create_join_event_v2_route( #[allow(clippy::too_many_lines)] pub(crate) async fn create_invite_route( body: Ruma, -) -> Result { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1827,9 +1827,9 @@ pub(crate) async fn create_invite_route( )?; } - Ok(create_invite::v2::Response { + Ok(RumaResponse(create_invite::v2::Response { event: PduEvent::convert_to_outgoing_federation_event(signed_event), - }) + })) } /// # `GET /_matrix/federation/v1/user/devices/{userId}` @@ -1837,7 +1837,7 @@ pub(crate) async fn create_invite_route( /// Gets information on all devices of the user. pub(crate) async fn get_devices_route( body: Ruma, -) -> Result { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -1848,7 +1848,7 @@ pub(crate) async fn get_devices_route( let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); - Ok(get_devices::v1::Response { + Ok(RumaResponse(get_devices::v1::Response { user_id: body.user_id.clone(), stream_id: services() .users @@ -1881,7 +1881,7 @@ pub(crate) async fn get_devices_route( &body.user_id, &|u| u.server_name() == sender_servername, )?, - }) + })) } /// # `GET /_matrix/federation/v1/query/directory` @@ -1889,16 +1889,16 @@ pub(crate) async fn get_devices_route( /// Resolve a room alias to a room id. pub(crate) async fn get_room_information_route( body: Ruma, -) -> Result { +) -> Result> { let room_id = services().rooms.alias.resolve_local_alias(&body.room_alias)?.ok_or( Error::BadRequest(ErrorKind::NotFound, "Room alias not found."), )?; - Ok(get_room_information::v1::Response { + Ok(RumaResponse(get_room_information::v1::Response { room_id, servers: vec![services().globals.server_name().to_owned()], - }) + })) } /// # `GET /_matrix/federation/v1/query/profile` @@ -1906,7 +1906,7 @@ pub(crate) async fn get_room_information_route( /// Gets information on a profile. pub(crate) async fn get_profile_information_route( body: Ruma, -) -> Result { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -1935,11 +1935,11 @@ pub(crate) async fn get_profile_information_route( } } - Ok(get_profile_information::v1::Response { + Ok(RumaResponse(get_profile_information::v1::Response { displayname, avatar_url, blurhash, - }) + })) } /// # `POST /_matrix/federation/v1/user/keys/query` @@ -1947,7 +1947,7 @@ pub(crate) async fn get_profile_information_route( /// Gets devices and identity keys for the given users. pub(crate) async fn get_keys_route( body: Ruma, -) -> Result { +) -> Result> { if body .device_keys .iter() @@ -1964,11 +1964,11 @@ pub(crate) async fn get_keys_route( }) .await?; - Ok(get_keys::v1::Response { + Ok(RumaResponse(get_keys::v1::Response { device_keys: result.device_keys, master_keys: result.master_keys, self_signing_keys: result.self_signing_keys, - }) + })) } /// # `POST /_matrix/federation/v1/user/keys/claim` @@ -1976,7 +1976,7 @@ pub(crate) async fn get_keys_route( /// Claims one-time keys. pub(crate) async fn claim_keys_route( body: Ruma, -) -> Result { +) -> Result> { if body .one_time_keys .iter() @@ -1990,9 +1990,9 @@ pub(crate) async fn claim_keys_route( let result = claim_keys_helper(&body.one_time_keys).await?; - Ok(claim_keys::v1::Response { + Ok(RumaResponse(claim_keys::v1::Response { one_time_keys: result.one_time_keys, - }) + })) } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index cccc7ba4..10eda195 100644 --- a/src/main.rs +++ b/src/main.rs @@ -579,11 +579,13 @@ macro_rules! impl_ruma_handler { ( $($ty:ident),* $(,)? ) => { #[axum::async_trait] #[allow(non_snake_case)] - impl RumaHandler<($($ty,)* Ruma,)> for F + impl + RumaHandler<($($ty,)* Ruma,)> for F where Req: IncomingRequest + Send + 'static, + Resp: IntoResponse, F: FnOnce($($ty,)* Ruma) -> Fut + Clone + Send + 'static, - Fut: Future> + Fut: Future> + Send, E: IntoResponse, $( $ty: FromRequestParts<()> + Send + 'static, )* @@ -600,7 +602,7 @@ macro_rules! impl_ruma_handler { on( method_filter, |$( $ty: $ty, )* req| async move { - handler($($ty,)* req).await.map(RumaResponse) + handler($($ty,)* req).await } ) ) From 7ea98dac7223eaa8ad63d3af149870ef05ff0fab Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 18 May 2024 18:59:46 -0700 Subject: [PATCH 121/617] rename `RumaResponse` to `Ra` It's very commonly used so having a short name is worthwhile, I think. --- src/api/client_server/account.rs | 38 +++++------- src/api/client_server/alias.rs | 14 ++--- src/api/client_server/backup.rs | 58 +++++++++--------- src/api/client_server/capabilities.rs | 6 +- src/api/client_server/config.rs | 18 +++--- src/api/client_server/context.rs | 6 +- src/api/client_server/device.rs | 22 +++---- src/api/client_server/directory.rs | 18 +++--- src/api/client_server/filter.rs | 10 ++-- src/api/client_server/keys.rs | 26 ++++----- src/api/client_server/media.rs | 29 +++++---- src/api/client_server/membership.rs | 52 ++++++++--------- src/api/client_server/message.rs | 14 ++--- src/api/client_server/profile.rs | 30 +++++----- src/api/client_server/push.rs | 42 ++++++------- src/api/client_server/read_marker.rs | 11 ++-- src/api/client_server/redact.rs | 6 +- src/api/client_server/relations.rs | 31 ++++------ src/api/client_server/report.rs | 6 +- src/api/client_server/room.rs | 18 +++--- src/api/client_server/search.rs | 6 +- src/api/client_server/session.rs | 18 +++--- src/api/client_server/space.rs | 6 +- src/api/client_server/state.rs | 20 +++---- src/api/client_server/sync.rs | 14 ++--- src/api/client_server/tag.rs | 14 ++--- src/api/client_server/thirdparty.rs | 6 +- src/api/client_server/threads.rs | 6 +- src/api/client_server/to_device.rs | 8 +-- src/api/client_server/typing.rs | 6 +- src/api/client_server/unversioned.rs | 6 +- src/api/client_server/user_directory.rs | 6 +- src/api/client_server/voip.rs | 6 +- src/api/ruma_wrapper.rs | 10 +++- src/api/ruma_wrapper/axum.rs | 4 +- src/api/server_server.rs | 78 ++++++++++++------------- src/main.rs | 4 +- src/utils/error.rs | 10 ++-- 38 files changed, 332 insertions(+), 351 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 52e6c5fc..39ec159a 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -19,9 +19,7 @@ use ruma::{ use tracing::{info, warn}; use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH}; -use crate::{ - api::client_server, services, utils, Error, Result, Ruma, RumaResponse, -}; +use crate::{api::client_server, services, utils, Error, Ra, Result, Ruma}; const RANDOM_USER_ID_LENGTH: usize = 10; @@ -38,7 +36,7 @@ const RANDOM_USER_ID_LENGTH: usize = 10; /// invalid when trying to register pub(crate) async fn get_register_available_route( body: Ruma, -) -> Result> { +) -> Result> { // Validate user id let user_id = UserId::parse_with_server_name( body.username.to_lowercase(), @@ -65,7 +63,7 @@ pub(crate) async fn get_register_available_route( // TODO add check for appservice namespaces // If no if check is true we have an username that's available to be used. - Ok(RumaResponse(get_username_availability::v3::Response { + Ok(Ra(get_username_availability::v3::Response { available: true, })) } @@ -90,7 +88,7 @@ pub(crate) async fn get_register_available_route( #[allow(clippy::too_many_lines)] pub(crate) async fn register_route( body: Ruma, -) -> Result> { +) -> Result> { if !services().globals.allow_registration() && body.appservice_info.is_none() { @@ -250,7 +248,7 @@ pub(crate) async fn register_route( // Inhibit login does not work for guests if !is_guest && body.inhibit_login { - return Ok(RumaResponse(register::v3::Response { + return Ok(Ra(register::v3::Response { access_token: None, user_id, device_id: None, @@ -302,7 +300,7 @@ pub(crate) async fn register_route( } } - Ok(RumaResponse(register::v3::Response { + Ok(Ra(register::v3::Response { access_token: Some(token), user_id, device_id: Some(device_id), @@ -330,7 +328,7 @@ pub(crate) async fn register_route( /// - Triggers device list updates pub(crate) async fn change_password_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -383,7 +381,7 @@ pub(crate) async fn change_password_route( format!("User {sender_user} changed their password."), )); - Ok(RumaResponse(change_password::v3::Response {})) + Ok(Ra(change_password::v3::Response {})) } /// # `GET _matrix/client/r0/account/whoami` @@ -393,11 +391,11 @@ pub(crate) async fn change_password_route( /// Note: Also works for Application Services pub(crate) async fn whoami_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device_id = body.sender_device.as_ref().cloned(); - Ok(RumaResponse(whoami::v3::Response { + Ok(Ra(whoami::v3::Response { user_id: sender_user.clone(), device_id, is_guest: services().users.is_deactivated(sender_user)? @@ -418,7 +416,7 @@ pub(crate) async fn whoami_route( /// - Removes ability to log in again pub(crate) async fn deactivate_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -463,7 +461,7 @@ pub(crate) async fn deactivate_route( format!("User {sender_user} deactivated their account."), )); - Ok(RumaResponse(deactivate::v3::Response { + Ok(Ra(deactivate::v3::Response { id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport, })) } @@ -475,11 +473,11 @@ pub(crate) async fn deactivate_route( /// - Currently always returns empty list pub(crate) async fn third_party_route( body: Ruma, -) -> Result> { +) -> Result> { let _sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(RumaResponse(get_3pids::v3::Response::new(Vec::new()))) + Ok(Ra(get_3pids::v3::Response::new(Vec::new()))) } /// # `POST /_matrix/client/v3/account/3pid/email/requestToken` @@ -491,8 +489,7 @@ pub(crate) async fn third_party_route( /// as a contact option. pub(crate) async fn request_3pid_management_token_via_email_route( _body: Ruma, -) -> Result> -{ +) -> Result> { Err(Error::BadRequest( ErrorKind::ThreepidDenied, "Third party identifier is not allowed", @@ -506,12 +503,9 @@ pub(crate) async fn request_3pid_management_token_via_email_route( /// /// - 403 signals that The homeserver does not allow the third party identifier /// as a contact option. -#[rustfmt::skip] pub(crate) async fn request_3pid_management_token_via_msisdn_route( _body: Ruma, -) --> Result> -{ +) -> Result> { Err(Error::BadRequest( ErrorKind::ThreepidDenied, "Third party identifier is not allowed", diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 41db0740..39a1fe67 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -11,14 +11,14 @@ use ruma::{ OwnedRoomAliasId, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}` /// /// Creates a new room alias on this server. pub(crate) async fn create_alias_route( body: Ruma, -) -> Result> { +) -> Result> { if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -46,7 +46,7 @@ pub(crate) async fn create_alias_route( services().rooms.alias.set_alias(&body.room_alias, &body.room_id)?; - Ok(RumaResponse(create_alias::v3::Response::new())) + Ok(Ra(create_alias::v3::Response::new())) } /// # `DELETE /_matrix/client/r0/directory/room/{roomAlias}` @@ -57,7 +57,7 @@ pub(crate) async fn create_alias_route( /// - TODO: Update canonical alias event pub(crate) async fn delete_alias_route( body: Ruma, -) -> Result> { +) -> Result> { if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -83,7 +83,7 @@ pub(crate) async fn delete_alias_route( // TODO: update alt_aliases? - Ok(RumaResponse(delete_alias::v3::Response::new())) + Ok(Ra(delete_alias::v3::Response::new())) } /// # `GET /_matrix/client/r0/directory/room/{roomAlias}` @@ -93,8 +93,8 @@ pub(crate) async fn delete_alias_route( /// - TODO: Suggest more servers to join via pub(crate) async fn get_alias_route( body: Ruma, -) -> Result> { - get_alias_helper(body.body.room_alias).await.map(RumaResponse) +) -> Result> { + get_alias_helper(body.body.room_alias).await.map(Ra) } pub(crate) async fn get_alias_helper( diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index 161f0fd2..a33b766a 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -9,19 +9,19 @@ use ruma::api::client::{ error::ErrorKind, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `POST /_matrix/client/r0/room_keys/version` /// /// Creates a new backup. pub(crate) async fn create_backup_version_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let version = services().key_backups.create_backup(sender_user, &body.algorithm)?; - Ok(RumaResponse(create_backup_version::v3::Response { + Ok(Ra(create_backup_version::v3::Response { version, })) } @@ -32,7 +32,7 @@ pub(crate) async fn create_backup_version_route( /// modified. pub(crate) async fn update_backup_version_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.update_backup( sender_user, @@ -40,7 +40,7 @@ pub(crate) async fn update_backup_version_route( &body.algorithm, )?; - Ok(RumaResponse(update_backup_version::v3::Response {})) + Ok(Ra(update_backup_version::v3::Response {})) } /// # `GET /_matrix/client/r0/room_keys/version` @@ -48,7 +48,7 @@ pub(crate) async fn update_backup_version_route( /// Get information about the latest backup version. pub(crate) async fn get_latest_backup_info_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let (version, algorithm) = services() @@ -59,7 +59,7 @@ pub(crate) async fn get_latest_backup_info_route( "Key backup does not exist.", ))?; - Ok(RumaResponse(get_latest_backup_info::v3::Response { + Ok(Ra(get_latest_backup_info::v3::Response { algorithm, count: services() .key_backups @@ -76,7 +76,7 @@ pub(crate) async fn get_latest_backup_info_route( /// Get information about an existing backup. pub(crate) async fn get_backup_info_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let algorithm = services() .key_backups @@ -86,7 +86,7 @@ pub(crate) async fn get_backup_info_route( "Key backup does not exist.", ))?; - Ok(RumaResponse(get_backup_info::v3::Response { + Ok(Ra(get_backup_info::v3::Response { algorithm, count: services() .key_backups @@ -106,12 +106,12 @@ pub(crate) async fn get_backup_info_route( /// to the backup pub(crate) async fn delete_backup_version_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_backup(sender_user, &body.version)?; - Ok(RumaResponse(delete_backup_version::v3::Response {})) + Ok(Ra(delete_backup_version::v3::Response {})) } /// # `PUT /_matrix/client/r0/room_keys/keys` @@ -124,7 +124,7 @@ pub(crate) async fn delete_backup_version_route( /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if Some(&body.version) @@ -152,7 +152,7 @@ pub(crate) async fn add_backup_keys_route( } } - Ok(RumaResponse(add_backup_keys::v3::Response { + Ok(Ra(add_backup_keys::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? @@ -172,7 +172,7 @@ pub(crate) async fn add_backup_keys_route( /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_room_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if Some(&body.version) @@ -198,7 +198,7 @@ pub(crate) async fn add_backup_keys_for_room_route( )?; } - Ok(RumaResponse(add_backup_keys_for_room::v3::Response { + Ok(Ra(add_backup_keys_for_room::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? @@ -218,7 +218,7 @@ pub(crate) async fn add_backup_keys_for_room_route( /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_session_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if Some(&body.version) @@ -242,7 +242,7 @@ pub(crate) async fn add_backup_keys_for_session_route( &body.session_data, )?; - Ok(RumaResponse(add_backup_keys_for_session::v3::Response { + Ok(Ra(add_backup_keys_for_session::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? @@ -257,12 +257,12 @@ pub(crate) async fn add_backup_keys_for_session_route( /// Retrieves all keys from the backup. pub(crate) async fn get_backup_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let rooms = services().key_backups.get_all(sender_user, &body.version)?; - Ok(RumaResponse(get_backup_keys::v3::Response { + Ok(Ra(get_backup_keys::v3::Response { rooms, })) } @@ -272,7 +272,7 @@ pub(crate) async fn get_backup_keys_route( /// Retrieves all keys from the backup for a given room. pub(crate) async fn get_backup_keys_for_room_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sessions = services().key_backups.get_room( @@ -281,7 +281,7 @@ pub(crate) async fn get_backup_keys_for_room_route( &body.room_id, )?; - Ok(RumaResponse(get_backup_keys_for_room::v3::Response { + Ok(Ra(get_backup_keys_for_room::v3::Response { sessions, })) } @@ -291,7 +291,7 @@ pub(crate) async fn get_backup_keys_for_room_route( /// Retrieves a key from the backup. pub(crate) async fn get_backup_keys_for_session_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let key_data = services() @@ -307,7 +307,7 @@ pub(crate) async fn get_backup_keys_for_session_route( "Backup key not found for this user's session.", ))?; - Ok(RumaResponse(get_backup_keys_for_session::v3::Response { + Ok(Ra(get_backup_keys_for_session::v3::Response { key_data, })) } @@ -317,12 +317,12 @@ pub(crate) async fn get_backup_keys_for_session_route( /// Delete the keys from the backup. pub(crate) async fn delete_backup_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_all_keys(sender_user, &body.version)?; - Ok(RumaResponse(delete_backup_keys::v3::Response { + Ok(Ra(delete_backup_keys::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? @@ -337,7 +337,7 @@ pub(crate) async fn delete_backup_keys_route( /// Delete the keys from the backup for a given room. pub(crate) async fn delete_backup_keys_for_room_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_room_keys( @@ -346,7 +346,7 @@ pub(crate) async fn delete_backup_keys_for_room_route( &body.room_id, )?; - Ok(RumaResponse(delete_backup_keys_for_room::v3::Response { + Ok(Ra(delete_backup_keys_for_room::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? @@ -361,7 +361,7 @@ pub(crate) async fn delete_backup_keys_for_room_route( /// Delete a key from the backup. pub(crate) async fn delete_backup_keys_for_session_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.delete_room_key( @@ -371,7 +371,7 @@ pub(crate) async fn delete_backup_keys_for_session_route( &body.session_id, )?; - Ok(RumaResponse(delete_backup_keys_for_session::v3::Response { + Ok(Ra(delete_backup_keys_for_session::v3::Response { count: services() .key_backups .count_keys(sender_user, &body.version)? diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index 410096bd..73c3a9c6 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -4,7 +4,7 @@ use ruma::api::client::discovery::get_capabilities::{ self, Capabilities, RoomVersionStability, RoomVersionsCapability, }; -use crate::{services, Result, Ruma, RumaResponse}; +use crate::{services, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/capabilities` /// @@ -12,7 +12,7 @@ use crate::{services, Result, Ruma, RumaResponse}; /// of this server. pub(crate) async fn get_capabilities_route( _body: Ruma, -) -> Result> { +) -> Result> { let mut available = BTreeMap::new(); for room_version in &services().globals.unstable_room_versions { available.insert(room_version.clone(), RoomVersionStability::Unstable); @@ -27,7 +27,7 @@ pub(crate) async fn get_capabilities_route( available, }; - Ok(RumaResponse(get_capabilities::v3::Response { + Ok(Ra(get_capabilities::v3::Response { capabilities, })) } diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index 2a0bcda8..dbff1e3b 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -14,14 +14,14 @@ use ruma::{ use serde::Deserialize; use serde_json::{json, value::RawValue as RawJsonValue}; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}` /// /// Sets some account data for the sender user. pub(crate) async fn set_global_account_data_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let data: serde_json::Value = serde_json::from_str(body.data.json().get()) @@ -41,7 +41,7 @@ pub(crate) async fn set_global_account_data_route( }), )?; - Ok(RumaResponse(set_global_account_data::v3::Response {})) + Ok(Ra(set_global_account_data::v3::Response {})) } /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}` @@ -49,7 +49,7 @@ pub(crate) async fn set_global_account_data_route( /// Sets some room account data for the sender user. pub(crate) async fn set_room_account_data_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let data: serde_json::Value = serde_json::from_str(body.data.json().get()) @@ -69,7 +69,7 @@ pub(crate) async fn set_room_account_data_route( }), )?; - Ok(RumaResponse(set_room_account_data::v3::Response {})) + Ok(Ra(set_room_account_data::v3::Response {})) } /// # `GET /_matrix/client/r0/user/{userId}/account_data/{type}` @@ -77,7 +77,7 @@ pub(crate) async fn set_room_account_data_route( /// Gets some account data for the sender user. pub(crate) async fn get_global_account_data_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event: Box = services() @@ -92,7 +92,7 @@ pub(crate) async fn get_global_account_data_route( })? .content; - Ok(RumaResponse(get_global_account_data::v3::Response { + Ok(Ra(get_global_account_data::v3::Response { account_data, })) } @@ -102,7 +102,7 @@ pub(crate) async fn get_global_account_data_route( /// Gets some room account data for the sender user. pub(crate) async fn get_room_account_data_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event: Box = services() @@ -117,7 +117,7 @@ pub(crate) async fn get_room_account_data_route( })? .content; - Ok(RumaResponse(get_room_account_data::v3::Response { + Ok(Ra(get_room_account_data::v3::Response { account_data, })) } diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 73a7453b..42cf8e62 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -9,7 +9,7 @@ use ruma::{ }; use tracing::error; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/rooms/{roomId}/context` /// @@ -21,7 +21,7 @@ use crate::{services, Error, Result, Ruma, RumaResponse}; #[allow(clippy::too_many_lines)] pub(crate) async fn get_context_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -187,5 +187,5 @@ pub(crate) async fn get_context_route( state, }; - Ok(RumaResponse(resp)) + Ok(Ra(resp)) } diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index b8cd3edc..92c67b41 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -8,14 +8,14 @@ use ruma::api::client::{ }; use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Result, Ruma, RumaResponse}; +use crate::{services, utils, Error, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/devices` /// /// Get metadata on all devices of the sender user. pub(crate) async fn get_devices_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let devices: Vec = services() @@ -24,7 +24,7 @@ pub(crate) async fn get_devices_route( .filter_map(Result::ok) .collect(); - Ok(RumaResponse(get_devices::v3::Response { + Ok(Ra(get_devices::v3::Response { devices, })) } @@ -34,7 +34,7 @@ pub(crate) async fn get_devices_route( /// Get metadata on a single device of the sender user. pub(crate) async fn get_device_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device = services() @@ -42,7 +42,7 @@ pub(crate) async fn get_device_route( .get_device_metadata(sender_user, &body.body.device_id)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?; - Ok(RumaResponse(get_device::v3::Response { + Ok(Ra(get_device::v3::Response { device, })) } @@ -52,7 +52,7 @@ pub(crate) async fn get_device_route( /// Updates the metadata on a given device of the sender user. pub(crate) async fn update_device_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let mut device = services() @@ -68,7 +68,7 @@ pub(crate) async fn update_device_route( &device, )?; - Ok(RumaResponse(update_device::v3::Response {})) + Ok(Ra(update_device::v3::Response {})) } /// # `DELETE /_matrix/client/r0/devices/{deviceId}` @@ -83,7 +83,7 @@ pub(crate) async fn update_device_route( /// - Triggers device list updates pub(crate) async fn delete_device_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -120,7 +120,7 @@ pub(crate) async fn delete_device_route( services().users.remove_device(sender_user, &body.device_id)?; - Ok(RumaResponse(delete_device::v3::Response {})) + Ok(Ra(delete_device::v3::Response {})) } /// # `PUT /_matrix/client/r0/devices/{deviceId}` @@ -137,7 +137,7 @@ pub(crate) async fn delete_device_route( /// - Triggers device list updates pub(crate) async fn delete_devices_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -176,5 +176,5 @@ pub(crate) async fn delete_devices_route( services().users.remove_device(sender_user, device_id)?; } - Ok(RumaResponse(delete_devices::v3::Response {})) + Ok(Ra(delete_devices::v3::Response {})) } diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 2ce36036..64727516 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -29,7 +29,7 @@ use ruma::{ }; use tracing::{error, info, warn}; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `POST /_matrix/client/r0/publicRooms` /// @@ -38,7 +38,7 @@ use crate::{services, Error, Result, Ruma, RumaResponse}; /// - Rooms are ordered by the number of joined members pub(crate) async fn get_public_rooms_filtered_route( body: Ruma, -) -> Result> { +) -> Result> { get_public_rooms_filtered_helper( body.server.as_deref(), body.limit, @@ -47,7 +47,7 @@ pub(crate) async fn get_public_rooms_filtered_route( &body.room_network, ) .await - .map(RumaResponse) + .map(Ra) } /// # `GET /_matrix/client/r0/publicRooms` @@ -57,7 +57,7 @@ pub(crate) async fn get_public_rooms_filtered_route( /// - Rooms are ordered by the number of joined members pub(crate) async fn get_public_rooms_route( body: Ruma, -) -> Result> { +) -> Result> { let response = get_public_rooms_filtered_helper( body.server.as_deref(), body.limit, @@ -67,7 +67,7 @@ pub(crate) async fn get_public_rooms_route( ) .await?; - Ok(RumaResponse(get_public_rooms::v3::Response { + Ok(Ra(get_public_rooms::v3::Response { chunk: response.chunk, prev_batch: response.prev_batch, next_batch: response.next_batch, @@ -82,7 +82,7 @@ pub(crate) async fn get_public_rooms_route( /// - TODO: Access control checks pub(crate) async fn set_room_visibility_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services().rooms.metadata.exists(&body.room_id)? { @@ -106,7 +106,7 @@ pub(crate) async fn set_room_visibility_route( } } - Ok(RumaResponse(set_room_visibility::v3::Response {})) + Ok(Ra(set_room_visibility::v3::Response {})) } /// # `GET /_matrix/client/r0/directory/list/room/{roomId}` @@ -114,13 +114,13 @@ pub(crate) async fn set_room_visibility_route( /// Gets the visibility of a given room in the room directory. pub(crate) async fn get_room_visibility_route( body: Ruma, -) -> Result> { +) -> Result> { if !services().rooms.metadata.exists(&body.room_id)? { // Return 404 if the room doesn't exist return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found")); } - Ok(RumaResponse(get_room_visibility::v3::Response { + Ok(Ra(get_room_visibility::v3::Response { visibility: if services() .rooms .directory diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index 197e3ff8..cfb2a4dc 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -3,7 +3,7 @@ use ruma::api::client::{ filter::{create_filter, get_filter}, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}` /// @@ -12,7 +12,7 @@ use crate::{services, Error, Result, Ruma, RumaResponse}; /// - A user can only access their own filters pub(crate) async fn get_filter_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? @@ -23,7 +23,7 @@ pub(crate) async fn get_filter_route( )); }; - Ok(RumaResponse(get_filter::v3::Response::new(filter))) + Ok(Ra(get_filter::v3::Response::new(filter))) } /// # `PUT /_matrix/client/r0/user/{userId}/filter` @@ -31,9 +31,9 @@ pub(crate) async fn get_filter_route( /// Creates a new filter to be used by other endpoints. pub(crate) async fn create_filter_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(RumaResponse(create_filter::v3::Response::new( + Ok(Ra(create_filter::v3::Response::new( services().users.create_filter(sender_user, &body.filter)?, ))) } diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 614840a1..3751f995 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -23,7 +23,7 @@ use serde_json::json; use tracing::debug; use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Result, Ruma, RumaResponse}; +use crate::{services, utils, Error, Ra, Result, Ruma}; /// # `POST /_matrix/client/r0/keys/upload` /// @@ -34,7 +34,7 @@ use crate::{services, utils, Error, Result, Ruma, RumaResponse}; /// existing keys?) pub(crate) async fn upload_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -64,7 +64,7 @@ pub(crate) async fn upload_keys_route( } } - Ok(RumaResponse(upload_keys::v3::Response { + Ok(Ra(upload_keys::v3::Response { one_time_key_counts: services() .users .count_one_time_keys(sender_user, sender_device)?, @@ -81,7 +81,7 @@ pub(crate) async fn upload_keys_route( /// allowed to see pub(crate) async fn get_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let response = get_keys_helper(Some(sender_user), &body.device_keys, |u| { @@ -89,7 +89,7 @@ pub(crate) async fn get_keys_route( }) .await?; - Ok(RumaResponse(response)) + Ok(Ra(response)) } /// # `POST /_matrix/client/r0/keys/claim` @@ -97,10 +97,10 @@ pub(crate) async fn get_keys_route( /// Claims one-time keys pub(crate) async fn claim_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let response = claim_keys_helper(&body.one_time_keys).await?; - Ok(RumaResponse(response)) + Ok(Ra(response)) } /// # `POST /_matrix/client/r0/keys/device_signing/upload` @@ -110,7 +110,7 @@ pub(crate) async fn claim_keys_route( /// - Requires UIAA to verify password pub(crate) async fn upload_signing_keys_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -156,7 +156,7 @@ pub(crate) async fn upload_signing_keys_route( )?; } - Ok(RumaResponse(upload_signing_keys::v3::Response {})) + Ok(Ra(upload_signing_keys::v3::Response {})) } /// # `POST /_matrix/client/r0/keys/signatures/upload` @@ -164,7 +164,7 @@ pub(crate) async fn upload_signing_keys_route( /// Uploads end-to-end key signatures from the sender user. pub(crate) async fn upload_signatures_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); for (user_id, keys) in &body.signed_keys { @@ -213,7 +213,7 @@ pub(crate) async fn upload_signatures_route( } } - Ok(RumaResponse(upload_signatures::v3::Response { + Ok(Ra(upload_signatures::v3::Response { // TODO: integrate failures: BTreeMap::new(), })) @@ -227,7 +227,7 @@ pub(crate) async fn upload_signatures_route( /// - TODO: left users pub(crate) async fn get_key_changes_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let mut device_list_updates = HashSet::new(); @@ -277,7 +277,7 @@ pub(crate) async fn get_key_changes_route( .filter_map(Result::ok), ); } - Ok(RumaResponse(get_key_changes::v3::Response { + Ok(Ra(get_key_changes::v3::Response { changed: device_list_updates.into_iter().collect(), // TODO left: Vec::new(), diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 16b72429..8b3aa85b 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -9,8 +9,7 @@ use ruma::api::client::{ }; use crate::{ - service::media::FileMeta, services, utils, Error, Result, Ruma, - RumaResponse, + service::media::FileMeta, services, utils, Error, Ra, Result, Ruma, }; const MXC_LENGTH: usize = 32; @@ -20,8 +19,8 @@ const MXC_LENGTH: usize = 32; /// Returns max upload size. pub(crate) async fn get_media_config_route( _body: Ruma, -) -> Result> { - Ok(RumaResponse(get_media_config::v3::Response { +) -> Result> { + Ok(Ra(get_media_config::v3::Response { upload_size: services().globals.max_request_size().into(), })) } @@ -34,7 +33,7 @@ pub(crate) async fn get_media_config_route( /// - Media will be saved in the media/ directory pub(crate) async fn create_content_route( body: Ruma, -) -> Result> { +) -> Result> { let mxc = format!( "mxc://{}/{}", services().globals.server_name(), @@ -54,7 +53,7 @@ pub(crate) async fn create_content_route( ) .await?; - Ok(RumaResponse(create_content::v3::Response { + Ok(Ra(create_content::v3::Response { content_uri: mxc.into(), blurhash: None, })) @@ -99,7 +98,7 @@ pub(crate) async fn get_remote_content( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_route( body: Ruma, -) -> Result> { +) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -108,7 +107,7 @@ pub(crate) async fn get_content_route( file, }) = services().media.get(mxc.clone()).await? { - Ok(RumaResponse(get_content::v3::Response { + Ok(Ra(get_content::v3::Response { file, content_type, content_disposition, @@ -120,7 +119,7 @@ pub(crate) async fn get_content_route( let remote_content_response = get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(RumaResponse(remote_content_response)) + Ok(Ra(remote_content_response)) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } @@ -133,7 +132,7 @@ pub(crate) async fn get_content_route( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_as_filename_route( body: Ruma, -) -> Result> { +) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -142,7 +141,7 @@ pub(crate) async fn get_content_as_filename_route( .. }) = services().media.get(mxc.clone()).await? { - Ok(RumaResponse(get_content_as_filename::v3::Response { + Ok(Ra(get_content_as_filename::v3::Response { file, content_type, content_disposition: Some(format!( @@ -158,7 +157,7 @@ pub(crate) async fn get_content_as_filename_route( get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(RumaResponse(get_content_as_filename::v3::Response { + Ok(Ra(get_content_as_filename::v3::Response { content_disposition: Some(format!( "inline: filename={}", body.filename @@ -179,7 +178,7 @@ pub(crate) async fn get_content_as_filename_route( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_thumbnail_route( body: Ruma, -) -> Result> { +) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -199,7 +198,7 @@ pub(crate) async fn get_content_thumbnail_route( ) .await? { - Ok(RumaResponse(get_content_thumbnail::v3::Response { + Ok(Ra(get_content_thumbnail::v3::Response { file, content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), @@ -236,7 +235,7 @@ pub(crate) async fn get_content_thumbnail_route( ) .await?; - Ok(RumaResponse(get_thumbnail_response)) + Ok(Ra(get_thumbnail_response)) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index d99b1d5c..229b45b0 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -36,7 +36,7 @@ use tracing::{debug, error, info, warn}; use super::get_alias_helper; use crate::{ service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Result, Ruma, RumaResponse, + services, utils, Error, PduEvent, Ra, Result, Ruma, }; /// # `POST /_matrix/client/r0/rooms/{roomId}/join` @@ -49,7 +49,7 @@ use crate::{ /// federation pub(crate) async fn join_room_by_id_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // There is no body.server_name for /roomId/join @@ -83,7 +83,7 @@ pub(crate) async fn join_room_by_id_route( body.third_party_signed.as_ref(), ) .await - .map(RumaResponse) + .map(Ra) } /// # `POST /_matrix/client/r0/join/{roomIdOrAlias}` @@ -96,7 +96,7 @@ pub(crate) async fn join_room_by_id_route( /// federation pub(crate) async fn join_room_by_id_or_alias_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_deref().expect("user is authenticated"); let body = body.body; @@ -148,7 +148,7 @@ pub(crate) async fn join_room_by_id_or_alias_route( ) .await?; - Ok(RumaResponse(join_room_by_id_or_alias::v3::Response { + Ok(Ra(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id, })) } @@ -160,12 +160,12 @@ pub(crate) async fn join_room_by_id_or_alias_route( /// - This should always work if the user is currently joined. pub(crate) async fn leave_room_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); leave_room(sender_user, &body.room_id, body.reason.clone()).await?; - Ok(RumaResponse(leave_room::v3::Response::new())) + Ok(Ra(leave_room::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/invite` @@ -173,7 +173,7 @@ pub(crate) async fn leave_room_route( /// Tries to send an invite event into the room. pub(crate) async fn invite_user_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let invite_user::v3::InvitationRecipient::UserId { @@ -188,7 +188,7 @@ pub(crate) async fn invite_user_route( false, ) .await?; - Ok(RumaResponse(invite_user::v3::Response {})) + Ok(Ra(invite_user::v3::Response {})) } else { Err(Error::BadRequest(ErrorKind::NotFound, "User not found.")) } @@ -199,13 +199,13 @@ pub(crate) async fn invite_user_route( /// Tries to send a kick event into the room. pub(crate) async fn kick_user_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(true) = services().rooms.state_cache.is_left(sender_user, &body.room_id) { - return Ok(RumaResponse(kick_user::v3::Response {})); + return Ok(Ra(kick_user::v3::Response {})); } let mut event: RoomMemberEventContent = serde_json::from_str( @@ -260,7 +260,7 @@ pub(crate) async fn kick_user_route( drop(state_lock); - Ok(RumaResponse(kick_user::v3::Response::new())) + Ok(Ra(kick_user::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/ban` @@ -268,14 +268,14 @@ pub(crate) async fn kick_user_route( /// Tries to send a ban event into the room. pub(crate) async fn ban_user_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(Some(membership_event)) = services().rooms.state_accessor.get_member(&body.room_id, sender_user) { if membership_event.membership == MembershipState::Ban { - return Ok(RumaResponse(ban_user::v3::Response {})); + return Ok(Ra(ban_user::v3::Response {})); } } @@ -344,7 +344,7 @@ pub(crate) async fn ban_user_route( drop(state_lock); - Ok(RumaResponse(ban_user::v3::Response::new())) + Ok(Ra(ban_user::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/unban` @@ -352,14 +352,14 @@ pub(crate) async fn ban_user_route( /// Tries to send an unban event into the room. pub(crate) async fn unban_user_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Ok(Some(membership_event)) = services().rooms.state_accessor.get_member(&body.room_id, sender_user) { if membership_event.membership != MembershipState::Ban { - return Ok(RumaResponse(unban_user::v3::Response {})); + return Ok(Ra(unban_user::v3::Response {})); } } @@ -415,7 +415,7 @@ pub(crate) async fn unban_user_route( drop(state_lock); - Ok(RumaResponse(unban_user::v3::Response::new())) + Ok(Ra(unban_user::v3::Response::new())) } /// # `POST /_matrix/client/r0/rooms/{roomId}/forget` @@ -429,12 +429,12 @@ pub(crate) async fn unban_user_route( /// forgotten, so this has to be called from every device pub(crate) async fn forget_room_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().rooms.state_cache.forget(&body.room_id, sender_user)?; - Ok(RumaResponse(forget_room::v3::Response::new())) + Ok(Ra(forget_room::v3::Response::new())) } /// # `POST /_matrix/client/r0/joined_rooms` @@ -442,10 +442,10 @@ pub(crate) async fn forget_room_route( /// Lists all rooms the user has joined. pub(crate) async fn joined_rooms_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(RumaResponse(joined_rooms::v3::Response { + Ok(Ra(joined_rooms::v3::Response { joined_rooms: services() .rooms .state_cache @@ -463,7 +463,7 @@ pub(crate) async fn joined_rooms_route( /// - Only works if the user is currently joined pub(crate) async fn get_member_events_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -477,7 +477,7 @@ pub(crate) async fn get_member_events_route( )); } - Ok(RumaResponse(get_member_events::v3::Response { + Ok(Ra(get_member_events::v3::Response { chunk: services() .rooms .state_accessor @@ -498,7 +498,7 @@ pub(crate) async fn get_member_events_route( /// - TODO: An appservice just needs a puppet joined pub(crate) async fn joined_members_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -531,7 +531,7 @@ pub(crate) async fn joined_members_route( ); } - Ok(RumaResponse(joined_members::v3::Response { + Ok(Ra(joined_members::v3::Response { joined, })) } diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index ef7b8947..6a16f6f6 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -14,7 +14,7 @@ use ruma::{ use crate::{ service::{pdu::PduBuilder, rooms::timeline::PduCount}, - services, utils, Error, Result, Ruma, RumaResponse, + services, utils, Error, Ra, Result, Ruma, }; /// # `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}` @@ -28,7 +28,7 @@ use crate::{ /// allowed pub(crate) async fn send_message_event_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); @@ -77,7 +77,7 @@ pub(crate) async fn send_message_event_route( .map_err(|_| { Error::bad_database("Invalid event id in txnid data.") })?; - return Ok(RumaResponse(send_message_event::v3::Response { + return Ok(Ra(send_message_event::v3::Response { event_id, })); } @@ -118,9 +118,7 @@ pub(crate) async fn send_message_event_route( drop(state_lock); - Ok(RumaResponse(send_message_event::v3::Response::new( - (*event_id).to_owned(), - ))) + Ok(Ra(send_message_event::v3::Response::new((*event_id).to_owned()))) } /// # `GET /_matrix/client/r0/rooms/{roomId}/messages` @@ -133,7 +131,7 @@ pub(crate) async fn send_message_event_route( #[allow(clippy::too_many_lines)] pub(crate) async fn get_message_events_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -310,5 +308,5 @@ pub(crate) async fn get_message_events_route( } */ - Ok(RumaResponse(resp)) + Ok(Ra(resp)) } diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index f05f5b14..1a51815d 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -18,9 +18,7 @@ use ruma::{ use serde_json::value::to_raw_value; use tracing::warn; -use crate::{ - service::pdu::PduBuilder, services, Error, Result, Ruma, RumaResponse, -}; +use crate::{service::pdu::PduBuilder, services, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/profile/{userId}/displayname` /// @@ -29,7 +27,7 @@ use crate::{ /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_displayname_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().users.set_displayname(sender_user, body.displayname.clone())?; @@ -109,7 +107,7 @@ pub(crate) async fn set_displayname_route( } } - Ok(RumaResponse(set_display_name::v3::Response {})) + Ok(Ra(set_display_name::v3::Response {})) } /// # `GET /_matrix/client/r0/profile/{userId}/displayname` @@ -119,7 +117,7 @@ pub(crate) async fn set_displayname_route( /// - If user is on another server: Fetches displayname over federation pub(crate) async fn get_displayname_route( body: Ruma, -) -> Result> { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() .sending @@ -132,12 +130,12 @@ pub(crate) async fn get_displayname_route( ) .await?; - return Ok(RumaResponse(get_display_name::v3::Response { + return Ok(Ra(get_display_name::v3::Response { displayname: response.displayname, })); } - Ok(RumaResponse(get_display_name::v3::Response { + Ok(Ra(get_display_name::v3::Response { displayname: services().users.displayname(&body.user_id)?, })) } @@ -149,7 +147,7 @@ pub(crate) async fn get_displayname_route( /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_avatar_url_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().users.set_avatar_url(sender_user, body.avatar_url.clone())?; @@ -231,7 +229,7 @@ pub(crate) async fn set_avatar_url_route( }; } - Ok(RumaResponse(set_avatar_url::v3::Response {})) + Ok(Ra(set_avatar_url::v3::Response {})) } /// # `GET /_matrix/client/r0/profile/{userId}/avatar_url` @@ -242,7 +240,7 @@ pub(crate) async fn set_avatar_url_route( /// federation pub(crate) async fn get_avatar_url_route( body: Ruma, -) -> Result> { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() .sending @@ -255,13 +253,13 @@ pub(crate) async fn get_avatar_url_route( ) .await?; - return Ok(RumaResponse(get_avatar_url::v3::Response { + return Ok(Ra(get_avatar_url::v3::Response { avatar_url: response.avatar_url, blurhash: response.blurhash, })); } - Ok(RumaResponse(get_avatar_url::v3::Response { + Ok(Ra(get_avatar_url::v3::Response { avatar_url: services().users.avatar_url(&body.user_id)?, blurhash: services().users.blurhash(&body.user_id)?, })) @@ -274,7 +272,7 @@ pub(crate) async fn get_avatar_url_route( /// - If user is on another server: Fetches profile over federation pub(crate) async fn get_profile_route( body: Ruma, -) -> Result> { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() .sending @@ -287,7 +285,7 @@ pub(crate) async fn get_profile_route( ) .await?; - return Ok(RumaResponse(get_profile::v3::Response { + return Ok(Ra(get_profile::v3::Response { displayname: response.displayname, avatar_url: response.avatar_url, blurhash: response.blurhash, @@ -302,7 +300,7 @@ pub(crate) async fn get_profile_route( )); } - Ok(RumaResponse(get_profile::v3::Response { + Ok(Ra(get_profile::v3::Response { avatar_url: services().users.avatar_url(&body.user_id)?, blurhash: services().users.blurhash(&body.user_id)?, displayname: services().users.displayname(&body.user_id)?, diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index e000a3c5..ae41a6d5 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -11,14 +11,14 @@ use ruma::{ push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/pushrules` /// /// Retrieves the push rules event for this user. pub(crate) async fn get_pushrules_all_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services() @@ -37,7 +37,7 @@ pub(crate) async fn get_pushrules_all_route( .map_err(|_| Error::bad_database("Invalid account data event in db."))? .content; - Ok(RumaResponse(get_pushrules_all::v3::Response { + Ok(Ra(get_pushrules_all::v3::Response { global: account_data.global, })) } @@ -47,7 +47,7 @@ pub(crate) async fn get_pushrules_all_route( /// Retrieves a single specified push rule for this user. pub(crate) async fn get_pushrule_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services() @@ -72,7 +72,7 @@ pub(crate) async fn get_pushrule_route( .map(Into::into); if let Some(rule) = rule { - Ok(RumaResponse(get_pushrule::v3::Response { + Ok(Ra(get_pushrule::v3::Response { rule, })) } else { @@ -85,7 +85,7 @@ pub(crate) async fn get_pushrule_route( /// Creates a single specified push rule for this user. pub(crate) async fn set_pushrule_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; @@ -157,7 +157,7 @@ pub(crate) async fn set_pushrule_route( .expect("to json value always works"), )?; - Ok(RumaResponse(set_pushrule::v3::Response {})) + Ok(Ra(set_pushrule::v3::Response {})) } /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions` @@ -165,7 +165,7 @@ pub(crate) async fn set_pushrule_route( /// Gets the actions of a single specified push rule for this user. pub(crate) async fn get_pushrule_actions_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -200,7 +200,7 @@ pub(crate) async fn get_pushrule_actions_route( "Push rule not found.", ))?; - Ok(RumaResponse(get_pushrule_actions::v3::Response { + Ok(Ra(get_pushrule_actions::v3::Response { actions, })) } @@ -210,7 +210,7 @@ pub(crate) async fn get_pushrule_actions_route( /// Sets the actions of a single specified push rule for this user. pub(crate) async fn set_pushrule_actions_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -257,7 +257,7 @@ pub(crate) async fn set_pushrule_actions_route( .expect("to json value always works"), )?; - Ok(RumaResponse(set_pushrule_actions::v3::Response {})) + Ok(Ra(set_pushrule_actions::v3::Response {})) } /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled` @@ -265,7 +265,7 @@ pub(crate) async fn set_pushrule_actions_route( /// Gets the enabled status of a single specified push rule for this user. pub(crate) async fn get_pushrule_enabled_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -301,7 +301,7 @@ pub(crate) async fn get_pushrule_enabled_route( "Push rule not found.", ))?; - Ok(RumaResponse(get_pushrule_enabled::v3::Response { + Ok(Ra(get_pushrule_enabled::v3::Response { enabled, })) } @@ -311,7 +311,7 @@ pub(crate) async fn get_pushrule_enabled_route( /// Sets the enabled status of a single specified push rule for this user. pub(crate) async fn set_pushrule_enabled_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -358,7 +358,7 @@ pub(crate) async fn set_pushrule_enabled_route( .expect("to json value always works"), )?; - Ok(RumaResponse(set_pushrule_enabled::v3::Response {})) + Ok(Ra(set_pushrule_enabled::v3::Response {})) } /// # `DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}` @@ -366,7 +366,7 @@ pub(crate) async fn set_pushrule_enabled_route( /// Deletes a single specified push rule for this user. pub(crate) async fn delete_pushrule_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if body.scope != RuleScope::Global { @@ -418,7 +418,7 @@ pub(crate) async fn delete_pushrule_route( .expect("to json value always works"), )?; - Ok(RumaResponse(delete_pushrule::v3::Response {})) + Ok(Ra(delete_pushrule::v3::Response {})) } /// # `GET /_matrix/client/r0/pushers` @@ -426,10 +426,10 @@ pub(crate) async fn delete_pushrule_route( /// Gets all currently active pushers for the sender user. pub(crate) async fn get_pushers_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - Ok(RumaResponse(get_pushers::v3::Response { + Ok(Ra(get_pushers::v3::Response { pushers: services().pusher.get_pushers(sender_user)?, })) } @@ -441,10 +441,10 @@ pub(crate) async fn get_pushers_route( /// - TODO: Handle `append` pub(crate) async fn set_pushers_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().pusher.set_pusher(sender_user, body.action.clone())?; - Ok(RumaResponse(set_pusher::v3::Response::default())) + Ok(Ra(set_pusher::v3::Response::default())) } diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index 9d0643e8..52eed945 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -12,8 +12,7 @@ use ruma::{ }; use crate::{ - service::rooms::timeline::PduCount, services, Error, Result, Ruma, - RumaResponse, + service::rooms::timeline::PduCount, services, Error, Ra, Result, Ruma, }; /// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers` @@ -25,7 +24,7 @@ use crate::{ /// EDU pub(crate) async fn set_read_marker_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Some(fully_read) = &body.fully_read { @@ -98,7 +97,7 @@ pub(crate) async fn set_read_marker_route( )?; } - Ok(RumaResponse(set_read_marker::v3::Response {})) + Ok(Ra(set_read_marker::v3::Response {})) } /// # `POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}` @@ -106,7 +105,7 @@ pub(crate) async fn set_read_marker_route( /// Sets private read marker and public read receipt EDU. pub(crate) async fn create_receipt_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if matches!( @@ -188,5 +187,5 @@ pub(crate) async fn create_receipt_route( _ => return Err(Error::bad_database("Unsupported receipt type")), } - Ok(RumaResponse(create_receipt::v3::Response {})) + Ok(Ra(create_receipt::v3::Response {})) } diff --git a/src/api/client_server/redact.rs b/src/api/client_server/redact.rs index 0114aa29..5f5395d8 100644 --- a/src/api/client_server/redact.rs +++ b/src/api/client_server/redact.rs @@ -6,7 +6,7 @@ use ruma::{ }; use serde_json::value::to_raw_value; -use crate::{service::pdu::PduBuilder, services, Result, Ruma, RumaResponse}; +use crate::{service::pdu::PduBuilder, services, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}` /// @@ -15,7 +15,7 @@ use crate::{service::pdu::PduBuilder, services, Result, Ruma, RumaResponse}; /// - TODO: Handle txn id pub(crate) async fn redact_event_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; @@ -54,7 +54,7 @@ pub(crate) async fn redact_event_route( drop(state_lock); let event_id = (*event_id).to_owned(); - Ok(RumaResponse(redact_event::v3::Response { + Ok(Ra(redact_event::v3::Response { event_id, })) } diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index d2068bc2..7124bc7b 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -6,18 +6,13 @@ use ruma::{ uint, }; -use crate::{ - service::rooms::timeline::PduCount, services, Result, Ruma, RumaResponse, -}; +use crate::{service::rooms::timeline::PduCount, services, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}` pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( body: Ruma, -) -> Result< - RumaResponse< - get_relating_events_with_rel_type_and_event_type::v1::Response, - >, -> { +) -> Result> +{ let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let from = match body.from.clone() { @@ -50,19 +45,17 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( limit, )?; - Ok(RumaResponse( - get_relating_events_with_rel_type_and_event_type::v1::Response { - chunk: res.chunk, - next_batch: res.next_batch, - prev_batch: res.prev_batch, - }, - )) + Ok(Ra(get_relating_events_with_rel_type_and_event_type::v1::Response { + chunk: res.chunk, + next_batch: res.next_batch, + prev_batch: res.prev_batch, + })) } /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}` pub(crate) async fn get_relating_events_with_rel_type_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let from = match body.from.clone() { @@ -95,7 +88,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route( limit, )?; - Ok(RumaResponse(get_relating_events_with_rel_type::v1::Response { + Ok(Ra(get_relating_events_with_rel_type::v1::Response { chunk: res.chunk, next_batch: res.next_batch, prev_batch: res.prev_batch, @@ -105,7 +98,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route( /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}` pub(crate) async fn get_relating_events_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let from = match body.from.clone() { @@ -140,5 +133,5 @@ pub(crate) async fn get_relating_events_route( to, limit, ) - .map(RumaResponse) + .map(Ra) } diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index 9dfd1a31..f1102fb5 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -4,14 +4,14 @@ use ruma::{ int, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `POST /_matrix/client/r0/rooms/{roomId}/report/{eventId}` /// /// Reports an inappropriate event to homeserver admins pub(crate) async fn report_event_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else { @@ -91,5 +91,5 @@ pub(crate) async fn report_event_route( ), )); - Ok(RumaResponse(report_content::v3::Response {})) + Ok(Ra(report_content::v3::Response {})) } diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index f177bebf..15e5613d 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -31,7 +31,7 @@ use tracing::{info, warn}; use crate::{ api::client_server::invite_helper, service::pdu::PduBuilder, services, - Error, Result, Ruma, RumaResponse, + Error, Ra, Result, Ruma, }; /// # `POST /_matrix/client/r0/createRoom` @@ -53,7 +53,7 @@ use crate::{ #[allow(clippy::too_many_lines)] pub(crate) async fn create_room_route( body: Ruma, -) -> Result> { +) -> Result> { use create_room::v3::RoomPreset; let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -538,7 +538,7 @@ pub(crate) async fn create_room_route( info!("{} created a room", sender_user); - Ok(RumaResponse(create_room::v3::Response::new(room_id))) + Ok(Ra(create_room::v3::Response::new(room_id))) } /// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}` @@ -549,7 +549,7 @@ pub(crate) async fn create_room_route( /// visibility) pub(crate) async fn get_room_event_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().rooms.timeline.get_pdu(&body.event_id)?.ok_or_else( @@ -573,7 +573,7 @@ pub(crate) async fn get_room_event_route( let mut event = (*event).clone(); event.add_age()?; - Ok(RumaResponse(get_room_event::v3::Response { + Ok(Ra(get_room_event::v3::Response { event: event.to_room_event(), })) } @@ -586,7 +586,7 @@ pub(crate) async fn get_room_event_route( /// user to call it if `history_visibility` is world readable pub(crate) async fn get_room_aliases_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services().rooms.state_cache.is_joined(sender_user, &body.room_id)? { @@ -596,7 +596,7 @@ pub(crate) async fn get_room_aliases_route( )); } - Ok(RumaResponse(aliases::v3::Response { + Ok(Ra(aliases::v3::Response { aliases: services() .rooms .alias @@ -619,7 +619,7 @@ pub(crate) async fn get_room_aliases_route( #[allow(clippy::too_many_lines)] pub(crate) async fn upgrade_room_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services().globals.supported_room_versions().contains(&body.new_version) @@ -914,7 +914,7 @@ pub(crate) async fn upgrade_room_route( drop(state_lock); // Return the replacement room id - Ok(RumaResponse(upgrade_room::v3::Response { + Ok(Ra(upgrade_room::v3::Response { replacement_room, })) } diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index afe44597..628a8b2c 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -14,7 +14,7 @@ use ruma::{ uint, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `POST /_matrix/client/r0/search` /// @@ -25,7 +25,7 @@ use crate::{services, Error, Result, Ruma, RumaResponse}; #[allow(clippy::too_many_lines)] pub(crate) async fn search_events_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let search_criteria = body.search_categories.room_events.as_ref().unwrap(); @@ -136,7 +136,7 @@ pub(crate) async fn search_events_route( Some((skip + limit).to_string()) }; - Ok(RumaResponse(search_events::v3::Response::new(ResultCategories { + Ok(Ra(search_events::v3::Response::new(ResultCategories { room_events: ResultRoomEvents { count: None, // TODO diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 13c86896..ed3b165a 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -16,7 +16,7 @@ use serde::Deserialize; use tracing::{info, warn}; use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; -use crate::{services, utils, Error, Result, Ruma, RumaResponse}; +use crate::{services, utils, Error, Ra, Result, Ruma}; #[derive(Debug, Deserialize)] struct Claims { @@ -29,8 +29,8 @@ struct Claims { /// the `type` field when logging in. pub(crate) async fn get_login_types_route( _body: Ruma, -) -> Result> { - Ok(RumaResponse(get_login_types::v3::Response::new(vec![ +) -> Result> { + Ok(Ra(get_login_types::v3::Response::new(vec![ get_login_types::v3::LoginType::Password(PasswordLoginType::default()), get_login_types::v3::LoginType::ApplicationService( ApplicationServiceLoginType::default(), @@ -54,7 +54,7 @@ pub(crate) async fn get_login_types_route( #[allow(clippy::too_many_lines)] pub(crate) async fn login_route( body: Ruma, -) -> Result> { +) -> Result> { // To allow deprecated login methods #![allow(deprecated)] // Validate login method @@ -256,7 +256,7 @@ pub(crate) async fn login_route( // Homeservers are still required to send the `home_server` field #[allow(deprecated)] - Ok(RumaResponse(login::v3::Response { + Ok(Ra(login::v3::Response { user_id, access_token: token, home_server: Some(services().globals.server_name().to_owned()), @@ -278,7 +278,7 @@ pub(crate) async fn login_route( /// - Triggers device list updates pub(crate) async fn logout_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_ref().expect("user is authenticated"); @@ -294,7 +294,7 @@ pub(crate) async fn logout_route( services().users.remove_device(sender_user, sender_device)?; - Ok(RumaResponse(logout::v3::Response::new())) + Ok(Ra(logout::v3::Response::new())) } /// # `POST /_matrix/client/r0/logout/all` @@ -311,7 +311,7 @@ pub(crate) async fn logout_route( /// /_matrix/client/r0/logout`](logout_route) from each device of this user. pub(crate) async fn logout_all_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Some(info) = &body.appservice_info { @@ -332,5 +332,5 @@ pub(crate) async fn logout_all_route( services().users.remove_device(sender_user, &device_id)?; } - Ok(RumaResponse(logout_all::v3::Response::new())) + Ok(Ra(logout_all::v3::Response::new())) } diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index 77396ed6..44c0939e 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,6 +1,6 @@ use ruma::{api::client::space::get_hierarchy, uint}; -use crate::{services, Result, Ruma, RumaResponse}; +use crate::{services, Ra, Result, Ruma}; /// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy` /// @@ -8,7 +8,7 @@ use crate::{services, Result, Ruma, RumaResponse}; /// of a given space. pub(crate) async fn get_hierarchy_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let skip = @@ -40,5 +40,5 @@ pub(crate) async fn get_hierarchy_route( body.suggested_only, ) .await - .map(RumaResponse) + .map(Ra) } diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 31b770d5..5cbf0b9c 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -14,9 +14,7 @@ use ruma::{ }; use tracing::log::warn; -use crate::{ - service::pdu::PduBuilder, services, Error, Result, Ruma, RumaResponse, -}; +use crate::{service::pdu::PduBuilder, services, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}` /// @@ -28,7 +26,7 @@ use crate::{ /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_key_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event_id = send_state_event_for_key_helper( @@ -42,7 +40,7 @@ pub(crate) async fn send_state_event_for_key_route( .await?; let event_id = (*event_id).to_owned(); - Ok(RumaResponse(send_state_event::v3::Response { + Ok(Ra(send_state_event::v3::Response { event_id, })) } @@ -57,7 +55,7 @@ pub(crate) async fn send_state_event_for_key_route( /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_empty_key_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // Forbid m.room.encryption if encryption is disabled @@ -94,7 +92,7 @@ pub(crate) async fn send_state_event_for_empty_key_route( /// readable pub(crate) async fn get_state_events_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -108,7 +106,7 @@ pub(crate) async fn get_state_events_route( )); } - Ok(RumaResponse(get_state_events::v3::Response { + Ok(Ra(get_state_events::v3::Response { room_state: services() .rooms .state_accessor @@ -128,7 +126,7 @@ pub(crate) async fn get_state_events_route( /// readable pub(crate) async fn get_state_events_for_key_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() @@ -154,7 +152,7 @@ pub(crate) async fn get_state_events_for_key_route( Error::BadRequest(ErrorKind::NotFound, "State event not found.") })?; - Ok(RumaResponse(get_state_events_for_key::v3::Response { + Ok(Ra(get_state_events_for_key::v3::Response { content: serde_json::from_str(event.content.get()).map_err(|_| { Error::bad_database("Invalid event content in database") })?, @@ -169,7 +167,7 @@ pub(crate) async fn get_state_events_for_key_route( /// readable pub(crate) async fn get_state_events_for_empty_key_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if !services() diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index a7176a3d..a1e78f52 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -29,7 +29,7 @@ use tracing::{debug, error}; use crate::{ service::{pdu::EventHash, rooms::timeline::PduCount}, - services, utils, Error, PduEvent, Result, Ruma, RumaResponse, + services, utils, Error, PduEvent, Ra, Result, Ruma, }; /// # `GET /_matrix/client/r0/sync` @@ -74,8 +74,7 @@ use crate::{ #[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_route( body: Ruma, -) -> Result, RumaResponse> -{ +) -> Result, Ra> { let sender_user = body.sender_user.expect("user is authenticated"); let sender_device = body.sender_device.expect("user is authenticated"); let body = body.body; @@ -458,10 +457,10 @@ pub(crate) async fn sync_events_route( Ok(x) => x.expect("watcher should succeed"), Err(error) => debug!(%error, "timed out"), }; - Ok(RumaResponse(response)) + Ok(Ra(response)) } else { // Only cache if we made progress - Ok(RumaResponse(response)) + Ok(Ra(response)) } } @@ -1128,8 +1127,7 @@ fn share_encrypted_room( #[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_v4_route( body: Ruma, -) -> Result, RumaResponse> -{ +) -> Result, Ra> { let sender_user = body.sender_user.expect("user is authenticated"); let sender_device = body.sender_device.expect("user is authenticated"); let mut body = body.body; @@ -1702,7 +1700,7 @@ pub(crate) async fn sync_events_v4_route( }; } - Ok(RumaResponse(sync_events::v4::Response { + Ok(Ra(sync_events::v4::Response { initial: globalsince == 0, txn_id: body.txn_id.clone(), pos: next_batch.to_string(), diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 81ba5f9a..94a2954a 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -8,7 +8,7 @@ use ruma::{ }, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}` /// @@ -17,7 +17,7 @@ use crate::{services, Error, Result, Ruma, RumaResponse}; /// - Inserts the tag into the tag event of the room account data. pub(crate) async fn update_tag_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( @@ -53,7 +53,7 @@ pub(crate) async fn update_tag_route( &serde_json::to_value(tags_event).expect("to json value always works"), )?; - Ok(RumaResponse(create_tag::v3::Response {})) + Ok(Ra(create_tag::v3::Response {})) } /// # `DELETE /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}` @@ -63,7 +63,7 @@ pub(crate) async fn update_tag_route( /// - Removes the tag from the tag event of the room account data. pub(crate) async fn delete_tag_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( @@ -96,7 +96,7 @@ pub(crate) async fn delete_tag_route( &serde_json::to_value(tags_event).expect("to json value always works"), )?; - Ok(RumaResponse(delete_tag::v3::Response {})) + Ok(Ra(delete_tag::v3::Response {})) } /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags` @@ -106,7 +106,7 @@ pub(crate) async fn delete_tag_route( /// - Gets the tag event of the room account data. pub(crate) async fn get_tags_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let event = services().account_data.get( @@ -130,7 +130,7 @@ pub(crate) async fn get_tags_route( }, )?; - Ok(RumaResponse(get_tags::v3::Response { + Ok(Ra(get_tags::v3::Response { tags: tags_event.content.tags, })) } diff --git a/src/api/client_server/thirdparty.rs b/src/api/client_server/thirdparty.rs index f93fc1f1..29f2da38 100644 --- a/src/api/client_server/thirdparty.rs +++ b/src/api/client_server/thirdparty.rs @@ -2,16 +2,16 @@ use std::collections::BTreeMap; use ruma::api::client::thirdparty::get_protocols; -use crate::{Result, Ruma, RumaResponse}; +use crate::{Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/thirdparty/protocols` /// /// TODO: Fetches all metadata about protocols supported by the homeserver. pub(crate) async fn get_protocols_route( _body: Ruma, -) -> Result> { +) -> Result> { // TODO - Ok(RumaResponse(get_protocols::v3::Response { + Ok(Ra(get_protocols::v3::Response { protocols: BTreeMap::new(), })) } diff --git a/src/api/client_server/threads.rs b/src/api/client_server/threads.rs index fdc1de01..518a736a 100644 --- a/src/api/client_server/threads.rs +++ b/src/api/client_server/threads.rs @@ -1,11 +1,11 @@ use ruma::api::client::{error::ErrorKind, threads::get_threads}; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `GET /_matrix/client/r0/rooms/{roomId}/threads` pub(crate) async fn get_threads_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); // Use limit or else 10, with maximum 100 @@ -36,7 +36,7 @@ pub(crate) async fn get_threads_route( let next_batch = threads.last().map(|(count, _)| count.to_string()); - Ok(RumaResponse(get_threads::v1::Response { + Ok(Ra(get_threads::v1::Response { chunk: threads .into_iter() .map(|(_, pdu)| pdu.to_room_event()) diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index 0fb36c59..17427f84 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -8,14 +8,14 @@ use ruma::{ to_device::DeviceIdOrAllDevices, }; -use crate::{services, Error, Result, Ruma, RumaResponse}; +use crate::{services, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}` /// /// Send a to-device event to a set of client devices. pub(crate) async fn send_event_to_device_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); @@ -25,7 +25,7 @@ pub(crate) async fn send_event_to_device_route( .existing_txnid(sender_user, sender_device, &body.txn_id)? .is_some() { - return Ok(RumaResponse(send_event_to_device::v3::Response {})); + return Ok(Ra(send_event_to_device::v3::Response {})); } for (target_user_id, map) in &body.messages { @@ -103,5 +103,5 @@ pub(crate) async fn send_event_to_device_route( &[], )?; - Ok(RumaResponse(send_event_to_device::v3::Response {})) + Ok(Ra(send_event_to_device::v3::Response {})) } diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index eddcfa48..d2226ee1 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -1,13 +1,13 @@ use ruma::api::client::{error::ErrorKind, typing::create_typing_event}; -use crate::{services, utils, Error, Result, Ruma, RumaResponse}; +use crate::{services, utils, Error, Ra, Result, Ruma}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}` /// /// Sets the typing state of the sender user. pub(crate) async fn create_typing_event_route( body: Ruma, -) -> Result> { +) -> Result> { use create_typing_event::v3::Typing; let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -40,5 +40,5 @@ pub(crate) async fn create_typing_event_route( .await?; } - Ok(RumaResponse(create_typing_event::v3::Response {})) + Ok(Ra(create_typing_event::v3::Response {})) } diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 0ad806a6..bdd8e048 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, iter::FromIterator}; use ruma::api::client::discovery::get_supported_versions; -use crate::{Result, Ruma, RumaResponse}; +use crate::{Ra, Result, Ruma}; /// # `GET /_matrix/client/versions` /// @@ -18,7 +18,7 @@ use crate::{Result, Ruma, RumaResponse}; /// should avoid using unstable features in their stable releases pub(crate) async fn get_supported_versions_route( _body: Ruma, -) -> Result> { +) -> Result> { let resp = get_supported_versions::Response { versions: vec![ "r0.5.0".to_owned(), @@ -35,5 +35,5 @@ pub(crate) async fn get_supported_versions_route( )]), }; - Ok(RumaResponse(resp)) + Ok(Ra(resp)) } diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 2667e927..06970f4b 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -6,7 +6,7 @@ use ruma::{ }, }; -use crate::{services, Result, Ruma, RumaResponse}; +use crate::{services, Ra, Result, Ruma}; /// # `POST /_matrix/client/r0/user_directory/search` /// @@ -17,7 +17,7 @@ use crate::{services, Result, Ruma, RumaResponse}; /// and don't share a room with the sender pub(crate) async fn search_users_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let limit = body.limit.try_into().unwrap_or(usize::MAX); @@ -99,7 +99,7 @@ pub(crate) async fn search_users_route( let results = users.by_ref().take(limit).collect(); let limited = users.next().is_some(); - Ok(RumaResponse(search_users::v3::Response { + Ok(Ra(search_users::v3::Response { results, limited, })) diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index 9c29c2d1..91507004 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -5,7 +5,7 @@ use hmac::{Hmac, Mac}; use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch}; use sha1::Sha1; -use crate::{services, Result, Ruma, RumaResponse}; +use crate::{services, Ra, Result, Ruma}; type HmacSha1 = Hmac; @@ -14,7 +14,7 @@ type HmacSha1 = Hmac; /// TODO: Returns information about the recommended turn server. pub(crate) async fn turn_server_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let turn_secret = services().globals.turn_secret().clone(); @@ -43,7 +43,7 @@ pub(crate) async fn turn_server_route( (username, password) }; - Ok(RumaResponse(get_turn_server_info::v3::Response { + Ok(Ra(get_turn_server_info::v3::Response { username, password, uris: services().globals.turn_uris().to_vec(), diff --git a/src/api/ruma_wrapper.rs b/src/api/ruma_wrapper.rs index 85173fe9..e0ab3368 100644 --- a/src/api/ruma_wrapper.rs +++ b/src/api/ruma_wrapper.rs @@ -28,16 +28,20 @@ impl Deref for Ruma { } } +/// A wrapper to convert Ruma data to an Axum response +/// +/// Named so because this converts from **R**uma to **A**xum. See also [`Ruma`], +/// which is roughly the inverse of this type. #[derive(Clone)] -pub(crate) struct RumaResponse(pub(crate) T); +pub(crate) struct Ra(pub(crate) T); -impl From for RumaResponse { +impl From for Ra { fn from(t: T) -> Self { Self(t) } } -impl From for RumaResponse { +impl From for Ra { fn from(t: Error) -> Self { t.to_response() } diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index f2e3e739..ef3460c2 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -24,7 +24,7 @@ use ruma::{ use serde::Deserialize; use tracing::{debug, error, warn}; -use super::{Ruma, RumaResponse}; +use super::{Ra, Ruma}; use crate::{service::appservice::RegistrationInfo, services, Error, Result}; enum Token { @@ -450,7 +450,7 @@ impl Credentials for XMatrix { } } -impl IntoResponse for RumaResponse { +impl IntoResponse for Ra { fn into_response(self) -> Response { match self.0.try_into_http_response::() { Ok(res) => res.map(BytesMut::freeze).map(Full::new).into_response(), diff --git a/src/api/server_server.rs b/src/api/server_server.rs index e66d9ab2..23bb3461 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -66,7 +66,7 @@ use tracing::{debug, error, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Result, Ruma, RumaResponse, + services, utils, Error, PduEvent, Ra, Result, Ruma, }; /// Wraps either an literal IP address plus port, or a hostname plus complement @@ -554,8 +554,8 @@ async fn request_well_known(destination: &str) -> Option { /// Get version information on this server. pub(crate) async fn get_server_version_route( _body: Ruma, -) -> Result> { - Ok(RumaResponse(get_server_version::v1::Response { +) -> Result> { + Ok(Ra(get_server_version::v1::Response { server: Some(get_server_version::v1::Server { name: Some(env!("CARGO_PKG_NAME").to_owned()), version: Some(crate::version()), @@ -631,7 +631,7 @@ pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { /// Lists the public rooms on this server. pub(crate) async fn get_public_rooms_filtered_route( body: Ruma, -) -> Result> { +) -> Result> { let response = client_server::get_public_rooms_filtered_helper( None, body.limit, @@ -641,7 +641,7 @@ pub(crate) async fn get_public_rooms_filtered_route( ) .await?; - Ok(RumaResponse(get_public_rooms_filtered::v1::Response { + Ok(Ra(get_public_rooms_filtered::v1::Response { chunk: response.chunk, prev_batch: response.prev_batch, next_batch: response.next_batch, @@ -654,7 +654,7 @@ pub(crate) async fn get_public_rooms_filtered_route( /// Lists the public rooms on this server. pub(crate) async fn get_public_rooms_route( body: Ruma, -) -> Result> { +) -> Result> { let response = client_server::get_public_rooms_filtered_helper( None, body.limit, @@ -664,7 +664,7 @@ pub(crate) async fn get_public_rooms_route( ) .await?; - Ok(RumaResponse(get_public_rooms::v1::Response { + Ok(Ra(get_public_rooms::v1::Response { chunk: response.chunk, prev_batch: response.prev_batch, next_batch: response.next_batch, @@ -709,7 +709,7 @@ pub(crate) fn parse_incoming_pdu( #[allow(clippy::too_many_lines)] pub(crate) async fn send_transaction_message_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -976,7 +976,7 @@ pub(crate) async fn send_transaction_message_route( } } - Ok(RumaResponse(send_transaction_message::v1::Response { + Ok(Ra(send_transaction_message::v1::Response { pdus: resolved_map .into_iter() .map(|(e, r)| (e, r.map_err(|e| e.sanitized_error()))) @@ -992,7 +992,7 @@ pub(crate) async fn send_transaction_message_route( /// room pub(crate) async fn get_event_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1035,7 +1035,7 @@ pub(crate) async fn get_event_route( )); } - Ok(RumaResponse(get_event::v1::Response { + Ok(Ra(get_event::v1::Response { origin: services().globals.server_name().to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch::now(), pdu: PduEvent::convert_to_outgoing_federation_event(event), @@ -1048,7 +1048,7 @@ pub(crate) async fn get_event_route( /// history visibility allows. pub(crate) async fn get_backfill_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1106,7 +1106,7 @@ pub(crate) async fn get_backfill_route( .map(PduEvent::convert_to_outgoing_federation_event) .collect(); - Ok(RumaResponse(get_backfill::v1::Response { + Ok(Ra(get_backfill::v1::Response { origin: services().globals.server_name().to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch::now(), pdus: events, @@ -1118,7 +1118,7 @@ pub(crate) async fn get_backfill_route( /// Retrieves events that the sender is missing. pub(crate) async fn get_missing_events_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1208,7 +1208,7 @@ pub(crate) async fn get_missing_events_route( i += 1; } - Ok(RumaResponse(get_missing_events::v1::Response { + Ok(Ra(get_missing_events::v1::Response { events, })) } @@ -1220,7 +1220,7 @@ pub(crate) async fn get_missing_events_route( /// - This does not include the event itself pub(crate) async fn get_event_authorization_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1263,7 +1263,7 @@ pub(crate) async fn get_event_authorization_route( .get_auth_chain(room_id, vec![Arc::from(&*body.event_id)]) .await?; - Ok(RumaResponse(get_event_authorization::v1::Response { + Ok(Ra(get_event_authorization::v1::Response { auth_chain: auth_chain_ids .filter_map(|id| { services().rooms.timeline.get_pdu_json(&id).ok()? @@ -1278,7 +1278,7 @@ pub(crate) async fn get_event_authorization_route( /// Retrieves the current state of the room. pub(crate) async fn get_room_state_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1326,7 +1326,7 @@ pub(crate) async fn get_room_state_route( .get_auth_chain(&body.room_id, vec![Arc::from(&*body.event_id)]) .await?; - Ok(RumaResponse(get_room_state::v1::Response { + Ok(Ra(get_room_state::v1::Response { auth_chain: auth_chain_ids .filter_map(|id| { if let Some(json) = @@ -1348,7 +1348,7 @@ pub(crate) async fn get_room_state_route( /// Retrieves the current state of the room. pub(crate) async fn get_room_state_ids_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1392,7 +1392,7 @@ pub(crate) async fn get_room_state_ids_route( .get_auth_chain(&body.room_id, vec![Arc::from(&*body.event_id)]) .await?; - Ok(RumaResponse(get_room_state_ids::v1::Response { + Ok(Ra(get_room_state_ids::v1::Response { auth_chain_ids: auth_chain_ids.map(|id| (*id).to_owned()).collect(), pdu_ids, })) @@ -1403,7 +1403,7 @@ pub(crate) async fn get_room_state_ids_route( /// Creates a join template. pub(crate) async fn create_join_event_template_route( body: Ruma, -) -> Result> { +) -> Result> { if !services().rooms.metadata.exists(&body.room_id)? { return Err(Error::BadRequest( ErrorKind::NotFound, @@ -1504,7 +1504,7 @@ pub(crate) async fn create_join_event_template_route( pdu_json.remove("event_id"); - Ok(RumaResponse(prepare_join_event::v1::Response { + Ok(Ra(prepare_join_event::v1::Response { room_version: Some(room_version_id), event: to_raw_value(&pdu_json) .expect("CanonicalJson can be serialized to JSON"), @@ -1661,14 +1661,14 @@ async fn create_join_event( /// Submits a signed join event. pub(crate) async fn create_join_event_v1_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); let room_state = create_join_event(sender_servername, &body.room_id, &body.pdu).await?; - Ok(RumaResponse(create_join_event::v1::Response { + Ok(Ra(create_join_event::v1::Response { room_state, })) } @@ -1678,7 +1678,7 @@ pub(crate) async fn create_join_event_v1_route( /// Submits a signed join event. pub(crate) async fn create_join_event_v2_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1695,7 +1695,7 @@ pub(crate) async fn create_join_event_v2_route( servers_in_room: None, }; - Ok(RumaResponse(create_join_event::v2::Response { + Ok(Ra(create_join_event::v2::Response { room_state, })) } @@ -1706,7 +1706,7 @@ pub(crate) async fn create_join_event_v2_route( #[allow(clippy::too_many_lines)] pub(crate) async fn create_invite_route( body: Ruma, -) -> Result> { +) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1827,7 +1827,7 @@ pub(crate) async fn create_invite_route( )?; } - Ok(RumaResponse(create_invite::v2::Response { + Ok(Ra(create_invite::v2::Response { event: PduEvent::convert_to_outgoing_federation_event(signed_event), })) } @@ -1837,7 +1837,7 @@ pub(crate) async fn create_invite_route( /// Gets information on all devices of the user. pub(crate) async fn get_devices_route( body: Ruma, -) -> Result> { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -1848,7 +1848,7 @@ pub(crate) async fn get_devices_route( let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); - Ok(RumaResponse(get_devices::v1::Response { + Ok(Ra(get_devices::v1::Response { user_id: body.user_id.clone(), stream_id: services() .users @@ -1889,13 +1889,13 @@ pub(crate) async fn get_devices_route( /// Resolve a room alias to a room id. pub(crate) async fn get_room_information_route( body: Ruma, -) -> Result> { +) -> Result> { let room_id = services().rooms.alias.resolve_local_alias(&body.room_alias)?.ok_or( Error::BadRequest(ErrorKind::NotFound, "Room alias not found."), )?; - Ok(RumaResponse(get_room_information::v1::Response { + Ok(Ra(get_room_information::v1::Response { room_id, servers: vec![services().globals.server_name().to_owned()], })) @@ -1906,7 +1906,7 @@ pub(crate) async fn get_room_information_route( /// Gets information on a profile. pub(crate) async fn get_profile_information_route( body: Ruma, -) -> Result> { +) -> Result> { if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -1935,7 +1935,7 @@ pub(crate) async fn get_profile_information_route( } } - Ok(RumaResponse(get_profile_information::v1::Response { + Ok(Ra(get_profile_information::v1::Response { displayname, avatar_url, blurhash, @@ -1947,7 +1947,7 @@ pub(crate) async fn get_profile_information_route( /// Gets devices and identity keys for the given users. pub(crate) async fn get_keys_route( body: Ruma, -) -> Result> { +) -> Result> { if body .device_keys .iter() @@ -1964,7 +1964,7 @@ pub(crate) async fn get_keys_route( }) .await?; - Ok(RumaResponse(get_keys::v1::Response { + Ok(Ra(get_keys::v1::Response { device_keys: result.device_keys, master_keys: result.master_keys, self_signing_keys: result.self_signing_keys, @@ -1976,7 +1976,7 @@ pub(crate) async fn get_keys_route( /// Claims one-time keys. pub(crate) async fn claim_keys_route( body: Ruma, -) -> Result> { +) -> Result> { if body .one_time_keys .iter() @@ -1990,7 +1990,7 @@ pub(crate) async fn claim_keys_route( let result = claim_keys_helper(&body.one_time_keys).await?; - Ok(RumaResponse(claim_keys::v1::Response { + Ok(Ra(claim_keys::v1::Response { one_time_keys: result.one_time_keys, })) } diff --git a/src/main.rs b/src/main.rs index 10eda195..c1a7d9e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,7 @@ mod database; mod service; mod utils; -pub(crate) use api::ruma_wrapper::{Ruma, RumaResponse}; +pub(crate) use api::ruma_wrapper::{Ra, Ruma}; use api::{client_server, server_server}; pub(crate) use config::Config; pub(crate) use database::KeyValueDatabase; @@ -295,7 +295,7 @@ async fn unrecognized_method( let inner = next.run(req).await; if inner.status() == axum::http::StatusCode::METHOD_NOT_ALLOWED { warn!("Method not allowed: {method} {uri}"); - return Ok(RumaResponse(UiaaResponse::MatrixError(RumaError { + return Ok(Ra(UiaaResponse::MatrixError(RumaError { body: ErrorBody::Standard { kind: ErrorKind::Unrecognized, message: "M_UNRECOGNIZED: Unrecognized request".to_owned(), diff --git a/src/utils/error.rs b/src/utils/error.rs index 581975ff..793a2890 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -11,7 +11,7 @@ use ruma::{ use thiserror::Error; use tracing::{error, info}; -use crate::RumaResponse; +use crate::Ra; pub(crate) type Result = std::result::Result; @@ -95,7 +95,7 @@ impl Error { Self::BadConfig(message) } - pub(crate) fn to_response(&self) -> RumaResponse { + pub(crate) fn to_response(&self) -> Ra { use ErrorKind::{ Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, ThreepidAuthFailed, ThreepidDenied, TooLarge, @@ -104,7 +104,7 @@ impl Error { }; if let Self::Uiaa(uiaainfo) = self { - return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone())); + return Ra(UiaaResponse::AuthResponse(uiaainfo.clone())); } if let Self::Federation(origin, error) = self { @@ -113,7 +113,7 @@ impl Error { kind: Unknown, message: format!("Answer from {origin}: {error}"), }; - return RumaResponse(UiaaResponse::MatrixError(error)); + return Ra(UiaaResponse::MatrixError(error)); } let message = format!("{self}"); @@ -149,7 +149,7 @@ impl Error { info!("Returning an error: {}: {}", status_code, message); - RumaResponse(UiaaResponse::MatrixError(RumaError { + Ra(UiaaResponse::MatrixError(RumaError { body: ErrorBody::Standard { kind, message, From f8961d5578b2b0cb8e6904244aff4da301c18e79 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 18 May 2024 19:01:27 -0700 Subject: [PATCH 122/617] rename `Ruma` to `Ar` This follows the pattern of the previous commit. --- src/api/client_server/account.rs | 18 +++++------ src/api/client_server/alias.rs | 8 ++--- src/api/client_server/backup.rs | 30 +++++++++---------- src/api/client_server/capabilities.rs | 4 +-- src/api/client_server/config.rs | 10 +++---- src/api/client_server/context.rs | 4 +-- src/api/client_server/device.rs | 12 ++++---- src/api/client_server/directory.rs | 10 +++---- src/api/client_server/filter.rs | 6 ++-- src/api/client_server/keys.rs | 14 ++++----- src/api/client_server/media.rs | 14 ++++----- src/api/client_server/membership.rs | 24 +++++++-------- src/api/client_server/message.rs | 6 ++-- src/api/client_server/profile.rs | 12 ++++---- src/api/client_server/push.rs | 22 +++++++------- src/api/client_server/read_marker.rs | 6 ++-- src/api/client_server/redact.rs | 4 +-- src/api/client_server/relations.rs | 8 ++--- src/api/client_server/report.rs | 4 +-- src/api/client_server/room.rs | 12 ++++---- src/api/client_server/search.rs | 4 +-- src/api/client_server/session.rs | 10 +++---- src/api/client_server/space.rs | 4 +-- src/api/client_server/state.rs | 12 ++++---- src/api/client_server/sync.rs | 6 ++-- src/api/client_server/tag.rs | 8 ++--- src/api/client_server/thirdparty.rs | 4 +-- src/api/client_server/threads.rs | 4 +-- src/api/client_server/to_device.rs | 4 +-- src/api/client_server/typing.rs | 4 +-- src/api/client_server/unversioned.rs | 4 +-- src/api/client_server/user_directory.rs | 4 +-- src/api/client_server/voip.rs | 4 +-- src/api/ruma_wrapper.rs | 12 +++++--- src/api/ruma_wrapper/axum.rs | 6 ++-- src/api/server_server.rs | 40 ++++++++++++------------- src/main.rs | 6 ++-- 37 files changed, 183 insertions(+), 181 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 39ec159a..180b90c2 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -19,7 +19,7 @@ use ruma::{ use tracing::{info, warn}; use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH}; -use crate::{api::client_server, services, utils, Error, Ra, Result, Ruma}; +use crate::{api::client_server, services, utils, Ar, Error, Ra, Result}; const RANDOM_USER_ID_LENGTH: usize = 10; @@ -35,7 +35,7 @@ const RANDOM_USER_ID_LENGTH: usize = 10; /// Note: This will not reserve the username, so the username might become /// invalid when trying to register pub(crate) async fn get_register_available_route( - body: Ruma, + body: Ar, ) -> Result> { // Validate user id let user_id = UserId::parse_with_server_name( @@ -87,7 +87,7 @@ pub(crate) async fn get_register_available_route( /// `access_token` #[allow(clippy::too_many_lines)] pub(crate) async fn register_route( - body: Ruma, + body: Ar, ) -> Result> { if !services().globals.allow_registration() && body.appservice_info.is_none() @@ -327,7 +327,7 @@ pub(crate) async fn register_route( /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn change_password_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = @@ -390,7 +390,7 @@ pub(crate) async fn change_password_route( /// /// Note: Also works for Application Services pub(crate) async fn whoami_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let device_id = body.sender_device.as_ref().cloned(); @@ -415,7 +415,7 @@ pub(crate) async fn whoami_route( /// - Triggers device list updates /// - Removes ability to log in again pub(crate) async fn deactivate_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = @@ -472,7 +472,7 @@ pub(crate) async fn deactivate_route( /// /// - Currently always returns empty list pub(crate) async fn third_party_route( - body: Ruma, + body: Ar, ) -> Result> { let _sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -488,7 +488,7 @@ pub(crate) async fn third_party_route( /// - 403 signals that The homeserver does not allow the third party identifier /// as a contact option. pub(crate) async fn request_3pid_management_token_via_email_route( - _body: Ruma, + _body: Ar, ) -> Result> { Err(Error::BadRequest( ErrorKind::ThreepidDenied, @@ -504,7 +504,7 @@ pub(crate) async fn request_3pid_management_token_via_email_route( /// - 403 signals that The homeserver does not allow the third party identifier /// as a contact option. pub(crate) async fn request_3pid_management_token_via_msisdn_route( - _body: Ruma, + _body: Ar, ) -> Result> { Err(Error::BadRequest( ErrorKind::ThreepidDenied, diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 39a1fe67..fb46dc55 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -11,13 +11,13 @@ use ruma::{ OwnedRoomAliasId, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}` /// /// Creates a new room alias on this server. pub(crate) async fn create_alias_route( - body: Ruma, + body: Ar, ) -> Result> { if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( @@ -56,7 +56,7 @@ pub(crate) async fn create_alias_route( /// - TODO: additional access control checks /// - TODO: Update canonical alias event pub(crate) async fn delete_alias_route( - body: Ruma, + body: Ar, ) -> Result> { if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( @@ -92,7 +92,7 @@ pub(crate) async fn delete_alias_route( /// /// - TODO: Suggest more servers to join via pub(crate) async fn get_alias_route( - body: Ruma, + body: Ar, ) -> Result> { get_alias_helper(body.body.room_alias).await.map(Ra) } diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index a33b766a..cc0a0640 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -9,13 +9,13 @@ use ruma::api::client::{ error::ErrorKind, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `POST /_matrix/client/r0/room_keys/version` /// /// Creates a new backup. pub(crate) async fn create_backup_version_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let version = @@ -31,7 +31,7 @@ pub(crate) async fn create_backup_version_route( /// Update information about an existing backup. Only `auth_data` can be /// modified. pub(crate) async fn update_backup_version_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); services().key_backups.update_backup( @@ -47,7 +47,7 @@ pub(crate) async fn update_backup_version_route( /// /// Get information about the latest backup version. pub(crate) async fn get_latest_backup_info_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -75,7 +75,7 @@ pub(crate) async fn get_latest_backup_info_route( /// /// Get information about an existing backup. pub(crate) async fn get_backup_info_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let algorithm = services() @@ -105,7 +105,7 @@ pub(crate) async fn get_backup_info_route( /// - Deletes both information about the backup, as well as all key data related /// to the backup pub(crate) async fn delete_backup_version_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -123,7 +123,7 @@ pub(crate) async fn delete_backup_version_route( /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -171,7 +171,7 @@ pub(crate) async fn add_backup_keys_route( /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_room_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -217,7 +217,7 @@ pub(crate) async fn add_backup_keys_for_room_route( /// - Adds the keys to the backup /// - Returns the new number of keys in this backup and the etag pub(crate) async fn add_backup_keys_for_session_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -256,7 +256,7 @@ pub(crate) async fn add_backup_keys_for_session_route( /// /// Retrieves all keys from the backup. pub(crate) async fn get_backup_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -271,7 +271,7 @@ pub(crate) async fn get_backup_keys_route( /// /// Retrieves all keys from the backup for a given room. pub(crate) async fn get_backup_keys_for_room_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -290,7 +290,7 @@ pub(crate) async fn get_backup_keys_for_room_route( /// /// Retrieves a key from the backup. pub(crate) async fn get_backup_keys_for_session_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -316,7 +316,7 @@ pub(crate) async fn get_backup_keys_for_session_route( /// /// Delete the keys from the backup. pub(crate) async fn delete_backup_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -336,7 +336,7 @@ pub(crate) async fn delete_backup_keys_route( /// /// Delete the keys from the backup for a given room. pub(crate) async fn delete_backup_keys_for_room_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -360,7 +360,7 @@ pub(crate) async fn delete_backup_keys_for_room_route( /// /// Delete a key from the backup. pub(crate) async fn delete_backup_keys_for_session_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index 73c3a9c6..8a7d6b10 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -4,14 +4,14 @@ use ruma::api::client::discovery::get_capabilities::{ self, Capabilities, RoomVersionStability, RoomVersionsCapability, }; -use crate::{services, Ra, Result, Ruma}; +use crate::{services, Ar, Ra, Result}; /// # `GET /_matrix/client/r0/capabilities` /// /// Get information on the supported feature set and other relevent capabilities /// of this server. pub(crate) async fn get_capabilities_route( - _body: Ruma, + _body: Ar, ) -> Result> { let mut available = BTreeMap::new(); for room_version in &services().globals.unstable_room_versions { diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index dbff1e3b..8d9a0a84 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -14,13 +14,13 @@ use ruma::{ use serde::Deserialize; use serde_json::{json, value::RawValue as RawJsonValue}; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}` /// /// Sets some account data for the sender user. pub(crate) async fn set_global_account_data_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -48,7 +48,7 @@ pub(crate) async fn set_global_account_data_route( /// /// Sets some room account data for the sender user. pub(crate) async fn set_room_account_data_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -76,7 +76,7 @@ pub(crate) async fn set_room_account_data_route( /// /// Gets some account data for the sender user. pub(crate) async fn get_global_account_data_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -101,7 +101,7 @@ pub(crate) async fn get_global_account_data_route( /// /// Gets some room account data for the sender user. pub(crate) async fn get_room_account_data_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 42cf8e62..e18706aa 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -9,7 +9,7 @@ use ruma::{ }; use tracing::error; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `GET /_matrix/client/r0/rooms/{roomId}/context` /// @@ -20,7 +20,7 @@ use crate::{services, Error, Ra, Result, Ruma}; /// joined, depending on `history_visibility`) #[allow(clippy::too_many_lines)] pub(crate) async fn get_context_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 92c67b41..ae243244 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -8,13 +8,13 @@ use ruma::api::client::{ }; use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Ra, Result, Ruma}; +use crate::{services, utils, Ar, Error, Ra, Result}; /// # `GET /_matrix/client/r0/devices` /// /// Get metadata on all devices of the sender user. pub(crate) async fn get_devices_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -33,7 +33,7 @@ pub(crate) async fn get_devices_route( /// /// Get metadata on a single device of the sender user. pub(crate) async fn get_device_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -51,7 +51,7 @@ pub(crate) async fn get_device_route( /// /// Updates the metadata on a given device of the sender user. pub(crate) async fn update_device_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -82,7 +82,7 @@ pub(crate) async fn update_device_route( /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn delete_device_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = @@ -136,7 +136,7 @@ pub(crate) async fn delete_device_route( /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn delete_devices_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 64727516..9aea4127 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -29,7 +29,7 @@ use ruma::{ }; use tracing::{error, info, warn}; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `POST /_matrix/client/r0/publicRooms` /// @@ -37,7 +37,7 @@ use crate::{services, Error, Ra, Result, Ruma}; /// /// - Rooms are ordered by the number of joined members pub(crate) async fn get_public_rooms_filtered_route( - body: Ruma, + body: Ar, ) -> Result> { get_public_rooms_filtered_helper( body.server.as_deref(), @@ -56,7 +56,7 @@ pub(crate) async fn get_public_rooms_filtered_route( /// /// - Rooms are ordered by the number of joined members pub(crate) async fn get_public_rooms_route( - body: Ruma, + body: Ar, ) -> Result> { let response = get_public_rooms_filtered_helper( body.server.as_deref(), @@ -81,7 +81,7 @@ pub(crate) async fn get_public_rooms_route( /// /// - TODO: Access control checks pub(crate) async fn set_room_visibility_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -113,7 +113,7 @@ pub(crate) async fn set_room_visibility_route( /// /// Gets the visibility of a given room in the room directory. pub(crate) async fn get_room_visibility_route( - body: Ruma, + body: Ar, ) -> Result> { if !services().rooms.metadata.exists(&body.room_id)? { // Return 404 if the room doesn't exist diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index cfb2a4dc..e99f8b17 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -3,7 +3,7 @@ use ruma::api::client::{ filter::{create_filter, get_filter}, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}` /// @@ -11,7 +11,7 @@ use crate::{services, Error, Ra, Result, Ruma}; /// /// - A user can only access their own filters pub(crate) async fn get_filter_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let Some(filter) = @@ -30,7 +30,7 @@ pub(crate) async fn get_filter_route( /// /// Creates a new filter to be used by other endpoints. pub(crate) async fn create_filter_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); Ok(Ra(create_filter::v3::Response::new( diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 3751f995..1c21e40b 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -23,7 +23,7 @@ use serde_json::json; use tracing::debug; use super::SESSION_ID_LENGTH; -use crate::{services, utils, Error, Ra, Result, Ruma}; +use crate::{services, utils, Ar, Error, Ra, Result}; /// # `POST /_matrix/client/r0/keys/upload` /// @@ -33,7 +33,7 @@ use crate::{services, utils, Error, Ra, Result, Ruma}; /// - If there are no device keys yet: Adds device keys (TODO: merge with /// existing keys?) pub(crate) async fn upload_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = @@ -80,7 +80,7 @@ pub(crate) async fn upload_keys_route( /// - The master and self-signing keys contain signatures that the user is /// allowed to see pub(crate) async fn get_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -96,7 +96,7 @@ pub(crate) async fn get_keys_route( /// /// Claims one-time keys pub(crate) async fn claim_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let response = claim_keys_helper(&body.one_time_keys).await?; @@ -109,7 +109,7 @@ pub(crate) async fn claim_keys_route( /// /// - Requires UIAA to verify password pub(crate) async fn upload_signing_keys_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = @@ -163,7 +163,7 @@ pub(crate) async fn upload_signing_keys_route( /// /// Uploads end-to-end key signatures from the sender user. pub(crate) async fn upload_signatures_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -226,7 +226,7 @@ pub(crate) async fn upload_signatures_route( /// /// - TODO: left users pub(crate) async fn get_key_changes_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 8b3aa85b..cbea2009 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -8,9 +8,7 @@ use ruma::api::client::{ }, }; -use crate::{ - service::media::FileMeta, services, utils, Error, Ra, Result, Ruma, -}; +use crate::{service::media::FileMeta, services, utils, Ar, Error, Ra, Result}; const MXC_LENGTH: usize = 32; @@ -18,7 +16,7 @@ const MXC_LENGTH: usize = 32; /// /// Returns max upload size. pub(crate) async fn get_media_config_route( - _body: Ruma, + _body: Ar, ) -> Result> { Ok(Ra(get_media_config::v3::Response { upload_size: services().globals.max_request_size().into(), @@ -32,7 +30,7 @@ pub(crate) async fn get_media_config_route( /// - Some metadata will be saved in the database /// - Media will be saved in the media/ directory pub(crate) async fn create_content_route( - body: Ruma, + body: Ar, ) -> Result> { let mxc = format!( "mxc://{}/{}", @@ -97,7 +95,7 @@ pub(crate) async fn get_remote_content( /// /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_route( - body: Ruma, + body: Ar, ) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); @@ -131,7 +129,7 @@ pub(crate) async fn get_content_route( /// /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_as_filename_route( - body: Ruma, + body: Ar, ) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); @@ -177,7 +175,7 @@ pub(crate) async fn get_content_as_filename_route( /// /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_thumbnail_route( - body: Ruma, + body: Ar, ) -> Result> { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 229b45b0..1586a4dd 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -36,7 +36,7 @@ use tracing::{debug, error, info, warn}; use super::get_alias_helper; use crate::{ service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Ra, Result, Ruma, + services, utils, Ar, Error, PduEvent, Ra, Result, }; /// # `POST /_matrix/client/r0/rooms/{roomId}/join` @@ -48,7 +48,7 @@ use crate::{ /// - If the server does not know about the room: asks other servers over /// federation pub(crate) async fn join_room_by_id_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -95,7 +95,7 @@ pub(crate) async fn join_room_by_id_route( /// - If the server does not know about the room: asks other servers over /// federation pub(crate) async fn join_room_by_id_or_alias_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_deref().expect("user is authenticated"); @@ -159,7 +159,7 @@ pub(crate) async fn join_room_by_id_or_alias_route( /// /// - This should always work if the user is currently joined. pub(crate) async fn leave_room_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -172,7 +172,7 @@ pub(crate) async fn leave_room_route( /// /// Tries to send an invite event into the room. pub(crate) async fn invite_user_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -198,7 +198,7 @@ pub(crate) async fn invite_user_route( /// /// Tries to send a kick event into the room. pub(crate) async fn kick_user_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -267,7 +267,7 @@ pub(crate) async fn kick_user_route( /// /// Tries to send a ban event into the room. pub(crate) async fn ban_user_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -351,7 +351,7 @@ pub(crate) async fn ban_user_route( /// /// Tries to send an unban event into the room. pub(crate) async fn unban_user_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -428,7 +428,7 @@ pub(crate) async fn unban_user_route( /// Note: Other devices of the user have no way of knowing the room was /// forgotten, so this has to be called from every device pub(crate) async fn forget_room_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -441,7 +441,7 @@ pub(crate) async fn forget_room_route( /// /// Lists all rooms the user has joined. pub(crate) async fn joined_rooms_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -462,7 +462,7 @@ pub(crate) async fn joined_rooms_route( /// /// - Only works if the user is currently joined pub(crate) async fn get_member_events_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -497,7 +497,7 @@ pub(crate) async fn get_member_events_route( /// - The sender user must be in the room /// - TODO: An appservice just needs a puppet joined pub(crate) async fn joined_members_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 6a16f6f6..6fe47f18 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -14,7 +14,7 @@ use ruma::{ use crate::{ service::{pdu::PduBuilder, rooms::timeline::PduCount}, - services, utils, Error, Ra, Result, Ruma, + services, utils, Ar, Error, Ra, Result, }; /// # `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}` @@ -27,7 +27,7 @@ use crate::{ /// - Tries to send the event into the room, auth rules will determine if it is /// allowed pub(crate) async fn send_message_event_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); @@ -130,7 +130,7 @@ pub(crate) async fn send_message_event_route( /// joined, depending on `history_visibility`) #[allow(clippy::too_many_lines)] pub(crate) async fn get_message_events_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index 1a51815d..8557baab 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -18,7 +18,7 @@ use ruma::{ use serde_json::value::to_raw_value; use tracing::warn; -use crate::{service::pdu::PduBuilder, services, Error, Ra, Result, Ruma}; +use crate::{service::pdu::PduBuilder, services, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/profile/{userId}/displayname` /// @@ -26,7 +26,7 @@ use crate::{service::pdu::PduBuilder, services, Error, Ra, Result, Ruma}; /// /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_displayname_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -116,7 +116,7 @@ pub(crate) async fn set_displayname_route( /// /// - If user is on another server: Fetches displayname over federation pub(crate) async fn get_displayname_route( - body: Ruma, + body: Ar, ) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() @@ -146,7 +146,7 @@ pub(crate) async fn get_displayname_route( /// /// - Also makes sure other users receive the update using presence EDUs pub(crate) async fn set_avatar_url_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -239,7 +239,7 @@ pub(crate) async fn set_avatar_url_route( /// - If user is on another server: Fetches `avatar_url` and `blurhash` over /// federation pub(crate) async fn get_avatar_url_route( - body: Ruma, + body: Ar, ) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() @@ -271,7 +271,7 @@ pub(crate) async fn get_avatar_url_route( /// /// - If user is on another server: Fetches profile over federation pub(crate) async fn get_profile_route( - body: Ruma, + body: Ar, ) -> Result> { if body.user_id.server_name() != services().globals.server_name() { let response = services() diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index ae41a6d5..39a2cd13 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -11,13 +11,13 @@ use ruma::{ push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `GET /_matrix/client/r0/pushrules` /// /// Retrieves the push rules event for this user. pub(crate) async fn get_pushrules_all_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -46,7 +46,7 @@ pub(crate) async fn get_pushrules_all_route( /// /// Retrieves a single specified push rule for this user. pub(crate) async fn get_pushrule_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -84,7 +84,7 @@ pub(crate) async fn get_pushrule_route( /// /// Creates a single specified push rule for this user. pub(crate) async fn set_pushrule_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; @@ -164,7 +164,7 @@ pub(crate) async fn set_pushrule_route( /// /// Gets the actions of a single specified push rule for this user. pub(crate) async fn get_pushrule_actions_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -209,7 +209,7 @@ pub(crate) async fn get_pushrule_actions_route( /// /// Sets the actions of a single specified push rule for this user. pub(crate) async fn set_pushrule_actions_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -264,7 +264,7 @@ pub(crate) async fn set_pushrule_actions_route( /// /// Gets the enabled status of a single specified push rule for this user. pub(crate) async fn get_pushrule_enabled_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -310,7 +310,7 @@ pub(crate) async fn get_pushrule_enabled_route( /// /// Sets the enabled status of a single specified push rule for this user. pub(crate) async fn set_pushrule_enabled_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -365,7 +365,7 @@ pub(crate) async fn set_pushrule_enabled_route( /// /// Deletes a single specified push rule for this user. pub(crate) async fn delete_pushrule_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -425,7 +425,7 @@ pub(crate) async fn delete_pushrule_route( /// /// Gets all currently active pushers for the sender user. pub(crate) async fn get_pushers_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -440,7 +440,7 @@ pub(crate) async fn get_pushers_route( /// /// - TODO: Handle `append` pub(crate) async fn set_pushers_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index 52eed945..c30a4cb8 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -12,7 +12,7 @@ use ruma::{ }; use crate::{ - service::rooms::timeline::PduCount, services, Error, Ra, Result, Ruma, + service::rooms::timeline::PduCount, services, Ar, Error, Ra, Result, }; /// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers` @@ -23,7 +23,7 @@ use crate::{ /// - If `read_receipt` is set: Update private marker and public read receipt /// EDU pub(crate) async fn set_read_marker_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -104,7 +104,7 @@ pub(crate) async fn set_read_marker_route( /// /// Sets private read marker and public read receipt EDU. pub(crate) async fn create_receipt_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/redact.rs b/src/api/client_server/redact.rs index 5f5395d8..5604bfb0 100644 --- a/src/api/client_server/redact.rs +++ b/src/api/client_server/redact.rs @@ -6,7 +6,7 @@ use ruma::{ }; use serde_json::value::to_raw_value; -use crate::{service::pdu::PduBuilder, services, Ra, Result, Ruma}; +use crate::{service::pdu::PduBuilder, services, Ar, Ra, Result}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}` /// @@ -14,7 +14,7 @@ use crate::{service::pdu::PduBuilder, services, Ra, Result, Ruma}; /// /// - TODO: Handle txn id pub(crate) async fn redact_event_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 7124bc7b..1713fb57 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -6,11 +6,11 @@ use ruma::{ uint, }; -use crate::{service::rooms::timeline::PduCount, services, Ra, Result, Ruma}; +use crate::{service::rooms::timeline::PduCount, services, Ar, Ra, Result}; /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}` pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -54,7 +54,7 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}` pub(crate) async fn get_relating_events_with_rel_type_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -97,7 +97,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route( /// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}` pub(crate) async fn get_relating_events_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index f1102fb5..edf18057 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -4,13 +4,13 @@ use ruma::{ int, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `POST /_matrix/client/r0/rooms/{roomId}/report/{eventId}` /// /// Reports an inappropriate event to homeserver admins pub(crate) async fn report_event_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 15e5613d..87111aa9 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -30,8 +30,8 @@ use serde_json::{json, value::to_raw_value}; use tracing::{info, warn}; use crate::{ - api::client_server::invite_helper, service::pdu::PduBuilder, services, - Error, Ra, Result, Ruma, + api::client_server::invite_helper, service::pdu::PduBuilder, services, Ar, + Error, Ra, Result, }; /// # `POST /_matrix/client/r0/createRoom` @@ -52,7 +52,7 @@ use crate::{ /// - Send invite events #[allow(clippy::too_many_lines)] pub(crate) async fn create_room_route( - body: Ruma, + body: Ar, ) -> Result> { use create_room::v3::RoomPreset; @@ -548,7 +548,7 @@ pub(crate) async fn create_room_route( /// - You have to currently be joined to the room (TODO: Respect history /// visibility) pub(crate) async fn get_room_event_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -585,7 +585,7 @@ pub(crate) async fn get_room_event_route( /// - Only users joined to the room are allowed to call this TODO: Allow any /// user to call it if `history_visibility` is world readable pub(crate) async fn get_room_aliases_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -618,7 +618,7 @@ pub(crate) async fn get_room_aliases_route( /// - Modifies old room power levels to prevent users from speaking #[allow(clippy::too_many_lines)] pub(crate) async fn upgrade_room_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 628a8b2c..111281a2 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -14,7 +14,7 @@ use ruma::{ uint, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `POST /_matrix/client/r0/search` /// @@ -24,7 +24,7 @@ use crate::{services, Error, Ra, Result, Ruma}; /// history visibility) #[allow(clippy::too_many_lines)] pub(crate) async fn search_events_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index ed3b165a..6d740acc 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -16,7 +16,7 @@ use serde::Deserialize; use tracing::{info, warn}; use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; -use crate::{services, utils, Error, Ra, Result, Ruma}; +use crate::{services, utils, Ar, Error, Ra, Result}; #[derive(Debug, Deserialize)] struct Claims { @@ -28,7 +28,7 @@ struct Claims { /// Get the supported login types of this server. One of these should be used as /// the `type` field when logging in. pub(crate) async fn get_login_types_route( - _body: Ruma, + _body: Ar, ) -> Result> { Ok(Ra(get_login_types::v3::Response::new(vec![ get_login_types::v3::LoginType::Password(PasswordLoginType::default()), @@ -53,7 +53,7 @@ pub(crate) async fn get_login_types_route( /// see supported login types. #[allow(clippy::too_many_lines)] pub(crate) async fn login_route( - body: Ruma, + body: Ar, ) -> Result> { // To allow deprecated login methods #![allow(deprecated)] @@ -277,7 +277,7 @@ pub(crate) async fn login_route( /// - Forgets to-device events /// - Triggers device list updates pub(crate) async fn logout_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = @@ -310,7 +310,7 @@ pub(crate) async fn logout_route( /// Note: This is equivalent to calling [`GET /// /_matrix/client/r0/logout`](logout_route) from each device of this user. pub(crate) async fn logout_all_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index 44c0939e..930814e8 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,13 +1,13 @@ use ruma::{api::client::space::get_hierarchy, uint}; -use crate::{services, Ra, Result, Ruma}; +use crate::{services, Ar, Ra, Result}; /// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy` /// /// Paginates over the space tree in a depth-first manner to locate child rooms /// of a given space. pub(crate) async fn get_hierarchy_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 5cbf0b9c..f04ae3fd 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -14,7 +14,7 @@ use ruma::{ }; use tracing::log::warn; -use crate::{service::pdu::PduBuilder, services, Error, Ra, Result, Ruma}; +use crate::{service::pdu::PduBuilder, services, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}` /// @@ -25,7 +25,7 @@ use crate::{service::pdu::PduBuilder, services, Error, Ra, Result, Ruma}; /// allowed /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_key_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -54,7 +54,7 @@ pub(crate) async fn send_state_event_for_key_route( /// allowed /// - If event is new `canonical_alias`: Rejects if alias is incorrect pub(crate) async fn send_state_event_for_empty_key_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -91,7 +91,7 @@ pub(crate) async fn send_state_event_for_empty_key_route( /// - If not joined: Only works if current room history visibility is world /// readable pub(crate) async fn get_state_events_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -125,7 +125,7 @@ pub(crate) async fn get_state_events_route( /// - If not joined: Only works if current room history visibility is world /// readable pub(crate) async fn get_state_events_for_key_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -166,7 +166,7 @@ pub(crate) async fn get_state_events_for_key_route( /// - If not joined: Only works if current room history visibility is world /// readable pub(crate) async fn get_state_events_for_empty_key_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index a1e78f52..cbca2b49 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -29,7 +29,7 @@ use tracing::{debug, error}; use crate::{ service::{pdu::EventHash, rooms::timeline::PduCount}, - services, utils, Error, PduEvent, Ra, Result, Ruma, + services, utils, Ar, Error, PduEvent, Ra, Result, }; /// # `GET /_matrix/client/r0/sync` @@ -73,7 +73,7 @@ use crate::{ /// subset of the state at the point of the leave) #[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_route( - body: Ruma, + body: Ar, ) -> Result, Ra> { let sender_user = body.sender_user.expect("user is authenticated"); let sender_device = body.sender_device.expect("user is authenticated"); @@ -1126,7 +1126,7 @@ fn share_encrypted_room( #[allow(clippy::too_many_lines)] pub(crate) async fn sync_events_v4_route( - body: Ruma, + body: Ar, ) -> Result, Ra> { let sender_user = body.sender_user.expect("user is authenticated"); let sender_device = body.sender_device.expect("user is authenticated"); diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 94a2954a..17367300 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -8,7 +8,7 @@ use ruma::{ }, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}` /// @@ -16,7 +16,7 @@ use crate::{services, Error, Ra, Result, Ruma}; /// /// - Inserts the tag into the tag event of the room account data. pub(crate) async fn update_tag_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -62,7 +62,7 @@ pub(crate) async fn update_tag_route( /// /// - Removes the tag from the tag event of the room account data. pub(crate) async fn delete_tag_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); @@ -105,7 +105,7 @@ pub(crate) async fn delete_tag_route( /// /// - Gets the tag event of the room account data. pub(crate) async fn get_tags_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/thirdparty.rs b/src/api/client_server/thirdparty.rs index 29f2da38..a22fa548 100644 --- a/src/api/client_server/thirdparty.rs +++ b/src/api/client_server/thirdparty.rs @@ -2,13 +2,13 @@ use std::collections::BTreeMap; use ruma::api::client::thirdparty::get_protocols; -use crate::{Ra, Result, Ruma}; +use crate::{Ar, Ra, Result}; /// # `GET /_matrix/client/r0/thirdparty/protocols` /// /// TODO: Fetches all metadata about protocols supported by the homeserver. pub(crate) async fn get_protocols_route( - _body: Ruma, + _body: Ar, ) -> Result> { // TODO Ok(Ra(get_protocols::v3::Response { diff --git a/src/api/client_server/threads.rs b/src/api/client_server/threads.rs index 518a736a..cf96e234 100644 --- a/src/api/client_server/threads.rs +++ b/src/api/client_server/threads.rs @@ -1,10 +1,10 @@ use ruma::api::client::{error::ErrorKind, threads::get_threads}; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `GET /_matrix/client/r0/rooms/{roomId}/threads` pub(crate) async fn get_threads_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index 17427f84..65fb198c 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -8,13 +8,13 @@ use ruma::{ to_device::DeviceIdOrAllDevices, }; -use crate::{services, Error, Ra, Result, Ruma}; +use crate::{services, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}` /// /// Send a to-device event to a set of client devices. pub(crate) async fn send_event_to_device_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index d2226ee1..f6cf9bc0 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -1,12 +1,12 @@ use ruma::api::client::{error::ErrorKind, typing::create_typing_event}; -use crate::{services, utils, Error, Ra, Result, Ruma}; +use crate::{services, utils, Ar, Error, Ra, Result}; /// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}` /// /// Sets the typing state of the sender user. pub(crate) async fn create_typing_event_route( - body: Ruma, + body: Ar, ) -> Result> { use create_typing_event::v3::Typing; diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index bdd8e048..8ea8136c 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, iter::FromIterator}; use ruma::api::client::discovery::get_supported_versions; -use crate::{Ra, Result, Ruma}; +use crate::{Ar, Ra, Result}; /// # `GET /_matrix/client/versions` /// @@ -17,7 +17,7 @@ use crate::{Ra, Result, Ruma}; /// Note: Unstable features are used while developing new features. Clients /// should avoid using unstable features in their stable releases pub(crate) async fn get_supported_versions_route( - _body: Ruma, + _body: Ar, ) -> Result> { let resp = get_supported_versions::Response { versions: vec![ diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 06970f4b..93fbd6cd 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -6,7 +6,7 @@ use ruma::{ }, }; -use crate::{services, Ra, Result, Ruma}; +use crate::{services, Ar, Ra, Result}; /// # `POST /_matrix/client/r0/user_directory/search` /// @@ -16,7 +16,7 @@ use crate::{services, Ra, Result, Ruma}; /// have the join rule set to public) /// and don't share a room with the sender pub(crate) async fn search_users_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let limit = body.limit.try_into().unwrap_or(usize::MAX); diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index 91507004..f24ca81e 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -5,7 +5,7 @@ use hmac::{Hmac, Mac}; use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch}; use sha1::Sha1; -use crate::{services, Ra, Result, Ruma}; +use crate::{services, Ar, Ra, Result}; type HmacSha1 = Hmac; @@ -13,7 +13,7 @@ type HmacSha1 = Hmac; /// /// TODO: Returns information about the recommended turn server. pub(crate) async fn turn_server_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); diff --git a/src/api/ruma_wrapper.rs b/src/api/ruma_wrapper.rs index e0ab3368..f64e29f8 100644 --- a/src/api/ruma_wrapper.rs +++ b/src/api/ruma_wrapper.rs @@ -9,8 +9,12 @@ use crate::{service::appservice::RegistrationInfo, Error}; mod axum; -/// Extractor for Ruma request structs -pub(crate) struct Ruma { +/// A wrapper to convert an Axum request to Ruma data +/// +/// Named so because this converts from **A**xum to **R**uma. See also [`Ra`], +/// which is roughly the inverse of this type. +pub(crate) struct Ar { + /// The Ruma type to deserialize the body into pub(crate) body: T, pub(crate) sender_user: Option, pub(crate) sender_device: Option, @@ -20,7 +24,7 @@ pub(crate) struct Ruma { pub(crate) appservice_info: Option, } -impl Deref for Ruma { +impl Deref for Ar { type Target = T; fn deref(&self) -> &Self::Target { @@ -30,7 +34,7 @@ impl Deref for Ruma { /// A wrapper to convert Ruma data to an Axum response /// -/// Named so because this converts from **R**uma to **A**xum. See also [`Ruma`], +/// Named so because this converts from **R**uma to **A**xum. See also [`Ar`], /// which is roughly the inverse of this type. #[derive(Clone)] pub(crate) struct Ra(pub(crate) T); diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index ef3460c2..f55ffd85 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -24,7 +24,7 @@ use ruma::{ use serde::Deserialize; use tracing::{debug, error, warn}; -use super::{Ra, Ruma}; +use super::{Ar, Ra}; use crate::{service::appservice::RegistrationInfo, services, Error, Result}; enum Token { @@ -35,7 +35,7 @@ enum Token { } #[async_trait] -impl FromRequest for Ruma +impl FromRequest for Ar where T: IncomingRequest, B: HttpBody + Send + 'static, @@ -379,7 +379,7 @@ where ) })?; - Ok(Ruma { + Ok(Ar { body, sender_user, sender_device, diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 23bb3461..4529e02d 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -66,7 +66,7 @@ use tracing::{debug, error, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Error, PduEvent, Ra, Result, Ruma, + services, utils, Ar, Error, PduEvent, Ra, Result, }; /// Wraps either an literal IP address plus port, or a hostname plus complement @@ -553,7 +553,7 @@ async fn request_well_known(destination: &str) -> Option { /// /// Get version information on this server. pub(crate) async fn get_server_version_route( - _body: Ruma, + _body: Ar, ) -> Result> { Ok(Ra(get_server_version::v1::Response { server: Some(get_server_version::v1::Server { @@ -630,7 +630,7 @@ pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { /// /// Lists the public rooms on this server. pub(crate) async fn get_public_rooms_filtered_route( - body: Ruma, + body: Ar, ) -> Result> { let response = client_server::get_public_rooms_filtered_helper( None, @@ -653,7 +653,7 @@ pub(crate) async fn get_public_rooms_filtered_route( /// /// Lists the public rooms on this server. pub(crate) async fn get_public_rooms_route( - body: Ruma, + body: Ar, ) -> Result> { let response = client_server::get_public_rooms_filtered_helper( None, @@ -708,7 +708,7 @@ pub(crate) fn parse_incoming_pdu( /// Push EDUs and PDUs to this server. #[allow(clippy::too_many_lines)] pub(crate) async fn send_transaction_message_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -991,7 +991,7 @@ pub(crate) async fn send_transaction_message_route( /// - Only works if a user of this server is currently invited or joined the /// room pub(crate) async fn get_event_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1047,7 +1047,7 @@ pub(crate) async fn get_event_route( /// Retrieves events from before the sender joined the room, if the room's /// history visibility allows. pub(crate) async fn get_backfill_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1117,7 +1117,7 @@ pub(crate) async fn get_backfill_route( /// /// Retrieves events that the sender is missing. pub(crate) async fn get_missing_events_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1219,7 +1219,7 @@ pub(crate) async fn get_missing_events_route( /// /// - This does not include the event itself pub(crate) async fn get_event_authorization_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1277,7 +1277,7 @@ pub(crate) async fn get_event_authorization_route( /// /// Retrieves the current state of the room. pub(crate) async fn get_room_state_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1347,7 +1347,7 @@ pub(crate) async fn get_room_state_route( /// /// Retrieves the current state of the room. pub(crate) async fn get_room_state_ids_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1402,7 +1402,7 @@ pub(crate) async fn get_room_state_ids_route( /// /// Creates a join template. pub(crate) async fn create_join_event_template_route( - body: Ruma, + body: Ar, ) -> Result> { if !services().rooms.metadata.exists(&body.room_id)? { return Err(Error::BadRequest( @@ -1660,7 +1660,7 @@ async fn create_join_event( /// /// Submits a signed join event. pub(crate) async fn create_join_event_v1_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1677,7 +1677,7 @@ pub(crate) async fn create_join_event_v1_route( /// /// Submits a signed join event. pub(crate) async fn create_join_event_v2_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1705,7 +1705,7 @@ pub(crate) async fn create_join_event_v2_route( /// Invites a remote user to a room. #[allow(clippy::too_many_lines)] pub(crate) async fn create_invite_route( - body: Ruma, + body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); @@ -1836,7 +1836,7 @@ pub(crate) async fn create_invite_route( /// /// Gets information on all devices of the user. pub(crate) async fn get_devices_route( - body: Ruma, + body: Ar, ) -> Result> { if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( @@ -1888,7 +1888,7 @@ pub(crate) async fn get_devices_route( /// /// Resolve a room alias to a room id. pub(crate) async fn get_room_information_route( - body: Ruma, + body: Ar, ) -> Result> { let room_id = services().rooms.alias.resolve_local_alias(&body.room_alias)?.ok_or( @@ -1905,7 +1905,7 @@ pub(crate) async fn get_room_information_route( /// /// Gets information on a profile. pub(crate) async fn get_profile_information_route( - body: Ruma, + body: Ar, ) -> Result> { if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( @@ -1946,7 +1946,7 @@ pub(crate) async fn get_profile_information_route( /// /// Gets devices and identity keys for the given users. pub(crate) async fn get_keys_route( - body: Ruma, + body: Ar, ) -> Result> { if body .device_keys @@ -1975,7 +1975,7 @@ pub(crate) async fn get_keys_route( /// /// Claims one-time keys. pub(crate) async fn claim_keys_route( - body: Ruma, + body: Ar, ) -> Result> { if body .one_time_keys diff --git a/src/main.rs b/src/main.rs index c1a7d9e3..ddfc8e18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,7 @@ mod database; mod service; mod utils; -pub(crate) use api::ruma_wrapper::{Ra, Ruma}; +pub(crate) use api::ruma_wrapper::{Ar, Ra}; use api::{client_server, server_server}; pub(crate) use config::Config; pub(crate) use database::KeyValueDatabase; @@ -580,11 +580,11 @@ macro_rules! impl_ruma_handler { #[axum::async_trait] #[allow(non_snake_case)] impl - RumaHandler<($($ty,)* Ruma,)> for F + RumaHandler<($($ty,)* Ar,)> for F where Req: IncomingRequest + Send + 'static, Resp: IntoResponse, - F: FnOnce($($ty,)* Ruma) -> Fut + Clone + Send + 'static, + F: FnOnce($($ty,)* Ar) -> Fut + Clone + Send + 'static, Fut: Future> + Send, E: IntoResponse, From 6024f866e3d2c1fffd4c6df5124c188dc30e8934 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 17 May 2024 15:11:48 -0700 Subject: [PATCH 123/617] fix mod/use order --- src/service/media.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 899320fe..9d43d607 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -1,7 +1,5 @@ -mod data; use std::io::Cursor; -pub(crate) use data::Data; use image::imageops::FilterType; use tokio::{ fs::File, @@ -10,6 +8,10 @@ use tokio::{ use crate::{services, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct FileMeta { pub(crate) content_disposition: Option, pub(crate) content_type: Option, From a60501189d2f6c5ed8da9e432fec73a2f2e80c86 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 17 May 2024 18:25:27 -0700 Subject: [PATCH 124/617] prevent xss via user-uploaded media Previously, `Content-Disposition` was always set to `inline`, even for HTML, which means that XSS could be easily acheived by uploading malicious HTML and getting someone to click on the Matrix HTTP API link for that piece of media. Now, we have an allowlist of safe values for `Content-Type` that use `inline` while everything else defaults to `attachment`, including HTML and SVG, which prevents XSS. We also set the `Content-Security-Policy` header because why not. A `set_header_or_panic` function is introduced to do what it says in case Ruma begins providing better or worse values for the relevant headers in the future. The safest way to handle such a case is simply to panic. --- Cargo.lock | 49 +++++++ Cargo.toml | 1 + src/api/client_server/media.rs | 226 +++++++++++++++++++++++++++++---- src/service/media.rs | 6 + 4 files changed, 259 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23f938e8..93d9cf08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -811,6 +811,7 @@ dependencies = [ "opentelemetry", "opentelemetry-jaeger", "parking_lot", + "phf", "rand", "regex", "reqwest", @@ -1625,6 +1626,48 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -2452,6 +2495,12 @@ dependencies = [ "time", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" diff --git a/Cargo.toml b/Cargo.toml index 5b37280d..f679e43d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,7 @@ num_cpus = "1.15.0" opentelemetry = { version = "0.18.0", features = ["rt-tokio"] } opentelemetry-jaeger = { version = "0.17.0", features = ["rt-tokio"] } parking_lot = { version = "0.12.1", optional = true } +phf = { version = "0.11.2", features = ["macros"] } rand = "0.8.5" regex = "1.8.1" reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index cbea2009..aed354a0 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -1,5 +1,11 @@ use std::time::Duration; +use axum::response::IntoResponse; +use http::{ + header::{CONTENT_DISPOSITION, CONTENT_SECURITY_POLICY, CONTENT_TYPE}, + HeaderName, HeaderValue, +}; +use phf::{phf_set, Set}; use ruma::api::client::{ error::ErrorKind, media::{ @@ -7,11 +13,104 @@ use ruma::api::client::{ get_content_thumbnail, get_media_config, }, }; +use tracing::error; use crate::{service::media::FileMeta, services, utils, Ar, Error, Ra, Result}; const MXC_LENGTH: usize = 32; +/// `Content-Type`s that can be rendered inline in a browser without risking XSS +/// +/// Cargo-culted from Synapse. Note that SVG can contain inline JavaScript. +static INLINE_CONTENT_TYPES: Set<&str> = phf_set! { + // Keep sorted + "application/json", + "application/ld+json", + "audio/aac", + "audio/flac", + "audio/mp4", + "audio/mpeg", + "audio/ogg", + "audio/wav", + "audio/wave", + "audio/webm", + "audio/x-flac", + "audio/x-pn-wav", + "audio/x-wav", + "image/apng", + "image/avif", + "image/gif", + "image/jpeg", + "image/png", + "image/webp", + "text/css", + "text/csv", + "text/plain", + "video/mp4", + "video/ogg", + "video/quicktime", + "video/webm", +}; + +/// Value for the `Content-Security-Policy` header +/// +/// Cargo-culted from Synapse. +fn content_security_policy() -> HeaderValue { + [ + "sandbox", + "default-src 'none'", + "script-src 'none'", + "plugin-types application/pdf", + "style-src 'unsafe-inline'", + "media-src 'self'", + "object-src 'self'", + ] + .join("; ") + .try_into() + .expect("hardcoded header value should be valid") +} + +/// Determine a `Content-Disposition` header that prevents XSS +// TODO: In some of the places this function is called, we could parse the +// desired filename out of an existing `Content-Disposition` header value, such +// as what we're storing in the database or what we receive over federation. +// Doing this correctly is tricky, so I'm skipping it for now. +fn content_disposition_for( + content_type: Option<&str>, + filename: Option<&str>, +) -> String { + match ( + content_type.is_some_and(|x| INLINE_CONTENT_TYPES.contains(x)), + filename, + ) { + (true, None) => "inline".to_owned(), + (true, Some(x)) => format!("inline; filename={x}"), + (false, None) => "attachment".to_owned(), + (false, Some(x)) => format!("attachment; filename={x}"), + } +} + +/// Set a header, but panic if it was already set +/// +/// # Panics +/// +/// Panics if the header was already set. +fn set_header_or_panic( + response: &mut axum::response::Response, + header_name: HeaderName, + header_value: HeaderValue, +) { + if let Some(header_value) = response.headers().get(&header_name) { + error!(?header_name, ?header_value, "unexpected pre-existing header"); + panic!( + "expected {header_name:?} to be unset but it was set to \ + {header_value:?}" + ); + } + + response.headers_mut().insert(header_name, header_value); +} + /// # `GET /_matrix/media/r0/config` /// /// Returns max upload size. @@ -86,7 +185,12 @@ pub(crate) async fn get_remote_content( ) .await?; - Ok(content_response) + Ok(get_content::v3::Response { + file: content_response.file, + content_disposition: content_response.content_disposition, + content_type: content_response.content_type, + cross_origin_resource_policy: Some("cross-origin".to_owned()), + }) } /// # `GET /_matrix/media/r0/download/{serverName}/{mediaId}` @@ -96,28 +200,55 @@ pub(crate) async fn get_remote_content( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_route( body: Ar, -) -> Result> { +) -> Result { + get_content_route_ruma(body).await.map(|x| { + let mut r = Ra(x).into_response(); + + set_header_or_panic( + &mut r, + CONTENT_SECURITY_POLICY, + content_security_policy(), + ); + + r + }) +} + +async fn get_content_route_ruma( + body: Ar, +) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { - content_disposition, content_type, file, + .. }) = services().media.get(mxc.clone()).await? { - Ok(Ra(get_content::v3::Response { + Ok(get_content::v3::Response { file, + content_disposition: Some(content_disposition_for( + content_type.as_deref(), + None, + )), content_type, - content_disposition, cross_origin_resource_policy: Some("cross-origin".to_owned()), - })) + }) } else if &*body.server_name != services().globals.server_name() && body.allow_remote { let remote_content_response = get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(Ra(remote_content_response)) + Ok(get_content::v3::Response { + file: remote_content_response.file, + content_disposition: Some(content_disposition_for( + remote_content_response.content_type.as_deref(), + None, + )), + content_type: remote_content_response.content_type, + cross_origin_resource_policy: Some("cross-origin".to_owned()), + }) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } @@ -130,7 +261,23 @@ pub(crate) async fn get_content_route( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_as_filename_route( body: Ar, -) -> Result> { +) -> Result { + get_content_as_filename_route_ruma(body).await.map(|x| { + let mut r = Ra(x).into_response(); + + set_header_or_panic( + &mut r, + CONTENT_SECURITY_POLICY, + content_security_policy(), + ); + + r + }) +} + +pub(crate) async fn get_content_as_filename_route_ruma( + body: Ar, +) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -139,15 +286,15 @@ pub(crate) async fn get_content_as_filename_route( .. }) = services().media.get(mxc.clone()).await? { - Ok(Ra(get_content_as_filename::v3::Response { + Ok(get_content_as_filename::v3::Response { file, - content_type, - content_disposition: Some(format!( - "inline; filename={}", - body.filename + content_disposition: Some(content_disposition_for( + content_type.as_deref(), + Some(body.filename.as_str()), )), + content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), - })) + }) } else if &*body.server_name != services().globals.server_name() && body.allow_remote { @@ -155,15 +302,15 @@ pub(crate) async fn get_content_as_filename_route( get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(Ra(get_content_as_filename::v3::Response { - content_disposition: Some(format!( - "inline: filename={}", - body.filename + Ok(get_content_as_filename::v3::Response { + content_disposition: Some(content_disposition_for( + remote_content_response.content_type.as_deref(), + Some(body.filename.as_str()), )), content_type: remote_content_response.content_type, file: remote_content_response.file, cross_origin_resource_policy: Some("cross-origin".to_owned()), - })) + }) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } @@ -176,7 +323,36 @@ pub(crate) async fn get_content_as_filename_route( /// - Only allows federation if `allow_remote` is true pub(crate) async fn get_content_thumbnail_route( body: Ar, -) -> Result> { +) -> Result { + get_content_thumbnail_route_ruma(body).await.map(|x| { + let mut r = Ra(x).into_response(); + + let content_type = r + .headers() + .get(CONTENT_TYPE) + .and_then(|x| std::str::from_utf8(x.as_ref()).ok()) + .map(ToOwned::to_owned); + + set_header_or_panic( + &mut r, + CONTENT_SECURITY_POLICY, + content_security_policy(), + ); + set_header_or_panic( + &mut r, + CONTENT_DISPOSITION, + content_disposition_for(content_type.as_deref(), None) + .try_into() + .expect("generated header value should be valid"), + ); + + r + }) +} + +async fn get_content_thumbnail_route_ruma( + body: Ar, +) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -196,11 +372,11 @@ pub(crate) async fn get_content_thumbnail_route( ) .await? { - Ok(Ra(get_content_thumbnail::v3::Response { + Ok(get_content_thumbnail::v3::Response { file, content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), - })) + }) } else if &*body.server_name != services().globals.server_name() && body.allow_remote { @@ -233,7 +409,11 @@ pub(crate) async fn get_content_thumbnail_route( ) .await?; - Ok(Ra(get_thumbnail_response)) + Ok(get_content_thumbnail::v3::Response { + file: get_thumbnail_response.file, + content_type: get_thumbnail_response.content_type, + cross_origin_resource_policy: Some("cross-origin".to_owned()), + }) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } diff --git a/src/service/media.rs b/src/service/media.rs index 9d43d607..f5127842 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -13,7 +13,13 @@ mod data; pub(crate) use data::Data; pub(crate) struct FileMeta { + // This gets written to the database but we no longer read it + // + // TODO: Write a database migration to get rid of this and instead store + // only the filename instead of the entire `Content-Disposition` header. + #[allow(dead_code)] pub(crate) content_disposition: Option, + pub(crate) content_type: Option, pub(crate) file: Vec, } From 092315e2cd956d12cf26d3e4abf69fa1b1764794 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 20 May 2024 16:49:20 +0000 Subject: [PATCH 125/617] remove unnecessary async and select!{} --- src/database.rs | 4 +-- src/service/admin.rs | 77 +++++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/database.rs b/src/database.rs index ffbf6ef2..4a3ca3ff 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1165,7 +1165,7 @@ impl KeyValueDatabase { services().sending.start_handler(); - Self::start_cleanup_task().await; + Self::start_cleanup_task(); Ok(()) } @@ -1182,7 +1182,7 @@ impl KeyValueDatabase { } #[tracing::instrument] - pub(crate) async fn start_cleanup_task() { + pub(crate) fn start_cleanup_task() { use std::time::{Duration, Instant}; #[cfg(unix)] diff --git a/src/service/admin.rs b/src/service/admin.rs index 562d0ec3..6d1386cc 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -239,45 +239,48 @@ impl Service { if let Ok(Some(grapevine_room)) = services().admin.get_admin_room() { loop { - tokio::select! { - Some(event) = receiver.recv() => { - let message_content = match event { - AdminRoomEvent::SendMessage(content) => content, - AdminRoomEvent::ProcessMessage(room_message) => { - self.process_admin_message(room_message).await - } - }; + let event = receiver + .recv() + .await + .expect("admin command channel has been closed"); - let mutex_state = Arc::clone( - services().globals - .roomid_mutex_state - .write() - .await - .entry(grapevine_room.clone()) - .or_default(), - ); - - let state_lock = mutex_state.lock().await; - - services() - .rooms - .timeline - .build_and_append_pdu( - PduBuilder { - event_type: TimelineEventType::RoomMessage, - content: to_raw_value(&message_content) - .expect("event is valid, we just created it"), - unsigned: None, - state_key: None, - redacts: None, - }, - &grapevine_user, - &grapevine_room, - &state_lock, - ) - .await.unwrap(); + let message_content = match event { + AdminRoomEvent::SendMessage(content) => content, + AdminRoomEvent::ProcessMessage(room_message) => { + self.process_admin_message(room_message).await } - } + }; + + let mutex_state = Arc::clone( + services() + .globals + .roomid_mutex_state + .write() + .await + .entry(grapevine_room.clone()) + .or_default(), + ); + + let state_lock = mutex_state.lock().await; + + services() + .rooms + .timeline + .build_and_append_pdu( + PduBuilder { + event_type: TimelineEventType::RoomMessage, + content: to_raw_value(&message_content) + .expect("event is valid, we just created it"), + unsigned: None, + state_key: None, + redacts: None, + }, + &grapevine_user, + &grapevine_room, + &state_lock, + ) + .await + .unwrap(); } } } From 5e9e5b76bc1534ece816f7c91c715bb3fa7c0b80 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 19 May 2024 18:06:13 +0000 Subject: [PATCH 126/617] service/sending: factor out closures into methods --- src/service/sending.rs | 207 ++++++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 96 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 1cef2ab2..832a6f87 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -108,6 +108,14 @@ enum TransactionStatus { Retrying(u32), } +struct HandlerInputs { + kind: OutgoingKind, + events: Vec, +} +type HandlerResponse = Result; + +type TransactionStatusMap = HashMap; + impl Service { pub(crate) fn build(db: &'static dyn Data, config: &Config) -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); @@ -128,14 +136,12 @@ impl Service { }); } - #[allow(clippy::too_many_lines)] async fn handler(&self) -> Result<()> { let mut receiver = self.receiver.lock().await; let mut futures = FuturesUnordered::new(); - let mut current_transaction_status = - HashMap::::new(); + let mut current_transaction_status = TransactionStatusMap::new(); // Retry requests we could not finish yet let mut initial_transactions = @@ -165,106 +171,115 @@ impl Service { futures.push(Self::handle_events(outgoing_kind.clone(), events)); } - let handle_futures = - |response, - current_transaction_status: &mut HashMap<_, _>, - futures: &mut FuturesUnordered<_>| { - match response { - Ok(outgoing_kind) => { - self.db - .delete_all_active_requests_for(&outgoing_kind)?; - - // Find events that have been added since starting the - // last request - let new_events = self - .db - .queued_requests(&outgoing_kind) - .filter_map(Result::ok) - .take(30) - .collect::>(); - - if new_events.is_empty() { - current_transaction_status.remove(&outgoing_kind); - } else { - // Insert pdus we found - self.db.mark_as_active(&new_events)?; - - futures.push(Self::handle_events( - outgoing_kind.clone(), - new_events - .into_iter() - .map(|(event, _)| event) - .collect(), - )); - } - } - Err((outgoing_kind, _)) => { - current_transaction_status - .entry(outgoing_kind) - .and_modify(|e| { - *e = match e { - TransactionStatus::Running => { - TransactionStatus::Failed( - 1, - Instant::now(), - ) - } - TransactionStatus::Retrying(n) => { - TransactionStatus::Failed( - *n + 1, - Instant::now(), - ) - } - TransactionStatus::Failed(..) => { - error!( - "Request that was not even \ - running failed?!" - ); - return; - } - } - }); - } - }; - - Result::<_>::Ok(()) - }; - - let handle_receiver = - |outgoing_kind, - event, - key, - current_transaction_status: &mut HashMap<_, _>, - futures: &mut FuturesUnordered<_>| { - if let Ok(Some(events)) = self.select_events( - &outgoing_kind, - vec![(event, key)], - current_transaction_status, - ) { - futures.push(Self::handle_events(outgoing_kind, events)); - } - }; - loop { select! { Some(response) = futures.next() => - handle_futures( - response, - &mut current_transaction_status, - &mut futures, - )?, + if let Some(HandlerInputs { kind, events }) = + self.handle_futures( + response, + &mut current_transaction_status, + )? + { + futures.push(Self::handle_events(kind, events)); + }, Some((outgoing_kind, event, key)) = receiver.recv() => - handle_receiver( - outgoing_kind, - event, - key, - &mut current_transaction_status, - &mut futures, - ), + if let Some(HandlerInputs { kind, events }) = + self.handle_receiver( + outgoing_kind, + event, + key, + &mut current_transaction_status, + ) + { + futures.push(Self::handle_events(kind, events)); + } } } } + fn handle_futures( + &self, + response: HandlerResponse, + current_transaction_status: &mut TransactionStatusMap, + ) -> Result> { + match response { + Ok(outgoing_kind) => { + self.db.delete_all_active_requests_for(&outgoing_kind)?; + + // Find events that have been added since starting the + // last request + let new_events = self + .db + .queued_requests(&outgoing_kind) + .filter_map(Result::ok) + .take(30) + .collect::>(); + + if new_events.is_empty() { + current_transaction_status.remove(&outgoing_kind); + Ok(None) + } else { + // Insert pdus we found + self.db.mark_as_active(&new_events)?; + + Ok(Some(HandlerInputs { + kind: outgoing_kind.clone(), + events: new_events + .into_iter() + .map(|(event, _)| event) + .collect(), + })) + } + } + Err((outgoing_kind, _)) => { + current_transaction_status.entry(outgoing_kind).and_modify( + |e| { + *e = match e { + TransactionStatus::Running => { + TransactionStatus::Failed(1, Instant::now()) + } + TransactionStatus::Retrying(n) => { + TransactionStatus::Failed( + *n + 1, + Instant::now(), + ) + } + TransactionStatus::Failed(..) => { + error!( + "Request that was not even running \ + failed?!" + ); + return; + } + } + }, + ); + Ok(None) + } + } + } + + fn handle_receiver( + &self, + outgoing_kind: OutgoingKind, + event: SendingEventType, + key: Vec, + current_transaction_status: &mut TransactionStatusMap, + ) -> Option { + if let Ok(Some(events)) = self.select_events( + &outgoing_kind, + vec![(event, key)], + current_transaction_status, + ) { + Some(HandlerInputs { + kind: outgoing_kind, + events, + }) + } else { + None + } + } + #[tracing::instrument(skip( self, outgoing_kind, @@ -566,7 +581,7 @@ impl Service { async fn handle_events( kind: OutgoingKind, events: Vec, - ) -> Result { + ) -> HandlerResponse { match &kind { OutgoingKind::Appservice(id) => { let mut pdu_jsons = Vec::new(); From ac42e0bfff6af8677636a3dc1a56701a3255071d Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 19 May 2024 21:39:13 +0000 Subject: [PATCH 127/617] Fix spans in tokio::spawn-ed tasks tokio::spawn is a span boundary, the spawned future has no parent span. For short futures, we simply inherit the current span with `.in_current_span()`. For long running futures containing a sleeping infinite loop, we don't actually want a span on the entire task or even the entire loop body, both would result in very long spans. Instead, we put the outermost span (created using #[tracing::instrument] or .instrument()) around the actual work happening after the sleep, which results in a new root span being created after every sleep. --- src/database.rs | 33 ++++++----- src/service/admin.rs | 125 +++++++++++++++++++++++------------------ src/service/sending.rs | 21 +++++++ 3 files changed, 109 insertions(+), 70 deletions(-) diff --git a/src/database.rs b/src/database.rs index 4a3ca3ff..d32b57f5 100644 --- a/src/database.rs +++ b/src/database.rs @@ -22,7 +22,7 @@ use ruma::{ CanonicalJsonValue, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, }; -use tracing::{debug, error, info, warn}; +use tracing::{debug, error, info, info_span, warn, Instrument}; use crate::{ service::rooms::timeline::PduCount, services, utils, Config, Error, @@ -1200,26 +1200,31 @@ impl KeyValueDatabase { loop { #[cfg(unix)] - tokio::select! { - _ = i.tick() => { + let msg = tokio::select! { + _ = i.tick() => || { debug!("cleanup: Timer ticked"); - } - _ = s.recv() => { + }, + _ = s.recv() => || { debug!("cleanup: Received SIGHUP"); - } + }, }; #[cfg(not(unix))] - { + let msg = { i.tick().await; - debug!("cleanup: Timer ticked") - } + || debug!("cleanup: Timer ticked") + }; - let start = Instant::now(); - if let Err(e) = services().globals.cleanup() { - error!("cleanup: Errored: {}", e); - } else { - debug!("cleanup: Finished in {:?}", start.elapsed()); + async { + msg(); + let start = Instant::now(); + if let Err(e) = services().globals.cleanup() { + error!("cleanup: Errored: {}", e); + } else { + debug!("cleanup: Finished in {:?}", start.elapsed()); + } } + .instrument(info_span!("database_cleanup")) + .await; } }); } diff --git a/src/service/admin.rs b/src/service/admin.rs index 6d1386cc..5d191fae 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -218,71 +218,84 @@ impl Service { pub(crate) fn start_handler(self: &Arc) { let self2 = Arc::clone(self); tokio::spawn(async move { - self2.handler().await; - }); - } + let mut receiver = self2.receiver.lock().await; + let grapevine_user = UserId::parse(format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + )) + .expect("admin bot username should be valid"); - async fn handler(&self) { - let mut receiver = self.receiver.lock().await; - // TODO: Use futures when we have long admin commands + let Ok(Some(grapevine_room)) = services().admin.get_admin_room() + else { + return; + }; - let grapevine_user = UserId::parse(format!( - "@{}:{}", - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name() - )) - .expect("admin bot username should be valid"); - - if let Ok(Some(grapevine_room)) = services().admin.get_admin_room() { loop { let event = receiver .recv() .await .expect("admin command channel has been closed"); - let message_content = match event { - AdminRoomEvent::SendMessage(content) => content, - AdminRoomEvent::ProcessMessage(room_message) => { - self.process_admin_message(room_message).await - } - }; - - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(grapevine_room.clone()) - .or_default(), - ); - - let state_lock = mutex_state.lock().await; - - services() - .rooms - .timeline - .build_and_append_pdu( - PduBuilder { - event_type: TimelineEventType::RoomMessage, - content: to_raw_value(&message_content) - .expect("event is valid, we just created it"), - unsigned: None, - state_key: None, - redacts: None, - }, - &grapevine_user, - &grapevine_room, - &state_lock, - ) - .await - .unwrap(); + Self::handle_event( + &self2, + event, + &grapevine_room, + &grapevine_user, + ) + .await; } - } + }); + } + + #[tracing::instrument(skip(self, grapevine_room, grapevine_user))] + async fn handle_event( + &self, + event: AdminRoomEvent, + grapevine_room: &OwnedRoomId, + grapevine_user: &ruma::OwnedUserId, + ) { + let message_content = match event { + AdminRoomEvent::SendMessage(content) => content, + AdminRoomEvent::ProcessMessage(room_message) => { + self.process_admin_message(room_message).await + } + }; + + let mutex_state = Arc::clone( + services() + .globals + .roomid_mutex_state + .write() + .await + .entry(grapevine_room.clone()) + .or_default(), + ); + + let state_lock = mutex_state.lock().await; + + services() + .rooms + .timeline + .build_and_append_pdu( + PduBuilder { + event_type: TimelineEventType::RoomMessage, + content: to_raw_value(&message_content) + .expect("event is valid, we just created it"), + unsigned: None, + state_key: None, + redacts: None, + }, + grapevine_user, + grapevine_room, + &state_lock, + ) + .await + .unwrap(); } pub(crate) fn process_message(&self, room_message: String) { diff --git a/src/service/sending.rs b/src/service/sending.rs index 832a6f87..fd4eb2c9 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -100,6 +100,7 @@ pub(crate) struct Service { >, } +#[derive(Debug)] enum TransactionStatus { Running, // number of times failed, time of last failure @@ -114,6 +115,12 @@ struct HandlerInputs { } type HandlerResponse = Result; +fn outgoing_kind_from_response(response: &HandlerResponse) -> &OutgoingKind { + match response { + Ok(kind) | Err((kind, _)) => kind, + } +} + type TransactionStatusMap = HashMap; impl Service { @@ -197,6 +204,14 @@ impl Service { } } + #[tracing::instrument( + skip(self, current_transaction_status), + fields( + current_status = ?current_transaction_status.get( + outgoing_kind_from_response(&response) + ), + ), + )] fn handle_futures( &self, response: HandlerResponse, @@ -259,6 +274,12 @@ impl Service { } } + #[tracing::instrument( + skip(self, event, key, current_transaction_status), + fields( + current_status = ?current_transaction_status.get(&outgoing_kind), + ), + )] fn handle_receiver( &self, outgoing_kind: OutgoingKind, From 5a178ba393b112b268c59f1fcf24d1d8b1ed9956 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 19 May 2024 18:28:11 +0000 Subject: [PATCH 128/617] Add comment to task spawner middleware --- src/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.rs b/src/main.rs index ddfc8e18..25fa6ff0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -274,6 +274,10 @@ async fn run_server() -> io::Result<()> { Ok(()) } +/// Ensures the request runs in a new tokio thread. +/// +/// The axum request handler task gets cancelled if the connection is shut down; +/// by spawning our own task, processing continue after the client disconnects. async fn spawn_task( req: axum::http::Request, next: axum::middleware::Next, From 9dbc7d92e2c87300bbf0192367743c4d2e7d4026 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 20 May 2024 16:49:42 +0000 Subject: [PATCH 129/617] utils: add helper for adding unbounded slices to tracing spans --- src/utils.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 0b40acbb..8d9b6764 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ pub(crate) mod error; use std::{ - cmp, + cmp, fmt, str::FromStr, time::{SystemTime, UNIX_EPOCH}, }; @@ -170,3 +170,42 @@ pub(crate) fn deserialize_from_str< } deserializer.deserialize_str(Visitor(std::marker::PhantomData)) } + +/// Debug-formats the given slice, but only up to the first `max_len` elements. +/// Any further elements are replaced by an ellipsis. +/// +/// See also [`debug_slice_truncated()`], +pub(crate) struct TruncatedDebugSlice<'a, T> { + inner: &'a [T], + max_len: usize, +} + +impl fmt::Debug for TruncatedDebugSlice<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.inner.len() <= self.max_len { + write!(f, "{:?}", self.inner) + } else { + f.debug_list() + .entries(&self.inner[..self.max_len]) + .entry(&"...") + .finish() + } + } +} + +/// See [`TruncatedDebugSlice`]. Useful for `#[instrument]`: +/// +/// ```ignore +/// #[tracing::instrument(fields( +/// foos = debug_slice_truncated(foos, N) +/// ))] +/// ``` +pub(crate) fn debug_slice_truncated( + slice: &[T], + max_len: usize, +) -> tracing::field::DebugValue> { + tracing::field::debug(TruncatedDebugSlice { + inner: slice, + max_len, + }) +} From c0ce2ebbf8b45b7b49563f1f62296c1ed623b19d Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 19 May 2024 20:38:39 +0000 Subject: [PATCH 130/617] Add utility function for truncating strings in logging --- src/utils.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 8d9b6764..bbf53631 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,7 @@ pub(crate) mod error; use std::{ + borrow::Cow, cmp, fmt, str::FromStr, time::{SystemTime, UNIX_EPOCH}, @@ -209,3 +210,44 @@ pub(crate) fn debug_slice_truncated( max_len, }) } + +/// Truncates a string to an approximate maximum length, replacing any extra +/// text with an ellipsis. +/// +/// Only to be used for debug logging, exact semantics are unspecified. +pub(crate) fn truncate_str_for_debug( + s: &str, + mut max_len: usize, +) -> Cow<'_, str> { + while max_len < s.len() && !s.is_char_boundary(max_len) { + max_len += 1; + } + + if s.len() <= max_len { + s.into() + } else { + #[allow(clippy::string_slice)] // we checked it's at a char boundary + format!("{}...", &s[..max_len]).into() + } +} + +#[cfg(test)] +mod tests { + use crate::utils::truncate_str_for_debug; + + #[test] + fn test_truncate_str_for_debug() { + assert_eq!(truncate_str_for_debug("short", 10), "short"); + assert_eq!( + truncate_str_for_debug("very long string", 10), + "very long ..." + ); + assert_eq!(truncate_str_for_debug("no info, only dots", 0), "..."); + assert_eq!(truncate_str_for_debug("", 0), ""); + assert_eq!(truncate_str_for_debug("unicöde", 5), "unicö..."); + let ok_hand = "👌🏽"; + assert_eq!(truncate_str_for_debug(ok_hand, 1), "👌..."); + assert_eq!(truncate_str_for_debug(ok_hand, ok_hand.len() - 1), "👌🏽"); + assert_eq!(truncate_str_for_debug(ok_hand, ok_hand.len()), "👌🏽"); + } +} From 5172f66c1a90e0e97b67be2897ae59fbc00208a4 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 19 May 2024 19:31:28 +0000 Subject: [PATCH 131/617] More useful tracing spans --- src/service/admin.rs | 30 ++++++++++++++++++++++++--- src/service/appservice.rs | 6 ++++++ src/service/globals.rs | 24 ++++++++++++++------- src/service/media.rs | 4 ++++ src/service/pdu.rs | 2 +- src/service/rooms/auth_chain.rs | 9 +++++--- src/service/rooms/edus/typing.rs | 6 ++++++ src/service/rooms/event_handler.rs | 6 ++++++ src/service/rooms/pdu_metadata.rs | 2 ++ src/service/rooms/spaces.rs | 3 +++ src/service/rooms/state.rs | 18 +++++++++++++++- src/service/rooms/state_accessor.rs | 11 ++++++++++ src/service/rooms/state_compressor.rs | 1 + src/service/sending.rs | 26 +++++++++++++---------- 14 files changed, 121 insertions(+), 27 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 5d191fae..bd14e045 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -38,11 +38,12 @@ use tracing::warn; use super::pdu::PduBuilder; use crate::{ api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH}, - services, utils, Error, PduEvent, Result, + services, + utils::{self, truncate_str_for_debug}, + Error, PduEvent, Result, }; -#[cfg_attr(test, derive(Debug))] -#[derive(Parser)] +#[derive(Debug, Parser)] #[command(name = "@grapevine:server.name:", version = env!("CARGO_PKG_VERSION"))] enum AdminCommand { #[command(verbatim_doc_comment)] @@ -298,10 +299,17 @@ impl Service { .unwrap(); } + #[tracing::instrument( + skip(self, room_message), + fields( + room_message = truncate_str_for_debug(&room_message, 50).as_ref(), + ), + )] pub(crate) fn process_message(&self, room_message: String) { self.sender.send(AdminRoomEvent::ProcessMessage(room_message)).unwrap(); } + #[tracing::instrument(skip(self, message_content))] pub(crate) fn send_message( &self, message_content: RoomMessageEventContent, @@ -310,6 +318,12 @@ impl Service { } // Parse and process a message from the admin room + #[tracing::instrument( + skip(self, room_message), + fields( + room_message = truncate_str_for_debug(&room_message, 50).as_ref(), + ), + )] async fn process_admin_message( &self, room_message: String, @@ -355,6 +369,12 @@ impl Service { } // Parse chat messages from the admin room into an AdminCommand object + #[tracing::instrument( + skip(command_line), + fields( + command_line = truncate_str_for_debug(command_line, 50).as_ref(), + ), + )] fn parse_admin_command( command_line: &str, ) -> std::result::Result { @@ -380,6 +400,7 @@ impl Service { } #[allow(clippy::too_many_lines)] + #[tracing::instrument(skip(self, body))] async fn process_admin_command( &self, command: AdminCommand, @@ -1067,6 +1088,7 @@ impl Service { } // Utility to turn clap's `--help` text to HTML. + #[tracing::instrument(skip_all)] fn usage_to_html(text: &str, server_name: &ServerName) -> String { // Replace `@grapevine:servername:-subcmdname` with // `@grapevine:servername: subcmdname` @@ -1151,6 +1173,7 @@ impl Service { /// be used to issue admin commands by talking to the server user inside /// it. #[allow(clippy::too_many_lines)] + #[tracing::instrument(skip(self))] pub(crate) async fn create_admin_room(&self) -> Result<()> { let room_id = RoomId::new(services().globals.server_name()); @@ -1435,6 +1458,7 @@ impl Service { /// Invite the user to the grapevine admin room. /// /// In grapevine, this is equivalent to granting admin privileges. + #[tracing::instrument(skip(self))] pub(crate) async fn make_user_admin( &self, user_id: &UserId, diff --git a/src/service/appservice.rs b/src/service/appservice.rs index 8abc7735..ff0b9785 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -136,6 +136,7 @@ impl Service { } /// Registers an appservice and returns the ID to the caller. + #[tracing::instrument(skip(self, yaml), fields(appservice_id = yaml.id))] pub(crate) async fn register_appservice( &self, yaml: Registration, @@ -154,6 +155,7 @@ impl Service { /// # Arguments /// /// * `service_name` - the name you send to register the service previously + #[tracing::instrument(skip(self))] pub(crate) async fn unregister_appservice( &self, service_name: &str, @@ -171,6 +173,7 @@ impl Service { self.db.unregister_appservice(service_name) } + #[tracing::instrument(skip(self))] pub(crate) async fn get_registration( &self, id: &str, @@ -187,6 +190,7 @@ impl Service { self.registration_info.read().await.keys().cloned().collect() } + #[tracing::instrument(skip(self))] pub(crate) async fn find_from_token( &self, token: &str, @@ -199,6 +203,7 @@ impl Service { } // Checks if a given user id matches any exclusive appservice regex + #[tracing::instrument(skip(self), ret(level = "trace"))] pub(crate) async fn is_exclusive_user_id(&self, user_id: &UserId) -> bool { self.read() .await @@ -207,6 +212,7 @@ impl Service { } // Checks if a given room alias matches any exclusive appservice regex + #[tracing::instrument(skip(self), ret(level = "trace"))] pub(crate) async fn is_exclusive_alias(&self, alias: &RoomAliasId) -> bool { self.read() .await diff --git a/src/service/globals.rs b/src/service/globals.rs index a97f08a9..131610b4 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -29,7 +29,7 @@ use ruma::{ OwnedServerSigningKeyId, RoomVersionId, ServerName, UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; -use tracing::{error, info}; +use tracing::{error, info, Instrument}; use trust_dns_resolver::TokioAsyncResolver; use crate::{api::server_server::FedDest, services, Config, Error, Result}; @@ -126,6 +126,7 @@ impl Resolver { } impl Resolve for Resolver { + #[tracing::instrument(skip(self))] fn resolve(&self, name: Name) -> Resolving { self.overrides .read() @@ -144,18 +145,25 @@ impl Resolve for Resolver { }) .unwrap_or_else(|| { let this = &mut self.inner.clone(); - Box::pin(HyperService::::call(this, name).map(|result| { - result.map(|addrs| -> Addrs { Box::new(addrs) }).map_err( - |err| -> Box { - Box::new(err) - }, - ) - })) + Box::pin( + HyperService::::call(this, name) + .map(|result| { + result + .map(|addrs| -> Addrs { Box::new(addrs) }) + .map_err( + |err| -> Box { + Box::new(err) + }, + ) + }) + .in_current_span(), + ) }) } } impl Service { + #[tracing::instrument(skip_all)] pub(crate) fn load(db: &'static dyn Data, config: Config) -> Result { let keypair = db.load_keypair(); diff --git a/src/service/media.rs b/src/service/media.rs index f5127842..c326575f 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -30,6 +30,7 @@ pub(crate) struct Service { impl Service { /// Uploads a file. + #[tracing::instrument(skip(self, file))] pub(crate) async fn create( &self, mxc: String, @@ -54,6 +55,7 @@ impl Service { /// Uploads or replaces a file thumbnail. #[allow(clippy::too_many_arguments)] + #[tracing::instrument(skip(self, file))] pub(crate) async fn upload_thumbnail( &self, mxc: String, @@ -79,6 +81,7 @@ impl Service { } /// Downloads a file. + #[tracing::instrument(skip(self))] pub(crate) async fn get(&self, mxc: String) -> Result> { if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc, 0, 0) @@ -129,6 +132,7 @@ impl Service { /// For width,height <= 96 the server uses another thumbnailing algorithm /// which crops the image afterwards. #[allow(clippy::too_many_lines)] + #[tracing::instrument(skip(self))] pub(crate) async fn get_thumbnail( &self, mxc: String, diff --git a/src/service/pdu.rs b/src/service/pdu.rs index a5296ceb..46dafa2e 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -351,7 +351,7 @@ impl PduEvent { } /// This does not return a full `Pdu` it is only to satisfy ruma's types. - #[tracing::instrument] + #[tracing::instrument(skip(pdu_json))] pub(crate) fn convert_to_outgoing_federation_event( mut pdu_json: CanonicalJsonObject, ) -> Box { diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 2a601830..e7383e1f 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -8,7 +8,7 @@ pub(crate) use data::Data; use ruma::{api::client::error::ErrorKind, EventId, RoomId}; use tracing::{debug, error, warn}; -use crate::{services, Error, Result}; +use crate::{services, utils::debug_slice_truncated, Error, Result}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -31,7 +31,10 @@ impl Service { self.db.cache_auth_chain(key, auth_chain) } - #[tracing::instrument(skip(self, starting_events))] + #[tracing::instrument( + skip(self, starting_events), + fields(starting_events = debug_slice_truncated(&starting_events, 5)), + )] pub(crate) async fn get_auth_chain<'a>( &self, room_id: &RoomId, @@ -138,7 +141,7 @@ impl Service { })) } - #[tracing::instrument(skip(self, event_id))] + #[tracing::instrument(skip(self))] fn get_auth_chain_inner( &self, room_id: &RoomId, diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 52761d69..49b5de95 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -21,6 +21,7 @@ pub(crate) struct Service { impl Service { /// Sets a user as typing until the timeout timestamp is reached or /// `roomtyping_remove` is called. + #[tracing::instrument(skip(self))] pub(crate) async fn typing_add( &self, user_id: &UserId, @@ -47,6 +48,7 @@ impl Service { } /// Removes a user from typing before the timeout is reached. + #[tracing::instrument(skip(self))] pub(crate) async fn typing_remove( &self, user_id: &UserId, @@ -71,6 +73,7 @@ impl Service { Ok(()) } + #[tracing::instrument(skip(self))] pub(crate) async fn wait_for_update(&self, room_id: &RoomId) -> Result<()> { let mut receiver = self.typing_update_sender.subscribe(); while let Ok(next) = receiver.recv().await { @@ -83,6 +86,7 @@ impl Service { } /// Makes sure that typing events with old timestamps get removed. + #[tracing::instrument(skip(self, room_id))] async fn typings_maintain(&self, room_id: &RoomId) -> Result<()> { let current_timestamp = utils::millis_since_unix_epoch(); let mut removable = Vec::new(); @@ -119,6 +123,7 @@ impl Service { } /// Returns the count of the last typing update in this room. + #[tracing::instrument(skip(self))] pub(crate) async fn last_typing_update( &self, room_id: &RoomId, @@ -134,6 +139,7 @@ impl Service { } /// Returns a new typing EDU. + #[tracing::instrument(skip(self))] pub(crate) async fn typings_all( &self, room_id: &RoomId, diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 48c40be1..3faf8f93 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1085,6 +1085,7 @@ impl Service { Ok(pdu_id) } + #[tracing::instrument(skip(self, room_version_id, incoming_state))] async fn resolve_state( &self, room_id: &RoomId, @@ -1397,6 +1398,8 @@ impl Service { }) } + #[tracing::instrument(skip_all)] + #[allow(clippy::type_complexity)] async fn fetch_unknown_prev_events( &self, origin: &ServerName, @@ -1558,6 +1561,7 @@ impl Service { // Gets a list of servers for which we don't have the signing key yet. We go // over the PDUs and either cache the key or add it to the list that // needs to be retrieved. + #[tracing::instrument(skip_all)] async fn get_server_keys_from_cache( &self, pdu: &RawJsonValue, @@ -1797,6 +1801,7 @@ impl Service { /// Returns Ok if the acl allows the server // Allowed because this function uses `services()` #[allow(clippy::unused_self)] + #[tracing::instrument(skip_all)] pub(crate) fn acl_check( &self, server_name: &ServerName, @@ -2010,6 +2015,7 @@ impl Service { Err(Error::BadServerResponse("Failed to find public key for server")) } + #[tracing::instrument(skip_all)] fn check_room_id(room_id: &RoomId, pdu: &PduEvent) -> Result<()> { if pdu.room_id != room_id { warn!("Found event from room {} in room {}", pdu.room_id, room_id); diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index d8acc698..7dcbbe84 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -51,6 +51,7 @@ impl Service { // Allowed because this function uses `services()` clippy::unused_self, )] + #[tracing::instrument(skip(self))] pub(crate) fn paginate_relations_with_filter( &self, sender_user: &UserId, @@ -178,6 +179,7 @@ impl Service { } } + #[tracing::instrument(skip_all)] pub(crate) fn relations_until<'a>( &'a self, user_id: &'a UserId, diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 854d6d44..6dccafed 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -51,6 +51,7 @@ pub(crate) struct Service { impl Service { #[allow(clippy::too_many_lines)] + #[tracing::instrument(skip(self))] pub(crate) async fn get_hierarchy( &self, sender_user: &UserId, @@ -326,6 +327,7 @@ impl Service { } #[allow(clippy::too_many_lines)] + #[tracing::instrument(skip(self, sender_user, children))] fn get_room_chunk( &self, sender_user: &UserId, @@ -525,6 +527,7 @@ impl Service { Ok(allowed) } + #[tracing::instrument(skip(self, sender_user))] fn handle_join_rule( &self, join_rule: &JoinRule, diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 07e5fc5e..a62f2394 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -20,7 +20,11 @@ use tokio::sync::MutexGuard; use tracing::warn; use super::state_compressor::CompressedStateEvent; -use crate::{services, utils::calculate_hash, Error, PduEvent, Result}; +use crate::{ + services, + utils::{calculate_hash, debug_slice_truncated}, + Error, PduEvent, Result, +}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -28,6 +32,12 @@ pub(crate) struct Service { impl Service { /// Set the room to the given statehash and update caches. + #[tracing::instrument(skip( + self, + statediffnew, + _statediffremoved, + state_lock + ))] pub(crate) async fn force_state( &self, room_id: &RoomId, @@ -354,6 +364,7 @@ impl Service { Ok(create_event_content.room_version) } + #[tracing::instrument(skip(self))] pub(crate) fn get_room_shortstatehash( &self, room_id: &RoomId, @@ -361,6 +372,7 @@ impl Service { self.db.get_room_shortstatehash(room_id) } + #[tracing::instrument(skip(self))] pub(crate) fn get_forward_extremities( &self, room_id: &RoomId, @@ -368,6 +380,10 @@ impl Service { self.db.get_forward_extremities(room_id) } + #[tracing::instrument( + skip(self, event_ids, state_lock), + fields(event_ids = debug_slice_truncated(&event_ids, 5)), + )] pub(crate) fn set_forward_extremities( &self, room_id: &RoomId, diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index d980b3bf..18d8e61e 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -47,6 +47,7 @@ impl Service { self.db.state_full_ids(shortstatehash).await } + #[tracing::instrument(skip(self))] pub(crate) async fn state_full( &self, shortstatehash: u64, @@ -68,6 +69,7 @@ impl Service { /// Returns a single PDU from `room_id` with key (`event_type`, /// `state_key`). + #[tracing::instrument(skip(self))] pub(crate) fn state_get( &self, shortstatehash: u64, @@ -78,6 +80,7 @@ impl Service { } /// Get membership for given user in state + #[tracing::instrument(skip(self))] fn user_membership( &self, shortstatehash: u64, @@ -100,6 +103,7 @@ impl Service { } /// The user was a joined member at this state (potentially in the past) + #[tracing::instrument(skip(self), ret(level = "trace"))] fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) .map(|s| s == MembershipState::Join) @@ -108,6 +112,7 @@ impl Service { /// The user was an invited or joined room member at this state (potentially /// in the past) + #[tracing::instrument(skip(self), ret(level = "trace"))] fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) .map(|s| s == MembershipState::Join || s == MembershipState::Invite) @@ -294,6 +299,7 @@ impl Service { } /// Returns the state hash for this pdu. + #[tracing::instrument(skip(self))] pub(crate) fn pdu_shortstatehash( &self, event_id: &EventId, @@ -334,6 +340,7 @@ impl Service { self.db.room_state_get(room_id, event_type, state_key) } + #[tracing::instrument(skip(self))] pub(crate) fn get_name(&self, room_id: &RoomId) -> Result> { self.room_state_get(room_id, &StateEventType::RoomName, "")?.map_or( Ok(None), @@ -354,6 +361,7 @@ impl Service { ) } + #[tracing::instrument(skip(self))] pub(crate) fn get_avatar( &self, room_id: &RoomId, @@ -372,6 +380,7 @@ impl Service { // Allowed because this function uses `services()` #[allow(clippy::unused_self)] + #[tracing::instrument(skip(self), ret(level = "trace"))] pub(crate) fn user_can_invite( &self, room_id: &RoomId, @@ -398,6 +407,7 @@ impl Service { .is_ok() } + #[tracing::instrument(skip(self))] pub(crate) fn get_member( &self, room_id: &RoomId, @@ -420,6 +430,7 @@ impl Service { /// If `federation` is `true`, it allows redaction events from any user of /// the same server as the original event sender, [as required by room /// versions >= v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions) + #[tracing::instrument(skip(self))] pub(crate) fn user_can_redact( &self, redacts: &EventId, diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 6e5fedf0..538c75f1 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -286,6 +286,7 @@ impl Service { /// Returns the new shortstatehash, and the state diff from the previous /// room state #[allow(clippy::type_complexity)] + #[tracing::instrument(skip(self, new_state_ids_compressed))] pub(crate) fn save_state( &self, room_id: &RoomId, diff --git a/src/service/sending.rs b/src/service/sending.rs index fd4eb2c9..bb392948 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -40,7 +40,7 @@ use tracing::{debug, error, warn}; use crate::{ api::{appservice_server, server_server}, services, - utils::calculate_hash, + utils::{calculate_hash, debug_slice_truncated}, Config, Error, PduEvent, Result, }; @@ -301,12 +301,13 @@ impl Service { } } - #[tracing::instrument(skip( - self, - outgoing_kind, - new_events, - current_transaction_status - ))] + #[tracing::instrument( + skip(self, new_events, current_transaction_status), + fields( + new_events = debug_slice_truncated(&new_events, 3), + current_status = ?current_transaction_status.get(outgoing_kind), + ), + )] fn select_events( &self, outgoing_kind: &OutgoingKind, @@ -386,7 +387,7 @@ impl Service { Ok(Some(events)) } - #[tracing::instrument(skip(self, server_name))] + #[tracing::instrument(skip(self))] pub(crate) fn select_edus( &self, server_name: &ServerName, @@ -598,7 +599,7 @@ impl Service { Ok(()) } - #[tracing::instrument(skip(events, kind))] + #[tracing::instrument(skip(events))] async fn handle_events( kind: OutgoingKind, events: Vec, @@ -876,7 +877,7 @@ impl Service { } } - #[tracing::instrument(skip(self, destination, request))] + #[tracing::instrument(skip(self, request))] pub(crate) async fn send_federation_request( &self, destination: &ServerName, @@ -906,7 +907,10 @@ impl Service { /// /// Only returns None if there is no url specified in the appservice /// registration file - #[tracing::instrument(skip(self, registration, request))] + #[tracing::instrument( + skip(self, registration, request), + fields(appservice_id = registration.id), + )] pub(crate) async fn send_appservice_request( &self, registration: Registration, From 8d09a7e490706e9bdfcb52986107ae9ee9918142 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 10 May 2024 17:17:50 -0700 Subject: [PATCH 132/617] don't return extra member count or e2ee device updates from sync Previously, we were returning redundant member count updates or encrypted device updates from the /sync endpoint in some cases. The extra member count updates are spec-compliant, but unnecessary, while the extra encrypted device updates violate the spec. The refactor necessary to fix this bug is also necessary to support filtering on state events in sync. Details: Joined room incremental sync needs to examine state events for four purposes: 1. determining whether we need to return an update to room member counts 2. determining the set of left/joined devices for encrypted rooms (returned in `device_lists`) 3. returning state events to the client (in `rooms.joined.*.state`) 4. tracking which member events we have sent to the client, so they can be omitted on future requests when lazy-loading is enabled. The state events that we need to examine for the first two cases is member events in the delta between `since` and the end of `timeline`. For the second two cases, we need the delta between `since` and the start of `timeline`, plus contextual member events for any senders that occur in `timeline`. The second list is subject to filtering, while the first is not. Before this change, we were using the same set of state events that we are returning to the client (cases 3/4) to do the analysis for cases 1/2. In a compliant implementation, this would result in us missing some relevant member events in 1/2 in addition to seeing redundant member events. In current grapevine this is not the case because the set of events that we return to the client is always a superset of the set that is needed for cases 1/2. This is because we don't support filtering, and we have an existing bug[1] where we are returning the delta between `since` and the end of `timeline` rather than the start. [1]: https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/5 Fixing this is necessary to implement filtering because otherwise we would start missing some member events for member count or encrypted device updates if the relevant member events are rejected by the filter. This would be much worse than our current behavior. --- src/api/client_server/sync.rs | 124 ++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index cbca2b49..a5a77448 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -748,8 +748,7 @@ async fn load_joined_room( // Incremental /sync let since_shortstatehash = since_shortstatehash.unwrap(); - let mut state_events = Vec::new(); - let mut lazy_loaded = HashSet::new(); + let mut delta_state_events = Vec::new(); if since_shortstatehash != current_shortstatehash { let current_state_ids = services() @@ -772,66 +771,12 @@ async fn load_joined_room( continue; }; - if pdu.kind == TimelineEventType::RoomMember { - match UserId::parse( - pdu.state_key - .as_ref() - .expect("State event has state key") - .clone(), - ) { - Ok(state_key_userid) => { - lazy_loaded.insert(state_key_userid); - } - Err(e) => error!( - "Invalid state key for member event: {}", - e - ), - } - } - - state_events.push(pdu); + delta_state_events.push(pdu); tokio::task::yield_now().await; } } } - for (_, event) in &timeline_pdus { - if lazy_loaded.contains(&event.sender) { - continue; - } - - if !services().rooms.lazy_loading.lazy_load_was_sent_before( - sender_user, - sender_device, - room_id, - &event.sender, - )? || lazy_load_send_redundant - { - if let Some(member_event) = - services().rooms.state_accessor.room_state_get( - room_id, - &StateEventType::RoomMember, - event.sender.as_str(), - )? - { - lazy_loaded.insert(event.sender.clone()); - state_events.push(member_event); - } - } - } - - services() - .rooms - .lazy_loading - .lazy_load_mark_sent( - sender_user, - sender_device, - room_id, - lazy_loaded, - next_batchcount, - ) - .await; - let encrypted_room = services() .rooms .state_accessor @@ -852,12 +797,12 @@ async fn load_joined_room( let new_encrypted_room = encrypted_room && since_encryption.is_none(); - let send_member_count = state_events + let send_member_count = delta_state_events .iter() .any(|event| event.kind == TimelineEventType::RoomMember); if encrypted_room { - for state_event in &state_events { + for state_event in &delta_state_events { if state_event.kind != TimelineEventType::RoomMember { continue; } @@ -935,6 +880,67 @@ async fn load_joined_room( (None, None, Vec::new()) }; + let mut state_events = delta_state_events; + let mut lazy_loaded = HashSet::new(); + + // Mark all member events we're returning as lazy-loaded + for pdu in &state_events { + if pdu.kind == TimelineEventType::RoomMember { + match UserId::parse( + pdu.state_key + .as_ref() + .expect("State event has state key") + .clone(), + ) { + Ok(state_key_userid) => { + lazy_loaded.insert(state_key_userid); + } + Err(e) => { + error!("Invalid state key for member event: {}", e); + } + } + } + } + + // Fetch contextual member state events for events from the + // timeline, and mark them as lazy-loaded as well. + for (_, event) in &timeline_pdus { + if lazy_loaded.contains(&event.sender) { + continue; + } + + if !services().rooms.lazy_loading.lazy_load_was_sent_before( + sender_user, + sender_device, + room_id, + &event.sender, + )? || lazy_load_send_redundant + { + if let Some(member_event) = + services().rooms.state_accessor.room_state_get( + room_id, + &StateEventType::RoomMember, + event.sender.as_str(), + )? + { + lazy_loaded.insert(event.sender.clone()); + state_events.push(member_event); + } + } + } + + services() + .rooms + .lazy_loading + .lazy_load_mark_sent( + sender_user, + sender_device, + room_id, + lazy_loaded, + next_batchcount, + ) + .await; + ( heroes, joined_member_count, From edfccea30a6766da1322a7dac1415a9970f47b88 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 20 May 2024 10:13:01 +0000 Subject: [PATCH 133/617] Convert giant tuple in state_compressor to struct --- src/database.rs | 4 +- .../key_value/rooms/state_accessor.rs | 6 +- src/service/rooms/state.rs | 8 +- src/service/rooms/state_compressor.rs | 82 +++++++------------ 4 files changed, 39 insertions(+), 61 deletions(-) diff --git a/src/database.rs b/src/database.rs index d32b57f5..9a31cf74 100644 --- a/src/database.rs +++ b/src/database.rs @@ -722,12 +722,12 @@ impl KeyValueDatabase { states_parents.last() { let statediffnew = current_state - .difference(&parent_stateinfo.1) + .difference(&parent_stateinfo.full_state) .copied() .collect::>(); let statediffremoved = parent_stateinfo - .1 + .full_state .difference(¤t_state) .copied() .collect::>(); diff --git a/src/database/key_value/rooms/state_accessor.rs b/src/database/key_value/rooms/state_accessor.rs index ed763bf3..39b7d1d6 100644 --- a/src/database/key_value/rooms/state_accessor.rs +++ b/src/database/key_value/rooms/state_accessor.rs @@ -20,7 +20,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { .load_shortstatehash_info(shortstatehash)? .pop() .expect("there is always one layer") - .1; + .full_state; let mut result = HashMap::new(); let mut i = 0; for compressed in full_state.iter() { @@ -48,7 +48,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { .load_shortstatehash_info(shortstatehash)? .pop() .expect("there is always one layer") - .1; + .full_state; let mut result = HashMap::new(); let mut i = 0; @@ -102,7 +102,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { .load_shortstatehash_info(shortstatehash)? .pop() .expect("there is always one layer") - .1; + .full_state; Ok(full_state .iter() .find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes())) diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index a62f2394..32037633 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -159,12 +159,12 @@ impl Service { let (statediffnew, statediffremoved) = if let Some(parent_stateinfo) = states_parents.last() { let statediffnew: HashSet<_> = state_ids_compressed - .difference(&parent_stateinfo.1) + .difference(&parent_stateinfo.full_state) .copied() .collect(); let statediffremoved: HashSet<_> = parent_stateinfo - .1 + .full_state .difference(&state_ids_compressed) .copied() .collect(); @@ -231,7 +231,7 @@ impl Service { let replaces = states_parents .last() .map(|info| { - info.1.iter().find(|bytes| { + info.full_state.iter().find(|bytes| { bytes.starts_with(&shortstatekey.to_be_bytes()) }) }) @@ -436,7 +436,7 @@ impl Service { .load_shortstatehash_info(shortstatehash)? .pop() .expect("there is always one layer") - .1; + .full_state; Ok(full_state .iter() diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 538c75f1..e9ed9c00 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -12,25 +12,19 @@ use ruma::{EventId, RoomId}; use self::data::StateDiff; use crate::{services, utils, Result}; +#[derive(Clone)] +pub(crate) struct CompressedStateLayer { + pub(crate) shortstatehash: u64, + pub(crate) full_state: Arc>, + pub(crate) added: Arc>, + pub(crate) removed: Arc>, +} + pub(crate) struct Service { pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub(crate) stateinfo_cache: Mutex< - LruCache< - u64, - Vec<( - // shortstatehash - u64, - // full state - Arc>, - // added - Arc>, - // removed - Arc>, - )>, - >, - >, + pub(crate) stateinfo_cache: Mutex>>, } pub(crate) type CompressedStateEvent = [u8; 2 * size_of::()]; @@ -43,18 +37,7 @@ impl Service { pub(crate) fn load_shortstatehash_info( &self, shortstatehash: u64, - ) -> Result< - Vec<( - // shortstatehash - u64, - // full state - Arc>, - // added - Arc>, - // removed - Arc>, - )>, - > { + ) -> Result> { if let Some(r) = self.stateinfo_cache.lock().unwrap().get_mut(&shortstatehash) { @@ -69,19 +52,19 @@ impl Service { if let Some(parent) = parent { let mut response = self.load_shortstatehash_info(parent)?; - let mut state = (*response.last().unwrap().1).clone(); + let mut state = (*response.last().unwrap().full_state).clone(); state.extend(added.iter().copied()); let removed = (*removed).clone(); for r in &removed { state.remove(r); } - response.push(( + response.push(CompressedStateLayer { shortstatehash, - Arc::new(state), + full_state: Arc::new(state), added, - Arc::new(removed), - )); + removed: Arc::new(removed), + }); self.stateinfo_cache .lock() @@ -90,8 +73,12 @@ impl Service { Ok(response) } else { - let response = - vec![(shortstatehash, added.clone(), added, removed)]; + let response = vec![CompressedStateLayer { + shortstatehash, + full_state: added.clone(), + added, + removed, + }]; self.stateinfo_cache .lock() .unwrap() @@ -167,16 +154,7 @@ impl Service { statediffnew: Arc>, statediffremoved: Arc>, diff_to_sibling: usize, - mut parent_states: Vec<( - // shortstatehash - u64, - // full state - Arc>, - // added - Arc>, - // removed - Arc>, - )>, + mut parent_states: Vec, ) -> Result<()> { let diffsum = statediffnew.len() + statediffremoved.len(); @@ -185,8 +163,8 @@ impl Service { // To many layers, we have to go deeper let parent = parent_states.pop().unwrap(); - let mut parent_new = (*parent.2).clone(); - let mut parent_removed = (*parent.3).clone(); + let mut parent_new = (*parent.added).clone(); + let mut parent_removed = (*parent.removed).clone(); for removed in statediffremoved.iter() { if !parent_new.remove(removed) { @@ -236,12 +214,12 @@ impl Service { // 2. We replace a layer above let parent = parent_states.pop().unwrap(); - let parent_diff = parent.2.len() + parent.3.len(); + let parent_diff = parent.added.len() + parent.removed.len(); if diffsum * diffsum >= 2 * diff_to_sibling * parent_diff { // Diff too big, we replace above layer(s) - let mut parent_new = (*parent.2).clone(); - let mut parent_removed = (*parent.3).clone(); + let mut parent_new = (*parent.added).clone(); + let mut parent_removed = (*parent.removed).clone(); for removed in statediffremoved.iter() { if !parent_new.remove(removed) { @@ -273,7 +251,7 @@ impl Service { self.db.save_statediff( shortstatehash, StateDiff { - parent: Some(parent.0), + parent: Some(parent.shortstatehash), added: statediffnew, removed: statediffremoved, }, @@ -325,12 +303,12 @@ impl Service { let (statediffnew, statediffremoved) = if let Some(parent_stateinfo) = states_parents.last() { let statediffnew: HashSet<_> = new_state_ids_compressed - .difference(&parent_stateinfo.1) + .difference(&parent_stateinfo.full_state) .copied() .collect(); let statediffremoved: HashSet<_> = parent_stateinfo - .1 + .full_state .difference(&new_state_ids_compressed) .copied() .collect(); From 23f99015df4ce87c7a3f1fd0499acce9231b8636 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 19 Apr 2024 14:25:31 -0700 Subject: [PATCH 134/617] return ExitCode instead of using panic or exit `panic!()` (and things that invoke it, such as `expect` and `unwrap`) produces terrible looking error messages and `std::process::exit()` doesn't run destructors. Instead, we'll make a `try_main` that can return a `Result` with a structured error type, but for now I'm going to be lazy and just use `Box`. Then, `main` will call it and return the appropriate `ExitCode` value based on `try_main`'s `Result`. This gives us the opportunity to produce good error messages and doesn't violently terminate the program. --- src/main.rs | 74 ++++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index 25fa6ff0..9c507f49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::{ future::Future, io, net::SocketAddr, + process::ExitCode, sync::{atomic, RwLock}, time::Duration, }; @@ -37,7 +38,7 @@ use tower_http::{ trace::TraceLayer, ServiceBuilderExt as _, }; -use tracing::{debug, error, info, warn}; +use tracing::{debug, info, warn}; use tracing_subscriber::{prelude::*, EnvFilter}; pub(crate) mod api; @@ -86,30 +87,32 @@ fn version() -> String { } #[tokio::main] -async fn main() { +async fn main() -> ExitCode { + let Err(e) = try_main().await else { + return ExitCode::SUCCESS; + }; + + eprintln!("error: {e}"); + + ExitCode::FAILURE +} + +/// Fallible entrypoint +async fn try_main() -> Result<(), Box> { clap::parse(); // Initialize config let raw_config = Figment::new() .merge( - Toml::file(Env::var("GRAPEVINE_CONFIG").expect( + Toml::file(Env::var("GRAPEVINE_CONFIG").ok_or( "The GRAPEVINE_CONFIG env var needs to be set. Example: \ /etc/grapevine.toml", - )) + )?) .nested(), ) .merge(Env::prefixed("GRAPEVINE_").global()); - let config = match raw_config.extract::() { - Ok(s) => s, - Err(e) => { - eprintln!( - "It looks like your config is invalid. The following error \ - occurred: {e}" - ); - std::process::exit(1); - } - }; + let config = raw_config.extract::()?; config.warn_deprecated(); @@ -120,51 +123,32 @@ async fn main() { let tracer = opentelemetry_jaeger::new_agent_pipeline() .with_auto_split_batch(true) .with_service_name("grapevine") - .install_batch(opentelemetry::runtime::Tokio) - .unwrap(); + .install_batch(opentelemetry::runtime::Tokio)?; let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - let filter_layer = match EnvFilter::try_new(&config.log) { - Ok(s) => s, - Err(e) => { - eprintln!( - "It looks like your log config is invalid. The following \ - error occurred: {e}" - ); - EnvFilter::try_new("warn").unwrap() - } - }; + let filter_layer = EnvFilter::try_new(&config.log)?; let subscriber = tracing_subscriber::Registry::default() .with(filter_layer) .with(telemetry); - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } else if config.tracing_flame { let registry = tracing_subscriber::Registry::default(); let (flame_layer, _guard) = - tracing_flame::FlameLayer::with_file("./tracing.folded").unwrap(); + tracing_flame::FlameLayer::with_file("./tracing.folded")?; let flame_layer = flame_layer.with_empty_samples(false); let filter_layer = EnvFilter::new("trace,h2=off"); let subscriber = registry.with(filter_layer).with(flame_layer); - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } else { let registry = tracing_subscriber::Registry::default(); let fmt_layer = tracing_subscriber::fmt::Layer::new(); - let filter_layer = match EnvFilter::try_new(&config.log) { - Ok(s) => s, - Err(e) => { - eprintln!( - "It looks like your config is invalid. The following \ - error occured while parsing it: {e}" - ); - EnvFilter::try_new("warn").unwrap() - } - }; + let filter_layer = EnvFilter::try_new(&config.log)?; let subscriber = registry.with(filter_layer).with(fmt_layer); - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } // This is needed for opening lots of file descriptors, which tends to @@ -179,19 +163,17 @@ async fn main() { .expect("should be able to increase the soft limit to the hard limit"); info!("Loading database"); - if let Err(error) = KeyValueDatabase::load_or_create(config).await { - error!(?error, "The database couldn't be loaded or created"); - - std::process::exit(1); - }; + KeyValueDatabase::load_or_create(config).await?; let config = &services().globals.config; info!("Starting server"); - run_server().await.unwrap(); + run_server().await?; if config.allow_jaeger { opentelemetry::global::shutdown_tracer_provider(); } + + Ok(()) } async fn run_server() -> io::Result<()> { From 4407e15d787c722a432c3a8b4af49cb06b48c80e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 19 Apr 2024 14:31:24 -0700 Subject: [PATCH 135/617] reword error message about `GRAPEVINE_CONFIG` The new message more accurately reflects the purpose and behaviors of the environment variable. --- src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9c507f49..c8abc651 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,8 +105,9 @@ async fn try_main() -> Result<(), Box> { let raw_config = Figment::new() .merge( Toml::file(Env::var("GRAPEVINE_CONFIG").ok_or( - "The GRAPEVINE_CONFIG env var needs to be set. Example: \ - /etc/grapevine.toml", + "the `GRAPEVINE_CONFIG` environment variable must either be \ + set to a configuration file path or set to the empty string \ + to force configuration through environment variables", )?) .nested(), ) From 49660b9e39c6a490d568baef0d3325a33008a46d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 19 Apr 2024 15:16:55 -0700 Subject: [PATCH 136/617] return a concrete error type from `try_main` Also adds a utility for formatting an error message in addition to all of its sources, i.e. errors that came before it that led to the current error. This helps us to provide better error messages to users by including more information: both our own error message and all of the underlying error messages, if any, instead of only one or the other. --- src/error.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 28 ++++++++++++++------- 2 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 src/error.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..dd50fee0 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,70 @@ +//! Error handling facilities + +use std::{fmt, iter}; + +use thiserror::Error; + +/// Formats an [`Error`][0] and its [`source`][1]s with a separator +/// +/// [0]: std::error::Error +/// [1]: std::error::Error::source +pub(crate) struct DisplayWithSources<'a> { + /// The error (and its sources) to write + pub(crate) error: &'a dyn std::error::Error, + + /// Separator to write between the original error and subsequent sources + pub(crate) infix: &'static str, +} + +impl fmt::Display for DisplayWithSources<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + + let mut source = self.error.source(); + + source + .into_iter() + .chain(iter::from_fn(|| { + source = source.and_then(std::error::Error::source); + source + })) + .try_for_each(|source| write!(f, "{}{source}", self.infix)) + } +} + +/// Top-level errors +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum Main { + #[error( + "the `{0}` environment variable must either be set to a configuration \ + file path or set to an empty string to force configuration through \ + environment variables" + )] + ConfigPathUnset(&'static str), + + #[error("invalid configuration")] + ConfigInvalid(#[from] figment::Error), + + // Upstream's documentation on what this error means is very sparse + #[error("opentelemetry error")] + Otel(#[from] opentelemetry::trace::TraceError), + + #[error("invalid log filter syntax")] + EnvFilter(#[from] tracing_subscriber::filter::ParseError), + + #[error("failed to install global default tracing subscriber")] + SetSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError), + + // Upstream's documentation on what this error means is very sparse + #[error("tracing_flame error")] + TracingFlame(#[from] tracing_flame::Error), + + #[error("failed to load or create the database")] + DatabaseError(#[source] crate::utils::error::Error), + + #[error("failed to serve requests")] + Serve(#[source] std::io::Error), +} diff --git a/src/main.rs b/src/main.rs index c8abc651..b9d165be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,7 @@ pub(crate) mod api; pub(crate) mod clap; mod config; mod database; +mod error; mod service; mod utils; @@ -92,23 +93,30 @@ async fn main() -> ExitCode { return ExitCode::SUCCESS; }; - eprintln!("error: {e}"); + eprintln!( + "Error: {}", + error::DisplayWithSources { + error: &e, + infix: "\n Caused by: " + } + ); ExitCode::FAILURE } /// Fallible entrypoint -async fn try_main() -> Result<(), Box> { +async fn try_main() -> Result<(), error::Main> { + use error::Main as Error; + clap::parse(); // Initialize config let raw_config = Figment::new() .merge( - Toml::file(Env::var("GRAPEVINE_CONFIG").ok_or( - "the `GRAPEVINE_CONFIG` environment variable must either be \ - set to a configuration file path or set to the empty string \ - to force configuration through environment variables", - )?) + Toml::file({ + let name = "GRAPEVINE_CONFIG"; + Env::var(name).ok_or(Error::ConfigPathUnset(name))? + }) .nested(), ) .merge(Env::prefixed("GRAPEVINE_").global()); @@ -164,11 +172,13 @@ async fn try_main() -> Result<(), Box> { .expect("should be able to increase the soft limit to the hard limit"); info!("Loading database"); - KeyValueDatabase::load_or_create(config).await?; + KeyValueDatabase::load_or_create(config) + .await + .map_err(Error::DatabaseError)?; let config = &services().globals.config; info!("Starting server"); - run_server().await?; + run_server().await.map_err(Error::Serve)?; if config.allow_jaeger { opentelemetry::global::shutdown_tracer_provider(); From a60a9551e1bbbf99888a80ae1a4fc32b0afdabd0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 20 May 2024 21:37:39 -0700 Subject: [PATCH 137/617] Revert "Merge branch 'check-if-membership-is-case-endpoints' into 'next'" This reverts commit 7ace9b0dff12caa1a69722b77aa39ab3e57f7903, reversing changes made to 624654a88b593d231029d7a209dfabd9ccc39bcb. --- src/api/client_server/membership.rs | 30 ----------------------------- 1 file changed, 30 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 1586a4dd..d50b99a7 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -202,12 +202,6 @@ pub(crate) async fn kick_user_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Ok(true) = - services().rooms.state_cache.is_left(sender_user, &body.room_id) - { - return Ok(Ra(kick_user::v3::Response {})); - } - let mut event: RoomMemberEventContent = serde_json::from_str( services() .rooms @@ -271,14 +265,6 @@ pub(crate) async fn ban_user_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Ok(Some(membership_event)) = - services().rooms.state_accessor.get_member(&body.room_id, sender_user) - { - if membership_event.membership == MembershipState::Ban { - return Ok(Ra(ban_user::v3::Response {})); - } - } - let event = services() .rooms .state_accessor @@ -355,14 +341,6 @@ pub(crate) async fn unban_user_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if let Ok(Some(membership_event)) = - services().rooms.state_accessor.get_member(&body.room_id, sender_user) - { - if membership_event.membership != MembershipState::Ban { - return Ok(Ra(unban_user::v3::Response {})); - } - } - let mut event: RoomMemberEventContent = serde_json::from_str( services() .rooms @@ -546,14 +524,6 @@ async fn join_room_by_id_helper( ) -> Result { let sender_user = sender_user.expect("user is authenticated"); - if let Ok(true) = - services().rooms.state_cache.is_joined(sender_user, room_id) - { - return Ok(join_room_by_id::v3::Response { - room_id: room_id.into(), - }); - } - let mutex_state = Arc::clone( services() .globals From 53fbd3fc417e4504cf2220f4fb88ca06abcfc5f8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 17:25:23 -0700 Subject: [PATCH 138/617] store old_cfs as a HashSet This list can't have duplicates. --- src/database/abstraction/rocksdb.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index f73a4d0f..da797d65 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, future::Future, pin::Pin, sync::{Arc, RwLock}, @@ -20,7 +21,7 @@ pub(crate) struct Engine { rocks: DBWithThreadMode, max_open_files: i32, cache: Cache, - old_cfs: Vec, + old_cfs: HashSet, } pub(crate) struct RocksDbEngineTree<'a> { @@ -84,6 +85,7 @@ impl KeyValueDatabaseEngine for Arc { &db_opts, &config.database_path, ) + .map(|x| x.into_iter().collect::>()) .unwrap_or_default(); let db = DBWithThreadMode::::open_cf_descriptors( From b3709f262eac8b421f527ae52d1895d907ffe6b7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 17:47:13 -0700 Subject: [PATCH 139/617] panic when a column family would be created twice `create_cf` already fails when the column family already exists, but this gives us a much better error message; namely, it tells us what column family name is at fault. --- src/database/abstraction/rocksdb.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index da797d65..176e56ea 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -2,7 +2,7 @@ use std::{ collections::HashSet, future::Future, pin::Pin, - sync::{Arc, RwLock}, + sync::{Arc, Mutex, RwLock}, }; use rocksdb::{ @@ -22,6 +22,7 @@ pub(crate) struct Engine { max_open_files: i32, cache: Cache, old_cfs: HashSet, + new_cfs: Mutex>, } pub(crate) struct RocksDbEngineTree<'a> { @@ -104,10 +105,21 @@ impl KeyValueDatabaseEngine for Arc { max_open_files: config.rocksdb_max_open_files, cache: rocksdb_cache, old_cfs: cfs, + new_cfs: Mutex::default(), })) } fn open_tree(&self, name: &'static str) -> Result> { + let mut new_cfs = + self.new_cfs.lock().expect("lock should not be poisoned"); + + let created_already = !new_cfs.insert(name); + + assert!( + !created_already, + "detected attempt to alias column family: {name}", + ); + if !self.old_cfs.contains(&name.to_owned()) { // Create if it didn't exist self.rocks From 6e2eec012ff330796be6ba362c908bb2872facff Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 17:56:56 -0700 Subject: [PATCH 140/617] special case `userroomid_highlightcount` This fixes the panic on startup with a fresh database. --- src/database/abstraction/rocksdb.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 176e56ea..12e98ed6 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -116,11 +116,15 @@ impl KeyValueDatabaseEngine for Arc { let created_already = !new_cfs.insert(name); assert!( - !created_already, + // userroomid_highlightcount is special-cased because it is an + // existing violation of this check that happens to work anyway. We + // should write a database migration to obviate the need for this. + !(created_already && name != "userroomid_highlightcount"), "detected attempt to alias column family: {name}", ); - if !self.old_cfs.contains(&name.to_owned()) { + // Remove `&& !created_already` when the above is addressed + if !self.old_cfs.contains(&name.to_owned()) && !created_already { // Create if it didn't exist self.rocks .create_cf(name, &db_options(self.max_open_files, &self.cache)) From 62bff27d50b1103f518116602697949bf7ed2f82 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 20 May 2024 16:13:58 +0000 Subject: [PATCH 141/617] Remove unused cache --- src/service.rs | 7 ------- src/service/rooms/timeline.rs | 9 +++------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/service.rs b/src/service.rs index 65548616..b610f6fc 100644 --- a/src/service.rs +++ b/src/service.rs @@ -124,7 +124,6 @@ impl Services { }, timeline: rooms::timeline::Service { db, - lasttimelinecount_cache: Mutex::new(HashMap::new()), }, threads: rooms::threads::Service { db, @@ -173,8 +172,6 @@ impl Services { .len(); let stateinfo_cache = self.rooms.state_compressor.stateinfo_cache.lock().unwrap().len(); - let lasttimelinecount_cache = - self.rooms.timeline.lasttimelinecount_cache.lock().await.len(); let roomid_spacechunk_cache = self.rooms.spaces.roomid_spacechunk_cache.lock().await.len(); @@ -184,7 +181,6 @@ lazy_load_waiting: {lazy_load_waiting} server_visibility_cache: {server_visibility_cache} user_visibility_cache: {user_visibility_cache} stateinfo_cache: {stateinfo_cache} -lasttimelinecount_cache: {lasttimelinecount_cache} roomid_spacechunk_cache: {roomid_spacechunk_cache}" ) } @@ -212,9 +208,6 @@ roomid_spacechunk_cache: {roomid_spacechunk_cache}" if amount > 3 { self.rooms.state_compressor.stateinfo_cache.lock().unwrap().clear(); } - if amount > 4 { - self.rooms.timeline.lasttimelinecount_cache.lock().await.clear(); - } if amount > 5 { self.rooms.spaces.roomid_spacechunk_cache.lock().await.clear(); } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 1cc479fc..9ef5758b 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -2,7 +2,7 @@ mod data; use std::{ cmp::Ordering, - collections::{BTreeMap, HashMap, HashSet}, + collections::{BTreeMap, HashSet}, sync::Arc, }; @@ -23,12 +23,11 @@ use ruma::{ serde::Base64, state_res::{self, Event, RoomVersion}, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, - OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId, - ServerName, UserId, + OwnedEventId, OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, }; use serde::Deserialize; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; -use tokio::sync::{Mutex, MutexGuard, RwLock}; +use tokio::sync::{MutexGuard, RwLock}; use tracing::{error, info, warn}; use super::state_compressor::CompressedStateEvent; @@ -92,8 +91,6 @@ impl Ord for PduCount { pub(crate) struct Service { pub(crate) db: &'static dyn Data, - - pub(crate) lasttimelinecount_cache: Mutex>, } impl Service { From 67cb6f817d1c31294af6d2523e1f565e85c7d098 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 20 May 2024 10:03:53 +0000 Subject: [PATCH 142/617] Instrument caches --- src/api/server_server.rs | 8 +++-- src/database/key_value/rooms/auth_chain.rs | 11 +++++- src/database/key_value/rooms/short.rs | 32 ++++++++++++++++- src/database/key_value/rooms/state_cache.rs | 15 ++++++-- src/database/key_value/rooms/timeline.rs | 18 ++++++++-- src/service/rooms/state_accessor.rs | 12 +++++-- src/service/rooms/state_compressor.rs | 38 +++++++++++---------- src/utils.rs | 29 ++++++++++++++++ 8 files changed, 132 insertions(+), 31 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 4529e02d..63fffd4a 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -66,7 +66,9 @@ use tracing::{debug, error, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Ar, Error, PduEvent, Ra, Result, + services, + utils::{self, FoundIn}, + Ar, Error, PduEvent, Ra, Result, }; /// Wraps either an literal IP address plus port, or a hostname plus complement @@ -125,7 +127,7 @@ impl FedDest { } } -#[tracing::instrument(skip(request))] +#[tracing::instrument(skip(request), fields(destination_cache_result))] pub(crate) async fn send_request( destination: &ServerName, request: T, @@ -156,6 +158,7 @@ where .cloned(); let (actual_destination, host) = if let Some(result) = cached_result { + FoundIn::Cache.record("destination_cache_result"); result } else { write_destination_to_cache = true; @@ -294,6 +297,7 @@ where let response = T::IncomingResponse::try_from_http_response(http_response); if response.is_ok() && write_destination_to_cache { + FoundIn::Remote.record("destination_cache_result"); services() .globals .actual_destination_cache diff --git a/src/database/key_value/rooms/auth_chain.rs b/src/database/key_value/rooms/auth_chain.rs index dee4269e..248eb53f 100644 --- a/src/database/key_value/rooms/auth_chain.rs +++ b/src/database/key_value/rooms/auth_chain.rs @@ -1,8 +1,14 @@ use std::{collections::HashSet, mem::size_of, sync::Arc}; -use crate::{database::KeyValueDatabase, service, utils, Result}; +use crate::{ + database::KeyValueDatabase, + service, + utils::{self, FoundIn}, + Result, +}; impl service::rooms::auth_chain::Data for KeyValueDatabase { + #[tracing::instrument(skip(self, key), fields(cache_result))] fn get_cached_eventid_authchain( &self, key: &[u64], @@ -10,6 +16,7 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { // Check RAM cache if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) { + FoundIn::Cache.record("cache_result"); return Ok(Some(Arc::clone(result))); } @@ -30,6 +37,7 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { }); if let Some(chain) = chain { + FoundIn::Database.record("cache_result"); let chain = Arc::new(chain); // Cache in RAM @@ -42,6 +50,7 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { } } + FoundIn::Nothing.record("cache_result"); Ok(None) } diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 2dd04f5e..bacbab4c 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -3,24 +3,33 @@ use std::sync::Arc; use ruma::{events::StateEventType, EventId, RoomId}; use crate::{ - database::KeyValueDatabase, service, services, utils, Error, Result, + database::KeyValueDatabase, + service, services, + utils::{self, FoundIn}, + Error, Result, }; impl service::rooms::short::Data for KeyValueDatabase { + #[tracing::instrument(skip(self), fields(cache_result))] fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { if let Some(short) = self.eventidshort_cache.lock().unwrap().get_mut(event_id) { + FoundIn::Cache.record("cache_result"); return Ok(*short); } let short = if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? { + FoundIn::Database.record("cache_result"); + utils::u64_from_bytes(&shorteventid).map_err(|_| { Error::bad_database("Invalid shorteventid in db.") })? } else { + FoundIn::Nothing.record("cache_result"); + let shorteventid = services().globals.next_count()?; self.eventid_shorteventid .insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?; @@ -37,6 +46,7 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(short) } + #[tracing::instrument(skip(self), fields(cache_result))] fn get_shortstatekey( &self, event_type: &StateEventType, @@ -48,6 +58,7 @@ impl service::rooms::short::Data for KeyValueDatabase { .unwrap() .get_mut(&(event_type.clone(), state_key.to_owned())) { + FoundIn::Cache.record("cache_result"); return Ok(Some(*short)); } @@ -66,15 +77,20 @@ impl service::rooms::short::Data for KeyValueDatabase { .transpose()?; if let Some(s) = short { + FoundIn::Database.record("cache_result"); + self.statekeyshort_cache .lock() .unwrap() .insert((event_type.clone(), state_key.to_owned()), s); + } else { + FoundIn::Nothing.record("cache_result"); } Ok(short) } + #[tracing::instrument(skip(self), fields(cache_result))] fn get_or_create_shortstatekey( &self, event_type: &StateEventType, @@ -86,6 +102,7 @@ impl service::rooms::short::Data for KeyValueDatabase { .unwrap() .get_mut(&(event_type.clone(), state_key.to_owned())) { + FoundIn::Cache.record("cache_result"); return Ok(*short); } @@ -96,10 +113,14 @@ impl service::rooms::short::Data for KeyValueDatabase { let short = if let Some(shortstatekey) = self.statekey_shortstatekey.get(&db_key)? { + FoundIn::Database.record("cache_result"); + utils::u64_from_bytes(&shortstatekey).map_err(|_| { Error::bad_database("Invalid shortstatekey in db.") })? } else { + FoundIn::Nothing.record("cache_result"); + let shortstatekey = services().globals.next_count()?; self.statekey_shortstatekey .insert(&db_key, &shortstatekey.to_be_bytes())?; @@ -116,6 +137,7 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(short) } + #[tracing::instrument(skip(self), fields(cache_result))] fn get_eventid_from_short( &self, shorteventid: u64, @@ -123,6 +145,7 @@ impl service::rooms::short::Data for KeyValueDatabase { if let Some(id) = self.shorteventid_cache.lock().unwrap().get_mut(&shorteventid) { + FoundIn::Cache.record("cache_result"); return Ok(Arc::clone(id)); } @@ -144,6 +167,8 @@ impl service::rooms::short::Data for KeyValueDatabase { Error::bad_database("EventId in shorteventid_eventid is invalid.") })?; + FoundIn::Database.record("cache_result"); + self.shorteventid_cache .lock() .unwrap() @@ -152,6 +177,7 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(event_id) } + #[tracing::instrument(skip(self), fields(cache_result))] fn get_statekey_from_short( &self, shortstatekey: u64, @@ -159,6 +185,7 @@ impl service::rooms::short::Data for KeyValueDatabase { if let Some(id) = self.shortstatekey_cache.lock().unwrap().get_mut(&shortstatekey) { + FoundIn::Cache.record("cache_result"); return Ok(id.clone()); } @@ -193,6 +220,8 @@ impl service::rooms::short::Data for KeyValueDatabase { let result = (event_type, state_key); + FoundIn::Database.record("cache_result"); + self.shortstatekey_cache .lock() .unwrap() @@ -202,6 +231,7 @@ impl service::rooms::short::Data for KeyValueDatabase { } /// Returns `(shortstatehash, already_existed)` + #[tracing::instrument(skip(self))] fn get_or_create_shortstatehash( &self, state_hash: &[u8], diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 776f10bd..c27b8afb 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -9,7 +9,9 @@ use ruma::{ use crate::{ database::KeyValueDatabase, service::{self, appservice::RegistrationInfo}, - services, utils, Error, Result, + services, + utils::{self, FoundIn}, + Error, Result, }; impl service::rooms::state_cache::Data for KeyValueDatabase { @@ -170,7 +172,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { Ok(()) } - #[tracing::instrument(skip(self, room_id))] + #[tracing::instrument(skip(self), fields(cache_result))] fn get_our_real_users( &self, room_id: &RoomId, @@ -178,16 +180,21 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { let maybe = self.our_real_users_cache.read().unwrap().get(room_id).cloned(); if let Some(users) = maybe { + FoundIn::Cache.record("cache_result"); Ok(users) } else { self.update_joined_count(room_id)?; + FoundIn::Database.record("cache_result"); Ok(Arc::clone( self.our_real_users_cache.read().unwrap().get(room_id).unwrap(), )) } } - #[tracing::instrument(skip(self, room_id, appservice))] + #[tracing::instrument( + skip(self, appservice), + fields(cache_result, appservice_id = appservice.registration.id), + )] fn appservice_in_room( &self, room_id: &RoomId, @@ -202,6 +209,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .copied(); if let Some(b) = maybe { + FoundIn::Cache.record("cache_result"); Ok(b) } else { let bridge_user_id = UserId::parse_with_server_name( @@ -218,6 +226,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { }) }); + FoundIn::Database.record("cache_result"); self.appservice_in_room_cache .write() .unwrap() diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 9d125908..5a33f72d 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -8,11 +8,14 @@ use service::rooms::timeline::PduCount; use tracing::error; use crate::{ - database::KeyValueDatabase, service, services, utils, Error, PduEvent, - Result, + database::KeyValueDatabase, + service, services, + utils::{self, FoundIn}, + Error, PduEvent, Result, }; impl service::rooms::timeline::Data for KeyValueDatabase { + #[tracing::instrument(skip(self), fields(cache_result))] fn last_timeline_count( &self, sender_user: &UserId, @@ -34,12 +37,17 @@ impl service::rooms::timeline::Data for KeyValueDatabase { r.ok() }) { + FoundIn::Database.record("cache_result"); Ok(*v.insert(last_count.0)) } else { + FoundIn::Nothing.record("cache_result"); Ok(PduCount::Normal(0)) } } - hash_map::Entry::Occupied(o) => Ok(*o.get()), + hash_map::Entry::Occupied(o) => { + FoundIn::Cache.record("cache_result"); + Ok(*o.get()) + } } } @@ -119,8 +127,10 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. + #[tracing::instrument(skip(self), fields(cache_result))] fn get_pdu(&self, event_id: &EventId) -> Result>> { if let Some(p) = self.pdu_cache.lock().unwrap().get_mut(event_id) { + FoundIn::Cache.record("cache_result"); return Ok(Some(Arc::clone(p))); } @@ -141,12 +151,14 @@ impl service::rooms::timeline::Data for KeyValueDatabase { )? .map(Arc::new) { + FoundIn::Database.record("cache_result"); self.pdu_cache .lock() .unwrap() .insert(event_id.to_owned(), Arc::clone(&pdu)); Ok(Some(pdu)) } else { + FoundIn::Nothing.record("cache_result"); Ok(None) } } diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 18d8e61e..fe0b25a1 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -27,7 +27,9 @@ use serde_json::value::to_raw_value; use tokio::sync::MutexGuard; use tracing::{error, warn}; -use crate::{service::pdu::PduBuilder, services, Error, PduEvent, Result}; +use crate::{ + service::pdu::PduBuilder, services, utils::FoundIn, Error, PduEvent, Result, +}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -121,7 +123,7 @@ impl Service { /// Whether a server is allowed to see an event through federation, based on /// the room's history_visibility at that event's state. - #[tracing::instrument(skip(self, origin, room_id, event_id))] + #[tracing::instrument(skip(self), fields(cache_result))] pub(crate) fn server_can_see_event( &self, origin: &ServerName, @@ -138,6 +140,7 @@ impl Service { .unwrap() .get_mut(&(origin.to_owned(), shortstatehash)) { + FoundIn::Cache.record("cache_result"); return Ok(*visibility); } @@ -188,6 +191,7 @@ impl Service { } }; + FoundIn::Database.record("cache_result"); self.server_visibility_cache .lock() .unwrap() @@ -198,7 +202,7 @@ impl Service { /// Whether a user is allowed to see an event, based on /// the room's history_visibility at that event's state. - #[tracing::instrument(skip(self, user_id, room_id, event_id))] + #[tracing::instrument(skip(self), fields(cache_result))] pub(crate) fn user_can_see_event( &self, user_id: &UserId, @@ -215,6 +219,7 @@ impl Service { .unwrap() .get_mut(&(user_id.to_owned(), shortstatehash)) { + FoundIn::Cache.record("cache_result"); return Ok(*visibility); } @@ -257,6 +262,7 @@ impl Service { } }; + FoundIn::Database.record("cache_result"); self.user_visibility_cache .lock() .unwrap() diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index e9ed9c00..85d55e97 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -10,7 +10,11 @@ use lru_cache::LruCache; use ruma::{EventId, RoomId}; use self::data::StateDiff; -use crate::{services, utils, Result}; +use crate::{ + services, + utils::{self, FoundIn}, + Result, +}; #[derive(Clone)] pub(crate) struct CompressedStateLayer { @@ -33,7 +37,7 @@ impl Service { /// Returns a stack with info on shortstatehash, full state, added diff and /// removed diff for the selected shortstatehash and each parent layer. #[allow(clippy::type_complexity)] - #[tracing::instrument(skip(self))] + #[tracing::instrument(skip(self), fields(cache_result))] pub(crate) fn load_shortstatehash_info( &self, shortstatehash: u64, @@ -41,6 +45,7 @@ impl Service { if let Some(r) = self.stateinfo_cache.lock().unwrap().get_mut(&shortstatehash) { + FoundIn::Cache.record("cache_result"); return Ok(r.clone()); } @@ -50,7 +55,7 @@ impl Service { removed, } = self.db.get_statediff(shortstatehash)?; - if let Some(parent) = parent { + let response = if let Some(parent) = parent { let mut response = self.load_shortstatehash_info(parent)?; let mut state = (*response.last().unwrap().full_state).clone(); state.extend(added.iter().copied()); @@ -65,26 +70,23 @@ impl Service { added, removed: Arc::new(removed), }); - - self.stateinfo_cache - .lock() - .unwrap() - .insert(shortstatehash, response.clone()); - - Ok(response) + response } else { - let response = vec![CompressedStateLayer { + vec![CompressedStateLayer { shortstatehash, full_state: added.clone(), added, removed, - }]; - self.stateinfo_cache - .lock() - .unwrap() - .insert(shortstatehash, response.clone()); - Ok(response) - } + }] + }; + + FoundIn::Database.record("cache_result"); + self.stateinfo_cache + .lock() + .unwrap() + .insert(shortstatehash, response.clone()); + + Ok(response) } // Allowed because this function uses `services()` diff --git a/src/utils.rs b/src/utils.rs index bbf53631..9db1f035 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -231,6 +231,35 @@ pub(crate) fn truncate_str_for_debug( } } +/// Type to record cache performance in a tracing span field. +pub(crate) enum FoundIn { + /// Found in cache + Cache, + /// Cache miss, but it was in the database. The cache has been updated. + Database, + /// Cache and database miss, but another server had it. The cache has been + /// updated. + Remote, + /// The entry could not be found anywhere. + Nothing, +} + +impl FoundIn { + fn value(&self) -> &'static str { + match self { + FoundIn::Cache => "hit", + FoundIn::Database => "miss-database", + FoundIn::Remote => "miss-remote", + FoundIn::Nothing => "not-found", + } + } + + // TODO: use tracing::Value instead if it ever becomes accessible + pub(crate) fn record(&self, field: &str) { + tracing::Span::current().record(field, self.value()); + } +} + #[cfg(test)] mod tests { use crate::utils::truncate_str_for_debug; From b5321d81c0730e7bebf47a31e508fc413c3dd9f6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 20:27:59 -0700 Subject: [PATCH 143/617] update flake.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nixpkgs and `rust-rocksdb` line up so we can drop our RocksDB input for the time being. Flake lock file updates: • Updated input 'attic': 'github:zhaofengli/attic/6eabc3f02fae3683bffab483e614bebfcd476b21' (2024-02-14) → 'github:zhaofengli/attic/4dbdbee45728d8ce5788db6461aaaa89d98081f0' (2024-03-29) • Updated input 'attic/nixpkgs': 'github:NixOS/nixpkgs/aa9d4729cbc99dabacb50e3994dcefb3ea0f7447' (2023-12-14) → 'github:NixOS/nixpkgs/07262b18b97000d16a4bdb003418bd2fb067a932' (2024-03-25) • Updated input 'attic/nixpkgs-stable': 'github:NixOS/nixpkgs/1e2e384c5b7c50dbf8e9c441a9e58d85f408b01f' (2023-12-17) → 'github:NixOS/nixpkgs/44733514b72e732bd49f5511bd0203dea9b9a434' (2024-03-26) • Updated input 'crane': 'github:ipetkov/crane/55f4939ac59ff8f89c6a4029730a2d49ea09105f' (2024-04-21) → 'github:ipetkov/crane/109987da061a1bf452f435f1653c47511587d919' (2024-05-24) • Updated input 'fenix': 'github:nix-community/fenix/c8943ea9e98d41325ff57d4ec14736d330b321b2' (2024-03-05) → 'github:nix-community/fenix/b6fc5035b28e36a98370d0eac44f4ef3fd323df6' (2024-05-22) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/9f14343f9ee24f53f17492c5f9b653427e2ad15e' (2024-03-04) → 'github:rust-lang/rust-analyzer/21ec8f523812b88418b2bfc64240c62b3dd967bd' (2024-05-19) • Updated input 'flake-utils': 'github:numtide/flake-utils/d465f4819400de7c8d874d50b982301f28a84605' (2024-02-28) → 'github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a' (2024-03-11) • Updated input 'nix-filter': 'github:numtide/nix-filter/3449dc925982ad46246cfc36469baf66e1b64f17' (2024-01-15) → 'github:numtide/nix-filter/3342559a24e85fc164b295c3444e8a139924675b' (2024-03-11) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/b8697e57f10292a6165a20f03d2f42920dfaf973' (2024-03-03) → 'github:NixOS/nixpkgs/5710852ba686cc1fd0d3b8e22b3117d43ba374c2' (2024-05-21) • Removed input 'rocksdb' --- Cargo.lock | 8 +++--- Cargo.toml | 2 +- flake.lock | 74 +++++++++++++++++++++--------------------------------- flake.nix | 9 ------- 4 files changed, 33 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93d9cf08..d72a9177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2163,9 +2163,9 @@ dependencies = [ [[package]] name = "rust-librocksdb-sys" -version = "0.21.0+9.1.1" +version = "0.22.0+9.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb7b9cd5ce3b3ce0757ceab2240f7471826780b8700845c0cfd418cb7e398d" +checksum = "eacd840bcfbba938d74f67981a32bc908fe57d1cf6f39b131cb8c3f33fe67d1c" dependencies = [ "bindgen", "bzip2-sys", @@ -2179,9 +2179,9 @@ dependencies = [ [[package]] name = "rust-rocksdb" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bcfb31b5bf2e3274686ebfdf9a946e9a327a3bc54adc7e5cda9f4fdcc4b55f1" +checksum = "aa60811a194b1d25b8b7761786fe8a4a3fb962ccb15ec246a337187ebcd9b8fd" dependencies = [ "libc", "rust-librocksdb-sys", diff --git a/Cargo.toml b/Cargo.toml index f679e43d..71b49f31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,7 +111,7 @@ rand = "0.8.5" regex = "1.8.1" reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } ring = "0.17.7" -rocksdb = { package = "rust-rocksdb", version = "0.25.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", rev = "5495b85aa311c2805302edb0a7de40399e22b397", 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" ] } rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } rust-argon2 = "1.0.0" diff --git a/flake.lock b/flake.lock index c7dd9319..f719ccbd 100644 --- a/flake.lock +++ b/flake.lock @@ -9,11 +9,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1707922053, - "narHash": "sha256-wSZjK+rOXn+UQiP1NbdNn5/UW6UcBxjvlqr2wh++MbM=", + "lastModified": 1711742460, + "narHash": "sha256-0O4v6e4a1toxXZ2gf5INhg4WPE5C5T+SVvsBt+45Mcc=", "owner": "zhaofengli", "repo": "attic", - "rev": "6eabc3f02fae3683bffab483e614bebfcd476b21", + "rev": "4dbdbee45728d8ce5788db6461aaaa89d98081f0", "type": "github" }, "original": { @@ -51,11 +51,11 @@ ] }, "locked": { - "lastModified": 1713721181, - "narHash": "sha256-Vz1KRVTzU3ClBfyhOj8gOehZk21q58T1YsXC30V23PU=", + "lastModified": 1716569590, + "narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=", "owner": "ipetkov", "repo": "crane", - "rev": "55f4939ac59ff8f89c6a4029730a2d49ea09105f", + "rev": "109987da061a1bf452f435f1653c47511587d919", "type": "github" }, "original": { @@ -73,11 +73,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1709619709, - "narHash": "sha256-l6EPVJfwfelWST7qWQeP6t/TDK3HHv5uUB1b2vw4mOQ=", + "lastModified": 1716359173, + "narHash": "sha256-pYcjP6Gy7i6jPWrjiWAVV0BCQp+DdmGaI/k65lBb/kM=", "owner": "nix-community", "repo": "fenix", - "rev": "c8943ea9e98d41325ff57d4ec14736d330b321b2", + "rev": "b6fc5035b28e36a98370d0eac44f4ef3fd323df6", "type": "github" }, "original": { @@ -140,11 +140,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1709126324, - "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "d465f4819400de7c8d874d50b982301f28a84605", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -156,11 +156,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1705332318, - "narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=", + "lastModified": 1710156097, + "narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=", "owner": "numtide", "repo": "nix-filter", - "rev": "3449dc925982ad46246cfc36469baf66e1b64f17", + "rev": "3342559a24e85fc164b295c3444e8a139924675b", "type": "github" }, "original": { @@ -172,11 +172,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1702539185, - "narHash": "sha256-KnIRG5NMdLIpEkZTnN5zovNYc0hhXjAgv6pfd5Z4c7U=", + "lastModified": 1711401922, + "narHash": "sha256-QoQqXoj8ClGo0sqD/qWKFWezgEwUL0SUh37/vY2jNhc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "aa9d4729cbc99dabacb50e3994dcefb3ea0f7447", + "rev": "07262b18b97000d16a4bdb003418bd2fb067a932", "type": "github" }, "original": { @@ -188,11 +188,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1702780907, - "narHash": "sha256-blbrBBXjjZt6OKTcYX1jpe9SRof2P9ZYWPzq22tzXAA=", + "lastModified": 1711460390, + "narHash": "sha256-akSgjDZL6pVHEfSE6sz1DNSXuYX6hq+P/1Z5IoYWs7E=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1e2e384c5b7c50dbf8e9c441a9e58d85f408b01f", + "rev": "44733514b72e732bd49f5511bd0203dea9b9a434", "type": "github" }, "original": { @@ -204,11 +204,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1709479366, - "narHash": "sha256-n6F0n8UV6lnTZbYPl1A9q1BS0p4hduAv1mGAP17CVd0=", + "lastModified": 1716330097, + "narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b8697e57f10292a6165a20f03d2f42920dfaf973", + "rev": "5710852ba686cc1fd0d3b8e22b3117d43ba374c2", "type": "github" }, "original": { @@ -218,23 +218,6 @@ "type": "github" } }, - "rocksdb": { - "flake": false, - "locked": { - "lastModified": 1713810944, - "narHash": "sha256-/Xf0bzNJPclH9IP80QNaABfhj4IAR5LycYET18VFCXc=", - "owner": "facebook", - "repo": "rocksdb", - "rev": "6f7cabeac80a3a6150be2c8a8369fcecb107bf43", - "type": "github" - }, - "original": { - "owner": "facebook", - "ref": "v9.1.1", - "repo": "rocksdb", - "type": "github" - } - }, "root": { "inputs": { "attic": "attic", @@ -243,18 +226,17 @@ "flake-compat": "flake-compat_2", "flake-utils": "flake-utils_2", "nix-filter": "nix-filter", - "nixpkgs": "nixpkgs_2", - "rocksdb": "rocksdb" + "nixpkgs": "nixpkgs_2" } }, "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1709571018, - "narHash": "sha256-ISFrxHxE0J5g7lDAscbK88hwaT5uewvWoma9TlFmRzM=", + "lastModified": 1716107283, + "narHash": "sha256-NJgrwLiLGHDrCia5AeIvZUHUY7xYGVryee0/9D3Ir1I=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "9f14343f9ee24f53f17492c5f9b653427e2ad15e", + "rev": "21ec8f523812b88418b2bfc64240c62b3dd967bd", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index dc042ad0..92ec8434 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,6 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.1"; flake = false; }; }; outputs = inputs: @@ -24,14 +23,6 @@ oci-image = self.callPackage ./nix/pkgs/oci-image {}; - rocksdb = pkgs.rocksdb.overrideAttrs (old: { - src = inputs.rocksdb; - version = pkgs.lib.removePrefix - "v" - (builtins.fromJSON (builtins.readFile ./flake.lock)) - .nodes.rocksdb.original.ref; - }); - shell = self.callPackage ./nix/shell.nix {}; # The Rust toolchain to use From 82c37f298383989967244ef3f31aab8330a08674 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 20:34:14 -0700 Subject: [PATCH 144/617] remove stale reference This should've been done in d5a9c6ac32e15ca11a0f684274be9ca8356cdfcc, my bad. --- rust-toolchain.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f7a94340..9dda51a6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,7 +2,6 @@ # # Other files that need upkeep when this changes: # -# * `.gitlab-ci.yml` # * `Cargo.toml` # * `flake.nix` # From 518d0c9cf33b974bff4bf194f9258ab89532dd65 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 20:36:04 -0700 Subject: [PATCH 145/617] update the rust toolchain, ignore new lints I will fix them in follow-up commits. Also apparently rustc doesn't warn you when you pass it lints that don't exist (yet) so that's annoying. --- Cargo.toml | 15 +++++++++++++-- flake.nix | 2 +- rust-toolchain.toml | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71b49f31..3d2ae5ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,9 @@ unused_extern_crates = "warn" unused_import_braces = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" -unused_qualifications = "warn" + +dead_code = "allow" +unused_qualifications = "allow" [workspace.lints.clippy] # Groups. Keep alphabetically sorted @@ -72,6 +74,15 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" +assigning_clones = "allow" +doc_markdown = "allow" +manual_is_variant_and = "allow" +mixed_attributes_style = "allow" +multiple_bound_locations = "allow" +option_as_ref_cloned = "allow" +thread_local_initializer_can_be_made_const = "allow" +unnecessary_to_owned = "allow" + [package] name = "grapevine" description = "A Matrix homeserver written in Rust" @@ -80,7 +91,7 @@ version = "0.1.0" edition = "2021" # See also `rust-toolchain.toml` -rust-version = "1.75.0" +rust-version = "1.78.0" [lints] workspace = true diff --git a/flake.nix b/flake.nix index 92ec8434..69e6194d 100644 --- a/flake.nix +++ b/flake.nix @@ -34,7 +34,7 @@ file = ./rust-toolchain.toml; # See also `rust-toolchain.toml` - sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8="; + sha256 = "sha256-opUgs6ckUQCyDxcB9Wy51pqhd0MPGHUVbwRKKPGiwZU="; }; }); in diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9dda51a6..19939854 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,7 +9,7 @@ # If you're having trouble making the relevant changes, bug a maintainer. [toolchain] -channel = "1.75.0" +channel = "1.78.0" components = [ # For rust-analyzer "rust-src", From d7e945f4c51fcbdddabbe90a800cd179e77b3a4a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 21:58:43 -0700 Subject: [PATCH 146/617] enable `dead_code` lint --- Cargo.toml | 1 - src/database.rs | 11 ------- src/database/abstraction.rs | 6 ++-- src/database/abstraction/rocksdb.rs | 7 ----- src/database/abstraction/sqlite.rs | 5 ---- src/database/key_value/rooms/outlier.rs | 12 +------- src/database/key_value/rooms/state_cache.rs | 33 --------------------- src/database/key_value/sending.rs | 17 ----------- src/database/key_value/users.rs | 13 -------- src/service/globals.rs | 5 +++- src/service/rooms/edus/read_receipt/data.rs | 4 +++ src/service/rooms/outlier/data.rs | 4 +-- src/service/rooms/state_cache.rs | 9 ------ src/service/rooms/state_cache/data.rs | 8 ++--- src/service/sending.rs | 11 ------- src/service/sending/data.rs | 4 --- src/service/users/data.rs | 2 -- 17 files changed, 15 insertions(+), 137 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d2ae5ad..f6de029b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ unused_import_braces = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" -dead_code = "allow" unused_qualifications = "allow" [workspace.lints.clippy] diff --git a/src/database.rs b/src/database.rs index 9a31cf74..698b67eb 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1170,17 +1170,6 @@ impl KeyValueDatabase { Ok(()) } - #[tracing::instrument(skip(self))] - pub(crate) fn flush(&self) -> Result<()> { - let start = std::time::Instant::now(); - - let res = self.db.flush(); - - debug!("flush: took {:?}", start.elapsed()); - - res - } - #[tracing::instrument] pub(crate) fn start_cleanup_task() { use std::time::{Duration, Instant}; diff --git a/src/database/abstraction.rs b/src/database/abstraction.rs index 09f17720..db14c728 100644 --- a/src/database/abstraction.rs +++ b/src/database/abstraction.rs @@ -1,6 +1,5 @@ use std::{future::Future, pin::Pin, sync::Arc}; -use super::Config; use crate::Result; #[cfg(feature = "sqlite")] @@ -13,11 +12,11 @@ pub(crate) mod rocksdb; pub(crate) mod watchers; pub(crate) trait KeyValueDatabaseEngine: Send + Sync { - fn open(config: &Config) -> Result + #[cfg(any(feature = "sqlite", feature = "rocksdb"))] + fn open(config: &super::Config) -> Result where Self: Sized; fn open_tree(&self, name: &'static str) -> Result>; - fn flush(&self) -> Result<()>; fn cleanup(&self) -> Result<()> { Ok(()) } @@ -25,7 +24,6 @@ pub(crate) trait KeyValueDatabaseEngine: Send + Sync { Ok("Current database engine does not support memory usage reporting." .to_owned()) } - fn clear_caches(&self) {} } pub(crate) trait KvTree: Send + Sync { diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 12e98ed6..ccfcedd9 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -139,11 +139,6 @@ impl KeyValueDatabaseEngine for Arc { })) } - fn flush(&self) -> Result<()> { - // TODO? - Ok(()) - } - #[allow(clippy::as_conversions, clippy::cast_precision_loss)] fn memory_usage(&self) -> Result { let stats = @@ -161,8 +156,6 @@ impl KeyValueDatabaseEngine for Arc { self.cache.get_pinned_usage() as f64 / 1024.0 / 1024.0, )) } - - fn clear_caches(&self) {} } impl RocksDbEngineTree<'_> { diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 1ddfff54..f0f0a9d2 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -164,11 +164,6 @@ impl KeyValueDatabaseEngine for Arc { })) } - fn flush(&self) -> Result<()> { - // we enabled PRAGMA synchronous=normal, so this should not be necessary - Ok(()) - } - fn cleanup(&self) -> Result<()> { self.flush_wal() } diff --git a/src/database/key_value/rooms/outlier.rs b/src/database/key_value/rooms/outlier.rs index f41a02ee..562331ef 100644 --- a/src/database/key_value/rooms/outlier.rs +++ b/src/database/key_value/rooms/outlier.rs @@ -1,6 +1,6 @@ use ruma::{CanonicalJsonObject, EventId}; -use crate::{database::KeyValueDatabase, service, Error, PduEvent, Result}; +use crate::{database::KeyValueDatabase, service, Error, Result}; impl service::rooms::outlier::Data for KeyValueDatabase { fn get_outlier_pdu_json( @@ -16,16 +16,6 @@ impl service::rooms::outlier::Data for KeyValueDatabase { ) } - fn get_outlier_pdu(&self, event_id: &EventId) -> Result> { - self.eventid_outlierpdu.get(event_id.as_bytes())?.map_or( - Ok(None), - |pdu| { - serde_json::from_slice(&pdu) - .map_err(|_| Error::bad_database("Invalid PDU in db.")) - }, - ) - } - #[tracing::instrument(skip(self, pdu))] fn add_pdu_outlier( &self, diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index c27b8afb..ce8ce4df 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -377,39 +377,6 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .transpose() } - /// Returns an iterator over all User IDs who ever joined a room. - #[tracing::instrument(skip(self))] - fn room_useroncejoined<'a>( - &'a self, - room_id: &RoomId, - ) -> Box> + 'a> { - let mut prefix = room_id.as_bytes().to_vec(); - prefix.push(0xFF); - - Box::new(self.roomuseroncejoinedids.scan_prefix(prefix).map( - |(key, _)| { - UserId::parse( - utils::string_from_bytes( - key.rsplit(|&b| b == 0xFF) - .next() - .expect("rsplit always returns an element"), - ) - .map_err(|_| { - Error::bad_database( - "User ID in room_useroncejoined is invalid \ - unicode.", - ) - })?, - ) - .map_err(|_| { - Error::bad_database( - "User ID in room_useroncejoined is invalid.", - ) - }) - }, - )) - } - /// Returns an iterator over all invited members of a room. #[tracing::instrument(skip(self))] fn room_members_invited<'a>( diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index bac027e6..29ba6fd7 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -48,23 +48,6 @@ impl service::sending::Data for KeyValueDatabase { Ok(()) } - fn delete_all_requests_for( - &self, - outgoing_kind: &OutgoingKind, - ) -> Result<()> { - let prefix = outgoing_kind.get_prefix(); - for (key, _) in self.servercurrentevent_data.scan_prefix(prefix.clone()) - { - self.servercurrentevent_data.remove(&key).unwrap(); - } - - for (key, _) in self.servernameevent_data.scan_prefix(prefix) { - self.servernameevent_data.remove(&key).unwrap(); - } - - Ok(()) - } - fn queue_requests( &self, requests: &[(&OutgoingKind, SendingEventType)], diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 0cef5414..6c4e4337 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -406,19 +406,6 @@ impl service::users::Data for KeyValueDatabase { Ok(()) } - fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { - self.userid_lastonetimekeyupdate.get(user_id.as_bytes())?.map_or( - Ok(0), - |bytes| { - utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database( - "Count in roomid_lastroomactiveupdate is invalid.", - ) - }) - }, - ) - } - fn take_one_time_key( &self, user_id: &UserId, diff --git a/src/service/globals.rs b/src/service/globals.rs index 131610b4..e31a47a6 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -83,7 +83,10 @@ pub(crate) struct Service { /// the database. pub(crate) struct RotationHandler( broadcast::Sender<()>, - broadcast::Receiver<()>, + // TODO: Determine if it's safe to delete this field. I'm not deleting it + // right now because I'm unsure what implications that would have for how + // the sender expects to work. + #[allow(dead_code)] broadcast::Receiver<()>, ); impl RotationHandler { diff --git a/src/service/rooms/edus/read_receipt/data.rs b/src/service/rooms/edus/read_receipt/data.rs index 9e468400..b48ec9e2 100644 --- a/src/service/rooms/edus/read_receipt/data.rs +++ b/src/service/rooms/edus/read_receipt/data.rs @@ -39,6 +39,8 @@ pub(crate) trait Data: Send + Sync { ) -> Result<()>; /// Returns the private read marker. + // TODO: Implement MSC2285 + #[allow(dead_code)] fn private_read_get( &self, room_id: &RoomId, @@ -46,6 +48,8 @@ pub(crate) trait Data: Send + Sync { ) -> Result>; /// Returns the count of the last typing update in this room. + // TODO: Implement MSC2285 + #[allow(dead_code)] fn last_privateread_update( &self, user_id: &UserId, diff --git a/src/service/rooms/outlier/data.rs b/src/service/rooms/outlier/data.rs index 3357c5c9..5722648b 100644 --- a/src/service/rooms/outlier/data.rs +++ b/src/service/rooms/outlier/data.rs @@ -1,6 +1,6 @@ use ruma::{CanonicalJsonObject, EventId}; -use crate::{PduEvent, Result}; +use crate::Result; pub(crate) trait Data: Send + Sync { /// Returns the pdu from the outlier tree. @@ -8,7 +8,7 @@ pub(crate) trait Data: Send + Sync { &self, event_id: &EventId, ) -> Result>; - fn get_outlier_pdu(&self, event_id: &EventId) -> Result>; + /// Append the PDU as an outlier. fn add_pdu_outlier( &self, diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index deaf1cc8..af6c7848 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -313,15 +313,6 @@ impl Service { self.db.room_invited_count(room_id) } - /// Returns an iterator over all User IDs who ever joined a room. - #[tracing::instrument(skip(self))] - pub(crate) fn room_useroncejoined<'a>( - &'a self, - room_id: &RoomId, - ) -> impl Iterator> + 'a { - self.db.room_useroncejoined(room_id) - } - /// Returns an iterator over all invited members of a room. #[tracing::instrument(skip(self))] pub(crate) fn room_members_invited<'a>( diff --git a/src/service/rooms/state_cache/data.rs b/src/service/rooms/state_cache/data.rs index 94791c85..5369e42f 100644 --- a/src/service/rooms/state_cache/data.rs +++ b/src/service/rooms/state_cache/data.rs @@ -68,12 +68,6 @@ pub(crate) trait Data: Send + Sync { fn room_invited_count(&self, room_id: &RoomId) -> Result>; - /// Returns an iterator over all User IDs who ever joined a room. - fn room_useroncejoined<'a>( - &'a self, - room_id: &RoomId, - ) -> Box> + 'a>; - /// Returns an iterator over all invited members of a room. fn room_members_invited<'a>( &'a self, @@ -137,5 +131,7 @@ pub(crate) trait Data: Send + Sync { fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> Result; + // TODO: Use this when implementing sync filtering + #[allow(dead_code)] fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> Result; } diff --git a/src/service/sending.rs b/src/service/sending.rs index bb392948..531ca3f2 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -588,17 +588,6 @@ impl Service { Ok(()) } - /// Cleanup event data - /// Used for instance after we remove an appservice registration - #[tracing::instrument(skip(self))] - pub(crate) fn cleanup_events(&self, appservice_id: String) -> Result<()> { - self.db.delete_all_requests_for(&OutgoingKind::Appservice( - appservice_id, - ))?; - - Ok(()) - } - #[tracing::instrument(skip(events))] async fn handle_events( kind: OutgoingKind, diff --git a/src/service/sending/data.rs b/src/service/sending/data.rs index b250c07c..040f9715 100644 --- a/src/service/sending/data.rs +++ b/src/service/sending/data.rs @@ -20,10 +20,6 @@ pub(crate) trait Data: Send + Sync { &self, outgoing_kind: &OutgoingKind, ) -> Result<()>; - fn delete_all_requests_for( - &self, - outgoing_kind: &OutgoingKind, - ) -> Result<()>; fn queue_requests( &self, requests: &[(&OutgoingKind, SendingEventType)], diff --git a/src/service/users/data.rs b/src/service/users/data.rs index ca6442e3..ea3959ed 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -116,8 +116,6 @@ pub(crate) trait Data: Send + Sync { one_time_key_value: &Raw, ) -> Result<()>; - fn last_one_time_keys_update(&self, user_id: &UserId) -> Result; - fn take_one_time_key( &self, user_id: &UserId, From 793d809ac6d119e100b89206173effd3ad8db620 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:00:54 -0700 Subject: [PATCH 147/617] enable `unused_qualifications` lint --- Cargo.toml | 3 +-- src/api/ruma_wrapper/axum.rs | 2 +- src/database.rs | 5 ++--- src/database/abstraction/watchers.rs | 2 +- src/main.rs | 6 +++--- src/service/rooms/spaces.rs | 2 +- src/utils.rs | 9 +++------ 7 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6de029b..ab21dec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,7 @@ unused_extern_crates = "warn" unused_import_braces = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" - -unused_qualifications = "allow" +unused_qualifications = "warn" [workspace.lints.clippy] # Groups. Keep alphabetically sorted diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index f55ffd85..8dd997e4 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -326,7 +326,7 @@ where }; let mut http_request = - http::Request::builder().uri(parts.uri).method(parts.method); + Request::builder().uri(parts.uri).method(parts.method); *http_request.headers_mut().unwrap() = parts.headers; if let Some(CanonicalJsonValue::Object(json_body)) = &mut json_body { diff --git a/src/database.rs b/src/database.rs index 698b67eb..4904ffd7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -305,7 +305,7 @@ impl KeyValueDatabase { Self::check_db_setup(&config)?; if !Path::new(&config.database_path).exists() { - std::fs::create_dir_all(&config.database_path).map_err(|_| { + fs::create_dir_all(&config.database_path).map_err(|_| { Error::BadConfig( "Database folder doesn't exists and couldn't be created \ (e.g. due to missing permissions). Please create the \ @@ -1085,8 +1085,7 @@ impl KeyValueDatabase { ) .unwrap(); - let user_default_rules = - ruma::push::Ruleset::server_default(&user); + let user_default_rules = Ruleset::server_default(&user); account_data .content .global diff --git a/src/database/abstraction/watchers.rs b/src/database/abstraction/watchers.rs index 9f6e5a00..b31bd082 100644 --- a/src/database/abstraction/watchers.rs +++ b/src/database/abstraction/watchers.rs @@ -23,7 +23,7 @@ impl Watchers { { hash_map::Entry::Occupied(o) => o.get().1.clone(), hash_map::Entry::Vacant(v) => { - let (tx, rx) = tokio::sync::watch::channel(()); + let (tx, rx) = watch::channel(()); v.insert((tx, rx.clone())); rx } diff --git a/src/main.rs b/src/main.rs index b9d165be..799727da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -272,7 +272,7 @@ async fn run_server() -> io::Result<()> { /// The axum request handler task gets cancelled if the connection is shut down; /// by spawning our own task, processing continue after the client disconnects. async fn spawn_task( - req: axum::http::Request, + req: http::Request, next: axum::middleware::Next, ) -> std::result::Result { if services().globals.shutdown.load(atomic::Ordering::Relaxed) { @@ -284,13 +284,13 @@ async fn spawn_task( } async fn unrecognized_method( - req: axum::http::Request, + req: http::Request, next: axum::middleware::Next, ) -> std::result::Result { let method = req.method().clone(); let uri = req.uri().clone(); let inner = next.run(req).await; - if inner.status() == axum::http::StatusCode::METHOD_NOT_ALLOWED { + if inner.status() == StatusCode::METHOD_NOT_ALLOWED { warn!("Method not allowed: {method} {uri}"); return Ok(Ra(UiaaResponse::MatrixError(RumaError { body: ErrorBody::Standard { diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 6dccafed..da54bed9 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -545,7 +545,7 @@ impl Service { match join_rule { JoinRule::Restricted(r) => { for rule in &r.allow { - if let join_rules::AllowRule::RoomMembership(rm) = rule { + if let AllowRule::RoomMembership(rm) = rule { if let Ok(true) = services() .rooms .state_cache diff --git a/src/utils.rs b/src/utils.rs index 9db1f035..0c8418a8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -145,20 +145,17 @@ pub(crate) fn deserialize_from_str< 'de, D: serde::de::Deserializer<'de>, T: FromStr, - E: std::fmt::Display, + E: fmt::Display, >( deserializer: D, ) -> Result { struct Visitor, E>(std::marker::PhantomData); - impl, Err: std::fmt::Display> serde::de::Visitor<'_> + impl, Err: fmt::Display> serde::de::Visitor<'_> for Visitor { type Value = T; - fn expecting( - &self, - formatter: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "a parsable string") } From c9859a9b2d1d2ecf3fe2f68b2b5ec0d96d843994 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:03:31 -0700 Subject: [PATCH 148/617] enable `assigning_clones` lint --- Cargo.toml | 1 - src/api/client_server/device.rs | 2 +- src/api/client_server/membership.rs | 4 ++-- src/database.rs | 6 +++--- src/service/pusher.rs | 4 ++-- src/service/users.rs | 11 ++++++----- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab21dec0..cb75b2f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -assigning_clones = "allow" doc_markdown = "allow" manual_is_variant_and = "allow" mixed_attributes_style = "allow" diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index ae243244..f94cae6e 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -60,7 +60,7 @@ pub(crate) async fn update_device_route( .get_device_metadata(sender_user, &body.device_id)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?; - device.display_name = body.display_name.clone(); + device.display_name.clone_from(&body.display_name); services().users.update_device_metadata( sender_user, diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index d50b99a7..49b4d4e2 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -221,7 +221,7 @@ pub(crate) async fn kick_user_route( .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = MembershipState::Leave; - event.reason = body.reason.clone(); + event.reason.clone_from(&body.reason); let mutex_state = Arc::clone( services() @@ -360,7 +360,7 @@ pub(crate) async fn unban_user_route( .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = MembershipState::Leave; - event.reason = body.reason.clone(); + event.reason.clone_from(&body.reason); let mutex_state = Arc::clone( services() diff --git a/src/database.rs b/src/database.rs index 4904ffd7..72d2e3c5 100644 --- a/src/database.rs +++ b/src/database.rs @@ -997,8 +997,8 @@ impl KeyValueDatabase { .get(content_rule_transformation[0]); if rule.is_some() { let mut rule = rule.unwrap().clone(); - rule.rule_id = - content_rule_transformation[1].to_owned(); + content_rule_transformation[1] + .clone_into(&mut rule.rule_id); rules_list .content .shift_remove(content_rule_transformation[0]); @@ -1027,7 +1027,7 @@ impl KeyValueDatabase { rules_list.underride.get(transformation[0]); if let Some(rule) = rule { let mut rule = rule.clone(); - rule.rule_id = transformation[1].to_owned(); + transformation[1].clone_into(&mut rule.rule_id); rules_list .underride .shift_remove(transformation[0]); diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 903f1a73..82bc7497 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -256,11 +256,11 @@ impl Service { pusher.ids.pushkey.clone(), ); device.data.default_payload = http.default_payload.clone(); - device.data.format = http.format.clone(); + device.data.format.clone_from(&http.format); // Tweaks are only added if the format is NOT event_id_only if !event_id_only { - device.tweaks = tweaks.clone(); + device.tweaks.clone_from(&tweaks); } let d = vec![device]; diff --git a/src/service/users.rs b/src/service/users.rs index 5fd8cfbd..fe0ac1ac 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -87,11 +87,12 @@ impl Service { for (list_id, list) in &mut request.lists { if let Some(cached_list) = cached.lists.get(list_id) { if list.sort.is_empty() { - list.sort = cached_list.sort.clone(); + list.sort.clone_from(&cached_list.sort); }; if list.room_details.required_state.is_empty() { - list.room_details.required_state = - cached_list.room_details.required_state.clone(); + list.room_details + .required_state + .clone_from(&cached_list.room_details.required_state); }; list.room_details.timeline_limit = list .room_details @@ -140,8 +141,8 @@ impl Service { (..) => {} } if list.bump_event_types.is_empty() { - list.bump_event_types = - cached_list.bump_event_types.clone(); + list.bump_event_types + .clone_from(&cached_list.bump_event_types); }; } cached.lists.insert(list_id.clone(), list.clone()); From 5048af3a8ff6488733c8458ec17a1d1edd5420fe Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:05:41 -0700 Subject: [PATCH 149/617] enable `doc_markdown` lint --- Cargo.toml | 1 - src/database.rs | 9 +++++---- src/service/admin.rs | 12 +++++++++++- src/utils/error.rs | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb75b2f3..fbe0d34f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -doc_markdown = "allow" manual_is_variant_and = "allow" mixed_attributes_style = "allow" multiple_bound_locations = "allow" diff --git a/src/database.rs b/src/database.rs index 72d2e3c5..44d7b1c4 100644 --- a/src/database.rs +++ b/src/database.rs @@ -159,7 +159,7 @@ pub(crate) struct KeyValueDatabase { /// Remember the state hash at events in the past. pub(super) shorteventid_shortstatehash: Arc, - /// StateKey = EventType + StateKey, ShortStateKey = Count + /// `StateKey = EventType + StateKey`, `ShortStateKey = Count` pub(super) statekey_shortstatekey: Arc, pub(super) shortstatekey_statekey: Arc, @@ -176,16 +176,17 @@ pub(crate) struct KeyValueDatabase { pub(super) shorteventid_authchain: Arc, - /// RoomId + EventId -> outlier PDU. + /// `RoomId + EventId -> outlier PDU` + /// /// Any pdu that has passed the steps 1-8 in the incoming event /// /federation/send/txn. pub(super) eventid_outlierpdu: Arc, pub(super) softfailedeventids: Arc, - /// ShortEventId + ShortEventId -> (). + /// `ShortEventId + ShortEventId -> ()` pub(super) tofrom_relation: Arc, - /// RoomId + EventId -> Parent PDU EventId. + /// `RoomId + EventId -> Parent PDU EventId` pub(super) referencedevents: Arc, // Trees "owned" by `self::key_value::account_data` diff --git a/src/service/admin.rs b/src/service/admin.rs index bd14e045..00f30e75 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -60,6 +60,8 @@ enum AdminCommand { /// # ``` /// # yaml content here /// # ``` + // Allowed because the doc comment gets parsed by our code later + #[allow(clippy::doc_markdown)] RegisterAppservice, /// Unregister an appservice using its ID @@ -107,6 +109,8 @@ enum AdminCommand { /// # ``` /// # User list here /// # ``` + // Allowed because the doc comment gets parsed by our code later + #[allow(clippy::doc_markdown)] DeactivateAll { #[arg(short, long)] /// Remove users from their joined rooms @@ -116,7 +120,7 @@ enum AdminCommand { force: bool, }, - /// Get the auth_chain of a PDU + /// Get the `auth_chain` of a PDU GetAuthChain { /// An event ID (the $ character followed by the base64 reference hash) event_id: Box, @@ -132,6 +136,8 @@ enum AdminCommand { /// # ``` /// # PDU json content here /// # ``` + // Allowed because the doc comment gets parsed by our code later + #[allow(clippy::doc_markdown)] ParsePdu, /// Retrieve and print a PDU by ID from the Grapevine database @@ -186,6 +192,8 @@ enum AdminCommand { /// # ``` /// # json here /// # ``` + // Allowed because the doc comment gets parsed by our code later + #[allow(clippy::doc_markdown)] SignJson, /// Verify json signatures @@ -193,6 +201,8 @@ enum AdminCommand { /// # ``` /// # json here /// # ``` + // Allowed because the doc comment gets parsed by our code later + #[allow(clippy::doc_markdown)] VerifyJson, } diff --git a/src/utils/error.rs b/src/utils/error.rs index 793a2890..3491d85c 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -63,7 +63,7 @@ pub(crate) enum Error { #[error("{0}")] BadConfig(&'static str), #[error("{0}")] - /// Don't create this directly. Use Error::bad_database instead. + /// Don't create this directly. Use [`Error::bad_database`] instead. BadDatabase(&'static str), #[error("uiaa")] Uiaa(UiaaInfo), From 41a5e6fb50b2c0beaf28e19d138ecf853079a999 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:06:45 -0700 Subject: [PATCH 150/617] enable `manual_is_variant_and` lint --- Cargo.toml | 1 - src/service/rooms/state_accessor.rs | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fbe0d34f..9e717e76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -manual_is_variant_and = "allow" mixed_attributes_style = "allow" multiple_bound_locations = "allow" option_as_ref_cloned = "allow" diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index fe0b25a1..844cccc0 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -108,17 +108,16 @@ impl Service { #[tracing::instrument(skip(self), ret(level = "trace"))] fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) - .map(|s| s == MembershipState::Join) - .unwrap_or_default() + .is_ok_and(|s| s == MembershipState::Join) } /// The user was an invited or joined room member at this state (potentially /// in the past) #[tracing::instrument(skip(self), ret(level = "trace"))] fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool { - self.user_membership(shortstatehash, user_id) - .map(|s| s == MembershipState::Join || s == MembershipState::Invite) - .unwrap_or_default() + self.user_membership(shortstatehash, user_id).is_ok_and(|s| { + s == MembershipState::Join || s == MembershipState::Invite + }) } /// Whether a server is allowed to see an event through federation, based on From 92d9f81a78281b42e54de19d9a6a0ada594cb6a4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:08:00 -0700 Subject: [PATCH 151/617] enable `mixed_attributes_style` lint --- Cargo.toml | 1 - src/api/client_server/session.rs | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e717e76..c780ad34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -mixed_attributes_style = "allow" multiple_bound_locations = "allow" option_as_ref_cloned = "allow" thread_local_initializer_can_be_made_const = "allow" diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 6d740acc..b11d6f31 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -51,12 +51,14 @@ pub(crate) async fn get_login_types_route( /// /// Note: You can use [`GET /_matrix/client/r0/login`](get_login_types_route) to /// see supported login types. -#[allow(clippy::too_many_lines)] +#[allow( + // To allow deprecated login methods + deprecated, + clippy::too_many_lines, +)] pub(crate) async fn login_route( body: Ar, ) -> Result> { - // To allow deprecated login methods - #![allow(deprecated)] // Validate login method // TODO: Other login methods let user_id = match &body.login_info { From eaeb7620d96f01ce4fa1b65aaec306ebf19e777c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:09:21 -0700 Subject: [PATCH 152/617] enable `multiple_bound_locations` lint --- Cargo.toml | 1 - src/api/appservice_server.rs | 4 ++-- src/api/server_server.rs | 4 ++-- src/service/pusher.rs | 4 ++-- src/service/sending.rs | 8 ++++---- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c780ad34..6e563018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -multiple_bound_locations = "allow" option_as_ref_cloned = "allow" thread_local_initializer_can_be_made_const = "allow" unnecessary_to_owned = "allow" diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index fbf03179..b6ef0465 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -14,12 +14,12 @@ use crate::{services, utils, Error, Result}; /// Only returns None if there is no url specified in the appservice /// registration file #[tracing::instrument(skip(request))] -pub(crate) async fn send_request( +pub(crate) async fn send_request( registration: Registration, request: T, ) -> Result> where - T: Debug, + T: OutgoingRequest + Debug, { let Some(destination) = registration.url else { return Ok(None); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 63fffd4a..2455f2b0 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -128,12 +128,12 @@ impl FedDest { } #[tracing::instrument(skip(request), fields(destination_cache_result))] -pub(crate) async fn send_request( +pub(crate) async fn send_request( destination: &ServerName, request: T, ) -> Result where - T: Debug, + T: OutgoingRequest + Debug, { if !services().globals.allow_federation() { return Err(Error::BadConfig("Federation is disabled.")); diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 82bc7497..94cb7fb4 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -62,13 +62,13 @@ impl Service { } #[tracing::instrument(skip(self, destination, request))] - pub(crate) async fn send_request( + pub(crate) async fn send_request( &self, destination: &str, request: T, ) -> Result where - T: Debug, + T: OutgoingRequest + Debug, { let destination = destination.replace("/_matrix/push/v1/notify", ""); diff --git a/src/service/sending.rs b/src/service/sending.rs index 531ca3f2..c0fd0fd9 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -867,13 +867,13 @@ impl Service { } #[tracing::instrument(skip(self, request))] - pub(crate) async fn send_federation_request( + pub(crate) async fn send_federation_request( &self, destination: &ServerName, request: T, ) -> Result where - T: Debug, + T: OutgoingRequest + Debug, { debug!("Waiting for permit"); let permit = self.maximum_requests.acquire().await; @@ -900,13 +900,13 @@ impl Service { skip(self, registration, request), fields(appservice_id = registration.id), )] - pub(crate) async fn send_appservice_request( + pub(crate) async fn send_appservice_request( &self, registration: Registration, request: T, ) -> Result> where - T: Debug, + T: OutgoingRequest + Debug, { let permit = self.maximum_requests.acquire().await; let response = From 3daf2229d6fbabf880b4f9fd867eba849937bb65 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:10:04 -0700 Subject: [PATCH 153/617] enable `option_as_ref_cloned` lint --- Cargo.toml | 1 - src/api/client_server/account.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e563018..b023f552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -option_as_ref_cloned = "allow" thread_local_initializer_can_be_made_const = "allow" unnecessary_to_owned = "allow" diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 180b90c2..eaf3f841 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -393,7 +393,7 @@ pub(crate) async fn whoami_route( body: Ar, ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let device_id = body.sender_device.as_ref().cloned(); + let device_id = body.sender_device.clone(); Ok(Ra(whoami::v3::Response { user_id: sender_user.clone(), From 7e7911abcf739f2b719c6e7fd291d562cc0971ab Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:11:19 -0700 Subject: [PATCH 154/617] enable thread_local_initializer_can_be_made_const There's barely enough room in the commit title for this lint's name... --- Cargo.toml | 1 - src/database/abstraction/sqlite.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b023f552..e55c3588 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -thread_local_initializer_can_be_made_const = "allow" unnecessary_to_owned = "allow" [package] diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index f0f0a9d2..871e2af4 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -16,9 +16,9 @@ use crate::{database::Config, Result}; thread_local! { static READ_CONNECTION: RefCell> = - RefCell::new(None); + const { RefCell::new(None) }; static READ_CONNECTION_ITERATOR: RefCell> = - RefCell::new(None); + const { RefCell::new(None) }; } struct PreparedStatementIterator<'a> { From 13de9ecd41623c3223cb16c4ac1454d351334418 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:12:42 -0700 Subject: [PATCH 155/617] enable `unnecessary_to_owned` lint *facepalm* --- Cargo.toml | 2 -- src/database/abstraction/rocksdb.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e55c3588..dac561db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,8 +72,6 @@ wildcard_dependencies = "warn" missing_errors_doc = "allow" missing_panics_doc = "allow" -unnecessary_to_owned = "allow" - [package] name = "grapevine" description = "A Matrix homeserver written in Rust" diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index ccfcedd9..9abfc44d 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -124,7 +124,7 @@ impl KeyValueDatabaseEngine for Arc { ); // Remove `&& !created_already` when the above is addressed - if !self.old_cfs.contains(&name.to_owned()) && !created_already { + if !self.old_cfs.contains(name) && !created_already { // Create if it didn't exist self.rocks .create_cf(name, &db_options(self.max_open_files, &self.cache)) From d5da913c793182071436ea69332c2bc991473d29 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 21 May 2024 22:20:31 -0700 Subject: [PATCH 156/617] pin nixos/nix to 2.18.2 Not sure if Lix has Docker images we can use yet, but we can at least do this for now. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f7a1273..aba71d2e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,7 +33,7 @@ before_script: ci: stage: ci - image: nixos/nix:2.20.4 + image: nixos/nix:2.18.2 script: - ./bin/nix-build-and-cache ci @@ -45,6 +45,6 @@ ci: artifacts: stage: artifacts - image: nixos/nix:2.20.4 + image: nixos/nix:2.18.2 script: - ./bin/nix-build-and-cache packages From 8f0fdfb2f236d68c3fe64f29ecf1f3cd233bc782 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 22 May 2024 17:42:05 -0700 Subject: [PATCH 157/617] upgrade all cargo dependencies Unfortunately we need to pull tracing-opentelemetry from git because there hasn't been a release including the dependency bump on the other opentelemetry crates. --- Cargo.lock | 1410 ++++++++++++++++----------- Cargo.toml | 77 +- src/api/client_server/account.rs | 2 +- src/api/client_server/context.rs | 2 +- src/api/client_server/membership.rs | 6 +- src/api/client_server/message.rs | 2 +- src/api/client_server/relations.rs | 4 + src/api/client_server/room.rs | 6 +- src/api/client_server/search.rs | 2 +- src/api/client_server/session.rs | 8 +- src/api/client_server/state.rs | 10 +- src/api/client_server/sync.rs | 6 +- src/api/client_server/typing.rs | 2 +- src/api/ruma_wrapper/axum.rs | 114 +-- src/api/server_server.rs | 14 +- src/database/key_value/uiaa.rs | 2 +- src/main.rs | 32 +- src/service/globals.rs | 16 +- src/service/media.rs | 2 +- src/service/rooms/auth_chain.rs | 2 +- src/service/rooms/event_handler.rs | 6 +- src/service/rooms/pdu_metadata.rs | 4 + src/service/rooms/spaces.rs | 2 +- src/service/rooms/timeline.rs | 16 +- src/service/uiaa.rs | 4 +- src/utils/error.rs | 4 +- 26 files changed, 993 insertions(+), 762 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d72a9177..11ba7590 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,30 +31,30 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "allocator-api2" -version = "0.2.16" +name = "anstyle" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] -name = "anstyle" -version = "1.0.6" +name = "anyhow" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arc-swap" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" @@ -81,14 +81,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" [[package]] -name = "async-trait" -version = "0.1.77" +name = "async-stream" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -101,10 +123,16 @@ dependencies = [ ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" @@ -113,14 +141,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes", "futures-util", - "headers", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "itoa", "matchit", "memchr", @@ -132,7 +188,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower", "tower-layer", "tower-service", @@ -147,8 +203,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -156,30 +212,77 @@ dependencies = [ ] [[package]] -name = "axum-server" -version = "0.5.1" +name = "axum-core" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447f28c85900215cc1bea282f32d4a2f22d55c5a300afdfbc661c8d6a632e063" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +dependencies = [ + "axum 0.7.5", + "axum-core 0.4.3", + "bytes", + "futures-util", + "headers", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-server" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad46c3ec4e12f4a4b6835e173ba21c25e484c9d02b49770bf006ce5367c036" dependencies = [ "arc-swap", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "pin-project-lite", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", + "tower", "tower-service", ] [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -196,6 +299,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -208,10 +317,10 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools", "lazy_static", "lazycell", "proc-macro2", @@ -219,7 +328,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn", ] [[package]] @@ -230,9 +339,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "blake2b_simd" @@ -256,15 +365,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] name = "byteorder" @@ -274,9 +383,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2-sys" @@ -291,12 +400,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.90" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -316,9 +426,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clang-sys" @@ -333,9 +443,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.2" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -353,14 +463,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -420,28 +530,13 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - [[package]] name = "crypto-common" version = "0.1.6" @@ -477,33 +572,26 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core", + "syn", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "date_header" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -556,29 +644,20 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "enum-as-inner" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -589,9 +668,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" @@ -610,15 +689,15 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "figment" -version = "0.10.14" +version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b6e5bc7bd59d60d0d45a6ccab6cf0f4ce28698fb4e81e750ddf229c9b824026" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ "atomic", "pear", @@ -630,9 +709,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -653,21 +732,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.30" @@ -675,7 +739,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -709,7 +772,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -730,13 +793,10 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", @@ -754,9 +814,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -792,24 +852,29 @@ name = "grapevine" version = "0.1.0" dependencies = [ "async-trait", - "axum", + "axum 0.7.5", + "axum-extra", "axum-server", - "base64", + "base64 0.22.1", "bytes", "clap", "figment", "futures-util", "hmac", "html-escape", - "http", - "hyper", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "image", "jsonwebtoken", "lru-cache", "nix", "num_cpus", "opentelemetry", - "opentelemetry-jaeger", + "opentelemetry-jaeger-propagator", + "opentelemetry-otlp", + "opentelemetry_sdk", "parking_lot", "phf", "rand", @@ -841,17 +906,36 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.2.5", + "http 0.2.12", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -866,33 +950,32 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", - "allocator-api2", ] [[package]] name = "hashlink" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] name = "headers" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "headers-core", - "http", + "http 1.1.0", "httpdate", "mime", "sha1", @@ -900,11 +983,11 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.1.0", ] [[package]] @@ -913,6 +996,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -959,6 +1048,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -966,15 +1066,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] [[package]] -name = "http-range-header" -version = "0.3.1" +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] [[package]] name = "httparse" @@ -998,9 +1115,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1013,26 +1130,81 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.24.2" +name = "hyper" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http", - "hyper", - "rustls", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.22.4", + "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.28", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-util" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] name = "idna" -version = "0.2.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -1049,17 +1221,18 @@ dependencies = [ [[package]] name = "image" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" dependencies = [ "bytemuck", "byteorder", "color_quant", "gif", - "jpeg-decoder", "num-traits", "png", + "zune-core", + "zune-jpeg", ] [[package]] @@ -1074,12 +1247,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] @@ -1089,12 +1262,6 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - [[package]] name = "ipconfig" version = "0.3.2" @@ -1104,7 +1271,7 @@ dependencies = [ "socket2", "widestring", "windows-sys 0.48.0", - "winreg", + "winreg 0.50.0", ] [[package]] @@ -1113,15 +1280,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -1133,25 +1291,19 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - [[package]] name = "js-sys" version = "0.3.69" @@ -1181,11 +1333,11 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "9.2.0" +version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ - "base64", + "base64 0.21.7", "js-sys", "pem", "ring", @@ -1196,9 +1348,9 @@ dependencies = [ [[package]] name = "konst" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d712a8c49d4274f8d8a5cf61368cb5f3c143d149882b1a2918129e53395fdb0" +checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9" dependencies = [ "const_panic", "konst_kernel", @@ -1207,9 +1359,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac6ea8c376b6e208a81cf39b8e82bebf49652454d98a4829e907dac16ef1790" +checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b" dependencies = [ "typewit", ] @@ -1228,9 +1380,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" @@ -1239,14 +1391,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", @@ -1255,9 +1407,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.15" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "pkg-config", @@ -1272,9 +1424,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1326,12 +1478,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.7.3" @@ -1340,9 +1486,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" @@ -1358,9 +1504,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", "simd-adler32", @@ -1379,11 +1525,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "cfg_aliases", "libc", @@ -1411,11 +1557,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -1437,9 +1582,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1477,50 +1622,12 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "opentelemetry" -version = "0.18.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d6c3d7288a106c0a363e4b0e8d308058d56902adefb16f4936f417ffef086e" +checksum = "1b69a91d4893e713e06f724597ad630f1fa76057a5e1026c0ca67054a9032a76" dependencies = [ - "opentelemetry_api", - "opentelemetry_sdk", -] - -[[package]] -name = "opentelemetry-jaeger" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e785d273968748578931e4dc3b4f5ec86b26e09d9e0d66b55adda7fce742f7a" -dependencies = [ - "async-trait", - "futures", - "futures-executor", - "once_cell", - "opentelemetry", - "opentelemetry-semantic-conventions", - "thiserror", - "thrift", - "tokio", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b02e0230abb0ab6636d18e2ba8fa02903ea63772281340ccac18e0af3ec9eeb" -dependencies = [ - "opentelemetry", -] - -[[package]] -name = "opentelemetry_api" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c24f96e21e7acc813c7a8394ee94978929db2bcc46cf6b5014fc612bf7760c22" -dependencies = [ - "fnv", - "futures-channel", - "futures-util", - "indexmap 1.9.3", + "futures-core", + "futures-sink", "js-sys", "once_cell", "pin-project-lite", @@ -1528,20 +1635,59 @@ dependencies = [ ] [[package]] -name = "opentelemetry_sdk" -version = "0.18.0" +name = "opentelemetry-jaeger-propagator" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca41c4933371b61c2a2f214bf16931499af4ec90543604ec828f7a625c09113" +checksum = "c190755e0aeec909343896f94670446ac686dd1eaf5e2beb4149a7148cfe1d6c" +dependencies = [ + "opentelemetry", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94c69209c05319cdf7460c6d4c055ed102be242a0a6245835d7bc42c6ec7f54" +dependencies = [ + "async-trait", + "futures-core", + "http 0.2.12", + "opentelemetry", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "thiserror", + "tokio", + "tonic", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "984806e6cf27f2b49282e2a05e288f30594f3dbc74eb7a6e99422bc48ed78162" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae312d58eaa90a82d2e627fd86e075cf5230b3f11794e2ed74199ebbe572d4fd" dependencies = [ "async-trait", - "crossbeam-channel", - "dashmap", - "fnv", "futures-channel", "futures-executor", "futures-util", + "glob", + "lazy_static", "once_cell", - "opentelemetry_api", + "opentelemetry", + "ordered-float", "percent-encoding", "rand", "thiserror", @@ -1551,9 +1697,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "1.1.1" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" dependencies = [ "num-traits", ] @@ -1566,9 +1712,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1576,22 +1722,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "pear" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" dependencies = [ "inlinable_string", "pear_codegen", @@ -1600,23 +1746,23 @@ dependencies = [ [[package]] name = "pear_codegen" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e22670e8eb757cff11d6c199ca7b987f352f0346e0be4dd23869ec72cb53c77" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "pem" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64", + "base64 0.22.1", "serde", ] @@ -1656,7 +1802,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -1685,14 +1831,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1718,9 +1864,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "platforms" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "png" @@ -1749,19 +1895,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_datetime", - "toml_edit", + "toml_edit 0.21.1", ] [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -1774,11 +1919,34 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", "version_check", "yansi", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1787,9 +1955,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1826,23 +1994,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -1862,7 +2030,7 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -1873,26 +2041,27 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.26" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64", + "base64 0.22.1", "bytes", - "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-rustls", + "hyper-util", "ipnet", "js-sys", "log", @@ -1900,23 +2069,23 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.22.4", "rustls-native-certs", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 0.1.2", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tokio-socks", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -1946,8 +2115,8 @@ dependencies = [ [[package]] name = "ruma" -version = "0.9.4" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.10.1" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "assign", "js_int", @@ -1961,12 +2130,13 @@ dependencies = [ "ruma-push-gateway-api", "ruma-signatures", "ruma-state-res", + "web-time", ] [[package]] name = "ruma-appservice-api" -version = "0.9.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.10.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "js_int", "ruma-common", @@ -1977,13 +2147,14 @@ dependencies = [ [[package]] name = "ruma-client-api" -version = "0.17.4" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.18.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "as_variant", "assign", "bytes", - "http", + "date_header", + "http 1.1.0", "js_int", "js_option", "maplit", @@ -1992,19 +2163,22 @@ dependencies = [ "serde", "serde_html_form", "serde_json", + "thiserror", + "url", + "web-time", ] [[package]] name = "ruma-common" -version = "0.12.1" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.13.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "as_variant", - "base64", + "base64 0.22.1", "bytes", "form_urlencoded", - "http", - "indexmap 2.2.5", + "http 1.1.0", + "indexmap 2.2.6", "js_int", "konst", "percent-encoding", @@ -2026,11 +2200,11 @@ dependencies = [ [[package]] name = "ruma-events" -version = "0.27.11" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.28.1" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "as_variant", - "indexmap 2.2.5", + "indexmap 2.2.6", "js_int", "js_option", "percent-encoding", @@ -2048,8 +2222,8 @@ dependencies = [ [[package]] name = "ruma-federation-api" -version = "0.8.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.9.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "js_int", "ruma-common", @@ -2060,8 +2234,8 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" -version = "0.9.3" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.9.5" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "js_int", "thiserror", @@ -2069,8 +2243,8 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" -version = "0.8.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.9.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "js_int", "ruma-common", @@ -2079,8 +2253,8 @@ dependencies = [ [[package]] name = "ruma-macros" -version = "0.12.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.13.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "once_cell", "proc-macro-crate", @@ -2088,14 +2262,14 @@ dependencies = [ "quote", "ruma-identifiers-validation", "serde", - "syn 2.0.52", + "syn", "toml", ] [[package]] name = "ruma-push-gateway-api" -version = "0.8.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.9.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ "js_int", "ruma-common", @@ -2106,10 +2280,10 @@ dependencies = [ [[package]] name = "ruma-signatures" -version = "0.14.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.15.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ - "base64", + "base64 0.22.1", "ed25519-dalek", "pkcs8", "rand", @@ -2122,10 +2296,10 @@ dependencies = [ [[package]] name = "ruma-state-res" -version = "0.10.0" -source = "git+https://github.com/ruma/ruma?rev=5495b85aa311c2805302edb0a7de40399e22b397#5495b85aa311c2805302edb0a7de40399e22b397" +version = "0.11.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" dependencies = [ - "itertools 0.11.0", + "itertools", "js_int", "ruma-common", "ruma-events", @@ -2137,11 +2311,11 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2151,14 +2325,13 @@ dependencies = [ [[package]] name = "rust-argon2" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5885493fdf0be6cdff808d1533ce878d21cfa49c7086fa00c66355cd9141bfc" +checksum = "9d9848531d60c9cbbcf9d166c885316c24bc0e2a9d3eba0956bb6cbbd79bc6e8" dependencies = [ - "base64", + "base64 0.21.7", "blake2b_simd", "constant_time_eq", - "crossbeam-utils", ] [[package]] @@ -2189,9 +2362,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -2210,37 +2383,59 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] [[package]] -name = "rustls-native-certs" -version = "0.6.3" +name = "rustls" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", "rustls-pemfile", + "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64", + "base64 0.22.1", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2252,16 +2447,27 @@ dependencies = [ ] [[package]] -name = "rustversion" -version = "1.0.14" +name = "rustls-webpki" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" @@ -2296,11 +2502,11 @@ checksum = "621e3680f3e07db4c9c2c3fb07c6223ab2fab2e54bd3c04c3ae037990f428c32" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -2309,9 +2515,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -2319,38 +2525,38 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "serde_html_form" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50437e6a58912eecc08865e35ea2e8d365fbb2db0debb1c8bb43bf1faf055f25" +checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" dependencies = [ "form_urlencoded", - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -2358,9 +2564,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -2379,9 +2585,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -2400,11 +2606,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -2461,9 +2667,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -2512,15 +2718,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2559,20 +2765,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -2586,44 +2781,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "system-configuration" -version = "0.5.1" +name = "sync_wrapper" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2636,28 +2816,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "thrift" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09678c4cdbb4eed72e18b7c2af1329c69825ed16fcbac62d083fc3e2b0590ff0" -dependencies = [ - "byteorder", - "integer-encoding", - "log", - "ordered-float", - "threadpool", -] - [[package]] name = "tikv-jemalloc-sys" version = "0.5.4+5.3.0-patched" @@ -2680,9 +2838,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -2701,9 +2859,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -2726,9 +2884,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -2742,6 +2900,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.2.0" @@ -2750,7 +2918,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2759,7 +2927,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -2777,9 +2956,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -2788,50 +2967,87 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.2" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +dependencies = [ + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.8", +] + +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.7", + "bytes", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", ] [[package]] @@ -2842,8 +3058,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap 1.9.3", "pin-project", "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -2851,17 +3072,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "pin-project-lite", "tower", "tower-layer", @@ -2901,7 +3120,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2925,17 +3144,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -2949,16 +3157,19 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de" +version = "0.23.0" +source = "git+https://github.com/tokio-rs/tracing-opentelemetry?branch=v0.1.x#2539f4f7bde3dc3f320e5fb935d2c13a69a540dd" dependencies = [ + "js-sys", "once_cell", "opentelemetry", + "opentelemetry_sdk", + "smallvec", "tracing", "tracing-core", - "tracing-log 0.1.4", + "tracing-log", "tracing-subscriber", + "web-time", ] [[package]] @@ -2976,14 +3187,14 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] name = "trust-dns-proto" -version = "0.22.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" dependencies = [ "async-trait", "cfg-if", @@ -2992,9 +3203,9 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.2.3", + "idna 0.4.0", "ipnet", - "lazy_static", + "once_cell", "rand", "smallvec", "thiserror", @@ -3006,16 +3217,17 @@ dependencies = [ [[package]] name = "trust-dns-resolver" -version = "0.22.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" dependencies = [ "cfg-if", "futures-util", "ipconfig", - "lazy_static", "lru-cache", + "once_cell", "parking_lot", + "rand", "resolv-conf", "smallvec", "thiserror", @@ -3083,9 +3295,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -3102,6 +3314,7 @@ dependencies = [ "form_urlencoded", "idna 0.5.0", "percent-encoding", + "serde", ] [[package]] @@ -3112,9 +3325,9 @@ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", ] @@ -3173,7 +3386,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn", "wasm-bindgen-shared", ] @@ -3207,7 +3420,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3246,15 +3459,15 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "wildmatch" -version = "2.3.1" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017f0a8ed8331210d91b7a4c30d4edef8f21a65c02f2540496e2e79725f6d8a8" +checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19" [[package]] name = "winapi" @@ -3293,7 +3506,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -3313,17 +3526,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -3334,9 +3548,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -3346,9 +3560,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -3358,9 +3572,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -3370,9 +3590,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -3382,9 +3602,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -3394,9 +3614,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -3406,9 +3626,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -3419,6 +3639,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -3429,6 +3658,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "yansi" version = "1.0.1" @@ -3437,22 +3676,22 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -3463,10 +3702,25 @@ checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index dac561db..f4f7e713 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,54 +87,59 @@ workspace = true # Keep sorted [dependencies] -async-trait = "0.1.68" -axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"] } -axum-server = { version = "0.5.1", features = ["tls-rustls"] } -base64 = "0.21.2" -bytes = "1.4.0" -clap = { version = "4.3.0", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } -figment = { version = "0.10.8", features = ["env", "toml"] } -futures-util = { version = "0.3.28", default-features = false } +async-trait = "0.1.80" +axum = { version = "0.7.5", default-features = false, features = ["form", "http1", "http2", "json", "matched-path"] } +axum-extra = { version = "0.9.3", features = ["typed-header"] } +axum-server = { version = "0.6.0", features = ["tls-rustls"] } +base64 = "0.22.1" +bytes = "1.6.0" +clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } +figment = { version = "0.10.19", features = ["env", "toml"] } +futures-util = { version = "0.3.30", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" -http = "0.2.9" -hyper = "0.14.26" -image = { version = "0.24.6", default-features = false, features = ["jpeg", "png", "gif"] } -jsonwebtoken = "9.2.0" +http = "1.1.0" +http-body-util = "0.1.1" +hyper = "1.3.1" +hyper-util = { version = "0.1.4", features = ["client", "client-legacy", "service"] } +image = { version = "0.25.1", default-features = false, features = ["jpeg", "png", "gif"] } +jsonwebtoken = "9.3.0" lru-cache = "0.1.2" -num_cpus = "1.15.0" -opentelemetry = { version = "0.18.0", features = ["rt-tokio"] } -opentelemetry-jaeger = { version = "0.17.0", features = ["rt-tokio"] } -parking_lot = { version = "0.12.1", optional = true } +num_cpus = "1.16.0" +opentelemetry = "0.23.0" +opentelemetry-jaeger-propagator = "0.2.0" +opentelemetry-otlp = "0.16.0" +opentelemetry_sdk = { version = "0.23.0", features = ["rt-tokio"] } +parking_lot = { version = "0.12.3", optional = true } phf = { version = "0.11.2", features = ["macros"] } rand = "0.8.5" -regex = "1.8.1" -reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] } -ring = "0.17.7" +regex = "1.10.4" +reqwest = { version = "0.12.4", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } +ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } -ruma = { git = "https://github.com/ruma/ruma", rev = "5495b85aa311c2805302edb0a7de40399e22b397", 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" ] } -rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] } -rust-argon2 = "1.0.0" +ruma = { git = "https://github.com/ruma/ruma", branch = "main", 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" ] } +rusqlite = { version = "0.31.0", optional = true, features = ["bundled"] } +rust-argon2 = "2.1.0" sd-notify = { version = "0.4.1", optional = true } -serde = { version = "1.0.163", features = ["rc"] } -serde_html_form = "0.2.0" -serde_json = { version = "1.0.96", features = ["raw_value"] } -serde_yaml = "0.9.21" +serde = { version = "1.0.202", features = ["rc"] } +serde_html_form = "0.2.6" +serde_json = { version = "1.0.117", features = ["raw_value"] } +serde_yaml = "0.9.34" sha-1 = "0.10.1" -thiserror = "1.0.40" -thread_local = "1.1.7" -tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -tokio = { version = "1.28.1", features = ["fs", "macros", "signal", "sync"] } +thiserror = "1.0.61" +thread_local = "1.1.8" +tikv-jemallocator = { version = "0.5.4", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } +tokio = { version = "1.37.0", features = ["fs", "macros", "signal", "sync"] } tower = { version = "0.4.13", features = ["util"] } -tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } -tracing = { version = "0.1.37", features = [] } +tower-http = { version = "0.5.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } +tracing = { version = "0.1.40", features = [] } tracing-flame = "0.2.0" -tracing-opentelemetry = "0.18.0" -tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } -trust-dns-resolver = "0.22.0" +tracing-opentelemetry = { git = "https://github.com/tokio-rs/tracing-opentelemetry", branch = "v0.1.x" } +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +trust-dns-resolver = "0.23.2" [target.'cfg(unix)'.dependencies] -nix = { version = "0.28", features = ["resource"] } +nix = { version = "0.29", features = ["resource"] } [features] default = ["rocksdb", "sqlite", "systemd"] diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index eaf3f841..1b180766 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -93,7 +93,7 @@ pub(crate) async fn register_route( && body.appservice_info.is_none() { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Registration has been disabled.", )); } diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index e18706aa..989b1c66 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -53,7 +53,7 @@ pub(crate) async fn get_context_route( &body.event_id, )? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this event.", )); } diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 49b4d4e2..e55a601e 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -450,7 +450,7 @@ pub(crate) async fn get_member_events_route( .user_can_see_state_events(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this room.", )); } @@ -485,7 +485,7 @@ pub(crate) async fn joined_members_route( .user_can_see_state_events(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this room.", )); } @@ -1399,7 +1399,7 @@ pub(crate) async fn invite_helper( if !services().rooms.state_cache.is_joined(sender_user, room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this room.", )); } diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 6fe47f18..162c2d63 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -48,7 +48,7 @@ pub(crate) async fn send_message_event_route( && !services().globals.allow_encryption() { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Encryption has been disabled", )); } diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 1713fb57..c40224c9 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -49,6 +49,8 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route( chunk: res.chunk, next_batch: res.next_batch, prev_batch: res.prev_batch, + // TODO + recursion_depth: None, })) } @@ -92,6 +94,8 @@ pub(crate) async fn get_relating_events_with_rel_type_route( chunk: res.chunk, next_batch: res.next_batch, prev_batch: res.prev_batch, + // TODO + recursion_depth: None, })) } diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 87111aa9..7c4dda90 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -78,7 +78,7 @@ pub(crate) async fn create_room_route( && !services().users.is_admin(sender_user)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Room creation has been disabled.", )); } @@ -565,7 +565,7 @@ pub(crate) async fn get_room_event_route( &body.event_id, )? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this event.", )); } @@ -591,7 +591,7 @@ pub(crate) async fn get_room_aliases_route( if !services().rooms.state_cache.is_joined(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this room.", )); } diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 111281a2..6e779a18 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -53,7 +53,7 @@ pub(crate) async fn search_events_route( for room_id in room_ids { if !services().rooms.state_cache.is_joined(sender_user, &room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view this room.", )); } diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index b11d6f31..f18dd3b2 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -81,7 +81,7 @@ pub(crate) async fn login_route( } else { warn!("Bad login type: {:?}", &body.login_info); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Bad login type.", )); } @@ -101,7 +101,7 @@ pub(crate) async fn login_route( let hash = services().users.password_hash(&user_id)?.ok_or( Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Wrong username or password.", ), )?; @@ -119,7 +119,7 @@ pub(crate) async fn login_route( if !hash_matches { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Wrong username or password.", )); } @@ -190,7 +190,7 @@ pub(crate) async fn login_route( } else { warn!("Bad login type: {:?}", &body.login_info); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Bad login type.", )); } diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index f04ae3fd..9da197b9 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -63,7 +63,7 @@ pub(crate) async fn send_state_event_for_empty_key_route( && !services().globals.allow_encryption() { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Encryption has been disabled", )); } @@ -101,7 +101,7 @@ pub(crate) async fn get_state_events_route( .user_can_see_state_events(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view the room state.", )); } @@ -135,7 +135,7 @@ pub(crate) async fn get_state_events_for_key_route( .user_can_see_state_events(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view the room state.", )); } @@ -176,7 +176,7 @@ pub(crate) async fn get_state_events_for_empty_key_route( .user_can_see_state_events(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You don't have permission to view the room state.", )); } @@ -232,7 +232,7 @@ async fn send_state_event_for_key_helper( .is_none() { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You are only allowed to send canonical_alias events when \ it's aliases already exists", )); diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index a5a77448..ca56edde 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -605,7 +605,9 @@ async fn load_joined_room( .state_cache .is_invited(&user_id, room_id)?) { - Ok::<_, Error>(Some(state_key.clone())) + Ok::<_, Error>(Some(state_key.parse().expect( + "`state_key` should be a valid user ID", + ))) } else { Ok(None) } @@ -1686,6 +1688,8 @@ pub(crate) async fn sync_events_v4_route( // Count events in timeline greater than global sync counter num_live: None, timestamp: None, + // TODO + heroes: None, }, ); } diff --git a/src/api/client_server/typing.rs b/src/api/client_server/typing.rs index f6cf9bc0..2657b084 100644 --- a/src/api/client_server/typing.rs +++ b/src/api/client_server/typing.rs @@ -14,7 +14,7 @@ pub(crate) async fn create_typing_event_route( if !services().rooms.state_cache.is_joined(sender_user, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "You are not in this room.", )); } diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 8dd997e4..98dc5e98 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -2,19 +2,22 @@ use std::{collections::BTreeMap, iter::FromIterator, str}; use axum::{ async_trait, - body::{Full, HttpBody}, - extract::{ - rejection::TypedHeaderRejectionReason, FromRequest, Path, TypedHeader, - }, + body::Body, + extract::{FromRequest, Path}, + response::{IntoResponse, Response}, + RequestExt, RequestPartsExt, +}; +use axum_extra::{ headers::{ authorization::{Bearer, Credentials}, Authorization, }, - response::{IntoResponse, Response}, - BoxError, RequestExt, RequestPartsExt, + typed_header::TypedHeaderRejectionReason, + TypedHeader, }; -use bytes::{Buf, BufMut, Bytes, BytesMut}; +use bytes::{BufMut, BytesMut}; use http::{Request, StatusCode}; +use http_body_util::BodyExt; use ruma::{ api::{ client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse, @@ -35,18 +38,15 @@ enum Token { } #[async_trait] -impl FromRequest for Ar +impl FromRequest for Ar where T: IncomingRequest, - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, { type Rejection = Error; #[allow(clippy::too_many_lines)] async fn from_request( - req: Request, + req: axum::extract::Request, _state: &S, ) -> Result { #[derive(Deserialize)] @@ -55,21 +55,17 @@ where user_id: Option, } - let (mut parts, mut body) = match req.with_limited_body() { - Ok(limited_req) => { - let (parts, body) = limited_req.into_parts(); - let body = to_bytes(body).await.map_err(|_| { + let (mut parts, mut body) = { + let limited_req = req.with_limited_body(); + let (parts, body) = limited_req.into_parts(); + let body = body + .collect() + .await + .map_err(|_| { Error::BadRequest(ErrorKind::MissingToken, "Missing token.") - })?; - (parts, body) - } - Err(original_req) => { - let (parts, body) = original_req.into_parts(); - let body = to_bytes(body).await.map_err(|_| { - Error::BadRequest(ErrorKind::MissingToken, "Missing token.") - })?; - (parts, body) - } + })? + .to_bytes(); + (parts, body) }; let metadata = T::METADATA; @@ -151,7 +147,7 @@ where if !services().users.exists(&user_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "User does not exist.", )); } @@ -196,7 +192,7 @@ where _ => "Unknown header-related error", }; - Error::BadRequest(ErrorKind::Forbidden, msg) + Error::BadRequest(ErrorKind::forbidden(), msg) })?; let origin_signatures = BTreeMap::from_iter([( @@ -261,7 +257,7 @@ where Err(e) => { warn!("Failed to fetch signing keys: {}", e); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Failed to fetch signing keys.", )); } @@ -294,7 +290,7 @@ where } return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Failed to verify X-Matrix signatures.", )); } @@ -453,62 +449,10 @@ impl Credentials for XMatrix { impl IntoResponse for Ra { fn into_response(self) -> Response { match self.0.try_into_http_response::() { - Ok(res) => res.map(BytesMut::freeze).map(Full::new).into_response(), + Ok(res) => { + res.map(BytesMut::freeze).map(Body::from).into_response() + } Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(), } } } - -// copied from hyper under the following license: -// Copyright (c) 2014-2021 Sean McArthur - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -pub(crate) async fn to_bytes(body: T) -> Result -where - T: HttpBody, -{ - futures_util::pin_mut!(body); - - // If there's only 1 chunk, we can just return Buf::to_bytes() - let mut first = if let Some(buf) = body.data().await { - buf? - } else { - return Ok(Bytes::new()); - }; - - let second = if let Some(buf) = body.data().await { - buf? - } else { - return Ok(first.copy_to_bytes(first.remaining())); - }; - - // With more than 1 buf, we gotta flatten into a Vec first. - let cap = first.remaining() - + second.remaining() - + body.size_hint().lower().try_into().unwrap_or(usize::MAX); - let mut vec = Vec::with_capacity(cap); - vec.put(first); - vec.put(second); - - while let Some(buf) = body.data().await { - vec.put(buf?); - } - - Ok(vec.into()) -} diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 2455f2b0..e69a52fc 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1023,7 +1023,7 @@ pub(crate) async fn get_event_route( .server_in_room(sender_servername, room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not in room", )); } @@ -1034,7 +1034,7 @@ pub(crate) async fn get_event_route( &body.event_id, )? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not allowed to see event.", )); } @@ -1064,7 +1064,7 @@ pub(crate) async fn get_backfill_route( .server_in_room(sender_servername, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not in room.", )); } @@ -1132,7 +1132,7 @@ pub(crate) async fn get_missing_events_route( .server_in_room(sender_servername, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not in room", )); } @@ -1234,7 +1234,7 @@ pub(crate) async fn get_event_authorization_route( .server_in_room(sender_servername, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not in room.", )); } @@ -1292,7 +1292,7 @@ pub(crate) async fn get_room_state_route( .server_in_room(sender_servername, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not in room.", )); } @@ -1362,7 +1362,7 @@ pub(crate) async fn get_room_state_ids_route( .server_in_room(sender_servername, &body.room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server is not in room.", )); } diff --git a/src/database/key_value/uiaa.rs b/src/database/key_value/uiaa.rs index 3452b990..8fac4a21 100644 --- a/src/database/key_value/uiaa.rs +++ b/src/database/key_value/uiaa.rs @@ -81,7 +81,7 @@ impl service::uiaa::Data for KeyValueDatabase { .userdevicesessionid_uiaainfo .get(&userdevicesessionid)? .ok_or(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "UIAA session does not exist.", ))?, ) diff --git a/src/main.rs b/src/main.rs index 799727da..0af72c4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,8 @@ use http::{ header::{self, HeaderName}, Method, StatusCode, Uri, }; +use opentelemetry::KeyValue; +use opentelemetry_sdk::Resource; use ruma::api::{ client::{ error::{Error as RumaError, ErrorBody, ErrorKind}, @@ -127,12 +129,20 @@ async fn try_main() -> Result<(), error::Main> { if config.allow_jaeger { opentelemetry::global::set_text_map_propagator( - opentelemetry_jaeger::Propagator::new(), + opentelemetry_jaeger_propagator::Propagator::new(), ); - let tracer = opentelemetry_jaeger::new_agent_pipeline() - .with_auto_split_batch(true) - .with_service_name("grapevine") - .install_batch(opentelemetry::runtime::Tokio)?; + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_trace_config( + opentelemetry_sdk::trace::config().with_resource( + Resource::new(vec![KeyValue::new( + "service.name", + env!("CARGO_PKG_NAME"), + )]), + ), + ) + .with_exporter(opentelemetry_otlp::new_exporter().tonic()) + .install_batch(opentelemetry_sdk::runtime::Tokio)?; let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); let filter_layer = EnvFilter::try_new(&config.log)?; @@ -271,9 +281,9 @@ async fn run_server() -> io::Result<()> { /// /// The axum request handler task gets cancelled if the connection is shut down; /// by spawning our own task, processing continue after the client disconnects. -async fn spawn_task( - req: http::Request, - next: axum::middleware::Next, +async fn spawn_task( + req: axum::extract::Request, + next: axum::middleware::Next, ) -> std::result::Result { if services().globals.shutdown.load(atomic::Ordering::Relaxed) { return Err(StatusCode::SERVICE_UNAVAILABLE); @@ -283,9 +293,9 @@ async fn spawn_task( .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } -async fn unrecognized_method( - req: http::Request, - next: axum::middleware::Next, +async fn unrecognized_method( + req: axum::extract::Request, + next: axum::middleware::Next, ) -> std::result::Result { let method = req.method().clone(); let uri = req.uri().clone(); diff --git a/src/service/globals.rs b/src/service/globals.rs index e31a47a6..45cc9ef5 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -17,11 +17,11 @@ use std::{ use base64::{engine::general_purpose, Engine as _}; pub(crate) use data::Data; use futures_util::FutureExt; -use hyper::{ - client::connect::dns::{GaiResolver, Name}, - service::Service as HyperService, +use hyper::service::Service as _; +use hyper_util::{ + client::legacy::connect::dns::GaiResolver, service::TowerToHyperService, }; -use reqwest::dns::{Addrs, Resolve, Resolving}; +use reqwest::dns::{Addrs, Name, Resolve, Resolving}; use ruma::{ api::federation::discovery::{ServerSigningKeys, VerifyKey}, serde::Base64, @@ -147,9 +147,13 @@ impl Resolve for Resolver { }) }) .unwrap_or_else(|| { - let this = &mut self.inner.clone(); + // This should never fail because reqwest's type is a wrapper + // around hyper-utils' type + let name = name.as_str().parse().expect("name should be valid"); + Box::pin( - HyperService::::call(this, name) + TowerToHyperService::new(self.inner.clone()) + .call(name) .map(|result| { result .map(|addrs| -> Addrs { Box::new(addrs) }) diff --git a/src/service/media.rs b/src/service/media.rs index c326575f..91a27e78 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -229,7 +229,7 @@ impl Service { let mut thumbnail_bytes = Vec::new(); thumbnail.write_to( &mut Cursor::new(&mut thumbnail_bytes), - image::ImageOutputFormat::Png, + image::ImageFormat::Png, )?; // Save thumbnail in database so we don't have to generate it diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index e7383e1f..4a551953 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -155,7 +155,7 @@ impl Service { Ok(Some(pdu)) => { if pdu.room_id != room_id { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Evil event in db", )); } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 3faf8f93..5710259c 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -96,7 +96,7 @@ impl Service { if services().rooms.metadata.is_disabled(room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Federation of this room is currently disabled on this server.", )); } @@ -170,7 +170,7 @@ impl Service { // Check for disabled again because it might have changed if services().rooms.metadata.is_disabled(room_id)? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Federation of this room is currently disabled on this \ server.", )); @@ -1836,7 +1836,7 @@ impl Service { server_name, room_id ); Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Server was denied by room ACL", )) } diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 7dcbbe84..319930bd 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -121,6 +121,8 @@ impl Service { chunk: events_after, next_batch: next_token.map(|t| t.stringify()), prev_batch: Some(from.stringify()), + // TODO + recursion_depth: None, }) } ruma::api::Direction::Backward => { @@ -174,6 +176,8 @@ impl Service { chunk: events_before, next_batch: next_token.map(|t| t.stringify()), prev_batch: Some(from.stringify()), + // TODO + recursion_depth: None, }) } } diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index da54bed9..03485f20 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -463,7 +463,7 @@ impl Service { debug!("User is not allowed to see room {room_id}"); // This error will be caught later return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "User is not allowed to see the room", )); } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 9ef5758b..53926c3e 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -794,7 +794,7 @@ impl Service { if !auth_check { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Event is not authorized.", )); } @@ -878,7 +878,7 @@ impl Service { TimelineEventType::RoomEncryption => { warn!("Encryption is not allowed in the admins room"); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Encryption is not allowed in the admins room.", )); } @@ -916,7 +916,7 @@ impl Service { room" ); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Grapevine user cannot leave from admins \ room.", )); @@ -935,7 +935,7 @@ impl Service { "Last admin cannot leave from admins room" ); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Last admin cannot leave from admins room.", )); } @@ -950,7 +950,7 @@ impl Service { admins room" ); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Grapevine user cannot be banned in \ admins room.", )); @@ -970,7 +970,7 @@ impl Service { room" ); return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "Last admin cannot be banned in admins \ room.", )); @@ -1004,7 +1004,7 @@ impl Service { false, )? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "User cannot redact this event.", )); } @@ -1026,7 +1026,7 @@ impl Service { false, )? { return Err(Error::BadRequest( - ErrorKind::Forbidden, + ErrorKind::forbidden(), "User cannot redact this event.", )); } diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 61e4e252..25c2bbe7 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -94,7 +94,7 @@ impl Service { if !hash_matches { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { - kind: ErrorKind::Forbidden, + kind: ErrorKind::forbidden(), message: "Invalid username or password." .to_owned(), }); @@ -113,7 +113,7 @@ impl Service { } else { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { - kind: ErrorKind::Forbidden, + kind: ErrorKind::forbidden(), message: "Invalid registration token.".to_owned(), }); return Ok((false, uiaainfo)); diff --git a/src/utils/error.rs b/src/utils/error.rs index 3491d85c..3b165767 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -125,7 +125,9 @@ impl Error { WrongRoomKeysVersion { .. } - | Forbidden + | Forbidden { + .. + } | GuestAccessForbidden | ThreepidAuthFailed | UserDeactivated From a275543494f687afe347dcdbbe83ada5f8b89f7d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 26 May 2024 19:29:40 -0700 Subject: [PATCH 158/617] enable axum's tracing feature This pretty much only makes it log a single error event, but it's one that's useful to know about. --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 11ba7590..7d5e3abb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,7 @@ dependencies = [ "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f4f7e713..bedeac21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ workspace = true # Keep sorted [dependencies] async-trait = "0.1.80" -axum = { version = "0.7.5", default-features = false, features = ["form", "http1", "http2", "json", "matched-path"] } +axum = { version = "0.7.5", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } axum-extra = { version = "0.9.3", features = ["typed-header"] } axum-server = { version = "0.6.0", features = ["tls-rustls"] } base64 = "0.22.1" From c17ab5328dedd9f101ac61eef93073369d867b88 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 23 May 2024 13:39:40 -0700 Subject: [PATCH 159/617] factor observability things into its own module And also create a new error for the observability initialization. --- src/error.rs | 22 +++++++++++----- src/main.rs | 52 ++---------------------------------- src/observability.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 src/observability.rs diff --git a/src/error.rs b/src/error.rs index dd50fee0..33e3bfce 100644 --- a/src/error.rs +++ b/src/error.rs @@ -48,6 +48,22 @@ pub(crate) enum Main { #[error("invalid configuration")] ConfigInvalid(#[from] figment::Error), + #[error("failed to initialize observability")] + Observability(#[from] Observability), + + #[error("failed to load or create the database")] + DatabaseError(#[source] crate::utils::error::Error), + + #[error("failed to serve requests")] + Serve(#[source] std::io::Error), +} + +/// Observability initialization errors +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum Observability { // Upstream's documentation on what this error means is very sparse #[error("opentelemetry error")] Otel(#[from] opentelemetry::trace::TraceError), @@ -61,10 +77,4 @@ pub(crate) enum Main { // Upstream's documentation on what this error means is very sparse #[error("tracing_flame error")] TracingFlame(#[from] tracing_flame::Error), - - #[error("failed to load or create the database")] - DatabaseError(#[source] crate::utils::error::Error), - - #[error("failed to serve requests")] - Serve(#[source] std::io::Error), } diff --git a/src/main.rs b/src/main.rs index 0af72c4d..6f8a4038 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,8 +24,6 @@ use http::{ header::{self, HeaderName}, Method, StatusCode, Uri, }; -use opentelemetry::KeyValue; -use opentelemetry_sdk::Resource; use ruma::api::{ client::{ error::{Error as RumaError, ErrorBody, ErrorKind}, @@ -41,13 +39,13 @@ use tower_http::{ ServiceBuilderExt as _, }; use tracing::{debug, info, warn}; -use tracing_subscriber::{prelude::*, EnvFilter}; pub(crate) mod api; pub(crate) mod clap; mod config; mod database; mod error; +mod observability; mod service; mod utils; @@ -127,48 +125,7 @@ async fn try_main() -> Result<(), error::Main> { config.warn_deprecated(); - if config.allow_jaeger { - opentelemetry::global::set_text_map_propagator( - opentelemetry_jaeger_propagator::Propagator::new(), - ); - let tracer = opentelemetry_otlp::new_pipeline() - .tracing() - .with_trace_config( - opentelemetry_sdk::trace::config().with_resource( - Resource::new(vec![KeyValue::new( - "service.name", - env!("CARGO_PKG_NAME"), - )]), - ), - ) - .with_exporter(opentelemetry_otlp::new_exporter().tonic()) - .install_batch(opentelemetry_sdk::runtime::Tokio)?; - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - - let filter_layer = EnvFilter::try_new(&config.log)?; - - let subscriber = tracing_subscriber::Registry::default() - .with(filter_layer) - .with(telemetry); - tracing::subscriber::set_global_default(subscriber)?; - } else if config.tracing_flame { - let registry = tracing_subscriber::Registry::default(); - let (flame_layer, _guard) = - tracing_flame::FlameLayer::with_file("./tracing.folded")?; - let flame_layer = flame_layer.with_empty_samples(false); - - let filter_layer = EnvFilter::new("trace,h2=off"); - - let subscriber = registry.with(filter_layer).with(flame_layer); - tracing::subscriber::set_global_default(subscriber)?; - } else { - let registry = tracing_subscriber::Registry::default(); - let fmt_layer = tracing_subscriber::fmt::Layer::new(); - let filter_layer = EnvFilter::try_new(&config.log)?; - - let subscriber = registry.with(filter_layer).with(fmt_layer); - tracing::subscriber::set_global_default(subscriber)?; - } + let _guard = observability::init(&config); // This is needed for opening lots of file descriptors, which tends to // happen more often when using RocksDB and making lots of federation @@ -185,15 +142,10 @@ async fn try_main() -> Result<(), error::Main> { KeyValueDatabase::load_or_create(config) .await .map_err(Error::DatabaseError)?; - let config = &services().globals.config; info!("Starting server"); run_server().await.map_err(Error::Serve)?; - if config.allow_jaeger { - opentelemetry::global::shutdown_tracer_provider(); - } - Ok(()) } diff --git a/src/observability.rs b/src/observability.rs new file mode 100644 index 00000000..17dc9201 --- /dev/null +++ b/src/observability.rs @@ -0,0 +1,63 @@ +//! Facilities for observing runtime behavior +#![warn(missing_docs, clippy::missing_docs_in_private_items)] + +use opentelemetry::KeyValue; +use opentelemetry_sdk::Resource; +use tracing_flame::FlameLayer; +use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; + +use crate::{config::Config, error, utils::error::Result}; + +/// Cleans up resources relating to observability when [`Drop`]ped +pub(crate) struct Guard; + +impl Drop for Guard { + fn drop(&mut self) { + opentelemetry::global::shutdown_tracer_provider(); + } +} + +/// Initialize observability +pub(crate) fn init(config: &Config) -> Result { + if config.allow_jaeger { + opentelemetry::global::set_text_map_propagator( + opentelemetry_jaeger_propagator::Propagator::new(), + ); + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_trace_config( + opentelemetry_sdk::trace::config().with_resource( + Resource::new(vec![KeyValue::new( + "service.name", + env!("CARGO_PKG_NAME"), + )]), + ), + ) + .with_exporter(opentelemetry_otlp::new_exporter().tonic()) + .install_batch(opentelemetry_sdk::runtime::Tokio)?; + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + + let filter_layer = EnvFilter::try_new(&config.log)?; + + let subscriber = Registry::default().with(filter_layer).with(telemetry); + tracing::subscriber::set_global_default(subscriber)?; + } else if config.tracing_flame { + let registry = Registry::default(); + let (flame_layer, _guard) = FlameLayer::with_file("./tracing.folded")?; + let flame_layer = flame_layer.with_empty_samples(false); + + let filter_layer = EnvFilter::new("trace,h2=off"); + + let subscriber = registry.with(filter_layer).with(flame_layer); + tracing::subscriber::set_global_default(subscriber)?; + } else { + let registry = Registry::default(); + let fmt_layer = tracing_subscriber::fmt::Layer::new(); + let filter_layer = EnvFilter::try_new(&config.log)?; + + let subscriber = registry.with(filter_layer).with(fmt_layer); + tracing::subscriber::set_global_default(subscriber)?; + } + + Ok(Guard) +} From 263edcc8a127ad2a541a3bb6ad35a8a459ea5616 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 24 May 2024 09:24:54 +0000 Subject: [PATCH 160/617] observability: don't drop tracing_flame drop guard until exit --- src/observability.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index 17dc9201..9b7a9c89 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -1,15 +1,21 @@ //! Facilities for observing runtime behavior #![warn(missing_docs, clippy::missing_docs_in_private_items)] +use std::{fs::File, io::BufWriter}; + use opentelemetry::KeyValue; use opentelemetry_sdk::Resource; -use tracing_flame::FlameLayer; +use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; use crate::{config::Config, error, utils::error::Result}; /// Cleans up resources relating to observability when [`Drop`]ped -pub(crate) struct Guard; +pub(crate) struct Guard { + /// Drop guard used to flush [`tracing_flame`] data on exit + #[allow(dead_code)] + flame_guard: Option>>, +} impl Drop for Guard { fn drop(&mut self) { @@ -19,6 +25,7 @@ impl Drop for Guard { /// Initialize observability pub(crate) fn init(config: &Config) -> Result { + let mut flame_guard = None; if config.allow_jaeger { opentelemetry::global::set_text_map_propagator( opentelemetry_jaeger_propagator::Propagator::new(), @@ -43,7 +50,8 @@ pub(crate) fn init(config: &Config) -> Result { tracing::subscriber::set_global_default(subscriber)?; } else if config.tracing_flame { let registry = Registry::default(); - let (flame_layer, _guard) = FlameLayer::with_file("./tracing.folded")?; + let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded")?; + flame_guard = Some(guard); let flame_layer = flame_layer.with_empty_samples(false); let filter_layer = EnvFilter::new("trace,h2=off"); @@ -59,5 +67,7 @@ pub(crate) fn init(config: &Config) -> Result { tracing::subscriber::set_global_default(subscriber)?; } - Ok(Guard) + Ok(Guard { + flame_guard, + }) } From 7a154f74166c1309ca5752149e02bbe44cd91431 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 23 May 2024 18:53:54 +0000 Subject: [PATCH 161/617] observability: allow combining multiple tracing layers Previously, jaeger, tracing_flame and stderr output were mutually exclusive. There's no good reason for this. --- src/observability.rs | 84 +++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index 9b7a9c89..0459a994 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -6,7 +6,7 @@ use std::{fs::File, io::BufWriter}; use opentelemetry::KeyValue; use opentelemetry_sdk::Resource; use tracing_flame::{FlameLayer, FlushGuard}; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; +use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; use crate::{config::Config, error, utils::error::Result}; @@ -25,47 +25,59 @@ impl Drop for Guard { /// Initialize observability pub(crate) fn init(config: &Config) -> Result { - let mut flame_guard = None; - if config.allow_jaeger { - opentelemetry::global::set_text_map_propagator( - opentelemetry_jaeger_propagator::Propagator::new(), - ); - let tracer = opentelemetry_otlp::new_pipeline() - .tracing() - .with_trace_config( - opentelemetry_sdk::trace::config().with_resource( - Resource::new(vec![KeyValue::new( - "service.name", - env!("CARGO_PKG_NAME"), - )]), - ), + let config_filter_layer = || EnvFilter::try_new(&config.log); + + let jaeger_layer = config + .allow_jaeger + .then(|| { + opentelemetry::global::set_text_map_propagator( + opentelemetry_jaeger_propagator::Propagator::new(), + ); + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_trace_config( + opentelemetry_sdk::trace::config().with_resource( + Resource::new(vec![KeyValue::new( + "service.name", + env!("CARGO_PKG_NAME"), + )]), + ), + ) + .with_exporter(opentelemetry_otlp::new_exporter().tonic()) + .install_batch(opentelemetry_sdk::runtime::Tokio)?; + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + + Ok::<_, error::Observability>( + telemetry.with_filter(config_filter_layer()?), ) - .with_exporter(opentelemetry_otlp::new_exporter().tonic()) - .install_batch(opentelemetry_sdk::runtime::Tokio)?; - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + }) + .transpose()?; - let filter_layer = EnvFilter::try_new(&config.log)?; + let (flame_layer, flame_guard) = config + .tracing_flame + .then(|| { + let (flame_layer, guard) = + FlameLayer::with_file("./tracing.folded")?; + let flame_layer = flame_layer.with_empty_samples(false); - let subscriber = Registry::default().with(filter_layer).with(telemetry); - tracing::subscriber::set_global_default(subscriber)?; - } else if config.tracing_flame { - let registry = Registry::default(); - let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded")?; - flame_guard = Some(guard); - let flame_layer = flame_layer.with_empty_samples(false); + let filter_layer = EnvFilter::new("trace,h2=off"); - let filter_layer = EnvFilter::new("trace,h2=off"); + Ok::<_, error::Observability>(( + flame_layer.with_filter(filter_layer), + guard, + )) + }) + .transpose()? + .unzip(); - let subscriber = registry.with(filter_layer).with(flame_layer); - tracing::subscriber::set_global_default(subscriber)?; - } else { - let registry = Registry::default(); - let fmt_layer = tracing_subscriber::fmt::Layer::new(); - let filter_layer = EnvFilter::try_new(&config.log)?; + let fmt_layer = tracing_subscriber::fmt::Layer::new() + .with_filter(config_filter_layer()?); - let subscriber = registry.with(filter_layer).with(fmt_layer); - tracing::subscriber::set_global_default(subscriber)?; - } + let subscriber = Registry::default() + .with(jaeger_layer) + .with(flame_layer) + .with(fmt_layer); + tracing::subscriber::set_global_default(subscriber)?; Ok(Guard { flame_guard, From 507de063f53f52e0cf8e2c1a67215a5ad87bb35a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 27 May 2024 10:06:26 -0700 Subject: [PATCH 162/617] don't hardcode EnvFilter for tracing-flame --- src/observability.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index 0459a994..1c28e978 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -60,10 +60,8 @@ pub(crate) fn init(config: &Config) -> Result { FlameLayer::with_file("./tracing.folded")?; let flame_layer = flame_layer.with_empty_samples(false); - let filter_layer = EnvFilter::new("trace,h2=off"); - Ok::<_, error::Observability>(( - flame_layer.with_filter(filter_layer), + flame_layer.with_filter(config_filter_layer()?), guard, )) }) From e294543ddb44e536f80f2d19bb9de2556792b126 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 18:28:26 +0000 Subject: [PATCH 163/617] sending.rs: add RequestKey Much easier to reason about than with a bunch of Vec everywhere. --- src/database/key_value/sending.rs | 39 +++++++++++++++++++------------ src/service/sending.rs | 21 +++++++++++++---- src/service/sending/data.rs | 17 +++++++------- 3 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 29ba6fd7..a03b2596 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -4,7 +4,7 @@ use crate::{ database::KeyValueDatabase, service::{ self, - sending::{OutgoingKind, SendingEventType}, + sending::{OutgoingKind, RequestKey, SendingEventType}, }, services, utils, Error, Result, }; @@ -13,10 +13,12 @@ impl service::sending::Data for KeyValueDatabase { fn active_requests<'a>( &'a self, ) -> Box< - dyn Iterator, OutgoingKind, SendingEventType)>> - + 'a, + dyn Iterator< + Item = Result<(RequestKey, OutgoingKind, SendingEventType)>, + > + 'a, > { Box::new(self.servercurrentevent_data.iter().map(|(key, v)| { + let key = RequestKey::new(key); parse_servercurrentevent(&key, v).map(|(k, e)| (key, k, e)) })) } @@ -24,16 +26,19 @@ impl service::sending::Data for KeyValueDatabase { fn active_requests_for<'a>( &'a self, outgoing_kind: &OutgoingKind, - ) -> Box, SendingEventType)>> + 'a> + ) -> Box> + 'a> { let prefix = outgoing_kind.get_prefix(); Box::new(self.servercurrentevent_data.scan_prefix(prefix).map( - |(key, v)| parse_servercurrentevent(&key, v).map(|(_, e)| (key, e)), + |(key, v)| { + let key = RequestKey::new(key); + parse_servercurrentevent(&key, v).map(|(_, e)| (key, e)) + }, )) } - fn delete_active_request(&self, key: Vec) -> Result<()> { - self.servercurrentevent_data.remove(&key) + fn delete_active_request(&self, key: RequestKey) -> Result<()> { + self.servercurrentevent_data.remove(key.as_bytes()) } fn delete_all_active_requests_for( @@ -51,7 +56,7 @@ impl service::sending::Data for KeyValueDatabase { fn queue_requests( &self, requests: &[(&OutgoingKind, SendingEventType)], - ) -> Result>> { + ) -> Result> { let mut batch = Vec::new(); let mut keys = Vec::new(); for (outgoing_kind, event) in requests { @@ -69,7 +74,7 @@ impl service::sending::Data for KeyValueDatabase { &[] }; batch.push((key.clone(), value.to_owned())); - keys.push(key); + keys.push(RequestKey::new(key)); } self.servernameevent_data.insert_batch(&mut batch.into_iter())?; Ok(keys) @@ -78,17 +83,20 @@ impl service::sending::Data for KeyValueDatabase { fn queued_requests<'a>( &'a self, outgoing_kind: &OutgoingKind, - ) -> Box)>> + 'a> + ) -> Box> + 'a> { let prefix = outgoing_kind.get_prefix(); return Box::new(self.servernameevent_data.scan_prefix(prefix).map( - |(k, v)| parse_servercurrentevent(&k, v).map(|(_, ev)| (ev, k)), + |(k, v)| { + let k = RequestKey::new(k); + parse_servercurrentevent(&k, v).map(|(_, ev)| (ev, k)) + }, )); } fn mark_as_active( &self, - events: &[(SendingEventType, Vec)], + events: &[(SendingEventType, RequestKey)], ) -> Result<()> { for (e, key) in events { let value = if let SendingEventType::Edu(value) = &e { @@ -96,8 +104,8 @@ impl service::sending::Data for KeyValueDatabase { } else { &[] }; - self.servercurrentevent_data.insert(key, value)?; - self.servernameevent_data.remove(key)?; + self.servercurrentevent_data.insert(key.as_bytes(), value)?; + self.servernameevent_data.remove(key.as_bytes())?; } Ok(()) @@ -126,9 +134,10 @@ impl service::sending::Data for KeyValueDatabase { #[tracing::instrument(skip(key))] fn parse_servercurrentevent( - key: &[u8], + key: &RequestKey, value: Vec, ) -> Result<(OutgoingKind, SendingEventType)> { + let key = key.as_bytes(); // Appservices start with a plus Ok::<_, Error>(if key.starts_with(b"+") { let mut parts = key[1..].splitn(2, |&b| b == 0xFF); diff --git a/src/service/sending.rs b/src/service/sending.rs index c0fd0fd9..7f994e3a 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -88,15 +88,28 @@ pub(crate) enum SendingEventType { Edu(Vec), } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub(crate) struct RequestKey(Vec); + +impl RequestKey { + pub(crate) fn new(key: Vec) -> Self { + Self(key) + } + + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + pub(crate) struct Service { db: &'static dyn Data, /// The state for a given state hash. pub(super) maximum_requests: Arc, pub(crate) sender: - mpsc::UnboundedSender<(OutgoingKind, SendingEventType, Vec)>, + mpsc::UnboundedSender<(OutgoingKind, SendingEventType, RequestKey)>, receiver: Mutex< - mpsc::UnboundedReceiver<(OutgoingKind, SendingEventType, Vec)>, + mpsc::UnboundedReceiver<(OutgoingKind, SendingEventType, RequestKey)>, >, } @@ -284,7 +297,7 @@ impl Service { &self, outgoing_kind: OutgoingKind, event: SendingEventType, - key: Vec, + key: RequestKey, current_transaction_status: &mut TransactionStatusMap, ) -> Option { if let Ok(Some(events)) = self.select_events( @@ -312,7 +325,7 @@ impl Service { &self, outgoing_kind: &OutgoingKind, // Events we want to send: event and full key - new_events: Vec<(SendingEventType, Vec)>, + new_events: Vec<(SendingEventType, RequestKey)>, current_transaction_status: &mut HashMap< OutgoingKind, TransactionStatus, diff --git a/src/service/sending/data.rs b/src/service/sending/data.rs index 040f9715..ad9915e5 100644 --- a/src/service/sending/data.rs +++ b/src/service/sending/data.rs @@ -1,6 +1,6 @@ use ruma::ServerName; -use super::{OutgoingKind, SendingEventType}; +use super::{OutgoingKind, RequestKey, SendingEventType}; use crate::Result; pub(crate) trait Data: Send + Sync { @@ -8,14 +8,15 @@ pub(crate) trait Data: Send + Sync { fn active_requests<'a>( &'a self, ) -> Box< - dyn Iterator, OutgoingKind, SendingEventType)>> - + 'a, + dyn Iterator< + Item = Result<(RequestKey, OutgoingKind, SendingEventType)>, + > + 'a, >; fn active_requests_for<'a>( &'a self, outgoing_kind: &OutgoingKind, - ) -> Box, SendingEventType)>> + 'a>; - fn delete_active_request(&self, key: Vec) -> Result<()>; + ) -> Box> + 'a>; + fn delete_active_request(&self, key: RequestKey) -> Result<()>; fn delete_all_active_requests_for( &self, outgoing_kind: &OutgoingKind, @@ -23,14 +24,14 @@ pub(crate) trait Data: Send + Sync { fn queue_requests( &self, requests: &[(&OutgoingKind, SendingEventType)], - ) -> Result>>; + ) -> Result>; fn queued_requests<'a>( &'a self, outgoing_kind: &OutgoingKind, - ) -> Box)>> + 'a>; + ) -> Box> + 'a>; fn mark_as_active( &self, - events: &[(SendingEventType, Vec)], + events: &[(SendingEventType, RequestKey)], ) -> Result<()>; fn set_latest_educount( &self, From 9961f1465f426b8b06361e24c825ab6c0190bb7d Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 18:40:44 +0000 Subject: [PATCH 164/617] sending.rs: add RequestData to replace big tuple --- src/service/sending.rs | 72 +++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 7f994e3a..c92a3745 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -101,16 +101,19 @@ impl RequestKey { } } +pub(crate) struct RequestData { + outgoing_kind: OutgoingKind, + event_type: SendingEventType, + key: RequestKey, +} + pub(crate) struct Service { db: &'static dyn Data, /// The state for a given state hash. pub(super) maximum_requests: Arc, - pub(crate) sender: - mpsc::UnboundedSender<(OutgoingKind, SendingEventType, RequestKey)>, - receiver: Mutex< - mpsc::UnboundedReceiver<(OutgoingKind, SendingEventType, RequestKey)>, - >, + pub(crate) sender: mpsc::UnboundedSender, + receiver: Mutex>, } #[derive(Debug)] @@ -202,12 +205,10 @@ impl Service { { futures.push(Self::handle_events(kind, events)); }, - Some((outgoing_kind, event, key)) = receiver.recv() => + Some(data) = receiver.recv() => if let Some(HandlerInputs { kind, events }) = self.handle_receiver( - outgoing_kind, - event, - key, + data, &mut current_transaction_status, ) { @@ -288,21 +289,23 @@ impl Service { } #[tracing::instrument( - skip(self, event, key, current_transaction_status), + skip(self, event_type, key, current_transaction_status), fields( current_status = ?current_transaction_status.get(&outgoing_kind), ), )] fn handle_receiver( &self, - outgoing_kind: OutgoingKind, - event: SendingEventType, - key: RequestKey, + RequestData { + outgoing_kind, + event_type, + key, + }: RequestData, current_transaction_status: &mut TransactionStatusMap, ) -> Option { if let Ok(Some(events)) = self.select_events( &outgoing_kind, - vec![(event, key)], + vec![(event_type, key)], current_transaction_status, ) { Some(HandlerInputs { @@ -531,11 +534,15 @@ impl Service { pushkey: String, ) -> Result<()> { let outgoing_kind = OutgoingKind::Push(user.to_owned(), pushkey); - let event = SendingEventType::Pdu(pdu_id.to_owned()); + let event_type = SendingEventType::Pdu(pdu_id.to_owned()); let keys = - self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; + self.db.queue_requests(&[(&outgoing_kind, event_type.clone())])?; self.sender - .send((outgoing_kind, event, keys.into_iter().next().unwrap())) + .send(RequestData { + outgoing_kind, + event_type, + key: keys.into_iter().next().unwrap(), + }) .unwrap(); Ok(()) @@ -559,8 +566,15 @@ impl Service { let keys = self.db.queue_requests( &requests.iter().map(|(o, e)| (o, e.clone())).collect::>(), )?; - for ((outgoing_kind, event), key) in requests.into_iter().zip(keys) { - self.sender.send((outgoing_kind.clone(), event, key)).unwrap(); + for ((outgoing_kind, event_type), key) in requests.into_iter().zip(keys) + { + self.sender + .send(RequestData { + outgoing_kind: outgoing_kind.clone(), + event_type, + key, + }) + .unwrap(); } Ok(()) @@ -574,11 +588,15 @@ impl Service { id: u64, ) -> Result<()> { let outgoing_kind = OutgoingKind::Normal(server.to_owned()); - let event = SendingEventType::Edu(serialized); + let event_type = SendingEventType::Edu(serialized); let keys = - self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; + self.db.queue_requests(&[(&outgoing_kind, event_type.clone())])?; self.sender - .send((outgoing_kind, event, keys.into_iter().next().unwrap())) + .send(RequestData { + outgoing_kind, + event_type, + key: keys.into_iter().next().unwrap(), + }) .unwrap(); Ok(()) @@ -591,11 +609,15 @@ impl Service { pdu_id: Vec, ) -> Result<()> { let outgoing_kind = OutgoingKind::Appservice(appservice_id); - let event = SendingEventType::Pdu(pdu_id); + let event_type = SendingEventType::Pdu(pdu_id); let keys = - self.db.queue_requests(&[(&outgoing_kind, event.clone())])?; + self.db.queue_requests(&[(&outgoing_kind, event_type.clone())])?; self.sender - .send((outgoing_kind, event, keys.into_iter().next().unwrap())) + .send(RequestData { + outgoing_kind, + event_type, + key: keys.into_iter().next().unwrap(), + }) .unwrap(); Ok(()) From aa69b7d15cae137a805585ae0f1d505a82735e75 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 18:48:16 +0000 Subject: [PATCH 165/617] sending.rs: rename handle_futures->handle_response The future is already done when this is called. --- src/service/sending.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index c92a3745..943bebbc 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -198,7 +198,7 @@ impl Service { select! { Some(response) = futures.next() => if let Some(HandlerInputs { kind, events }) = - self.handle_futures( + self.handle_response( response, &mut current_transaction_status, )? @@ -226,7 +226,7 @@ impl Service { ), ), )] - fn handle_futures( + fn handle_response( &self, response: HandlerResponse, current_transaction_status: &mut TransactionStatusMap, From 5caafdec0624da7293ba55e9b7eb9229f4274b07 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 18:51:08 +0000 Subject: [PATCH 166/617] sending.rs: formatting --- src/service/sending.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 943bebbc..021bec20 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -196,24 +196,27 @@ impl Service { loop { select! { - Some(response) = futures.next() => - if let Some(HandlerInputs { kind, events }) = - self.handle_response( - response, - &mut current_transaction_status, - )? - { + Some(response) = futures.next() => { + if let Some(HandlerInputs { + kind, + events, + }) = self.handle_response( + response, + &mut current_transaction_status, + )? { futures.push(Self::handle_events(kind, events)); - }, - Some(data) = receiver.recv() => - if let Some(HandlerInputs { kind, events }) = - self.handle_receiver( - data, - &mut current_transaction_status, - ) + } + } + Some(data) = receiver.recv() => { + if let Some(HandlerInputs { + kind, + events, + }) = self + .handle_receiver(data, &mut current_transaction_status) { futures.push(Self::handle_events(kind, events)); } + } } } } From 13f79dfee1481e23ea223e874aca1494ad4ad34c Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 20:34:47 +0000 Subject: [PATCH 167/617] sending.rs: factor out event handlers into separate functions This allows for much nicer control flow, since they don't need to return Result. --- src/service/sending.rs | 492 +++++++++++++++++++---------------------- 1 file changed, 228 insertions(+), 264 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 021bec20..3dbfb714 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -627,280 +627,244 @@ impl Service { } #[tracing::instrument(skip(events))] + async fn handle_appservice_event( + id: &str, + events: Vec, + ) -> Result<()> { + let mut pdu_jsons = Vec::new(); + + for event in &events { + match event { + SendingEventType::Pdu(pdu_id) => { + pdu_jsons.push( + services() + .rooms + .timeline + .get_pdu_from_id(pdu_id)? + .ok_or_else(|| { + Error::bad_database( + "[Appservice] Event in \ + servernameevent_data not found in db.", + ) + })? + .to_room_event(), + ); + } + SendingEventType::Edu(_) => { + // Appservices don't need EDUs (?) + } + } + } + + let permit = services().sending.maximum_requests.acquire().await; + + appservice_server::send_request( + services().appservice.get_registration(id).await.ok_or_else( + || { + Error::bad_database( + "[Appservice] Could not load registration from db.", + ) + }, + )?, + appservice::event::push_events::v1::Request { + events: pdu_jsons, + txn_id: general_purpose::URL_SAFE_NO_PAD + .encode(calculate_hash( + &events + .iter() + .map(|e| match e { + SendingEventType::Edu(b) + | SendingEventType::Pdu(b) => &**b, + }) + .collect::>(), + )) + .into(), + }, + ) + .await?; + + drop(permit); + + Ok(()) + } + + #[tracing::instrument(skip(events))] + async fn handle_push_event( + userid: &UserId, + pushkey: &str, + events: Vec, + ) -> Result<()> { + let mut pdus = Vec::new(); + + for event in &events { + match event { + SendingEventType::Pdu(pdu_id) => { + pdus.push( + services() + .rooms + .timeline + .get_pdu_from_id(pdu_id)? + .ok_or_else(|| { + Error::bad_database( + "[Push] Event in servernamevent_datas not \ + found in db.", + ) + })?, + ); + } + // Push gateways don't need EDUs (?) + SendingEventType::Edu(_) => {} + } + } + + for pdu in pdus { + // Redacted events are not notification targets (we don't + // send push for them) + if let Some(unsigned) = &pdu.unsigned { + if let Ok(unsigned) = + serde_json::from_str::(unsigned.get()) + { + if unsigned.get("redacted_because").is_some() { + continue; + } + } + } + + let Some(pusher) = services().pusher.get_pusher(userid, pushkey)? + else { + continue; + }; + + let rules_for_user = services() + .account_data + .get( + None, + userid, + GlobalAccountDataEventType::PushRules.to_string().into(), + ) + .unwrap_or_default() + .and_then(|event| { + serde_json::from_str::(event.get()).ok() + }) + .map_or_else( + || push::Ruleset::server_default(userid), + |ev: PushRulesEvent| ev.content.global, + ); + + let unread: UInt = services() + .rooms + .user + .notification_count(userid, &pdu.room_id)? + .try_into() + .expect("notification count can't go that high"); + + let permit = services().sending.maximum_requests.acquire().await; + + services() + .pusher + .send_push_notice(userid, unread, &pusher, rules_for_user, &pdu) + .await?; + + drop(permit); + } + + Ok(()) + } + + #[tracing::instrument(skip(events))] + async fn handle_federation_event( + server: &ServerName, + events: Vec, + ) -> Result<()> { + let mut edu_jsons = Vec::new(); + let mut pdu_jsons = Vec::new(); + + for event in &events { + match event { + SendingEventType::Pdu(pdu_id) => { + // TODO: check room version and remove event_id if + // needed + pdu_jsons.push( + PduEvent::convert_to_outgoing_federation_event( + services() + .rooms + .timeline + .get_pdu_json_from_id(pdu_id)? + .ok_or_else(|| { + error!( + "event not found: {server} {pdu_id:?}" + ); + Error::bad_database( + "[Normal] Event in \ + servernamevent_datas not found in db.", + ) + })?, + ), + ); + } + SendingEventType::Edu(edu) => { + if let Ok(raw) = serde_json::from_slice(edu) { + edu_jsons.push(raw); + } + } + } + } + + let permit = services().sending.maximum_requests.acquire().await; + + let response = server_server::send_request( + server, + send_transaction_message::v1::Request { + origin: services().globals.server_name().to_owned(), + pdus: pdu_jsons, + edus: edu_jsons, + origin_server_ts: MilliSecondsSinceUnixEpoch::now(), + transaction_id: general_purpose::URL_SAFE_NO_PAD + .encode(calculate_hash( + &events + .iter() + .map(|e| match e { + SendingEventType::Edu(b) + | SendingEventType::Pdu(b) => &**b, + }) + .collect::>(), + )) + .into(), + }, + ) + .await?; + + for pdu in response.pdus { + if pdu.1.is_err() { + warn!("Failed to send to {}: {:?}", server, pdu); + } + } + + drop(permit); + + Ok(()) + } + + #[tracing::instrument(skip_all)] async fn handle_events( kind: OutgoingKind, events: Vec, ) -> HandlerResponse { - match &kind { + let ret = match &kind { OutgoingKind::Appservice(id) => { - let mut pdu_jsons = Vec::new(); - - for event in &events { - match event { - SendingEventType::Pdu(pdu_id) => { - pdu_jsons.push( - services() - .rooms - .timeline - .get_pdu_from_id(pdu_id) - .map_err(|e| (kind.clone(), e))? - .ok_or_else(|| { - ( - kind.clone(), - Error::bad_database( - "[Appservice] Event in \ - servernameevent_data not \ - found in db.", - ), - ) - })? - .to_room_event(), - ); - } - SendingEventType::Edu(_) => { - // Appservices don't need EDUs (?) - } - } - } - - let permit = - services().sending.maximum_requests.acquire().await; - - let response = match appservice_server::send_request( - services() - .appservice - .get_registration(id) - .await - .ok_or_else(|| { - ( - kind.clone(), - Error::bad_database( - "[Appservice] Could not load registration \ - from db.", - ), - ) - })?, - appservice::event::push_events::v1::Request { - events: pdu_jsons, - txn_id: (&*general_purpose::URL_SAFE_NO_PAD.encode( - calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) - | SendingEventType::Pdu(b) => &**b, - }) - .collect::>(), - ), - )) - .into(), - }, - ) - .await - { - Ok(_) => Ok(kind.clone()), - Err(e) => Err((kind.clone(), e)), - }; - - drop(permit); - - response + Self::handle_appservice_event(id, events).await } OutgoingKind::Push(userid, pushkey) => { - let mut pdus = Vec::new(); - - for event in &events { - match event { - SendingEventType::Pdu(pdu_id) => { - pdus.push( - services() - .rooms - .timeline - .get_pdu_from_id(pdu_id) - .map_err(|e| (kind.clone(), e))? - .ok_or_else(|| { - ( - kind.clone(), - Error::bad_database( - "[Push] Event in \ - servernamevent_datas not \ - found in db.", - ), - ) - })?, - ); - } - // Push gateways don't need EDUs (?) - SendingEventType::Edu(_) => {} - } - } - - for pdu in pdus { - // Redacted events are not notification targets (we don't - // send push for them) - if let Some(unsigned) = &pdu.unsigned { - if let Ok(unsigned) = - serde_json::from_str::( - unsigned.get(), - ) - { - if unsigned.get("redacted_because").is_some() { - continue; - } - } - } - - let Some(pusher) = services() - .pusher - .get_pusher(userid, pushkey) - .map_err(|e| { - ( - OutgoingKind::Push( - userid.clone(), - pushkey.clone(), - ), - e, - ) - })? - else { - continue; - }; - - let rules_for_user = services() - .account_data - .get( - None, - userid, - GlobalAccountDataEventType::PushRules - .to_string() - .into(), - ) - .unwrap_or_default() - .and_then(|event| { - serde_json::from_str::(event.get()) - .ok() - }) - .map_or_else( - || push::Ruleset::server_default(userid), - |ev: PushRulesEvent| ev.content.global, - ); - - let unread: UInt = services() - .rooms - .user - .notification_count(userid, &pdu.room_id) - .map_err(|e| (kind.clone(), e))? - .try_into() - .expect("notification count can't go that high"); - - let permit = - services().sending.maximum_requests.acquire().await; - - let _response = services() - .pusher - .send_push_notice( - userid, - unread, - &pusher, - rules_for_user, - &pdu, - ) - .await - .map(|_response| kind.clone()) - .map_err(|e| (kind.clone(), e)); - - drop(permit); - } - Ok(OutgoingKind::Push(userid.clone(), pushkey.clone())) + Self::handle_push_event(userid, pushkey, events).await } OutgoingKind::Normal(server) => { - let mut edu_jsons = Vec::new(); - let mut pdu_jsons = Vec::new(); - - for event in &events { - match event { - SendingEventType::Pdu(pdu_id) => { - // TODO: check room version and remove event_id if - // needed - let raw = - PduEvent::convert_to_outgoing_federation_event( - services() - .rooms - .timeline - .get_pdu_json_from_id(pdu_id) - .map_err(|e| { - ( - OutgoingKind::Normal( - server.clone(), - ), - e, - ) - })? - .ok_or_else(|| { - error!( - "event not found: {server} \ - {pdu_id:?}" - ); - ( - OutgoingKind::Normal( - server.clone(), - ), - Error::bad_database( - "[Normal] Event in \ - servernamevent_datas not \ - found in db.", - ), - ) - })?, - ); - pdu_jsons.push(raw); - } - SendingEventType::Edu(edu) => { - if let Ok(raw) = serde_json::from_slice(edu) { - edu_jsons.push(raw); - } - } - } - } - - let permit = - services().sending.maximum_requests.acquire().await; - - let response = server_server::send_request( - server, - send_transaction_message::v1::Request { - origin: services().globals.server_name().to_owned(), - pdus: pdu_jsons, - edus: edu_jsons, - origin_server_ts: MilliSecondsSinceUnixEpoch::now(), - transaction_id: (&*general_purpose::URL_SAFE_NO_PAD - .encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) - | SendingEventType::Pdu(b) => &**b, - }) - .collect::>(), - ))) - .into(), - }, - ) - .await - .map(|response| { - for pdu in response.pdus { - if pdu.1.is_err() { - warn!("Failed to send to {}: {:?}", server, pdu); - } - } - kind.clone() - }) - .map_err(|e| (kind, e)); - - drop(permit); - - response + Self::handle_federation_event(server, events).await } + }; + + match ret { + Ok(()) => Ok(kind), + Err(e) => Err((kind, e)), } } From 9071e11e060047a3dfed5f2b8792f9874080dbb5 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 20:59:36 +0000 Subject: [PATCH 168/617] sending.rs: move handler functions out of service These don't take `self`, no reason for them to be associated functions. --- src/service/sending.rs | 484 ++++++++++++++++++++--------------------- 1 file changed, 239 insertions(+), 245 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 3dbfb714..6e1c03a7 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -191,7 +191,7 @@ impl Service { for (outgoing_kind, events) in initial_transactions { current_transaction_status .insert(outgoing_kind.clone(), TransactionStatus::Running); - futures.push(Self::handle_events(outgoing_kind.clone(), events)); + futures.push(handle_events(outgoing_kind.clone(), events)); } loop { @@ -204,7 +204,7 @@ impl Service { response, &mut current_transaction_status, )? { - futures.push(Self::handle_events(kind, events)); + futures.push(handle_events(kind, events)); } } Some(data) = receiver.recv() => { @@ -214,7 +214,7 @@ impl Service { }) = self .handle_receiver(data, &mut current_transaction_status) { - futures.push(Self::handle_events(kind, events)); + futures.push(handle_events(kind, events)); } } } @@ -626,248 +626,6 @@ impl Service { Ok(()) } - #[tracing::instrument(skip(events))] - async fn handle_appservice_event( - id: &str, - events: Vec, - ) -> Result<()> { - let mut pdu_jsons = Vec::new(); - - for event in &events { - match event { - SendingEventType::Pdu(pdu_id) => { - pdu_jsons.push( - services() - .rooms - .timeline - .get_pdu_from_id(pdu_id)? - .ok_or_else(|| { - Error::bad_database( - "[Appservice] Event in \ - servernameevent_data not found in db.", - ) - })? - .to_room_event(), - ); - } - SendingEventType::Edu(_) => { - // Appservices don't need EDUs (?) - } - } - } - - let permit = services().sending.maximum_requests.acquire().await; - - appservice_server::send_request( - services().appservice.get_registration(id).await.ok_or_else( - || { - Error::bad_database( - "[Appservice] Could not load registration from db.", - ) - }, - )?, - appservice::event::push_events::v1::Request { - events: pdu_jsons, - txn_id: general_purpose::URL_SAFE_NO_PAD - .encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) - | SendingEventType::Pdu(b) => &**b, - }) - .collect::>(), - )) - .into(), - }, - ) - .await?; - - drop(permit); - - Ok(()) - } - - #[tracing::instrument(skip(events))] - async fn handle_push_event( - userid: &UserId, - pushkey: &str, - events: Vec, - ) -> Result<()> { - let mut pdus = Vec::new(); - - for event in &events { - match event { - SendingEventType::Pdu(pdu_id) => { - pdus.push( - services() - .rooms - .timeline - .get_pdu_from_id(pdu_id)? - .ok_or_else(|| { - Error::bad_database( - "[Push] Event in servernamevent_datas not \ - found in db.", - ) - })?, - ); - } - // Push gateways don't need EDUs (?) - SendingEventType::Edu(_) => {} - } - } - - for pdu in pdus { - // Redacted events are not notification targets (we don't - // send push for them) - if let Some(unsigned) = &pdu.unsigned { - if let Ok(unsigned) = - serde_json::from_str::(unsigned.get()) - { - if unsigned.get("redacted_because").is_some() { - continue; - } - } - } - - let Some(pusher) = services().pusher.get_pusher(userid, pushkey)? - else { - continue; - }; - - let rules_for_user = services() - .account_data - .get( - None, - userid, - GlobalAccountDataEventType::PushRules.to_string().into(), - ) - .unwrap_or_default() - .and_then(|event| { - serde_json::from_str::(event.get()).ok() - }) - .map_or_else( - || push::Ruleset::server_default(userid), - |ev: PushRulesEvent| ev.content.global, - ); - - let unread: UInt = services() - .rooms - .user - .notification_count(userid, &pdu.room_id)? - .try_into() - .expect("notification count can't go that high"); - - let permit = services().sending.maximum_requests.acquire().await; - - services() - .pusher - .send_push_notice(userid, unread, &pusher, rules_for_user, &pdu) - .await?; - - drop(permit); - } - - Ok(()) - } - - #[tracing::instrument(skip(events))] - async fn handle_federation_event( - server: &ServerName, - events: Vec, - ) -> Result<()> { - let mut edu_jsons = Vec::new(); - let mut pdu_jsons = Vec::new(); - - for event in &events { - match event { - SendingEventType::Pdu(pdu_id) => { - // TODO: check room version and remove event_id if - // needed - pdu_jsons.push( - PduEvent::convert_to_outgoing_federation_event( - services() - .rooms - .timeline - .get_pdu_json_from_id(pdu_id)? - .ok_or_else(|| { - error!( - "event not found: {server} {pdu_id:?}" - ); - Error::bad_database( - "[Normal] Event in \ - servernamevent_datas not found in db.", - ) - })?, - ), - ); - } - SendingEventType::Edu(edu) => { - if let Ok(raw) = serde_json::from_slice(edu) { - edu_jsons.push(raw); - } - } - } - } - - let permit = services().sending.maximum_requests.acquire().await; - - let response = server_server::send_request( - server, - send_transaction_message::v1::Request { - origin: services().globals.server_name().to_owned(), - pdus: pdu_jsons, - edus: edu_jsons, - origin_server_ts: MilliSecondsSinceUnixEpoch::now(), - transaction_id: general_purpose::URL_SAFE_NO_PAD - .encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) - | SendingEventType::Pdu(b) => &**b, - }) - .collect::>(), - )) - .into(), - }, - ) - .await?; - - for pdu in response.pdus { - if pdu.1.is_err() { - warn!("Failed to send to {}: {:?}", server, pdu); - } - } - - drop(permit); - - Ok(()) - } - - #[tracing::instrument(skip_all)] - async fn handle_events( - kind: OutgoingKind, - events: Vec, - ) -> HandlerResponse { - let ret = match &kind { - OutgoingKind::Appservice(id) => { - Self::handle_appservice_event(id, events).await - } - OutgoingKind::Push(userid, pushkey) => { - Self::handle_push_event(userid, pushkey, events).await - } - OutgoingKind::Normal(server) => { - Self::handle_federation_event(server, events).await - } - }; - - match ret { - Ok(()) => Ok(kind), - Err(e) => Err((kind, e)), - } - } - #[tracing::instrument(skip(self, request))] pub(crate) async fn send_federation_request( &self, @@ -918,3 +676,239 @@ impl Service { response } } + +#[tracing::instrument(skip(events))] +async fn handle_appservice_event( + id: &str, + events: Vec, +) -> Result<()> { + let mut pdu_jsons = Vec::new(); + + for event in &events { + match event { + SendingEventType::Pdu(pdu_id) => { + pdu_jsons.push( + services() + .rooms + .timeline + .get_pdu_from_id(pdu_id)? + .ok_or_else(|| { + Error::bad_database( + "[Appservice] Event in servernameevent_data \ + not found in db.", + ) + })? + .to_room_event(), + ); + } + SendingEventType::Edu(_) => { + // Appservices don't need EDUs (?) + } + } + } + + let permit = services().sending.maximum_requests.acquire().await; + + appservice_server::send_request( + services().appservice.get_registration(id).await.ok_or_else(|| { + Error::bad_database( + "[Appservice] Could not load registration from db.", + ) + })?, + appservice::event::push_events::v1::Request { + events: pdu_jsons, + txn_id: general_purpose::URL_SAFE_NO_PAD + .encode(calculate_hash( + &events + .iter() + .map(|e| match e { + SendingEventType::Edu(b) + | SendingEventType::Pdu(b) => &**b, + }) + .collect::>(), + )) + .into(), + }, + ) + .await?; + + drop(permit); + + Ok(()) +} + +#[tracing::instrument(skip(events))] +async fn handle_push_event( + userid: &UserId, + pushkey: &str, + events: Vec, +) -> Result<()> { + let mut pdus = Vec::new(); + + for event in &events { + match event { + SendingEventType::Pdu(pdu_id) => { + pdus.push( + services() + .rooms + .timeline + .get_pdu_from_id(pdu_id)? + .ok_or_else(|| { + Error::bad_database( + "[Push] Event in servernamevent_datas not \ + found in db.", + ) + })?, + ); + } + // Push gateways don't need EDUs (?) + SendingEventType::Edu(_) => {} + } + } + + for pdu in pdus { + // Redacted events are not notification targets (we don't + // send push for them) + if let Some(unsigned) = &pdu.unsigned { + if let Ok(unsigned) = + serde_json::from_str::(unsigned.get()) + { + if unsigned.get("redacted_because").is_some() { + continue; + } + } + } + + let Some(pusher) = services().pusher.get_pusher(userid, pushkey)? + else { + continue; + }; + + let rules_for_user = services() + .account_data + .get( + None, + userid, + GlobalAccountDataEventType::PushRules.to_string().into(), + ) + .unwrap_or_default() + .and_then(|event| { + serde_json::from_str::(event.get()).ok() + }) + .map_or_else( + || push::Ruleset::server_default(userid), + |ev: PushRulesEvent| ev.content.global, + ); + + let unread: UInt = services() + .rooms + .user + .notification_count(userid, &pdu.room_id)? + .try_into() + .expect("notification count can't go that high"); + + let permit = services().sending.maximum_requests.acquire().await; + + services() + .pusher + .send_push_notice(userid, unread, &pusher, rules_for_user, &pdu) + .await?; + + drop(permit); + } + + Ok(()) +} + +#[tracing::instrument(skip(events))] +async fn handle_federation_event( + server: &ServerName, + events: Vec, +) -> Result<()> { + let mut edu_jsons = Vec::new(); + let mut pdu_jsons = Vec::new(); + + for event in &events { + match event { + SendingEventType::Pdu(pdu_id) => { + // TODO: check room version and remove event_id if + // needed + pdu_jsons.push(PduEvent::convert_to_outgoing_federation_event( + services() + .rooms + .timeline + .get_pdu_json_from_id(pdu_id)? + .ok_or_else(|| { + error!("event not found: {server} {pdu_id:?}"); + Error::bad_database( + "[Normal] Event in servernamevent_datas not \ + found in db.", + ) + })?, + )); + } + SendingEventType::Edu(edu) => { + if let Ok(raw) = serde_json::from_slice(edu) { + edu_jsons.push(raw); + } + } + } + } + + let permit = services().sending.maximum_requests.acquire().await; + + let response = server_server::send_request( + server, + send_transaction_message::v1::Request { + origin: services().globals.server_name().to_owned(), + pdus: pdu_jsons, + edus: edu_jsons, + origin_server_ts: MilliSecondsSinceUnixEpoch::now(), + transaction_id: general_purpose::URL_SAFE_NO_PAD + .encode(calculate_hash( + &events + .iter() + .map(|e| match e { + SendingEventType::Edu(b) + | SendingEventType::Pdu(b) => &**b, + }) + .collect::>(), + )) + .into(), + }, + ) + .await?; + + for pdu in response.pdus { + if pdu.1.is_err() { + warn!("Failed to send to {}: {:?}", server, pdu); + } + } + + drop(permit); + + Ok(()) +} + +#[tracing::instrument(skip_all)] +async fn handle_events( + kind: OutgoingKind, + events: Vec, +) -> HandlerResponse { + let ret = match &kind { + OutgoingKind::Appservice(id) => { + handle_appservice_event(id, events).await + } + OutgoingKind::Push(userid, pushkey) => { + handle_push_event(userid, pushkey, events).await + } + OutgoingKind::Normal(server) => { + handle_federation_event(server, events).await + } + }; + + match ret { + Ok(()) => Ok(kind), + Err(e) => Err((kind, e)), + } +} From 18992b4d1d6aa8b1ebaacf441992f4681e16d2a0 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 21:19:55 +0000 Subject: [PATCH 169/617] sending.rs: rename OutgoingKind to Destination That's what it is. It describes the destination of the event. --- src/database/key_value/sending.rs | 33 +++---- src/service/sending.rs | 155 ++++++++++++++---------------- src/service/sending/data.rs | 15 ++- 3 files changed, 96 insertions(+), 107 deletions(-) diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index a03b2596..10d36ad3 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -4,7 +4,7 @@ use crate::{ database::KeyValueDatabase, service::{ self, - sending::{OutgoingKind, RequestKey, SendingEventType}, + sending::{Destination, RequestKey, SendingEventType}, }, services, utils, Error, Result, }; @@ -13,9 +13,8 @@ impl service::sending::Data for KeyValueDatabase { fn active_requests<'a>( &'a self, ) -> Box< - dyn Iterator< - Item = Result<(RequestKey, OutgoingKind, SendingEventType)>, - > + 'a, + dyn Iterator> + + 'a, > { Box::new(self.servercurrentevent_data.iter().map(|(key, v)| { let key = RequestKey::new(key); @@ -25,10 +24,10 @@ impl service::sending::Data for KeyValueDatabase { fn active_requests_for<'a>( &'a self, - outgoing_kind: &OutgoingKind, + destination: &Destination, ) -> Box> + 'a> { - let prefix = outgoing_kind.get_prefix(); + let prefix = destination.get_prefix(); Box::new(self.servercurrentevent_data.scan_prefix(prefix).map( |(key, v)| { let key = RequestKey::new(key); @@ -43,9 +42,9 @@ impl service::sending::Data for KeyValueDatabase { fn delete_all_active_requests_for( &self, - outgoing_kind: &OutgoingKind, + destination: &Destination, ) -> Result<()> { - let prefix = outgoing_kind.get_prefix(); + let prefix = destination.get_prefix(); for (key, _) in self.servercurrentevent_data.scan_prefix(prefix) { self.servercurrentevent_data.remove(&key)?; } @@ -55,12 +54,12 @@ impl service::sending::Data for KeyValueDatabase { fn queue_requests( &self, - requests: &[(&OutgoingKind, SendingEventType)], + requests: &[(&Destination, SendingEventType)], ) -> Result> { let mut batch = Vec::new(); let mut keys = Vec::new(); - for (outgoing_kind, event) in requests { - let mut key = outgoing_kind.get_prefix(); + for (destination, event) in requests { + let mut key = destination.get_prefix(); if let SendingEventType::Pdu(value) = &event { key.extend_from_slice(value); } else { @@ -82,10 +81,10 @@ impl service::sending::Data for KeyValueDatabase { fn queued_requests<'a>( &'a self, - outgoing_kind: &OutgoingKind, + destination: &Destination, ) -> Box> + 'a> { - let prefix = outgoing_kind.get_prefix(); + let prefix = destination.get_prefix(); return Box::new(self.servernameevent_data.scan_prefix(prefix).map( |(k, v)| { let k = RequestKey::new(k); @@ -136,7 +135,7 @@ impl service::sending::Data for KeyValueDatabase { fn parse_servercurrentevent( key: &RequestKey, value: Vec, -) -> Result<(OutgoingKind, SendingEventType)> { +) -> Result<(Destination, SendingEventType)> { let key = key.as_bytes(); // Appservices start with a plus Ok::<_, Error>(if key.starts_with(b"+") { @@ -154,7 +153,7 @@ fn parse_servercurrentevent( })?; ( - OutgoingKind::Appservice(server), + Destination::Appservice(server), if value.is_empty() { SendingEventType::Pdu(event.to_vec()) } else { @@ -185,7 +184,7 @@ fn parse_servercurrentevent( })?; ( - OutgoingKind::Push(user_id, pushkey_string), + Destination::Push(user_id, pushkey_string), if value.is_empty() { SendingEventType::Pdu(event.to_vec()) } else { @@ -208,7 +207,7 @@ fn parse_servercurrentevent( })?; ( - OutgoingKind::Normal(ServerName::parse(server).map_err(|_| { + Destination::Normal(ServerName::parse(server).map_err(|_| { Error::bad_database( "Invalid server string in server_currenttransaction", ) diff --git a/src/service/sending.rs b/src/service/sending.rs index 6e1c03a7..ec4768bf 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -45,30 +45,30 @@ use crate::{ }; #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub(crate) enum OutgoingKind { +pub(crate) enum Destination { Appservice(String), // user and pushkey Push(OwnedUserId, String), Normal(OwnedServerName), } -impl OutgoingKind { +impl Destination { #[tracing::instrument(skip(self))] pub(crate) fn get_prefix(&self) -> Vec { let mut prefix = match self { - OutgoingKind::Appservice(server) => { + Destination::Appservice(server) => { let mut p = b"+".to_vec(); p.extend_from_slice(server.as_bytes()); p } - OutgoingKind::Push(user, pushkey) => { + Destination::Push(user, pushkey) => { let mut p = b"$".to_vec(); p.extend_from_slice(user.as_bytes()); p.push(0xFF); p.extend_from_slice(pushkey.as_bytes()); p } - OutgoingKind::Normal(server) => { + Destination::Normal(server) => { let mut p = Vec::new(); p.extend_from_slice(server.as_bytes()); p @@ -102,7 +102,7 @@ impl RequestKey { } pub(crate) struct RequestData { - outgoing_kind: OutgoingKind, + destination: Destination, event_type: SendingEventType, key: RequestKey, } @@ -126,18 +126,18 @@ enum TransactionStatus { } struct HandlerInputs { - kind: OutgoingKind, + destination: Destination, events: Vec, } -type HandlerResponse = Result; +type HandlerResponse = Result; -fn outgoing_kind_from_response(response: &HandlerResponse) -> &OutgoingKind { +fn destination_from_response(response: &HandlerResponse) -> &Destination { match response { Ok(kind) | Err((kind, _)) => kind, } } -type TransactionStatusMap = HashMap; +type TransactionStatusMap = HashMap; impl Service { pub(crate) fn build(db: &'static dyn Data, config: &Config) -> Arc { @@ -168,18 +168,18 @@ impl Service { // Retry requests we could not finish yet let mut initial_transactions = - HashMap::>::new(); + HashMap::>::new(); - for (key, outgoing_kind, event) in + for (key, destination, event) in self.db.active_requests().filter_map(Result::ok) { let entry = - initial_transactions.entry(outgoing_kind.clone()).or_default(); + initial_transactions.entry(destination.clone()).or_default(); if entry.len() > 30 { warn!( "Dropping some current events: {:?} {:?} {:?}", - key, outgoing_kind, event + key, destination, event ); self.db.delete_active_request(key)?; continue; @@ -188,33 +188,33 @@ impl Service { entry.push(event); } - for (outgoing_kind, events) in initial_transactions { + for (destination, events) in initial_transactions { current_transaction_status - .insert(outgoing_kind.clone(), TransactionStatus::Running); - futures.push(handle_events(outgoing_kind.clone(), events)); + .insert(destination.clone(), TransactionStatus::Running); + futures.push(handle_events(destination.clone(), events)); } loop { select! { Some(response) = futures.next() => { if let Some(HandlerInputs { - kind, + destination, events, }) = self.handle_response( response, &mut current_transaction_status, )? { - futures.push(handle_events(kind, events)); + futures.push(handle_events(destination, events)); } } Some(data) = receiver.recv() => { if let Some(HandlerInputs { - kind, + destination, events, }) = self .handle_receiver(data, &mut current_transaction_status) { - futures.push(handle_events(kind, events)); + futures.push(handle_events(destination, events)); } } } @@ -225,7 +225,7 @@ impl Service { skip(self, current_transaction_status), fields( current_status = ?current_transaction_status.get( - outgoing_kind_from_response(&response) + destination_from_response(&response) ), ), )] @@ -235,27 +235,27 @@ impl Service { current_transaction_status: &mut TransactionStatusMap, ) -> Result> { match response { - Ok(outgoing_kind) => { - self.db.delete_all_active_requests_for(&outgoing_kind)?; + Ok(destination) => { + self.db.delete_all_active_requests_for(&destination)?; // Find events that have been added since starting the // last request let new_events = self .db - .queued_requests(&outgoing_kind) + .queued_requests(&destination) .filter_map(Result::ok) .take(30) .collect::>(); if new_events.is_empty() { - current_transaction_status.remove(&outgoing_kind); + current_transaction_status.remove(&destination); Ok(None) } else { // Insert pdus we found self.db.mark_as_active(&new_events)?; Ok(Some(HandlerInputs { - kind: outgoing_kind.clone(), + destination: destination.clone(), events: new_events .into_iter() .map(|(event, _)| event) @@ -263,29 +263,23 @@ impl Service { })) } } - Err((outgoing_kind, _)) => { - current_transaction_status.entry(outgoing_kind).and_modify( - |e| { - *e = match e { - TransactionStatus::Running => { - TransactionStatus::Failed(1, Instant::now()) - } - TransactionStatus::Retrying(n) => { - TransactionStatus::Failed( - *n + 1, - Instant::now(), - ) - } - TransactionStatus::Failed(..) => { - error!( - "Request that was not even running \ - failed?!" - ); - return; - } + Err((destination, _)) => { + current_transaction_status.entry(destination).and_modify(|e| { + *e = match e { + TransactionStatus::Running => { + TransactionStatus::Failed(1, Instant::now()) } - }, - ); + TransactionStatus::Retrying(n) => { + TransactionStatus::Failed(*n + 1, Instant::now()) + } + TransactionStatus::Failed(..) => { + error!( + "Request that was not even running failed?!" + ); + return; + } + } + }); Ok(None) } } @@ -294,25 +288,25 @@ impl Service { #[tracing::instrument( skip(self, event_type, key, current_transaction_status), fields( - current_status = ?current_transaction_status.get(&outgoing_kind), + current_status = ?current_transaction_status.get(&destination), ), )] fn handle_receiver( &self, RequestData { - outgoing_kind, + destination, event_type, key, }: RequestData, current_transaction_status: &mut TransactionStatusMap, ) -> Option { if let Ok(Some(events)) = self.select_events( - &outgoing_kind, + &destination, vec![(event_type, key)], current_transaction_status, ) { Some(HandlerInputs { - kind: outgoing_kind, + destination, events, }) } else { @@ -324,23 +318,23 @@ impl Service { skip(self, new_events, current_transaction_status), fields( new_events = debug_slice_truncated(&new_events, 3), - current_status = ?current_transaction_status.get(outgoing_kind), + current_status = ?current_transaction_status.get(destination), ), )] fn select_events( &self, - outgoing_kind: &OutgoingKind, + destination: &Destination, // Events we want to send: event and full key new_events: Vec<(SendingEventType, RequestKey)>, current_transaction_status: &mut HashMap< - OutgoingKind, + Destination, TransactionStatus, >, ) -> Result>> { let mut retry = false; let mut allow = true; - let entry = current_transaction_status.entry(outgoing_kind.clone()); + let entry = current_transaction_status.entry(destination.clone()); entry .and_modify(|e| match e { @@ -377,10 +371,8 @@ impl Service { if retry { // We retry the previous transaction - for (_, e) in self - .db - .active_requests_for(outgoing_kind) - .filter_map(Result::ok) + for (_, e) in + self.db.active_requests_for(destination).filter_map(Result::ok) { events.push(e); } @@ -390,7 +382,7 @@ impl Service { events.push(e); } - if let OutgoingKind::Normal(server_name) = outgoing_kind { + if let Destination::Normal(server_name) = destination { if let Ok((select_edus, last_count)) = self.select_edus(server_name) { @@ -536,13 +528,13 @@ impl Service { user: &UserId, pushkey: String, ) -> Result<()> { - let outgoing_kind = OutgoingKind::Push(user.to_owned(), pushkey); + let destination = Destination::Push(user.to_owned(), pushkey); let event_type = SendingEventType::Pdu(pdu_id.to_owned()); let keys = - self.db.queue_requests(&[(&outgoing_kind, event_type.clone())])?; + self.db.queue_requests(&[(&destination, event_type.clone())])?; self.sender .send(RequestData { - outgoing_kind, + destination, event_type, key: keys.into_iter().next().unwrap(), }) @@ -561,7 +553,7 @@ impl Service { .into_iter() .map(|server| { ( - OutgoingKind::Normal(server), + Destination::Normal(server), SendingEventType::Pdu(pdu_id.to_owned()), ) }) @@ -569,11 +561,10 @@ impl Service { let keys = self.db.queue_requests( &requests.iter().map(|(o, e)| (o, e.clone())).collect::>(), )?; - for ((outgoing_kind, event_type), key) in requests.into_iter().zip(keys) - { + for ((destination, event_type), key) in requests.into_iter().zip(keys) { self.sender .send(RequestData { - outgoing_kind: outgoing_kind.clone(), + destination: destination.clone(), event_type, key, }) @@ -590,13 +581,13 @@ impl Service { serialized: Vec, id: u64, ) -> Result<()> { - let outgoing_kind = OutgoingKind::Normal(server.to_owned()); + let destination = Destination::Normal(server.to_owned()); let event_type = SendingEventType::Edu(serialized); let keys = - self.db.queue_requests(&[(&outgoing_kind, event_type.clone())])?; + self.db.queue_requests(&[(&destination, event_type.clone())])?; self.sender .send(RequestData { - outgoing_kind, + destination, event_type, key: keys.into_iter().next().unwrap(), }) @@ -611,13 +602,13 @@ impl Service { appservice_id: String, pdu_id: Vec, ) -> Result<()> { - let outgoing_kind = OutgoingKind::Appservice(appservice_id); + let destination = Destination::Appservice(appservice_id); let event_type = SendingEventType::Pdu(pdu_id); let keys = - self.db.queue_requests(&[(&outgoing_kind, event_type.clone())])?; + self.db.queue_requests(&[(&destination, event_type.clone())])?; self.sender .send(RequestData { - outgoing_kind, + destination, event_type, key: keys.into_iter().next().unwrap(), }) @@ -892,23 +883,23 @@ async fn handle_federation_event( #[tracing::instrument(skip_all)] async fn handle_events( - kind: OutgoingKind, + destination: Destination, events: Vec, ) -> HandlerResponse { - let ret = match &kind { - OutgoingKind::Appservice(id) => { + let ret = match &destination { + Destination::Appservice(id) => { handle_appservice_event(id, events).await } - OutgoingKind::Push(userid, pushkey) => { + Destination::Push(userid, pushkey) => { handle_push_event(userid, pushkey, events).await } - OutgoingKind::Normal(server) => { + Destination::Normal(server) => { handle_federation_event(server, events).await } }; match ret { - Ok(()) => Ok(kind), - Err(e) => Err((kind, e)), + Ok(()) => Ok(destination), + Err(e) => Err((destination, e)), } } diff --git a/src/service/sending/data.rs b/src/service/sending/data.rs index ad9915e5..00795faf 100644 --- a/src/service/sending/data.rs +++ b/src/service/sending/data.rs @@ -1,6 +1,6 @@ use ruma::ServerName; -use super::{OutgoingKind, RequestKey, SendingEventType}; +use super::{Destination, RequestKey, SendingEventType}; use crate::Result; pub(crate) trait Data: Send + Sync { @@ -8,26 +8,25 @@ pub(crate) trait Data: Send + Sync { fn active_requests<'a>( &'a self, ) -> Box< - dyn Iterator< - Item = Result<(RequestKey, OutgoingKind, SendingEventType)>, - > + 'a, + dyn Iterator> + + 'a, >; fn active_requests_for<'a>( &'a self, - outgoing_kind: &OutgoingKind, + destination: &Destination, ) -> Box> + 'a>; fn delete_active_request(&self, key: RequestKey) -> Result<()>; fn delete_all_active_requests_for( &self, - outgoing_kind: &OutgoingKind, + destination: &Destination, ) -> Result<()>; fn queue_requests( &self, - requests: &[(&OutgoingKind, SendingEventType)], + requests: &[(&Destination, SendingEventType)], ) -> Result>; fn queued_requests<'a>( &'a self, - outgoing_kind: &OutgoingKind, + destination: &Destination, ) -> Box> + 'a>; fn mark_as_active( &self, From fb52ded222d165b83822d3c104a098f1023f4295 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 23 May 2024 20:11:01 +0000 Subject: [PATCH 170/617] sending.rs: try to preserve requester spans Add a `follows_from` relationship to the dispatched worker's span pointing to the caller of `send_*()`, if known. --- src/service/sending.rs | 157 ++++++++++++++++++++++++++++------------- 1 file changed, 107 insertions(+), 50 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index ec4768bf..307824af 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -35,7 +35,7 @@ use tokio::{ select, sync::{mpsc, Mutex, Semaphore}, }; -use tracing::{debug, error, warn}; +use tracing::{debug, error, warn, Span}; use crate::{ api::{appservice_server, server_server}, @@ -105,6 +105,8 @@ pub(crate) struct RequestData { destination: Destination, event_type: SendingEventType, key: RequestKey, + /// Span of the original `send_*()` method call + requester_span: Span, } pub(crate) struct Service { @@ -128,17 +130,27 @@ enum TransactionStatus { struct HandlerInputs { destination: Destination, events: Vec, + /// Span of the original `send_*()` method call, if known (gets lost when + /// event is persisted to database) + requester_span: Option, } -type HandlerResponse = Result; -fn destination_from_response(response: &HandlerResponse) -> &Destination { - match response { - Ok(kind) | Err((kind, _)) => kind, - } +#[derive(Debug)] +struct HandlerResponse { + destination: Destination, + result: Result<()>, + /// The span of the just-completed handler, for follows-from relationships. + handler_span: Span, } type TransactionStatusMap = HashMap; +enum SelectedEvents { + None, + Retries(Vec), + New(Vec), +} + impl Service { pub(crate) fn build(db: &'static dyn Data, config: &Config) -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); @@ -191,30 +203,28 @@ impl Service { for (destination, events) in initial_transactions { current_transaction_status .insert(destination.clone(), TransactionStatus::Running); - futures.push(handle_events(destination.clone(), events)); + futures.push(handle_events(HandlerInputs { + destination: destination.clone(), + events, + requester_span: None, + })); } loop { select! { Some(response) = futures.next() => { - if let Some(HandlerInputs { - destination, - events, - }) = self.handle_response( + if let Some(inputs) = self.handle_response( response, &mut current_transaction_status, )? { - futures.push(handle_events(destination, events)); + futures.push(handle_events(inputs)); } } Some(data) = receiver.recv() => { - if let Some(HandlerInputs { - destination, - events, - }) = self - .handle_receiver(data, &mut current_transaction_status) - { - futures.push(handle_events(destination, events)); + if let Some(inputs) = self.handle_receiver( + data, &mut current_transaction_status + ) { + futures.push(handle_events(inputs)); } } } @@ -222,20 +232,27 @@ impl Service { } #[tracing::instrument( - skip(self, current_transaction_status), + skip(self, handler_span, current_transaction_status), fields( current_status = ?current_transaction_status.get( - destination_from_response(&response) + &destination ), ), )] fn handle_response( &self, - response: HandlerResponse, + HandlerResponse { + destination, + result, + handler_span, + }: HandlerResponse, current_transaction_status: &mut TransactionStatusMap, ) -> Result> { - match response { - Ok(destination) => { + // clone() is required for the relationship to show up in jaeger + Span::current().follows_from(handler_span.clone()); + + match result { + Ok(()) => { self.db.delete_all_active_requests_for(&destination)?; // Find events that have been added since starting the @@ -260,10 +277,12 @@ impl Service { .into_iter() .map(|(event, _)| event) .collect(), + requester_span: None, })) } } - Err((destination, _)) => { + Err(_err) => { + warn!("Marking transaction as failed"); current_transaction_status.entry(destination).and_modify(|e| { *e = match e { TransactionStatus::Running => { @@ -286,7 +305,7 @@ impl Service { } #[tracing::instrument( - skip(self, event_type, key, current_transaction_status), + skip(self, event_type, key, requester_span, current_transaction_status), fields( current_status = ?current_transaction_status.get(&destination), ), @@ -297,20 +316,42 @@ impl Service { destination, event_type, key, + requester_span, }: RequestData, current_transaction_status: &mut TransactionStatusMap, ) -> Option { - if let Ok(Some(events)) = self.select_events( + // clone() is required for the relationship to show up in jaeger + Span::current().follows_from(requester_span.clone()); + + match self.select_events( &destination, vec![(event_type, key)], current_transaction_status, ) { - Some(HandlerInputs { - destination, - events, - }) - } else { - None + Ok(SelectedEvents::Retries(events)) => { + debug!("retrying old events"); + Some(HandlerInputs { + destination, + events, + requester_span: None, + }) + } + Ok(SelectedEvents::New(events)) => { + debug!("sending new event"); + Some(HandlerInputs { + destination, + events, + requester_span: Some(requester_span), + }) + } + Ok(SelectedEvents::None) => { + debug!("holding off from sending any events"); + None + } + Err(error) => { + error!(%error, "Failed to select events to send"); + None + } } } @@ -330,7 +371,7 @@ impl Service { Destination, TransactionStatus, >, - ) -> Result>> { + ) -> Result { let mut retry = false; let mut allow = true; @@ -364,19 +405,22 @@ impl Service { .or_insert(TransactionStatus::Running); if !allow { - return Ok(None); + return Ok(SelectedEvents::None); } - let mut events = Vec::new(); - if retry { // We retry the previous transaction - for (_, e) in - self.db.active_requests_for(destination).filter_map(Result::ok) - { - events.push(e); - } + let events = self + .db + .active_requests_for(destination) + .filter_map(Result::ok) + .map(|(_, e)| e) + .collect(); + + Ok(SelectedEvents::Retries(events)) } else { + let mut events = Vec::new(); + self.db.mark_as_active(&new_events)?; for (e, _) in new_events { events.push(e); @@ -393,9 +437,9 @@ impl Service { self.db.set_latest_educount(server_name, last_count)?; } } - } - Ok(Some(events)) + Ok(SelectedEvents::New(events)) + } } #[tracing::instrument(skip(self))] @@ -537,6 +581,7 @@ impl Service { destination, event_type, key: keys.into_iter().next().unwrap(), + requester_span: Span::current(), }) .unwrap(); @@ -567,6 +612,7 @@ impl Service { destination: destination.clone(), event_type, key, + requester_span: Span::current(), }) .unwrap(); } @@ -590,6 +636,7 @@ impl Service { destination, event_type, key: keys.into_iter().next().unwrap(), + requester_span: Span::current(), }) .unwrap(); @@ -611,6 +658,7 @@ impl Service { destination, event_type, key: keys.into_iter().next().unwrap(), + requester_span: Span::current(), }) .unwrap(); @@ -883,10 +931,18 @@ async fn handle_federation_event( #[tracing::instrument(skip_all)] async fn handle_events( - destination: Destination, - events: Vec, + HandlerInputs { + destination, + events, + requester_span, + }: HandlerInputs, ) -> HandlerResponse { - let ret = match &destination { + if let Some(span) = requester_span { + // clone() is required for the relationship to show up in jaeger + Span::current().follows_from(span.clone()); + } + + let result = match &destination { Destination::Appservice(id) => { handle_appservice_event(id, events).await } @@ -898,8 +954,9 @@ async fn handle_events( } }; - match ret { - Ok(()) => Ok(destination), - Err(e) => Err((destination, e)), + HandlerResponse { + destination, + result, + handler_span: Span::current(), } } From 25353da8b8bef71f0052b2d71572364492c56ccf Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 24 May 2024 16:20:00 +0000 Subject: [PATCH 171/617] sending.rs: record handler errors using Display --- src/service/sending.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 307824af..03593208 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -232,11 +232,12 @@ impl Service { } #[tracing::instrument( - skip(self, handler_span, current_transaction_status), + skip(self, result, handler_span, current_transaction_status), fields( current_status = ?current_transaction_status.get( &destination ), + error, ), )] fn handle_response( @@ -251,6 +252,10 @@ impl Service { // clone() is required for the relationship to show up in jaeger Span::current().follows_from(handler_span.clone()); + if let Err(e) = &result { + Span::current().record("error", e.to_string()); + } + match result { Ok(()) => { self.db.delete_all_active_requests_for(&destination)?; From 12e7f525aa4bc6d7eadf26d2d270855461062fae Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 21:27:03 +0000 Subject: [PATCH 172/617] Clean up and deduplicate parse_servercurrentevent() --- src/database/key_value/sending.rs | 60 +++++++++++++------------------ 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 10d36ad3..89c8c7ca 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -131,14 +131,13 @@ impl service::sending::Data for KeyValueDatabase { } } -#[tracing::instrument(skip(key))] +#[tracing::instrument(skip(key, value))] fn parse_servercurrentevent( key: &RequestKey, value: Vec, ) -> Result<(Destination, SendingEventType)> { let key = key.as_bytes(); - // Appservices start with a plus - Ok::<_, Error>(if key.starts_with(b"+") { + let (destination, event) = if key.starts_with(b"+") { let mut parts = key[1..].splitn(2, |&b| b == 0xFF); let server = parts.next().expect("splitn always returns one element"); @@ -152,14 +151,7 @@ fn parse_servercurrentevent( ) })?; - ( - Destination::Appservice(server), - if value.is_empty() { - SendingEventType::Pdu(event.to_vec()) - } else { - SendingEventType::Edu(value) - }, - ) + (Destination::Appservice(server), event) } else if key.starts_with(b"$") { let mut parts = key[1..].splitn(3, |&b| b == 0xFF); @@ -183,15 +175,7 @@ fn parse_servercurrentevent( Error::bad_database("Invalid bytes in servercurrentpdus.") })?; - ( - Destination::Push(user_id, pushkey_string), - if value.is_empty() { - SendingEventType::Pdu(event.to_vec()) - } else { - // I'm pretty sure this should never be called - SendingEventType::Edu(value) - }, - ) + (Destination::Push(user_id, pushkey_string), event) } else { let mut parts = key.splitn(2, |&b| b == 0xFF); @@ -200,23 +184,27 @@ fn parse_servercurrentevent( Error::bad_database("Invalid bytes in servercurrentpdus.") })?; - let server = utils::string_from_bytes(server).map_err(|_| { - Error::bad_database( - "Invalid server bytes in server_currenttransaction", - ) - })?; - - ( - Destination::Normal(ServerName::parse(server).map_err(|_| { + let server = utils::string_from_bytes(server) + .map_err(|_| { + Error::bad_database( + "Invalid server bytes in server_currenttransaction", + ) + })? + .try_into() + .map_err(|_| { Error::bad_database( "Invalid server string in server_currenttransaction", ) - })?), - if value.is_empty() { - SendingEventType::Pdu(event.to_vec()) - } else { - SendingEventType::Edu(value) - }, - ) - }) + })?; + (Destination::Normal(server), event) + }; + + Ok(( + destination, + if value.is_empty() { + SendingEventType::Pdu(event.to_vec()) + } else { + SendingEventType::Edu(value) + }, + )) } From 72962c6402b67869178e4a08455515c5f16ce9c5 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 09:30:13 -0700 Subject: [PATCH 173/617] enable allow_federation config by default We inherited the disabled-by-default setting from conduit. Conduwuit change it to enabled-by-default in [1]. This can make things confusing for users migrating from conduwuit to grapevine, especially since we currently do not log a warning when federation is disabled. [1]: https://github.com/girlbossceo/conduwuit/commit/24605e151d735080bc53074fcccc66f6d37e7248 --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 2ea4c313..88e07794 100644 --- a/src/config.rs +++ b/src/config.rs @@ -49,7 +49,7 @@ pub(crate) struct Config { pub(crate) registration_token: Option, #[serde(default = "true_fn")] pub(crate) allow_encryption: bool, - #[serde(default = "false_fn")] + #[serde(default = "true_fn")] pub(crate) allow_federation: bool, #[serde(default = "true_fn")] pub(crate) allow_room_creation: bool, From ec1b086a3594d30f047991229ff5af81811e3a9c Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 09:47:19 -0700 Subject: [PATCH 174/617] very minor cleanup in the sync endpoint I meant to do this in 146465693ecb6c1adb7edcb40e9f3759c350dd1f, but looks like I forgot. --- src/api/client_server/sync.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index ca56edde..45d617b2 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -457,11 +457,8 @@ pub(crate) async fn sync_events_route( Ok(x) => x.expect("watcher should succeed"), Err(error) => debug!(%error, "timed out"), }; - Ok(Ra(response)) - } else { - // Only cache if we made progress - Ok(Ra(response)) } + Ok(Ra(response)) } #[allow(clippy::too_many_arguments, clippy::too_many_lines)] From c973485c736a750adbc7ed73f32ce652a7670e3f Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 27 May 2024 19:42:46 +0000 Subject: [PATCH 175/617] service/media: refactor to reduce indentation --- src/service/media.rs | 209 +++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 107 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 91a27e78..147c0963 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -151,116 +151,111 @@ impl Service { let mut file = Vec::new(); File::open(path).await?.read_to_end(&mut file).await?; - Ok(Some(FileMeta { + return Ok(Some(FileMeta { content_disposition, content_type, file: file.clone(), - })) - } else if let Ok((content_disposition, content_type, key)) = - self.db.search_file_metadata(mxc.clone(), 0, 0) - { - // Generate a thumbnail - let path = services().globals.get_media_file(&key); - let mut file = Vec::new(); - File::open(path).await?.read_to_end(&mut file).await?; - - if let Ok(image) = image::load_from_memory(&file) { - let original_width = image.width(); - let original_height = image.height(); - if width > original_width || height > original_height { - return Ok(Some(FileMeta { - content_disposition, - content_type, - file: file.clone(), - })); - } - - let thumbnail = if crop { - image.resize_to_fill(width, height, FilterType::CatmullRom) - } else { - let (exact_width, exact_height) = { - // Copied from image::dynimage::resize_dimensions - let use_width = (u64::from(width) - * u64::from(original_height)) - <= (u64::from(original_width) * u64::from(height)); - let intermediate = if use_width { - u64::from(original_height) * u64::from(width) - / u64::from(original_width) - } else { - u64::from(original_width) * u64::from(height) - / u64::from(original_height) - }; - if use_width { - if intermediate <= u64::from(::std::u32::MAX) { - ( - width, - intermediate.try_into().unwrap_or(u32::MAX), - ) - } else { - ( - (u64::from(width) - * u64::from(::std::u32::MAX) - / intermediate) - .try_into() - .unwrap_or(u32::MAX), - ::std::u32::MAX, - ) - } - } else if intermediate <= u64::from(::std::u32::MAX) { - ( - intermediate.try_into().unwrap_or(u32::MAX), - height, - ) - } else { - ( - ::std::u32::MAX, - (u64::from(height) - * u64::from(::std::u32::MAX) - / intermediate) - .try_into() - .unwrap_or(u32::MAX), - ) - } - }; - - image.thumbnail_exact(exact_width, exact_height) - }; - - let mut thumbnail_bytes = Vec::new(); - thumbnail.write_to( - &mut Cursor::new(&mut thumbnail_bytes), - image::ImageFormat::Png, - )?; - - // Save thumbnail in database so we don't have to generate it - // again next time - let thumbnail_key = self.db.create_file_metadata( - mxc, - width, - height, - content_disposition.as_deref(), - content_type.as_deref(), - )?; - - let path = services().globals.get_media_file(&thumbnail_key); - let mut f = File::create(path).await?; - f.write_all(&thumbnail_bytes).await?; - - Ok(Some(FileMeta { - content_disposition, - content_type, - file: thumbnail_bytes.clone(), - })) - } else { - // Couldn't parse file to generate thumbnail, send original - Ok(Some(FileMeta { - content_disposition, - content_type, - file: file.clone(), - })) - } - } else { - Ok(None) + })); } + + // thumbnail not found, generate + + let Ok((content_disposition, content_type, key)) = + self.db.search_file_metadata(mxc.clone(), 0, 0) + else { + return Ok(None); + }; + + // Generate a thumbnail + let path = services().globals.get_media_file(&key); + let mut file = Vec::new(); + File::open(path).await?.read_to_end(&mut file).await?; + + let Ok(image) = image::load_from_memory(&file) else { + // Couldn't parse file to generate thumbnail, send original + return Ok(Some(FileMeta { + content_disposition, + content_type, + file: file.clone(), + })); + }; + + let original_width = image.width(); + let original_height = image.height(); + if width > original_width || height > original_height { + return Ok(Some(FileMeta { + content_disposition, + content_type, + file: file.clone(), + })); + } + + let thumbnail = if crop { + image.resize_to_fill(width, height, FilterType::CatmullRom) + } else { + let (exact_width, exact_height) = { + // Copied from image::dynimage::resize_dimensions + let use_width = (u64::from(width) * u64::from(original_height)) + <= (u64::from(original_width) * u64::from(height)); + let intermediate = if use_width { + u64::from(original_height) * u64::from(width) + / u64::from(original_width) + } else { + u64::from(original_width) * u64::from(height) + / u64::from(original_height) + }; + if use_width { + if intermediate <= u64::from(::std::u32::MAX) { + (width, intermediate.try_into().unwrap_or(u32::MAX)) + } else { + ( + (u64::from(width) * u64::from(::std::u32::MAX) + / intermediate) + .try_into() + .unwrap_or(u32::MAX), + ::std::u32::MAX, + ) + } + } else if intermediate <= u64::from(::std::u32::MAX) { + (intermediate.try_into().unwrap_or(u32::MAX), height) + } else { + ( + ::std::u32::MAX, + (u64::from(height) * u64::from(::std::u32::MAX) + / intermediate) + .try_into() + .unwrap_or(u32::MAX), + ) + } + }; + + image.thumbnail_exact(exact_width, exact_height) + }; + + let mut thumbnail_bytes = Vec::new(); + thumbnail.write_to( + &mut Cursor::new(&mut thumbnail_bytes), + image::ImageFormat::Png, + )?; + + // Save thumbnail in database so we don't have to generate it + // again next time + let thumbnail_key = self.db.create_file_metadata( + mxc, + width, + height, + content_disposition.as_deref(), + content_type.as_deref(), + )?; + + let path = services().globals.get_media_file(&thumbnail_key); + let mut f = File::create(path).await?; + f.write_all(&thumbnail_bytes).await?; + + Ok(Some(FileMeta { + content_disposition, + content_type, + file: thumbnail_bytes.clone(), + })) } } From 0a92c72566699c4f91a14874f242dd19944f3727 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 24 May 2024 18:30:59 +0000 Subject: [PATCH 176/617] Put thumbnail creation inside spawn_blocking() This can take milliseconds or even several seconds for huge inputs, while the rule of thumb is <100us between await points. --- src/service/media.rs | 146 +++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 61 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 147c0963..6bfe5293 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -118,6 +118,76 @@ impl Service { } } + /// Generates a thumbnail from the given image file contents. Returns + /// `Ok(None)` if the input image should be used as-is. + #[tracing::instrument(skip(file), fields(input_size = file.len()))] + fn generate_thumbnail( + file: &[u8], + width: u32, + height: u32, + crop: bool, + ) -> Result>> { + let Ok(image) = image::load_from_memory(file) else { + return Ok(None); + }; + + let original_width = image.width(); + let original_height = image.height(); + if width > original_width || height > original_height { + return Ok(None); + } + + let thumbnail = if crop { + image.resize_to_fill(width, height, FilterType::CatmullRom) + } else { + let (exact_width, exact_height) = { + // Copied from image::dynimage::resize_dimensions + let use_width = (u64::from(width) * u64::from(original_height)) + <= (u64::from(original_width) * u64::from(height)); + let intermediate = if use_width { + u64::from(original_height) * u64::from(width) + / u64::from(original_width) + } else { + u64::from(original_width) * u64::from(height) + / u64::from(original_height) + }; + if use_width { + if intermediate <= u64::from(::std::u32::MAX) { + (width, intermediate.try_into().unwrap_or(u32::MAX)) + } else { + ( + (u64::from(width) * u64::from(::std::u32::MAX) + / intermediate) + .try_into() + .unwrap_or(u32::MAX), + ::std::u32::MAX, + ) + } + } else if intermediate <= u64::from(::std::u32::MAX) { + (intermediate.try_into().unwrap_or(u32::MAX), height) + } else { + ( + ::std::u32::MAX, + (u64::from(height) * u64::from(::std::u32::MAX) + / intermediate) + .try_into() + .unwrap_or(u32::MAX), + ) + } + }; + + image.thumbnail_exact(exact_width, exact_height) + }; + + let mut thumbnail_bytes = Vec::new(); + thumbnail.write_to( + &mut Cursor::new(&mut thumbnail_bytes), + image::ImageFormat::Png, + )?; + + Ok(Some(thumbnail_bytes)) + } + /// Downloads a file's thumbnail. /// /// Here's an example on how it works: @@ -171,73 +241,27 @@ impl Service { let mut file = Vec::new(); File::open(path).await?.read_to_end(&mut file).await?; - let Ok(image) = image::load_from_memory(&file) else { - // Couldn't parse file to generate thumbnail, send original + let thumbnail_result = { + let file = file.clone(); + let outer_span = tracing::span::Span::current(); + + tokio::task::spawn_blocking(move || { + outer_span.in_scope(|| { + Self::generate_thumbnail(&file, width, height, crop) + }) + }) + .await + .expect("failed to join thumbnailer task") + }; + + let Some(thumbnail_bytes) = thumbnail_result? else { return Ok(Some(FileMeta { content_disposition, content_type, - file: file.clone(), + file, })); }; - let original_width = image.width(); - let original_height = image.height(); - if width > original_width || height > original_height { - return Ok(Some(FileMeta { - content_disposition, - content_type, - file: file.clone(), - })); - } - - let thumbnail = if crop { - image.resize_to_fill(width, height, FilterType::CatmullRom) - } else { - let (exact_width, exact_height) = { - // Copied from image::dynimage::resize_dimensions - let use_width = (u64::from(width) * u64::from(original_height)) - <= (u64::from(original_width) * u64::from(height)); - let intermediate = if use_width { - u64::from(original_height) * u64::from(width) - / u64::from(original_width) - } else { - u64::from(original_width) * u64::from(height) - / u64::from(original_height) - }; - if use_width { - if intermediate <= u64::from(::std::u32::MAX) { - (width, intermediate.try_into().unwrap_or(u32::MAX)) - } else { - ( - (u64::from(width) * u64::from(::std::u32::MAX) - / intermediate) - .try_into() - .unwrap_or(u32::MAX), - ::std::u32::MAX, - ) - } - } else if intermediate <= u64::from(::std::u32::MAX) { - (intermediate.try_into().unwrap_or(u32::MAX), height) - } else { - ( - ::std::u32::MAX, - (u64::from(height) * u64::from(::std::u32::MAX) - / intermediate) - .try_into() - .unwrap_or(u32::MAX), - ) - } - }; - - image.thumbnail_exact(exact_width, exact_height) - }; - - let mut thumbnail_bytes = Vec::new(); - thumbnail.write_to( - &mut Cursor::new(&mut thumbnail_bytes), - image::ImageFormat::Png, - )?; - // Save thumbnail in database so we don't have to generate it // again next time let thumbnail_key = self.db.create_file_metadata( From b6fc9b0feb390f1db976c05f2f3e865c1aaef4da Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 27 May 2024 19:34:17 +0000 Subject: [PATCH 177/617] service/media: add some tracing --- src/service/media.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 6bfe5293..91b5d135 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -5,6 +5,7 @@ use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt, BufReader}, }; +use tracing::{debug, warn}; use crate::{services, Result}; @@ -120,20 +121,31 @@ impl Service { /// Generates a thumbnail from the given image file contents. Returns /// `Ok(None)` if the input image should be used as-is. - #[tracing::instrument(skip(file), fields(input_size = file.len()))] + #[tracing::instrument( + skip(file), + fields(input_size = file.len(), original_width, original_height), + )] fn generate_thumbnail( file: &[u8], width: u32, height: u32, crop: bool, ) -> Result>> { - let Ok(image) = image::load_from_memory(file) else { - return Ok(None); + let image = match image::load_from_memory(file) { + Ok(image) => image, + Err(error) => { + warn!(%error, "Failed to parse source image"); + return Ok(None); + } }; let original_width = image.width(); let original_height = image.height(); + tracing::Span::current().record("original_width", original_width); + tracing::Span::current().record("original_height", original_height); + if width > original_width || height > original_height { + debug!("Requested thumbnail is larger than source image"); return Ok(None); } @@ -179,6 +191,7 @@ impl Service { image.thumbnail_exact(exact_width, exact_height) }; + debug!("Serializing thumbnail as PNG"); let mut thumbnail_bytes = Vec::new(); thumbnail.write_to( &mut Cursor::new(&mut thumbnail_bytes), @@ -216,7 +229,7 @@ impl Service { if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc.clone(), width, height) { - // Using saved thumbnail + debug!("Using saved thumbnail"); let path = services().globals.get_media_file(&key); let mut file = Vec::new(); File::open(path).await?.read_to_end(&mut file).await?; @@ -228,19 +241,18 @@ impl Service { })); } - // thumbnail not found, generate - let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc.clone(), 0, 0) else { + debug!("Original image not found, can't generate thumbnail"); return Ok(None); }; - // Generate a thumbnail let path = services().globals.get_media_file(&key); let mut file = Vec::new(); File::open(path).await?.read_to_end(&mut file).await?; + debug!("Generating thumbnail"); let thumbnail_result = { let file = file.clone(); let outer_span = tracing::span::Span::current(); @@ -255,6 +267,7 @@ impl Service { }; let Some(thumbnail_bytes) = thumbnail_result? else { + debug!("Returning source image as-is"); return Ok(Some(FileMeta { content_disposition, content_type, @@ -262,6 +275,8 @@ impl Service { })); }; + debug!("Saving created thumbnail"); + // Save thumbnail in database so we don't have to generate it // again next time let thumbnail_key = self.db.create_file_metadata( From ee43c2ff4caf117e8a2bc2cf2f699ec9c61ecfde Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 23 May 2024 13:32:43 -0700 Subject: [PATCH 178/617] only link to one jemalloc build Without setting JEMALLOC_OVERRIDE, we end up linking to two different jemalloc builds. Once dynamically, as a transitive dependency through rocksdb, and a second time to the static jemalloc that tikv-jemalloc-sys builds. This fixes dynamically-linked jemalloc builds, for the reasons described in . --- nix/pkgs/default/default.nix | 18 +++++++++++++++++- nix/shell.nix | 6 +++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index d5accf32..0a7ef92f 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -6,6 +6,7 @@ , pkgsBuildHost , rocksdb , rust +, rust-jemalloc-sys , stdenv # Options (keep sorted) @@ -15,10 +16,23 @@ }: let + featureEnabled = feature : builtins.elem feature features; + + # This derivation will set the JEMALLOC_OVERRIDE variable, causing the + # tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's + # own. In order for this to work, we need to set flags on the build that match + # whatever flags tikv-jemalloc-sys was going to use. These are dependent on + # which features we enable in tikv-jemalloc-sys. + rust-jemalloc-sys' = (rust-jemalloc-sys.override { + # tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms feature + unprefixed = true; + }); + buildDepsOnlyEnv = let rocksdb' = rocksdb.override { - enableJemalloc = builtins.elem "jemalloc" features; + jemalloc = rust-jemalloc-sys'; + enableJemalloc = featureEnabled "jemalloc"; }; in { @@ -60,6 +74,8 @@ let ]; }; + buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys'; + nativeBuildInputs = [ # bindgen needs the build platform's libclang. Apparently due to "splicing # weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the diff --git a/nix/shell.nix b/nix/shell.nix index e9d7f493..ebfe1dc6 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -30,5 +30,9 @@ mkShell { toolchain ] ++ - default.nativeBuildInputs; + default.nativeBuildInputs + ++ + default.propagatedBuildInputs + ++ + default.buildInputs; } From 8f24ac1f27d5e09cb7ba36cc4bf0d7835945d5ba Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 23 May 2024 15:11:06 -0700 Subject: [PATCH 179/617] do default-feature unification in nix Some of the features affect nix dependencies, so we need to have a full feature list available when constructing the nix derivation. This incidentally fixes the bug where we weren't enabling jemalloc on rocksdb in CI/devshells, because jemalloc is now a default feature. It does not fix the more general class of that issue, where CI is performing an `--all-features` build in a nix devshell built for default-features. I am now passing `--no-default-features` to cargo, and having it use our unified feature list rather than duplicating the unification inside cargo. --- nix/pkgs/default/default.nix | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 0a7ef92f..4313ea6e 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -16,7 +16,14 @@ }: let - featureEnabled = feature : builtins.elem feature features; + # We perform default-feature unification in nix, because some of the dependencies + # on the nix side depend on feature values. + allDefaultFeatures = + (lib.importTOML "${inputs.self}/Cargo.toml").features.default; + features' = lib.unique + (features ++ lib.optionals default-features allDefaultFeatures); + + featureEnabled = feature : builtins.elem feature features'; # This derivation will set the JEMALLOC_OVERRIDE variable, causing the # tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's @@ -96,13 +103,10 @@ craneLib.buildPackage ( commonAttrs // { env = buildDepsOnlyEnv; }); - cargoExtraArgs = "--locked " + cargoExtraArgs = "--locked --no-default-features " + lib.optionalString - (!default-features) - "--no-default-features " - + lib.optionalString - (features != []) - "--features " + (builtins.concatStringsSep "," features); + (features' != []) + "--features " + (builtins.concatStringsSep "," features'); # This is redundant with CI doCheck = false; From fa48c48d525060e212bd9a6cc52d4d6125ebe64a Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 00:44:30 -0700 Subject: [PATCH 180/617] add all-features devshell Because the nix rocksdb build depends on the jemalloc feature, you need to use a different devshell when passing --all-features to cargo than the default. --- flake.nix | 8 ++++++++ nix/pkgs/default/default.nix | 14 +++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 69e6194d..1d45525b 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,11 @@ oci-image = self.callPackage ./nix/pkgs/oci-image {}; + # Return a new scope with overrides applied to the 'default' package + overrideDefaultPackage = args: self.overrideScope (final: prev: { + default = prev.default.override args; + }); + shell = self.callPackage ./nix/shell.nix {}; # The Rust toolchain to use @@ -84,6 +89,9 @@ ); devShells.default = (mkScope pkgs).shell; + devShells.all-features = ((mkScope pkgs).overrideDefaultPackage { + all-features = true; + }).shell; } ) // diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 4313ea6e..6689e86c 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -11,6 +11,7 @@ # Options (keep sorted) , default-features ? true +, all-features ? false , features ? [] , profile ? "release" }: @@ -18,10 +19,17 @@ let # We perform default-feature unification in nix, because some of the dependencies # on the nix side depend on feature values. - allDefaultFeatures = - (lib.importTOML "${inputs.self}/Cargo.toml").features.default; + cargoManifest = lib.importTOML "${inputs.self}/Cargo.toml"; + allDefaultFeatures = cargoManifest.features.default; + allFeatures = lib.unique ( + lib.remove "default" (lib.attrNames cargoManifest.features) ++ + lib.attrNames + (lib.filterAttrs (_: dependency: dependency.optional or false) + cargoManifest.dependencies)); features' = lib.unique - (features ++ lib.optionals default-features allDefaultFeatures); + (features ++ + lib.optionals default-features allDefaultFeatures ++ + lib.optionals all-features allFeatures); featureEnabled = feature : builtins.elem feature features'; From 467417c32a5ef8b69a3989d9515607872a2f434f Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 00:45:22 -0700 Subject: [PATCH 181/617] use all-features devshell for clippy/all in CI Without this, we're building a static rocksdb inside the rust-rocksdb build script every time. As far as I know this doesn't change clippy's behavior, but it does take a *long* time. --- .envrc | 2 +- bin/nix-build-and-cache | 1 + engage.toml | 16 +++++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.envrc b/.envrc index e080e228..56451c1a 100644 --- a/.envrc +++ b/.envrc @@ -1,6 +1,6 @@ #!/usr/bin/env bash -use flake +use flake ".#${DIRENV_DEVSHELL:-default}" PATH_add bin diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 4c2a8d71..ff06bc96 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -51,6 +51,7 @@ ci() { # Keep sorted "$toplevel#devShells.x86_64-linux.default" + "$toplevel#devShells.x86_64-linux.all-features" attic#default nixpkgs#direnv nixpkgs#jq diff --git a/engage.toml b/engage.toml index ec3b4066..3a78a737 100644 --- a/engage.toml +++ b/engage.toml @@ -68,13 +68,15 @@ script = "cargo clippy --workspace --all-targets --color=always -- -D warnings" name = "cargo-clippy/all" group = "lints" script = """ -cargo clippy \ - --workspace \ - --all-targets \ - --all-features \ - --color=always \ - -- \ - -D warnings +env DIRENV_DEVSHELL=all-features \ + direnv exec . \ + cargo clippy \ + --workspace \ + --all-targets \ + --all-features \ + --color=always \ + -- \ + -D warnings """ [[task]] From 823515b475d137255b7cfeb304652d2d1dd2090e Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 00:49:56 -0700 Subject: [PATCH 182/617] load .env file before initializing flake This allows specifying DIRENV_DEVSHELL in .env --- .envrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.envrc b/.envrc index 56451c1a..952ec2f8 100644 --- a/.envrc +++ b/.envrc @@ -1,7 +1,7 @@ #!/usr/bin/env bash +dotenv_if_exists + use flake ".#${DIRENV_DEVSHELL:-default}" PATH_add bin - -dotenv_if_exists From 3a71b8e5d182acd967a69b86da62158028d6df8a Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 01:15:19 -0700 Subject: [PATCH 183/617] optionally use nom to build devshell dependencies This is nice when messing around with the nix configuration, since you'll often end up building rocksdb *many* times. We shouldn't need to do anything to get the logs in CI because we're calling `nix-build-and-cache ci` at the beginning --- .envrc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.envrc b/.envrc index 952ec2f8..ede83d9a 100644 --- a/.envrc +++ b/.envrc @@ -2,6 +2,16 @@ dotenv_if_exists -use flake ".#${DIRENV_DEVSHELL:-default}" +system="$(nix eval --impure --raw --expr 'builtins.currentSystem')" +devshell="${DIRENV_DEVSHELL:-default}" + +if command -v nom &> /dev/null && [ -t 0 ]; then + # if nom is available, build the devshell dependencies with it to get nicer + # progress monitoring. Don't do this when stdout is piped, because it shows + # up weird in engage. + nom build ".#devShells.$system.$devshell" +fi + +use flake ".#$devshell" PATH_add bin From 624ff5741427e8dfeff52243b3cb1fbc54a37295 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 30 May 2024 21:09:38 -0700 Subject: [PATCH 184/617] test all-features in CI This should catch if jemalloc linking is broken, which wouldn't be caught by the `lints::cargo-clippy/all` task. --- engage.toml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/engage.toml b/engage.toml index 3a78a737..19acec13 100644 --- a/engage.toml +++ b/engage.toml @@ -80,7 +80,7 @@ env DIRENV_DEVSHELL=all-features \ """ [[task]] -name = "cargo" +name = "cargo/default" group = "tests" script = """ cargo test \ @@ -90,3 +90,18 @@ cargo test \ -- \ --color=always """ + +[[task]] +name = "cargo/all" +group = "tests" +script = """ +env DIRENV_DEVSHELL=all-features \ + direnv exec . \ + cargo test \ + --all-features \ + --workspace \ + --all-targets \ + --color=always \ + -- \ + --color=always +""" From bf1d54defc70e96b2c7a25ca421596d08de51c90 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 31 May 2024 10:23:49 +0000 Subject: [PATCH 185/617] axum: factor out non-generic parts of request conversion This saves ~10% in binary size! --- src/api/ruma_wrapper/axum.rs | 677 ++++++++++++++++++----------------- 1 file changed, 346 insertions(+), 331 deletions(-) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 98dc5e98..4abef7fb 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -15,12 +15,13 @@ use axum_extra::{ typed_header::TypedHeaderRejectionReason, TypedHeader, }; -use bytes::{BufMut, BytesMut}; +use bytes::{BufMut, Bytes, BytesMut}; use http::{Request, StatusCode}; use http_body_util::BodyExt; use ruma::{ api::{ - client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse, + client::error::ErrorKind, AuthScheme, IncomingRequest, Metadata, + OutgoingResponse, }, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId, }; @@ -37,6 +38,334 @@ enum Token { None, } +/// Return value of [`ar_from_request_inner()`], used to construct an [`Ar`]. +struct ArPieces { + sender_user: Option, + sender_device: Option, + sender_servername: Option, + json_body: Option, + appservice_info: Option, + path_params: Path>, + http_request: Request, +} + +/// Non-generic part of [`Ar::from_request()`]. Splitting this out reduces +/// binary size by ~10%. +#[allow(clippy::too_many_lines)] +async fn ar_from_request_inner( + req: axum::extract::Request, + metadata: Metadata, +) -> Result { + #[derive(Deserialize)] + struct QueryParams { + access_token: Option, + user_id: Option, + } + + let (mut parts, mut body) = { + let limited_req = req.with_limited_body(); + let (parts, body) = limited_req.into_parts(); + let body = body + .collect() + .await + .map_err(|_| { + Error::BadRequest(ErrorKind::MissingToken, "Missing token.") + })? + .to_bytes(); + (parts, body) + }; + + let auth_header: Option>> = + parts.extract().await?; + let path_params: Path> = parts.extract().await?; + + let query = parts.uri.query().unwrap_or_default(); + let query_params: QueryParams = match serde_html_form::from_str(query) { + Ok(params) => params, + Err(e) => { + error!(%query, "Failed to deserialize query parameters: {}", e); + return Err(Error::BadRequest( + ErrorKind::Unknown, + "Failed to read query parameters", + )); + } + }; + + let token = match &auth_header { + Some(TypedHeader(Authorization(bearer))) => Some(bearer.token()), + None => query_params.access_token.as_deref(), + }; + + let token = if let Some(token) = token { + if let Some(reg_info) = + services().appservice.find_from_token(token).await + { + Token::Appservice(Box::new(reg_info.clone())) + } else if let Some((user_id, device_id)) = + services().users.find_from_token(token)? + { + Token::User((user_id, OwnedDeviceId::from(device_id))) + } else { + Token::Invalid + } + } else { + Token::None + }; + + let mut json_body = + serde_json::from_slice::(&body).ok(); + + let (sender_user, sender_device, sender_servername, appservice_info) = + match (metadata.authentication, token) { + (_, Token::Invalid) => { + return Err(Error::BadRequest( + ErrorKind::UnknownToken { + soft_logout: false, + }, + "Unknown access token.", + )) + } + (AuthScheme::AccessToken, Token::Appservice(info)) => { + let user_id = query_params + .user_id + .map_or_else( + || { + UserId::parse_with_server_name( + info.registration.sender_localpart.as_str(), + services().globals.server_name(), + ) + }, + UserId::parse, + ) + .map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidUsername, + "Username is invalid.", + ) + })?; + + if !info.is_user_match(&user_id) { + return Err(Error::BadRequest( + ErrorKind::Exclusive, + "User is not in namespace.", + )); + } + + if !services().users.exists(&user_id)? { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "User does not exist.", + )); + } + + (Some(user_id), None, None, Some(*info)) + } + ( + AuthScheme::None + | AuthScheme::AppserviceToken + | AuthScheme::AccessTokenOptional, + Token::Appservice(info), + ) => (None, None, None, Some(*info)), + (AuthScheme::AccessToken, Token::None) => { + return Err(Error::BadRequest( + ErrorKind::MissingToken, + "Missing access token.", + )); + } + ( + AuthScheme::AccessToken + | AuthScheme::AccessTokenOptional + | AuthScheme::None, + Token::User((user_id, device_id)), + ) => (Some(user_id), Some(device_id), None, None), + (AuthScheme::ServerSignatures, Token::None) => { + let TypedHeader(Authorization(x_matrix)) = parts + .extract::>>() + .await + .map_err(|e| { + warn!("Missing or invalid Authorization header: {}", e); + + let msg = match e.reason() { + TypedHeaderRejectionReason::Missing => { + "Missing Authorization header." + } + TypedHeaderRejectionReason::Error(_) => { + "Invalid X-Matrix signatures." + } + _ => "Unknown header-related error", + }; + + Error::BadRequest(ErrorKind::forbidden(), msg) + })?; + + let origin_signatures = BTreeMap::from_iter([( + x_matrix.key.clone(), + CanonicalJsonValue::String(x_matrix.sig), + )]); + + let signatures = BTreeMap::from_iter([( + x_matrix.origin.as_str().to_owned(), + CanonicalJsonValue::Object(origin_signatures), + )]); + + let mut request_map = BTreeMap::from_iter([ + ( + "method".to_owned(), + CanonicalJsonValue::String(parts.method.to_string()), + ), + ( + "uri".to_owned(), + CanonicalJsonValue::String(parts.uri.to_string()), + ), + ( + "origin".to_owned(), + CanonicalJsonValue::String( + x_matrix.origin.as_str().to_owned(), + ), + ), + ( + "destination".to_owned(), + CanonicalJsonValue::String( + services() + .globals + .server_name() + .as_str() + .to_owned(), + ), + ), + ( + "signatures".to_owned(), + CanonicalJsonValue::Object(signatures), + ), + ]); + + if let Some(json_body) = &json_body { + request_map.insert("content".to_owned(), json_body.clone()); + }; + + let keys_result = services() + .rooms + .event_handler + .fetch_signing_keys( + &x_matrix.origin, + vec![x_matrix.key.clone()], + ) + .await; + + let keys = match keys_result { + Ok(b) => b, + Err(e) => { + warn!("Failed to fetch signing keys: {}", e); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Failed to fetch signing keys.", + )); + } + }; + + let pub_key_map = BTreeMap::from_iter([( + x_matrix.origin.as_str().to_owned(), + keys, + )]); + + match ruma::signatures::verify_json(&pub_key_map, &request_map) + { + Ok(()) => (None, None, Some(x_matrix.origin), None), + Err(e) => { + warn!( + "Failed to verify json request from {}: {}\n{:?}", + x_matrix.origin, e, request_map + ); + + if parts.uri.to_string().contains('@') { + warn!( + "Request uri contained '@' character. Make \ + sure your reverse proxy gives Grapevine the \ + raw uri (apache: use nocanon)" + ); + } + + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Failed to verify X-Matrix signatures.", + )); + } + } + } + ( + AuthScheme::None + | AuthScheme::AppserviceToken + | AuthScheme::AccessTokenOptional, + Token::None, + ) => (None, None, None, None), + ( + AuthScheme::ServerSignatures, + Token::Appservice(_) | Token::User(_), + ) => { + return Err(Error::BadRequest( + ErrorKind::Unauthorized, + "Only server signatures should be used on this endpoint.", + )); + } + (AuthScheme::AppserviceToken, Token::User(_)) => { + return Err(Error::BadRequest( + ErrorKind::Unauthorized, + "Only appservice access tokens should be used on this \ + endpoint.", + )); + } + }; + + let mut http_request = + Request::builder().uri(parts.uri).method(parts.method); + *http_request.headers_mut().unwrap() = parts.headers; + + if let Some(CanonicalJsonValue::Object(json_body)) = &mut json_body { + let user_id = sender_user.clone().unwrap_or_else(|| { + UserId::parse_with_server_name("", services().globals.server_name()) + .expect("we know this is valid") + }); + + let uiaa_request = json_body + .get("auth") + .and_then(|auth| auth.as_object()) + .and_then(|auth| auth.get("session")) + .and_then(|session| session.as_str()) + .and_then(|session| { + services().uiaa.get_uiaa_request( + &user_id, + &sender_device.clone().unwrap_or_else(|| "".into()), + session, + ) + }); + + if let Some(CanonicalJsonValue::Object(initial_request)) = uiaa_request + { + for (key, value) in initial_request { + json_body.entry(key).or_insert(value); + } + } + + let mut buf = BytesMut::new().writer(); + serde_json::to_writer(&mut buf, json_body) + .expect("value serialization can't fail"); + body = buf.into_inner().freeze(); + } + let http_request = http_request.body(body).unwrap(); + + debug!("{:?}", http_request); + + Ok(ArPieces { + sender_user, + sender_device, + sender_servername, + json_body, + appservice_info, + path_params, + http_request, + }) +} + #[async_trait] impl FromRequest for Ar where @@ -44,344 +373,30 @@ where { type Rejection = Error; - #[allow(clippy::too_many_lines)] async fn from_request( req: axum::extract::Request, _state: &S, ) -> Result { - #[derive(Deserialize)] - struct QueryParams { - access_token: Option, - user_id: Option, - } + let pieces = ar_from_request_inner(req, T::METADATA).await?; - let (mut parts, mut body) = { - let limited_req = req.with_limited_body(); - let (parts, body) = limited_req.into_parts(); - let body = body - .collect() - .await - .map_err(|_| { - Error::BadRequest(ErrorKind::MissingToken, "Missing token.") - })? - .to_bytes(); - (parts, body) - }; - - let metadata = T::METADATA; - let auth_header: Option>> = - parts.extract().await?; - let path_params: Path> = parts.extract().await?; - - let query = parts.uri.query().unwrap_or_default(); - let query_params: QueryParams = match serde_html_form::from_str(query) { - Ok(params) => params, - Err(e) => { - error!(%query, "Failed to deserialize query parameters: {}", e); - return Err(Error::BadRequest( - ErrorKind::Unknown, - "Failed to read query parameters", - )); - } - }; - - let token = match &auth_header { - Some(TypedHeader(Authorization(bearer))) => Some(bearer.token()), - None => query_params.access_token.as_deref(), - }; - - let token = if let Some(token) = token { - if let Some(reg_info) = - services().appservice.find_from_token(token).await - { - Token::Appservice(Box::new(reg_info.clone())) - } else if let Some((user_id, device_id)) = - services().users.find_from_token(token)? - { - Token::User((user_id, OwnedDeviceId::from(device_id))) - } else { - Token::Invalid - } - } else { - Token::None - }; - - let mut json_body = - serde_json::from_slice::(&body).ok(); - - let (sender_user, sender_device, sender_servername, appservice_info) = - match (metadata.authentication, token) { - (_, Token::Invalid) => { - return Err(Error::BadRequest( - ErrorKind::UnknownToken { - soft_logout: false, - }, - "Unknown access token.", - )) - } - (AuthScheme::AccessToken, Token::Appservice(info)) => { - let user_id = query_params - .user_id - .map_or_else( - || { - UserId::parse_with_server_name( - info.registration.sender_localpart.as_str(), - services().globals.server_name(), - ) - }, - UserId::parse, - ) - .map_err(|_| { - Error::BadRequest( - ErrorKind::InvalidUsername, - "Username is invalid.", - ) - })?; - - if !info.is_user_match(&user_id) { - return Err(Error::BadRequest( - ErrorKind::Exclusive, - "User is not in namespace.", - )); - } - - if !services().users.exists(&user_id)? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User does not exist.", - )); - } - - (Some(user_id), None, None, Some(*info)) - } - ( - AuthScheme::None - | AuthScheme::AppserviceToken - | AuthScheme::AccessTokenOptional, - Token::Appservice(info), - ) => (None, None, None, Some(*info)), - (AuthScheme::AccessToken, Token::None) => { - return Err(Error::BadRequest( - ErrorKind::MissingToken, - "Missing access token.", - )); - } - ( - AuthScheme::AccessToken - | AuthScheme::AccessTokenOptional - | AuthScheme::None, - Token::User((user_id, device_id)), - ) => (Some(user_id), Some(device_id), None, None), - (AuthScheme::ServerSignatures, Token::None) => { - let TypedHeader(Authorization(x_matrix)) = parts - .extract::>>() - .await - .map_err(|e| { - warn!( - "Missing or invalid Authorization header: {}", - e - ); - - let msg = match e.reason() { - TypedHeaderRejectionReason::Missing => { - "Missing Authorization header." - } - TypedHeaderRejectionReason::Error(_) => { - "Invalid X-Matrix signatures." - } - _ => "Unknown header-related error", - }; - - Error::BadRequest(ErrorKind::forbidden(), msg) - })?; - - let origin_signatures = BTreeMap::from_iter([( - x_matrix.key.clone(), - CanonicalJsonValue::String(x_matrix.sig), - )]); - - let signatures = BTreeMap::from_iter([( - x_matrix.origin.as_str().to_owned(), - CanonicalJsonValue::Object(origin_signatures), - )]); - - let mut request_map = BTreeMap::from_iter([ - ( - "method".to_owned(), - CanonicalJsonValue::String( - parts.method.to_string(), - ), - ), - ( - "uri".to_owned(), - CanonicalJsonValue::String(parts.uri.to_string()), - ), - ( - "origin".to_owned(), - CanonicalJsonValue::String( - x_matrix.origin.as_str().to_owned(), - ), - ), - ( - "destination".to_owned(), - CanonicalJsonValue::String( - services() - .globals - .server_name() - .as_str() - .to_owned(), - ), - ), - ( - "signatures".to_owned(), - CanonicalJsonValue::Object(signatures), - ), - ]); - - if let Some(json_body) = &json_body { - request_map - .insert("content".to_owned(), json_body.clone()); - }; - - let keys_result = services() - .rooms - .event_handler - .fetch_signing_keys( - &x_matrix.origin, - vec![x_matrix.key.clone()], - ) - .await; - - let keys = match keys_result { - Ok(b) => b, - Err(e) => { - warn!("Failed to fetch signing keys: {}", e); - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "Failed to fetch signing keys.", - )); - } - }; - - let pub_key_map = BTreeMap::from_iter([( - x_matrix.origin.as_str().to_owned(), - keys, - )]); - - match ruma::signatures::verify_json( - &pub_key_map, - &request_map, - ) { - Ok(()) => (None, None, Some(x_matrix.origin), None), - Err(e) => { - warn!( - "Failed to verify json request from {}: \ - {}\n{:?}", - x_matrix.origin, e, request_map - ); - - if parts.uri.to_string().contains('@') { - warn!( - "Request uri contained '@' character. \ - Make sure your reverse proxy gives \ - Grapevine the raw uri (apache: use \ - nocanon)" - ); - } - - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "Failed to verify X-Matrix signatures.", - )); - } - } - } - ( - AuthScheme::None - | AuthScheme::AppserviceToken - | AuthScheme::AccessTokenOptional, - Token::None, - ) => (None, None, None, None), - ( - AuthScheme::ServerSignatures, - Token::Appservice(_) | Token::User(_), - ) => { - return Err(Error::BadRequest( - ErrorKind::Unauthorized, - "Only server signatures should be used on this \ - endpoint.", - )); - } - (AuthScheme::AppserviceToken, Token::User(_)) => { - return Err(Error::BadRequest( - ErrorKind::Unauthorized, - "Only appservice access tokens should be used on this \ - endpoint.", - )); - } - }; - - let mut http_request = - Request::builder().uri(parts.uri).method(parts.method); - *http_request.headers_mut().unwrap() = parts.headers; - - if let Some(CanonicalJsonValue::Object(json_body)) = &mut json_body { - let user_id = sender_user.clone().unwrap_or_else(|| { - UserId::parse_with_server_name( - "", - services().globals.server_name(), - ) - .expect("we know this is valid") - }); - - let uiaa_request = json_body - .get("auth") - .and_then(|auth| auth.as_object()) - .and_then(|auth| auth.get("session")) - .and_then(|session| session.as_str()) - .and_then(|session| { - services().uiaa.get_uiaa_request( - &user_id, - &sender_device.clone().unwrap_or_else(|| "".into()), - session, + let body = + T::try_from_http_request(pieces.http_request, &pieces.path_params) + .map_err(|e| { + warn!("try_from_http_request failed: {:?}", e); + debug!("JSON body: {:?}", pieces.json_body); + Error::BadRequest( + ErrorKind::BadJson, + "Failed to deserialize request.", ) - }); - - if let Some(CanonicalJsonValue::Object(initial_request)) = - uiaa_request - { - for (key, value) in initial_request { - json_body.entry(key).or_insert(value); - } - } - - let mut buf = BytesMut::new().writer(); - serde_json::to_writer(&mut buf, json_body) - .expect("value serialization can't fail"); - body = buf.into_inner().freeze(); - } - - let http_request = http_request.body(&*body).unwrap(); - - debug!("{:?}", http_request); - - let body = T::try_from_http_request(http_request, &path_params) - .map_err(|e| { - warn!("try_from_http_request failed: {:?}", e); - debug!("JSON body: {:?}", json_body); - Error::BadRequest( - ErrorKind::BadJson, - "Failed to deserialize request.", - ) - })?; + })?; Ok(Ar { body, - sender_user, - sender_device, - sender_servername, - json_body, - appservice_info, + sender_user: pieces.sender_user, + sender_device: pieces.sender_device, + sender_servername: pieces.sender_servername, + json_body: pieces.json_body, + appservice_info: pieces.appservice_info, }) } } From aec314ce850fb7073c2a537a56fcaa4966a5926c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 2 Jun 2024 17:27:54 -0700 Subject: [PATCH 186/617] get `tracing-opentelemetry` from crates.io They made a release with the changes we needed; we can stop pulling it from git now. --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d5e3abb..1f913bef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3158,8 +3158,9 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.23.0" -source = "git+https://github.com/tokio-rs/tracing-opentelemetry?branch=v0.1.x#2539f4f7bde3dc3f320e5fb935d2c13a69a540dd" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f68803492bf28ab40aeccaecc7021096bd256baf7ca77c3d425d89b35a7be4e4" dependencies = [ "js-sys", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index bedeac21..ba259e43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,7 +134,7 @@ tower = { version = "0.4.13", features = ["util"] } tower-http = { version = "0.5.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.40", features = [] } tracing-flame = "0.2.0" -tracing-opentelemetry = { git = "https://github.com/tokio-rs/tracing-opentelemetry", branch = "v0.1.x" } +tracing-opentelemetry = "0.24.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } trust-dns-resolver = "0.23.2" From 62dd097f49a0c6d597391194724bdd1e058da487 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 3 Jun 2024 16:17:43 +0000 Subject: [PATCH 187/617] Use Ruma XMatrix type instead of rolling our own Both the hand-rolled parser and serialization were wrong in countless ways. The current Ruma parser is much better, and the Ruma serialization will be fixed by https://github.com/ruma/ruma/pull/1830. --- Cargo.lock | 18 +++++++++ Cargo.toml | 2 +- src/api/ruma_wrapper/axum.rs | 71 ++---------------------------------- src/api/server_server.rs | 42 +++++++++++---------- 4 files changed, 45 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f913bef..1f3762d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2129,6 +2129,7 @@ dependencies = [ "ruma-federation-api", "ruma-identity-service-api", "ruma-push-gateway-api", + "ruma-server-util", "ruma-signatures", "ruma-state-res", "web-time", @@ -2279,6 +2280,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ruma-server-util" +version = "0.3.0" +source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +dependencies = [ + "headers", + "ruma-common", + "tracing", + "yap", +] + [[package]] name = "ruma-signatures" version = "0.15.0" @@ -3676,6 +3688,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yap" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe269e7b803a5e8e20cbd97860e136529cd83bf2c9c6d37b142467e7e1f051f" + [[package]] name = "zerocopy" version = "0.7.34" diff --git a/Cargo.toml b/Cargo.toml index ba259e43..7b9a6872 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,7 +117,7 @@ regex = "1.10.4" reqwest = { version = "0.12.4", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } -ruma = { git = "https://github.com/ruma/ruma", branch = "main", 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/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.31.0", optional = true, features = ["bundled"] } rust-argon2 = "2.1.0" sd-notify = { version = "0.4.1", optional = true } diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 4abef7fb..451b6fda 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -8,10 +8,7 @@ use axum::{ RequestExt, RequestPartsExt, }; use axum_extra::{ - headers::{ - authorization::{Bearer, Credentials}, - Authorization, - }, + headers::{authorization::Bearer, Authorization}, typed_header::TypedHeaderRejectionReason, TypedHeader, }; @@ -23,6 +20,7 @@ use ruma::{ client::error::ErrorKind, AuthScheme, IncomingRequest, Metadata, OutgoingResponse, }, + server_util::authorization::XMatrix, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId, }; use serde::Deserialize; @@ -199,7 +197,7 @@ async fn ar_from_request_inner( })?; let origin_signatures = BTreeMap::from_iter([( - x_matrix.key.clone(), + x_matrix.key.to_string(), CanonicalJsonValue::String(x_matrix.sig), )]); @@ -248,7 +246,7 @@ async fn ar_from_request_inner( .event_handler .fetch_signing_keys( &x_matrix.origin, - vec![x_matrix.key.clone()], + vec![x_matrix.key.to_string()], ) .await; @@ -400,67 +398,6 @@ where }) } } - -struct XMatrix { - origin: OwnedServerName, - // KeyName? - key: String, - sig: String, -} - -impl Credentials for XMatrix { - const SCHEME: &'static str = "X-Matrix"; - - fn decode(value: &http::HeaderValue) -> Option { - debug_assert!( - value.as_bytes().starts_with(b"X-Matrix "), - "HeaderValue to decode should start with \"X-Matrix ..\", \ - received = {value:?}", - ); - - let parameters = str::from_utf8(&value.as_bytes()["X-Matrix ".len()..]) - .ok()? - .trim_start(); - - let mut origin = None; - let mut key = None; - let mut sig = None; - - for entry in parameters.split_terminator(',') { - let (name, value) = entry.split_once('=')?; - - // It's not at all clear why some fields are quoted and others not - // in the spec, let's simply accept either form for - // every field. - let value = value - .strip_prefix('"') - .and_then(|rest| rest.strip_suffix('"')) - .unwrap_or(value); - - // FIXME: Catch multiple fields of the same name - match name { - "origin" => origin = Some(value.try_into().ok()?), - "key" => key = Some(value.to_owned()), - "sig" => sig = Some(value.to_owned()), - _ => debug!( - "Unexpected field `{}` in X-Matrix Authorization header", - name - ), - } - } - - Some(Self { - origin: origin?, - key: key?, - sig: sig?, - }) - } - - fn encode(&self) -> http::HeaderValue { - todo!() - } -} - impl IntoResponse for Ra { fn into_response(self) -> Response { match self.0.try_into_http_response::() { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index e69a52fc..b936b1e7 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -10,8 +10,8 @@ use std::{ }; use axum::{response::IntoResponse, Json}; +use axum_extra::headers::{Authorization, HeaderMapExt}; use get_profile_information::v1::ProfileField; -use http::header::{HeaderValue, AUTHORIZATION}; use ruma::{ api::{ client::error::{Error as RumaError, ErrorKind}, @@ -54,10 +54,12 @@ use ruma::{ StateEventType, TimelineEventType, }, serde::{Base64, JsonObject, Raw}, + server_util::authorization::XMatrix, to_device::DeviceIdOrAllDevices, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, - OwnedServerSigningKeyId, OwnedUserId, RoomId, ServerName, + OwnedServerSigningKeyId, OwnedSigningKeyId, OwnedUserId, RoomId, + ServerName, }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; @@ -225,25 +227,25 @@ where serde_json::from_slice(&serde_json::to_vec(&request_json).unwrap()) .unwrap(); - let signatures = - request_json["signatures"].as_object().unwrap().values().map(|v| { - v.as_object().unwrap().iter().map(|(k, v)| (k, v.as_str().unwrap())) - }); + // There's exactly the one signature we just created, fish it back out again + let (key_id, signature) = request_json["signatures"] + .get(services().globals.server_name().as_str()) + .unwrap() + .as_object() + .unwrap() + .iter() + .next() + .unwrap(); - for signature_server in signatures { - for s in signature_server { - http_request.headers_mut().insert( - AUTHORIZATION, - HeaderValue::from_str(&format!( - "X-Matrix origin={},key=\"{}\",sig=\"{}\"", - services().globals.server_name(), - s.0, - s.1 - )) - .unwrap(), - ); - } - } + let key_id = OwnedSigningKeyId::try_from(key_id.clone()).unwrap(); + let signature = signature.as_str().unwrap().to_owned(); + + http_request.headers_mut().typed_insert(Authorization(XMatrix::new( + services().globals.server_name().to_owned(), + None, + key_id, + signature, + ))); let reqwest_request = reqwest::Request::try_from(http_request)?; From 5c39c7c5fff0e1a59999f0c13bf4aa48cb159129 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 3 Jun 2024 16:35:00 +0000 Subject: [PATCH 188/617] Use destination field in X-Matrix Authorization header Both validating and sending it is a MUST since Matrix v1.3. --- src/api/ruma_wrapper/axum.rs | 13 +++++++++++++ src/api/server_server.rs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 451b6fda..14655976 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -196,6 +196,19 @@ async fn ar_from_request_inner( Error::BadRequest(ErrorKind::forbidden(), msg) })?; + if let Some(destination) = x_matrix.destination { + if destination != services().globals.server_name() { + warn!( + %destination, + "Incorrect destination in X-Matrix header" + ); + return Err(Error::BadRequest( + ErrorKind::Unauthorized, + "Incorrect destination in X-Matrix header", + )); + } + } + let origin_signatures = BTreeMap::from_iter([( x_matrix.key.to_string(), CanonicalJsonValue::String(x_matrix.sig), diff --git a/src/api/server_server.rs b/src/api/server_server.rs index b936b1e7..38e37304 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -242,7 +242,7 @@ where http_request.headers_mut().typed_insert(Authorization(XMatrix::new( services().globals.server_name().to_owned(), - None, + Some(destination.to_owned()), key_id, signature, ))); From c64a474954e4573f0fdeace41c44c29c99dcc086 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 3 Jun 2024 17:41:11 -0700 Subject: [PATCH 189/617] workaround to fix search in element We inherited a similar workaround from conduit, but removed it in 71c48f66c4922813c2dc30b7b875200e06ce4b75. At the time, it was not clear that this had broken search. Fixes: !26 --- src/api/client_server/search.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 6e779a18..115b01b9 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -11,7 +11,7 @@ use ruma::{ }, }, }, - uint, + uint, UInt, }; use crate::{services, Ar, Error, Ra, Result}; @@ -138,7 +138,13 @@ pub(crate) async fn search_events_route( Ok(Ra(search_events::v3::Response::new(ResultCategories { room_events: ResultRoomEvents { - count: None, + // TODO(compat): this is not a good estimate of the total number of + // results. we should just be returning None, but + // element incorrectly relies on this field. Switch back + // to None when [1] is fixed + // + // [1]: https://github.com/element-hq/element-web/issues/27517 + count: Some(results.len().try_into().unwrap_or(UInt::MAX)), // TODO groups: BTreeMap::new(), next_batch, From 94fda7c875d26fd216a66284ef1bb327bdab45f0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 29 May 2024 14:34:55 -0700 Subject: [PATCH 190/617] improve usage of Resource * Extract into reusable function (we'll need this later) * Merge the values we want to override over the defaults, instead of dropping the defaults completely * Don't unnecessarily allocate (the `vec![]` usage is gone) --- src/observability.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index 1c28e978..83900a3f 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -36,12 +36,8 @@ pub(crate) fn init(config: &Config) -> Result { let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_trace_config( - opentelemetry_sdk::trace::config().with_resource( - Resource::new(vec![KeyValue::new( - "service.name", - env!("CARGO_PKG_NAME"), - )]), - ), + opentelemetry_sdk::trace::config() + .with_resource(standard_resource()), ) .with_exporter(opentelemetry_otlp::new_exporter().tonic()) .install_batch(opentelemetry_sdk::runtime::Tokio)?; @@ -81,3 +77,11 @@ pub(crate) fn init(config: &Config) -> Result { flame_guard, }) } + +/// Construct the standard [`Resource`] value to use for this service +fn standard_resource() -> Resource { + Resource::default().merge(&Resource::new([KeyValue::new( + "service.name", + env!("CARGO_PKG_NAME"), + )])) +} From a0b92c82e8f3acb6111a81beee26ea9a02b80a7b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 29 May 2024 14:49:49 -0700 Subject: [PATCH 191/617] set up opentelemetry for metrics Also adds an `allow_prometheus` option (disabled by default) to expose a `/metrics` endpoint that returns Prometheus data. --- Cargo.lock | 37 ++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +++ src/config.rs | 2 ++ src/main.rs | 9 +++++++++ src/observability.rs | 48 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 97 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f3762d1..e72fd491 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -872,12 +872,15 @@ dependencies = [ "lru-cache", "nix", "num_cpus", + "once_cell", "opentelemetry", "opentelemetry-jaeger-propagator", "opentelemetry-otlp", + "opentelemetry-prometheus", "opentelemetry_sdk", "parking_lot", "phf", + "prometheus", "rand", "regex", "reqwest", @@ -1662,6 +1665,19 @@ dependencies = [ "tonic", ] +[[package]] +name = "opentelemetry-prometheus" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1a24eafe47b693cb938f8505f240dc26c71db60df9aca376b4f857e9653ec7" +dependencies = [ + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "prometheus", + "protobuf", +] + [[package]] name = "opentelemetry-proto" version = "0.6.0" @@ -1925,6 +1941,21 @@ dependencies = [ "yansi", ] +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "protobuf", + "thiserror", +] + [[package]] name = "prost" version = "0.12.6" @@ -1948,6 +1979,12 @@ dependencies = [ "syn", ] +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "quick-error" version = "1.2.3" diff --git a/Cargo.toml b/Cargo.toml index 7b9a6872..24d7dbb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,12 +106,15 @@ image = { version = "0.25.1", default-features = false, features = ["jpeg", "png jsonwebtoken = "9.3.0" lru-cache = "0.1.2" num_cpus = "1.16.0" +once_cell = "1.19.0" opentelemetry = "0.23.0" opentelemetry-jaeger-propagator = "0.2.0" opentelemetry-otlp = "0.16.0" +opentelemetry-prometheus = "0.16.0" opentelemetry_sdk = { version = "0.23.0", features = ["rt-tokio"] } parking_lot = { version = "0.12.3", optional = true } phf = { version = "0.11.2", features = ["macros"] } +prometheus = "0.13.4" rand = "0.8.5" regex = "1.10.4" reqwest = { version = "0.12.4", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } diff --git a/src/config.rs b/src/config.rs index 88e07794..96a4d2fd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -60,6 +60,8 @@ pub(crate) struct Config { #[serde(default = "false_fn")] pub(crate) allow_jaeger: bool, #[serde(default = "false_fn")] + pub(crate) allow_prometheus: bool, + #[serde(default = "false_fn")] pub(crate) tracing_flame: bool, #[serde(default)] pub(crate) proxy: ProxyConfig, diff --git a/src/main.rs b/src/main.rs index 6f8a4038..dc3e94a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -412,6 +412,15 @@ fn routes(config: &Config) -> Router { .put(c2s::send_state_event_for_empty_key_route), ); + let router = if config.allow_prometheus { + router.route( + "/metrics", + get(|| async { observability::METRICS.export() }), + ) + } else { + router + }; + let router = router .route( "/_matrix/client/r0/rooms/:room_id/initialSync", diff --git a/src/observability.rs b/src/observability.rs index 83900a3f..9a2094a9 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -3,13 +3,17 @@ use std::{fs::File, io::BufWriter}; -use opentelemetry::KeyValue; -use opentelemetry_sdk::Resource; +use once_cell::sync::Lazy; +use opentelemetry::{metrics::MeterProvider, KeyValue}; +use opentelemetry_sdk::{metrics::SdkMeterProvider, Resource}; use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; use crate::{config::Config, error, utils::error::Result}; +/// Globally accessible metrics state +pub(crate) static METRICS: Lazy = Lazy::new(Metrics::new); + /// Cleans up resources relating to observability when [`Drop`]ped pub(crate) struct Guard { /// Drop guard used to flush [`tracing_flame`] data on exit @@ -85,3 +89,43 @@ fn standard_resource() -> Resource { env!("CARGO_PKG_NAME"), )])) } + +/// Holds state relating to metrics +pub(crate) struct Metrics { + /// Internal state for OpenTelemetry metrics + /// + /// We never directly read from [`SdkMeterProvider`], but it needs to + /// outlive all calls to `self.otel_state.0.gather()`, otherwise + /// metrics collection will fail. + otel_state: (prometheus::Registry, SdkMeterProvider), +} + +impl Metrics { + /// Initializes metric-collecting and exporting facilities + fn new() -> Self { + // Set up OpenTelemetry state + let registry = prometheus::Registry::new(); + let exporter = opentelemetry_prometheus::exporter() + .with_registry(registry.clone()) + .build() + .expect("exporter configuration should be valid"); + let provider = SdkMeterProvider::builder() + .with_reader(exporter) + .with_resource(standard_resource()) + .build(); + let _meter = provider.meter(env!("CARGO_PKG_NAME")); + + // TODO: Add some metrics + + Metrics { + otel_state: (registry, provider), + } + } + + /// Export metrics to a string suitable for consumption by e.g. Prometheus + pub(crate) fn export(&self) -> String { + prometheus::TextEncoder::new() + .encode_to_string(&self.otel_state.0.gather()) + .expect("should be able to encode metrics") + } +} From 04ecf4972ef08d68ef88ef8a7f3d9ce001294149 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 29 May 2024 19:40:08 -0700 Subject: [PATCH 192/617] record histogram of http requests --- src/main.rs | 3 +- src/observability.rs | 98 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index dc3e94a7..ea9f535c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -196,7 +196,8 @@ async fn run_server() -> io::Result<()> { .max_request_size .try_into() .expect("failed to convert max request size"), - )); + )) + .layer(axum::middleware::from_fn(observability::http_metrics_layer)); let app = routes(config).layer(middlewares).into_make_service(); let handle = ServerHandle::new(); diff --git a/src/observability.rs b/src/observability.rs index 9a2094a9..5bdbccff 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -1,11 +1,24 @@ //! Facilities for observing runtime behavior #![warn(missing_docs, clippy::missing_docs_in_private_items)] -use std::{fs::File, io::BufWriter}; +use std::{collections::HashSet, fs::File, io::BufWriter}; +use axum::{ + extract::{MatchedPath, Request}, + middleware::Next, + response::Response, +}; +use http::Method; use once_cell::sync::Lazy; -use opentelemetry::{metrics::MeterProvider, KeyValue}; -use opentelemetry_sdk::{metrics::SdkMeterProvider, Resource}; +use opentelemetry::{ + metrics::{MeterProvider, Unit}, + KeyValue, +}; +use opentelemetry_sdk::{ + metrics::{new_view, Aggregation, Instrument, SdkMeterProvider, Stream}, + Resource, +}; +use tokio::time::Instant; use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; @@ -98,11 +111,17 @@ pub(crate) struct Metrics { /// outlive all calls to `self.otel_state.0.gather()`, otherwise /// metrics collection will fail. otel_state: (prometheus::Registry, SdkMeterProvider), + + /// Histogram of HTTP requests + http_requests_histogram: opentelemetry::metrics::Histogram, } impl Metrics { /// Initializes metric-collecting and exporting facilities fn new() -> Self { + // Metric names + let http_requests_histogram_name = "http.requests"; + // Set up OpenTelemetry state let registry = prometheus::Registry::new(); let exporter = opentelemetry_prometheus::exporter() @@ -111,14 +130,38 @@ impl Metrics { .expect("exporter configuration should be valid"); let provider = SdkMeterProvider::builder() .with_reader(exporter) + .with_view( + new_view( + Instrument::new().name(http_requests_histogram_name), + Stream::new().aggregation( + Aggregation::ExplicitBucketHistogram { + boundaries: vec![ + 0., 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, + 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, + 0.8, 0.9, 1., 2., 3., 4., 5., 6., 7., 8., 9., + 10., 20., 30., 40., 50., + ], + record_min_max: true, + }, + ), + ) + .expect("view should be valid"), + ) .with_resource(standard_resource()) .build(); - let _meter = provider.meter(env!("CARGO_PKG_NAME")); + let meter = provider.meter(env!("CARGO_PKG_NAME")); - // TODO: Add some metrics + // Define metrics + + let http_requests_histogram = meter + .f64_histogram(http_requests_histogram_name) + .with_unit(Unit::new("seconds")) + .with_description("Histogram of HTTP requests") + .init(); Metrics { otel_state: (registry, provider), + http_requests_histogram, } } @@ -129,3 +172,48 @@ impl Metrics { .expect("should be able to encode metrics") } } + +/// Track HTTP metrics by converting this into an [`axum`] layer +pub(crate) async fn http_metrics_layer(req: Request, next: Next) -> Response { + /// Routes that should not be included in the metrics + static IGNORED_ROUTES: Lazy> = + Lazy::new(|| [(&Method::GET, "/metrics")].into_iter().collect()); + + let matched_path = + req.extensions().get::().map(|x| x.as_str().to_owned()); + + let method = req.method().to_owned(); + + match matched_path { + // Run the next layer if the route should be ignored + Some(matched_path) + if IGNORED_ROUTES.contains(&(&method, matched_path.as_str())) => + { + next.run(req).await + } + + // Run the next layer if the route is unknown + None => next.run(req).await, + + // Otherwise, run the next layer and record metrics + Some(matched_path) => { + let start = Instant::now(); + let resp = next.run(req).await; + let elapsed = start.elapsed(); + + let status_code = resp.status().as_str().to_owned(); + + let attrs = &[ + KeyValue::new("method", method.as_str().to_owned()), + KeyValue::new("path", matched_path), + KeyValue::new("status_code", status_code), + ]; + + METRICS + .http_requests_histogram + .record(elapsed.as_secs_f64(), attrs); + + resp + } + } +} From c1ec1111ae668677e6e7adb6477618f4cd3c55d7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 30 May 2024 16:43:49 -0700 Subject: [PATCH 193/617] improve route-not-found tracing event --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index ea9f535c..7cb705e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -504,8 +504,8 @@ async fn federation_disabled(_: Uri) -> impl IntoResponse { Error::bad_config("Federation is disabled.") } -async fn not_found(uri: Uri) -> impl IntoResponse { - warn!("Not found: {uri}"); +async fn not_found(method: Method, uri: Uri) -> impl IntoResponse { + debug!(%method, %uri, "unknown route"); Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request") } From 0cdf03288ab8fa363c313bd929c8b5183d14ab77 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 4 Jun 2024 00:38:53 -0700 Subject: [PATCH 194/617] fix missing next_batch for search The previous code would fail to return next_batch if any of the events in the window were not visible to the user. It would also return an unnecessary next_batch when no more results are available if the total number of results is exactly `skip + limit`. This bug will become much more severe with a full filtering implementation, because we will be more likely to trigger it by filtering out events in a search call. Currently, it is only possible to trigger with rooms that have history visibility set to "invited" or "joined". --- src/api/client_server/search.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 115b01b9..c9b9cd4e 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -130,11 +130,8 @@ pub(crate) async fn search_events_route( .take(limit) .collect(); - let next_batch = if results.len() < limit { - None - } else { - Some((skip + limit).to_string()) - }; + let more_unloaded_results = searches.iter_mut().any(|s| s.peek().is_some()); + let next_batch = more_unloaded_results.then(|| (skip + limit).to_string()); Ok(Ra(search_events::v3::Response::new(ResultCategories { room_events: ResultRoomEvents { From 3551a6ef7a29219b9b30f50a7e8c92b92debcdcf Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 4 Jun 2024 00:43:05 -0700 Subject: [PATCH 195/617] fix dropped events in search The previous code would drop some events entirely if any events between `skip` and `skip + limit` were not visible to the user. This would cause the set of events skipped by the `skip(skip)` method to extend past `skip` in the raw result set, because `skip(skip)` was being called *after* filtering out invisible events. This bug will become much more severe with a full filtering implementation, because it will be more likely for events to be filtered out. Currently, it is only possible to trigger with rooms that have history visibility set to "invited" or "joined". --- src/api/client_server/search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index c9b9cd4e..761f89b7 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -93,6 +93,7 @@ pub(crate) async fn search_events_route( let results: Vec<_> = results .iter() + .skip(skip) .filter_map(|result| { services() .rooms @@ -126,7 +127,6 @@ pub(crate) async fn search_events_route( }) }) .filter_map(Result::ok) - .skip(skip) .take(limit) .collect(); From 148df18989978a37a4c3c93b01dfc24664372304 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 22 May 2024 21:29:08 +0000 Subject: [PATCH 196/617] Stop debug-logging every incoming request --- src/api/ruma_wrapper/axum.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 14655976..c3c7c57d 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -364,8 +364,6 @@ async fn ar_from_request_inner( } let http_request = http_request.body(body).unwrap(); - debug!("{:?}", http_request); - Ok(ArPieces { sender_user, sender_device, From 33656081011a3f937be1961749a1d41a221a458b Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 23 May 2024 19:14:46 +0000 Subject: [PATCH 197/617] tracing: record HTTP request method --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7cb705e3..8ae705a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,7 +168,7 @@ async fn run_server() -> io::Result<()> { request.uri().path() }; - tracing::info_span!("http_request", %path) + tracing::info_span!("http_request", %path, method = %request.method()) }, )) .layer(axum::middleware::from_fn(unrecognized_method)) From 2e34241465126c87be8dfbb17deab72b3de7672e Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 23 May 2024 19:15:36 +0000 Subject: [PATCH 198/617] tracing: add span for globals.watch() This spawns a ton of futures which are otherwise all flat in the parent. --- src/database/key_value/globals.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index aab8e101..52fde75f 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -30,6 +30,7 @@ impl service::globals::Data for KeyValueDatabase { }) } + #[tracing::instrument(skip(self))] async fn watch( &self, user_id: &UserId, From f35cbfd89e909a7c7fb365f399a0bd8015474a36 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 23 May 2024 21:53:23 +0000 Subject: [PATCH 199/617] More tracing spans --- src/api/client_server/sync.rs | 1 + src/api/server_server.rs | 5 +++++ src/database/key_value/rooms/edus/read_receipt.rs | 1 + src/database/key_value/users.rs | 8 ++++++++ 4 files changed, 15 insertions(+) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 45d617b2..ae779f92 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -461,6 +461,7 @@ pub(crate) async fn sync_events_route( Ok(Ra(response)) } +#[tracing::instrument(skip_all, fields(room_id = %room_id))] #[allow(clippy::too_many_arguments, clippy::too_many_lines)] async fn load_joined_room( sender_user: &UserId, diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 38e37304..09cc0799 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -361,6 +361,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { /// Numbers in comments below refer to bullet points in linked section of /// specification #[allow(clippy::too_many_lines)] +#[tracing::instrument(ret(level = "debug"))] async fn find_actual_destination( destination: &'_ ServerName, ) -> (FedDest, FedDest) { @@ -504,6 +505,7 @@ async fn find_actual_destination( (actual_destination, hostname) } +#[tracing::instrument(ret(level = "debug"))] async fn query_given_srv_record(record: &str) -> Option { services() .globals @@ -525,6 +527,7 @@ async fn query_given_srv_record(record: &str) -> Option { .unwrap_or(None) } +#[tracing::instrument(ret(level = "debug"))] async fn query_srv_record(hostname: &'_ str) -> Option { let hostname = hostname.trim_end_matches('.'); @@ -537,6 +540,7 @@ async fn query_srv_record(hostname: &'_ str) -> Option { } } +#[tracing::instrument(ret(level = "debug"))] async fn request_well_known(destination: &str) -> Option { let response = services() .globals @@ -678,6 +682,7 @@ pub(crate) async fn get_public_rooms_route( })) } +#[tracing::instrument(skip(pdu))] pub(crate) fn parse_incoming_pdu( pdu: &RawJsonValue, ) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> { diff --git a/src/database/key_value/rooms/edus/read_receipt.rs b/src/database/key_value/rooms/edus/read_receipt.rs index 28b66c27..a897ca6d 100644 --- a/src/database/key_value/rooms/edus/read_receipt.rs +++ b/src/database/key_value/rooms/edus/read_receipt.rs @@ -10,6 +10,7 @@ use crate::{ }; impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { + #[tracing::instrument(skip(self, event))] fn readreceipt_update( &self, user_id: &UserId, diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 6c4e4337..b0c209ce 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -489,6 +489,7 @@ impl service::users::Data for KeyValueDatabase { Ok(counts) } + #[tracing::instrument(skip(self, device_keys))] fn add_device_keys( &self, user_id: &UserId, @@ -510,6 +511,12 @@ impl service::users::Data for KeyValueDatabase { Ok(()) } + #[tracing::instrument(skip( + self, + master_key, + self_signing_key, + user_signing_key + ))] fn add_cross_signing_keys( &self, user_id: &UserId, @@ -724,6 +731,7 @@ impl service::users::Data for KeyValueDatabase { ) } + #[tracing::instrument(skip(self))] fn mark_device_key_update(&self, user_id: &UserId) -> Result<()> { let count = services().globals.next_count()?.to_be_bytes(); for room_id in services() From 88bb2ea6004c8d578eafd8a078701588ea250148 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 24 May 2024 16:18:10 +0000 Subject: [PATCH 200/617] Remove redundant span attributes There's no need to record attributes that are already present in all callers. --- src/api/server_server.rs | 2 +- src/service/rooms/event_handler.rs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 09cc0799..c2e22ac7 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -361,7 +361,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { /// Numbers in comments below refer to bullet points in linked section of /// specification #[allow(clippy::too_many_lines)] -#[tracing::instrument(ret(level = "debug"))] +#[tracing::instrument(skip(destination), ret(level = "debug"))] async fn find_actual_destination( destination: &'_ ServerName, ) -> (FedDest, FedDest) { diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 5710259c..ad30f938 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -308,7 +308,7 @@ impl Service { } #[allow(clippy::type_complexity, clippy::too_many_arguments)] - #[tracing::instrument(skip(self, create_event, value, pub_key_map))] + #[tracing::instrument(skip(self, origin, room_id, value, pub_key_map))] fn handle_outlier_pdu<'a>( &'a self, origin: &'a ServerName, @@ -512,12 +512,9 @@ impl Service { }) } - #[tracing::instrument(skip( - self, - incoming_pdu, - val, - create_event, - pub_key_map + #[tracing::instrument(skip_all, fields( + incoming_pdu = %incoming_pdu.event_id, + create_event = %create_event.event_id, ))] pub(crate) async fn upgrade_outlier_to_timeline_pdu( &self, From f7a0e3012b622b73ffd6a6be9c67bb387a0d3d59 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 30 May 2024 19:44:48 +0000 Subject: [PATCH 201/617] server_server: log ignored signing key updates --- src/api/server_server.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index c2e22ac7..f2ee748d 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -971,6 +971,7 @@ pub(crate) async fn send_transaction_message_route( self_signing_key, }) => { if user_id.server_name() != sender_servername { + warn!(%user_id, %sender_servername, "Got signing key update from incorrect homeserver, ignoring"); continue; } if let Some(master_key) = master_key { From 0b9e43cbdd1a014651a8ee877d146847e26b58d1 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Jun 2024 14:34:17 +0000 Subject: [PATCH 202/617] Record authentication info in incoming requests --- src/main.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8ae705a3..180f2e03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,7 @@ use tower_http::{ trace::TraceLayer, ServiceBuilderExt as _, }; -use tracing::{debug, info, warn}; +use tracing::{debug, info, info_span, warn, Instrument}; pub(crate) mod api; pub(crate) mod clap; @@ -570,8 +570,17 @@ macro_rules! impl_ruma_handler { path, on( method_filter, - |$( $ty: $ty, )* req| async move { - handler($($ty,)* req).await + |$( $ty: $ty, )* req: Ar| async move { + let span = info_span!( + "run_ruma_handler", + auth.user = ?req.sender_user, + auth.device = ?req.sender_device, + auth.servername = ?req.sender_servername, + auth.appservice_id = ?req.appservice_info + .as_ref() + .map(|i| &i.registration.id) + ); + handler($($ty,)* req).instrument(span).await } ) ) From dd6cda4519d9b87b31e08d05833138a3b894e9bd Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Jun 2024 14:50:28 +0000 Subject: [PATCH 203/617] Useful span names for incoming HTTP requests Usually it's not possible to make span names dynamic, but the magic otel.name attribute changes the span name in the OpenTelemetry tracing backend. --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 180f2e03..25bc16e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,7 +168,7 @@ async fn run_server() -> io::Result<()> { request.uri().path() }; - tracing::info_span!("http_request", %path, method = %request.method()) + tracing::info_span!("http_request", otel.name = path, %path, method = %request.method()) }, )) .layer(axum::middleware::from_fn(unrecognized_method)) From c6f75a1b9308f253ec29606ae02de6bc9fcd1903 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Jun 2024 15:56:58 +0000 Subject: [PATCH 204/617] Add tracing span for Ar::from_request() The fact that this is called for every request is somewhat obscured, it should be obvious in tracing at least. --- src/api/ruma_wrapper/axum.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index c3c7c57d..33744e74 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -382,6 +382,7 @@ where { type Rejection = Error; + #[tracing::instrument("ar_from_request", skip_all)] async fn from_request( req: axum::extract::Request, _state: &S, From 7dbae9ac2b8b88173ca92c9ee80299699e1193e1 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Jun 2024 16:48:22 +0000 Subject: [PATCH 205/617] Fix tracing in send_request() --- src/api/server_server.rs | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index f2ee748d..07fda0e7 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -63,7 +63,7 @@ use ruma::{ }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; -use tracing::{debug, error, warn}; +use tracing::{debug, error, field, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, @@ -129,7 +129,7 @@ impl FedDest { } } -#[tracing::instrument(skip(request), fields(destination_cache_result))] +#[tracing::instrument(skip(request), fields(destination_cache_result, url))] pub(crate) async fn send_request( destination: &ServerName, request: T, @@ -147,7 +147,7 @@ where )); } - debug!("Preparing to send request to {destination}"); + debug!("Preparing to send request"); let mut write_destination_to_cache = false; @@ -250,16 +250,17 @@ where let reqwest_request = reqwest::Request::try_from(http_request)?; let url = reqwest_request.url().clone(); + tracing::Span::current().record("url", field::display(url)); - debug!("Sending request to {destination} at {url}"); + debug!("Sending request"); let response = services().globals.federation_client().execute(reqwest_request).await; - debug!("Received response from {destination} at {url}"); match response { Ok(mut response) => { // reqwest::Response -> http::Response conversion let status = response.status(); + debug!(status = u16::from(status), "Received response"); let mut http_response_builder = http::Response::builder() .status(status) .version(response.version()); @@ -270,23 +271,22 @@ where .expect("http::response::Builder is usable"), ); - debug!("Getting response bytes from {destination}"); + debug!("Getting response bytes"); // TODO: handle timeout let body = response.bytes().await.unwrap_or_else(|e| { warn!("server error {}", e); Vec::new().into() }); - debug!("Got response bytes from {destination}"); + debug!("Got response bytes"); if status != 200 { warn!( - "{} {}: {}", - url, - status, - String::from_utf8_lossy(&body) + status = u16::from(status), + response = String::from_utf8_lossy(&body) .lines() .collect::>() - .join(" ") + .join(" "), + "Received error over federation", ); } @@ -295,7 +295,7 @@ where .expect("reqwest body is valid http body"); if status == 200 { - debug!("Parsing response bytes from {destination}"); + debug!("Parsing response bytes"); let response = T::IncomingResponse::try_from_http_response(http_response); if response.is_ok() && write_destination_to_cache { @@ -312,16 +312,12 @@ where } response.map_err(|e| { - warn!( - "Invalid 200 response from {} on: {} {}", - &destination, url, e - ); + warn!(error = %e, "Invalid 200 response",); Error::BadServerResponse( "Server returned bad 200 response.", ) }) } else { - debug!("Returning error from {destination}"); Err(Error::Federation( destination.to_owned(), RumaError::from_http_response(http_response), @@ -330,8 +326,8 @@ where } Err(e) => { warn!( - "Could not send request to {} at {}: {}", - destination, actual_destination_str, e + error = %e, + "Could not send request", ); Err(e.into()) } From 60d32ddf489f8f88c656f740996418d8f9a96185 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Jun 2024 17:27:11 +0000 Subject: [PATCH 206/617] Fix tracing in fetch_signing_keys() --- src/service/rooms/event_handler.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index ad30f938..17306f39 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -42,7 +42,10 @@ use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; use super::state_compressor::CompressedStateEvent; -use crate::{service::pdu, services, Error, PduEvent, Result}; +use crate::{ + service::pdu, services, utils::debug_slice_truncated, Error, PduEvent, + Result, +}; pub(crate) struct Service; @@ -1841,7 +1844,10 @@ impl Service { /// Search the DB for the signing keys of the given server, if we don't have /// them fetch them from the server and save to our DB. - #[tracing::instrument(skip_all)] + #[tracing::instrument( + skip(self, signature_ids), + fields(signature_ids = debug_slice_truncated(&signature_ids, 3)) + )] pub(crate) async fn fetch_signing_keys( &self, origin: &ServerName, @@ -1913,7 +1919,7 @@ impl Service { } } - trace!("Loading signing keys for {}", origin); + trace!("Loading signing keys from database"); let mut result: BTreeMap<_, _> = services() .globals @@ -1926,7 +1932,7 @@ impl Service { return Ok(result); } - debug!("Fetching signing keys for {} over federation", origin); + debug!("Fetching signing keys over federation"); if let Some(server_key) = services() .sending @@ -1959,7 +1965,7 @@ impl Service { } for server in services().globals.trusted_servers() { - debug!("Asking {} for {}'s signing key", server, origin); + debug!(trusted_server = %server, "Asking trusted server for signing keys"); if let Some(server_keys) = services() .sending .send_federation_request( @@ -1983,7 +1989,7 @@ impl Service { .collect::>() }) { - trace!("Got signing keys: {:?}", server_keys); + trace!(?server_keys, "Got signing keys from trusted server"); for k in server_keys { services().globals.add_signing_key(origin, k.clone())?; result.extend( @@ -2008,7 +2014,7 @@ impl Service { back_off(signature_ids).await; - warn!("Failed to find public key for server: {}", origin); + warn!("Failed to find all public keys"); Err(Error::BadServerResponse("Failed to find public key for server")) } From aa4cd8b1e11323a5733b29bf233d0b098b6cbb83 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 2 Jun 2024 17:52:43 -0700 Subject: [PATCH 207/617] switch to RustCrypto's argon2 crate --- Cargo.lock | 58 ++++++++++++++------------------ Cargo.toml | 2 +- src/api/client_server/session.rs | 6 +--- src/database.rs | 7 ++-- src/service/uiaa.rs | 6 +--- src/utils.rs | 37 ++++++++++++++------ 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e72fd491..550e2e78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,16 +57,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] -name = "arrayref" -version = "0.3.7" +name = "argon2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] [[package]] name = "as_variant" @@ -345,14 +345,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] -name = "blake2b_simd" -version = "1.0.2" +name = "blake2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", + "digest", ] [[package]] @@ -498,12 +496,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -852,6 +844,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" name = "grapevine" version = "0.1.0" dependencies = [ + "argon2", "async-trait", "axum 0.7.5", "axum-extra", @@ -887,7 +880,6 @@ dependencies = [ "ring", "ruma", "rusqlite", - "rust-argon2", "rust-rocksdb", "sd-notify", "serde", @@ -1750,6 +1742,17 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "pear" version = "0.2.9" @@ -2373,17 +2376,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rust-argon2" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9848531d60c9cbbcf9d166c885316c24bc0e2a9d3eba0956bb6cbbd79bc6e8" -dependencies = [ - "base64 0.21.7", - "blake2b_simd", - "constant_time_eq", -] - [[package]] name = "rust-librocksdb-sys" version = "0.22.0+9.2.1" diff --git a/Cargo.toml b/Cargo.toml index 24d7dbb1..3ab71e30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ workspace = true # Keep sorted [dependencies] +argon2 = "0.5.3" async-trait = "0.1.80" axum = { version = "0.7.5", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } axum-extra = { version = "0.9.3", features = ["typed-header"] } @@ -122,7 +123,6 @@ ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.31.0", optional = true, features = ["bundled"] } -rust-argon2 = "2.1.0" sd-notify = { version = "0.4.1", optional = true } serde = { version = "1.0.202", features = ["rc"] } serde_html_form = "0.2.6" diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index f18dd3b2..69e4cd9b 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -113,11 +113,7 @@ pub(crate) async fn login_route( )); } - let hash_matches = - argon2::verify_encoded(&hash, password.as_bytes()) - .unwrap_or(false); - - if !hash_matches { + if !utils::verify_password_hash(hash, password) { return Err(Error::BadRequest( ErrorKind::forbidden(), "Wrong username or password.", diff --git a/src/database.rs b/src/database.rs index 44d7b1c4..b53ab8a1 100644 --- a/src/database.rs +++ b/src/database.rs @@ -584,10 +584,9 @@ impl KeyValueDatabase { for (userid, password) in db.userid_password.iter() { let password = utils::string_from_bytes(&password); - let empty_hashed_password = - password.map_or(false, |password| { - argon2::verify_encoded(&password, b"") - .unwrap_or(false) + let empty_hashed_password = password + .map_or(false, |password| { + utils::verify_password_hash("", password) }); if empty_hashed_password { diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 25c2bbe7..2a130896 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -87,11 +87,7 @@ impl Service { // Check if password is correct if let Some(hash) = services().users.password_hash(&user_id)? { - let hash_matches = - argon2::verify_encoded(&hash, password.as_bytes()) - .unwrap_or(false); - - if !hash_matches { + if !utils::verify_password_hash(hash, password) { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { kind: ErrorKind::forbidden(), diff --git a/src/utils.rs b/src/utils.rs index 0c8418a8..9694595f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -7,9 +7,9 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use argon2::{Config, Variant}; +use argon2::{password_hash, Argon2, PasswordHasher, PasswordVerifier}; use cmp::Ordering; -use rand::prelude::*; +use rand::{prelude::*, rngs::OsRng}; use ring::digest; use ruma::{ canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, @@ -72,16 +72,33 @@ pub(crate) fn random_string(length: usize) -> String { } /// Calculate a new hash for the given password -pub(crate) fn calculate_password_hash( - password: &str, -) -> Result { - let hashing_config = Config { - variant: Variant::Argon2id, - ..Default::default() +pub(crate) fn calculate_password_hash( + password: B, +) -> Result +where + B: AsRef<[u8]>, +{ + Argon2::default() + .hash_password( + password.as_ref(), + &password_hash::SaltString::generate(&mut OsRng), + ) + .map(|x| x.serialize()) +} + +/// Compare a password to a hash +/// +/// Returns `true` if the password matches the hash, `false` otherwise. +pub(crate) fn verify_password_hash(hash: S, password: B) -> bool +where + S: AsRef, + B: AsRef<[u8]>, +{ + let Ok(hash) = password_hash::PasswordHash::new(hash.as_ref()) else { + return false; }; - let salt = random_string(32); - argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &hashing_config) + Argon2::default().verify_password(password.as_ref(), &hash).is_ok() } #[tracing::instrument(skip(keys))] From 71f3d84115eee7c15f1eb340fed13c294ac5ac67 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 2 Jun 2024 17:54:16 -0700 Subject: [PATCH 208/617] rename password-related utils functions --- src/api/client_server/session.rs | 2 +- src/database.rs | 2 +- src/database/key_value/users.rs | 2 +- src/service/uiaa.rs | 2 +- src/utils.rs | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 69e4cd9b..123a66c7 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -113,7 +113,7 @@ pub(crate) async fn login_route( )); } - if !utils::verify_password_hash(hash, password) { + if !utils::verify_password(hash, password) { return Err(Error::BadRequest( ErrorKind::forbidden(), "Wrong username or password.", diff --git a/src/database.rs b/src/database.rs index b53ab8a1..a9f2abc6 100644 --- a/src/database.rs +++ b/src/database.rs @@ -586,7 +586,7 @@ impl KeyValueDatabase { let empty_hashed_password = password .map_or(false, |password| { - utils::verify_password_hash("", password) + utils::verify_password("", password) }); if empty_hashed_password { diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index b0c209ce..3040e237 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -135,7 +135,7 @@ impl service::users::Data for KeyValueDatabase { password: Option<&str>, ) -> Result<()> { if let Some(password) = password { - if let Ok(hash) = utils::calculate_password_hash(password) { + if let Ok(hash) = utils::hash_password(password) { self.userid_password .insert(user_id.as_bytes(), hash.as_bytes())?; Ok(()) diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 2a130896..109e214d 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -87,7 +87,7 @@ impl Service { // Check if password is correct if let Some(hash) = services().users.password_hash(&user_id)? { - if !utils::verify_password_hash(hash, password) { + if !utils::verify_password(hash, password) { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { kind: ErrorKind::forbidden(), diff --git a/src/utils.rs b/src/utils.rs index 9694595f..746d0b84 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -71,8 +71,8 @@ pub(crate) fn random_string(length: usize) -> String { .collect() } -/// Calculate a new hash for the given password -pub(crate) fn calculate_password_hash( +/// Hash the given password +pub(crate) fn hash_password( password: B, ) -> Result where @@ -89,7 +89,7 @@ where /// Compare a password to a hash /// /// Returns `true` if the password matches the hash, `false` otherwise. -pub(crate) fn verify_password_hash(hash: S, password: B) -> bool +pub(crate) fn verify_password(hash: S, password: B) -> bool where S: AsRef, B: AsRef<[u8]>, From 972c25becae783000c2d19f356f5e2441065b907 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 5 Jun 2024 16:36:21 -0700 Subject: [PATCH 209/617] remove unnecessary whitespace --- nix/pkgs/default/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 6689e86c..4dae8b5e 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -106,7 +106,7 @@ let }; in -craneLib.buildPackage ( commonAttrs // { +craneLib.buildPackage (commonAttrs // { cargoArtifacts = craneLib.buildDepsOnly (commonAttrs // { env = buildDepsOnlyEnv; }); From 0bd2c43dab9b89dbfd7e8edde80ca735770367ba Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 5 Jun 2024 16:41:38 -0700 Subject: [PATCH 210/617] don't strip binary for dev builds --- nix/pkgs/default/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 4dae8b5e..b03c2c50 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -89,6 +89,8 @@ let ]; }; + dontStrip = profile == "dev"; + buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys'; nativeBuildInputs = [ From 22dd7f1a54251310aa19b6a5b0e90d518ccfb56a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 4 Jun 2024 12:24:01 -0700 Subject: [PATCH 211/617] move FoundIn to observability.rs --- src/api/server_server.rs | 5 ++-- src/database/key_value/rooms/auth_chain.rs | 5 +--- src/database/key_value/rooms/short.rs | 6 ++-- src/database/key_value/rooms/state_cache.rs | 5 ++-- src/database/key_value/rooms/timeline.rs | 6 ++-- src/observability.rs | 31 +++++++++++++++++++++ src/service/rooms/state_accessor.rs | 3 +- src/service/rooms/state_compressor.rs | 6 +--- src/utils.rs | 29 ------------------- 9 files changed, 43 insertions(+), 53 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 07fda0e7..f9d56705 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -67,10 +67,9 @@ use tracing::{debug, error, field, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, + observability::FoundIn, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, - utils::{self, FoundIn}, - Ar, Error, PduEvent, Ra, Result, + services, utils, Ar, Error, PduEvent, Ra, Result, }; /// Wraps either an literal IP address plus port, or a hostname plus complement diff --git a/src/database/key_value/rooms/auth_chain.rs b/src/database/key_value/rooms/auth_chain.rs index 248eb53f..f1e23491 100644 --- a/src/database/key_value/rooms/auth_chain.rs +++ b/src/database/key_value/rooms/auth_chain.rs @@ -1,10 +1,7 @@ use std::{collections::HashSet, mem::size_of, sync::Arc}; use crate::{ - database::KeyValueDatabase, - service, - utils::{self, FoundIn}, - Result, + database::KeyValueDatabase, observability::FoundIn, service, utils, Result, }; impl service::rooms::auth_chain::Data for KeyValueDatabase { diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index bacbab4c..4cc7fc47 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -3,10 +3,8 @@ use std::sync::Arc; use ruma::{events::StateEventType, EventId, RoomId}; use crate::{ - database::KeyValueDatabase, - service, services, - utils::{self, FoundIn}, - Error, Result, + database::KeyValueDatabase, observability::FoundIn, service, services, + utils, Error, Result, }; impl service::rooms::short::Data for KeyValueDatabase { diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index ce8ce4df..84a8f668 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -8,10 +8,9 @@ use ruma::{ use crate::{ database::KeyValueDatabase, + observability::FoundIn, service::{self, appservice::RegistrationInfo}, - services, - utils::{self, FoundIn}, - Error, Result, + services, utils, Error, Result, }; impl service::rooms::state_cache::Data for KeyValueDatabase { diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 5a33f72d..3bfbff8d 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -8,10 +8,8 @@ use service::rooms::timeline::PduCount; use tracing::error; use crate::{ - database::KeyValueDatabase, - service, services, - utils::{self, FoundIn}, - Error, PduEvent, Result, + database::KeyValueDatabase, observability::FoundIn, service, services, + utils, Error, PduEvent, Result, }; impl service::rooms::timeline::Data for KeyValueDatabase { diff --git a/src/observability.rs b/src/observability.rs index 5bdbccff..e1e681ef 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -40,6 +40,37 @@ impl Drop for Guard { } } +/// Type to record cache performance in a tracing span field. +pub(crate) enum FoundIn { + /// Found in cache + Cache, + /// Cache miss, but it was in the database. The cache has been updated. + Database, + /// Cache and database miss, but another server had it. The cache has been + /// updated. + Remote, + /// The entry could not be found anywhere. + Nothing, +} + +impl FoundIn { + /// Returns a stringified representation of the current value + fn value(&self) -> &'static str { + match self { + FoundIn::Cache => "hit", + FoundIn::Database => "miss-database", + FoundIn::Remote => "miss-remote", + FoundIn::Nothing => "not-found", + } + } + + /// Record the current value to the current [`tracing::Span`] + // TODO: use tracing::Value instead if it ever becomes accessible + pub(crate) fn record(&self, field: &str) { + tracing::Span::current().record(field, self.value()); + } +} + /// Initialize observability pub(crate) fn init(config: &Config) -> Result { let config_filter_layer = || EnvFilter::try_new(&config.log); diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 844cccc0..67ebf8ec 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -28,7 +28,8 @@ use tokio::sync::MutexGuard; use tracing::{error, warn}; use crate::{ - service::pdu::PduBuilder, services, utils::FoundIn, Error, PduEvent, Result, + observability::FoundIn, service::pdu::PduBuilder, services, Error, + PduEvent, Result, }; pub(crate) struct Service { diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 85d55e97..942dd67c 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -10,11 +10,7 @@ use lru_cache::LruCache; use ruma::{EventId, RoomId}; use self::data::StateDiff; -use crate::{ - services, - utils::{self, FoundIn}, - Result, -}; +use crate::{observability::FoundIn, services, utils, Result}; #[derive(Clone)] pub(crate) struct CompressedStateLayer { diff --git a/src/utils.rs b/src/utils.rs index 746d0b84..5b59c8aa 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -245,35 +245,6 @@ pub(crate) fn truncate_str_for_debug( } } -/// Type to record cache performance in a tracing span field. -pub(crate) enum FoundIn { - /// Found in cache - Cache, - /// Cache miss, but it was in the database. The cache has been updated. - Database, - /// Cache and database miss, but another server had it. The cache has been - /// updated. - Remote, - /// The entry could not be found anywhere. - Nothing, -} - -impl FoundIn { - fn value(&self) -> &'static str { - match self { - FoundIn::Cache => "hit", - FoundIn::Database => "miss-database", - FoundIn::Remote => "miss-remote", - FoundIn::Nothing => "not-found", - } - } - - // TODO: use tracing::Value instead if it ever becomes accessible - pub(crate) fn record(&self, field: &str) { - tracing::Span::current().record(field, self.value()); - } -} - #[cfg(test)] mod tests { use crate::utils::truncate_str_for_debug; From 38382128ac9233979afbc5ea677dc2f565a005a6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 4 Jun 2024 12:24:51 -0700 Subject: [PATCH 212/617] rename FoundIn::{value -> as_str} --- src/observability.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index e1e681ef..5e9f2d2d 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -55,7 +55,7 @@ pub(crate) enum FoundIn { impl FoundIn { /// Returns a stringified representation of the current value - fn value(&self) -> &'static str { + fn as_str(&self) -> &'static str { match self { FoundIn::Cache => "hit", FoundIn::Database => "miss-database", @@ -67,7 +67,7 @@ impl FoundIn { /// Record the current value to the current [`tracing::Span`] // TODO: use tracing::Value instead if it ever becomes accessible pub(crate) fn record(&self, field: &str) { - tracing::Span::current().record(field, self.value()); + tracing::Span::current().record(field, self.as_str()); } } From 784b86a1f69948d367a48d0f6c6e973e89978fd3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 4 Jun 2024 12:25:37 -0700 Subject: [PATCH 213/617] make stringified names mirror variant names --- src/observability.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index 5e9f2d2d..054ee7a9 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -57,10 +57,10 @@ impl FoundIn { /// Returns a stringified representation of the current value fn as_str(&self) -> &'static str { match self { - FoundIn::Cache => "hit", - FoundIn::Database => "miss-database", - FoundIn::Remote => "miss-remote", - FoundIn::Nothing => "not-found", + FoundIn::Cache => "Cache", + FoundIn::Database => "Database", + FoundIn::Remote => "Remote", + FoundIn::Nothing => "Nothing", } } From 792300d2206c6bae9a0e110630d2786f71addd1d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 4 Jun 2024 12:31:24 -0700 Subject: [PATCH 214/617] derive Copy on FoundIn --- src/observability.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index 054ee7a9..4e4cec0a 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -41,6 +41,7 @@ impl Drop for Guard { } /// Type to record cache performance in a tracing span field. +#[derive(Clone, Copy)] pub(crate) enum FoundIn { /// Found in cache Cache, @@ -55,7 +56,7 @@ pub(crate) enum FoundIn { impl FoundIn { /// Returns a stringified representation of the current value - fn as_str(&self) -> &'static str { + fn as_str(self) -> &'static str { match self { FoundIn::Cache => "Cache", FoundIn::Database => "Database", @@ -66,7 +67,7 @@ impl FoundIn { /// Record the current value to the current [`tracing::Span`] // TODO: use tracing::Value instead if it ever becomes accessible - pub(crate) fn record(&self, field: &str) { + pub(crate) fn record(self, field: &str) { tracing::Span::current().record(field, self.as_str()); } } From 9364d44ce29fd221fe1f32ca516a80feb8a5d102 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 4 Jun 2024 12:34:09 -0700 Subject: [PATCH 215/617] use `strum` instead --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 1 + src/observability.rs | 15 +++------------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 550e2e78..3ab74856 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -887,6 +887,7 @@ dependencies = [ "serde_json", "serde_yaml", "sha-1", + "strum", "thiserror", "thread_local", "tikv-jemallocator", @@ -2790,6 +2791,28 @@ dependencies = [ "der", ] +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subslice" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 3ab71e30..9fb3466a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -129,6 +129,7 @@ serde_html_form = "0.2.6" serde_json = { version = "1.0.117", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" +strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.61" thread_local = "1.1.8" tikv-jemallocator = { version = "0.5.4", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } diff --git a/src/observability.rs b/src/observability.rs index 4e4cec0a..820487d5 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -18,6 +18,7 @@ use opentelemetry_sdk::{ metrics::{new_view, Aggregation, Instrument, SdkMeterProvider, Stream}, Resource, }; +use strum::AsRefStr; use tokio::time::Instant; use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; @@ -41,7 +42,7 @@ impl Drop for Guard { } /// Type to record cache performance in a tracing span field. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, AsRefStr)] pub(crate) enum FoundIn { /// Found in cache Cache, @@ -55,20 +56,10 @@ pub(crate) enum FoundIn { } impl FoundIn { - /// Returns a stringified representation of the current value - fn as_str(self) -> &'static str { - match self { - FoundIn::Cache => "Cache", - FoundIn::Database => "Database", - FoundIn::Remote => "Remote", - FoundIn::Nothing => "Nothing", - } - } - /// Record the current value to the current [`tracing::Span`] // TODO: use tracing::Value instead if it ever becomes accessible pub(crate) fn record(self, field: &str) { - tracing::Span::current().record(field, self.as_str()); + tracing::Span::current().record(field, self.as_ref()); } } From 0c2094a56fafd233de9fe6dafa749b76116c0c4e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 4 Jun 2024 13:26:23 -0700 Subject: [PATCH 216/617] record FoundIn with metrics instead of traces This is much more efficient in terms of network use and data storage, and also easier to visualize. --- src/api/server_server.rs | 11 ++-- src/database/key_value/rooms/auth_chain.rs | 14 +++-- src/database/key_value/rooms/short.rs | 49 ++++++++++------- src/database/key_value/rooms/state_cache.rs | 18 ++++--- src/database/key_value/rooms/timeline.rs | 25 +++++---- src/observability.rs | 59 +++++++++++++++++---- src/service/rooms/state_accessor.rs | 21 +++++--- src/service/rooms/state_compressor.rs | 13 +++-- 8 files changed, 142 insertions(+), 68 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index f9d56705..81ec6c58 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -67,7 +67,7 @@ use tracing::{debug, error, field, warn}; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, - observability::FoundIn, + observability::{FoundIn, Lookup, METRICS}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, services, utils, Ar, Error, PduEvent, Ra, Result, }; @@ -128,7 +128,7 @@ impl FedDest { } } -#[tracing::instrument(skip(request), fields(destination_cache_result, url))] +#[tracing::instrument(skip(request), fields(url))] pub(crate) async fn send_request( destination: &ServerName, request: T, @@ -159,7 +159,7 @@ where .cloned(); let (actual_destination, host) = if let Some(result) = cached_result { - FoundIn::Cache.record("destination_cache_result"); + METRICS.record_lookup(Lookup::FederationDestination, FoundIn::Cache); result } else { write_destination_to_cache = true; @@ -298,7 +298,10 @@ where let response = T::IncomingResponse::try_from_http_response(http_response); if response.is_ok() && write_destination_to_cache { - FoundIn::Remote.record("destination_cache_result"); + METRICS.record_lookup( + Lookup::FederationDestination, + FoundIn::Remote, + ); services() .globals .actual_destination_cache diff --git a/src/database/key_value/rooms/auth_chain.rs b/src/database/key_value/rooms/auth_chain.rs index f1e23491..896a0f40 100644 --- a/src/database/key_value/rooms/auth_chain.rs +++ b/src/database/key_value/rooms/auth_chain.rs @@ -1,19 +1,23 @@ use std::{collections::HashSet, mem::size_of, sync::Arc}; use crate::{ - database::KeyValueDatabase, observability::FoundIn, service, utils, Result, + database::KeyValueDatabase, + observability::{FoundIn, Lookup, METRICS}, + service, utils, Result, }; impl service::rooms::auth_chain::Data for KeyValueDatabase { - #[tracing::instrument(skip(self, key), fields(cache_result))] + #[tracing::instrument(skip(self, key))] fn get_cached_eventid_authchain( &self, key: &[u64], ) -> Result>>> { + let lookup = Lookup::AuthChain; + // Check RAM cache if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(Some(Arc::clone(result))); } @@ -34,7 +38,7 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { }); if let Some(chain) = chain { - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); let chain = Arc::new(chain); // Cache in RAM @@ -47,7 +51,7 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { } } - FoundIn::Nothing.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Nothing); Ok(None) } diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 4cc7fc47..c665c092 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -3,30 +3,33 @@ use std::sync::Arc; use ruma::{events::StateEventType, EventId, RoomId}; use crate::{ - database::KeyValueDatabase, observability::FoundIn, service, services, - utils, Error, Result, + database::KeyValueDatabase, + observability::{FoundIn, Lookup, METRICS}, + service, services, utils, Error, Result, }; impl service::rooms::short::Data for KeyValueDatabase { - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { + let lookup = Lookup::CreateEventIdToShort; + if let Some(short) = self.eventidshort_cache.lock().unwrap().get_mut(event_id) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(*short); } let short = if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? { - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); utils::u64_from_bytes(&shorteventid).map_err(|_| { Error::bad_database("Invalid shorteventid in db.") })? } else { - FoundIn::Nothing.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Nothing); let shorteventid = services().globals.next_count()?; self.eventid_shorteventid @@ -50,13 +53,15 @@ impl service::rooms::short::Data for KeyValueDatabase { event_type: &StateEventType, state_key: &str, ) -> Result> { + let lookup = Lookup::StateKeyToShort; + if let Some(short) = self .statekeyshort_cache .lock() .unwrap() .get_mut(&(event_type.clone(), state_key.to_owned())) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(Some(*short)); } @@ -75,32 +80,34 @@ impl service::rooms::short::Data for KeyValueDatabase { .transpose()?; if let Some(s) = short { - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.statekeyshort_cache .lock() .unwrap() .insert((event_type.clone(), state_key.to_owned()), s); } else { - FoundIn::Nothing.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Nothing); } Ok(short) } - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn get_or_create_shortstatekey( &self, event_type: &StateEventType, state_key: &str, ) -> Result { + let lookup = Lookup::CreateStateKeyToShort; + if let Some(short) = self .statekeyshort_cache .lock() .unwrap() .get_mut(&(event_type.clone(), state_key.to_owned())) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(*short); } @@ -111,13 +118,13 @@ impl service::rooms::short::Data for KeyValueDatabase { let short = if let Some(shortstatekey) = self.statekey_shortstatekey.get(&db_key)? { - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); utils::u64_from_bytes(&shortstatekey).map_err(|_| { Error::bad_database("Invalid shortstatekey in db.") })? } else { - FoundIn::Nothing.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Nothing); let shortstatekey = services().globals.next_count()?; self.statekey_shortstatekey @@ -135,15 +142,17 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(short) } - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn get_eventid_from_short( &self, shorteventid: u64, ) -> Result> { + let lookup = Lookup::ShortToEventId; + if let Some(id) = self.shorteventid_cache.lock().unwrap().get_mut(&shorteventid) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(Arc::clone(id)); } @@ -165,7 +174,7 @@ impl service::rooms::short::Data for KeyValueDatabase { Error::bad_database("EventId in shorteventid_eventid is invalid.") })?; - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.shorteventid_cache .lock() @@ -175,15 +184,17 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(event_id) } - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn get_statekey_from_short( &self, shortstatekey: u64, ) -> Result<(StateEventType, String)> { + let lookup = Lookup::ShortToStateKey; + if let Some(id) = self.shortstatekey_cache.lock().unwrap().get_mut(&shortstatekey) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(id.clone()); } @@ -218,7 +229,7 @@ impl service::rooms::short::Data for KeyValueDatabase { let result = (event_type, state_key); - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.shortstatekey_cache .lock() diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 84a8f668..115511eb 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -8,7 +8,7 @@ use ruma::{ use crate::{ database::KeyValueDatabase, - observability::FoundIn, + observability::{FoundIn, Lookup, METRICS}, service::{self, appservice::RegistrationInfo}, services, utils, Error, Result, }; @@ -171,19 +171,21 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { Ok(()) } - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn get_our_real_users( &self, room_id: &RoomId, ) -> Result>> { + let lookup = Lookup::OurRealUsers; + let maybe = self.our_real_users_cache.read().unwrap().get(room_id).cloned(); if let Some(users) = maybe { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); Ok(users) } else { self.update_joined_count(room_id)?; - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); Ok(Arc::clone( self.our_real_users_cache.read().unwrap().get(room_id).unwrap(), )) @@ -192,13 +194,15 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { #[tracing::instrument( skip(self, appservice), - fields(cache_result, appservice_id = appservice.registration.id), + fields(appservice_id = appservice.registration.id), )] fn appservice_in_room( &self, room_id: &RoomId, appservice: &RegistrationInfo, ) -> Result { + let lookup = Lookup::AppserviceInRoom; + let maybe = self .appservice_in_room_cache .read() @@ -208,7 +212,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .copied(); if let Some(b) = maybe { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); Ok(b) } else { let bridge_user_id = UserId::parse_with_server_name( @@ -225,7 +229,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { }) }); - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.appservice_in_room_cache .write() .unwrap() diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 3bfbff8d..5e64cd7c 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -8,17 +8,20 @@ use service::rooms::timeline::PduCount; use tracing::error; use crate::{ - database::KeyValueDatabase, observability::FoundIn, service, services, - utils, Error, PduEvent, Result, + database::KeyValueDatabase, + observability::{FoundIn, Lookup, METRICS}, + service, services, utils, Error, PduEvent, Result, }; impl service::rooms::timeline::Data for KeyValueDatabase { - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn last_timeline_count( &self, sender_user: &UserId, room_id: &RoomId, ) -> Result { + let lookup = Lookup::LastTimelineCount; + match self .lasttimelinecount_cache .lock() @@ -35,15 +38,15 @@ impl service::rooms::timeline::Data for KeyValueDatabase { r.ok() }) { - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); Ok(*v.insert(last_count.0)) } else { - FoundIn::Nothing.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Nothing); Ok(PduCount::Normal(0)) } } hash_map::Entry::Occupied(o) => { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); Ok(*o.get()) } } @@ -125,10 +128,12 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Returns the pdu. /// /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] fn get_pdu(&self, event_id: &EventId) -> Result>> { + let lookup = Lookup::Pdu; + if let Some(p) = self.pdu_cache.lock().unwrap().get_mut(event_id) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(Some(Arc::clone(p))); } @@ -149,14 +154,14 @@ impl service::rooms::timeline::Data for KeyValueDatabase { )? .map(Arc::new) { - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.pdu_cache .lock() .unwrap() .insert(event_id.to_owned(), Arc::clone(&pdu)); Ok(Some(pdu)) } else { - FoundIn::Nothing.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Nothing); Ok(None) } } diff --git a/src/observability.rs b/src/observability.rs index 820487d5..f182219f 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -18,7 +18,7 @@ use opentelemetry_sdk::{ metrics::{new_view, Aggregation, Instrument, SdkMeterProvider, Stream}, Resource, }; -use strum::AsRefStr; +use strum::{AsRefStr, IntoStaticStr}; use tokio::time::Instant; use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; @@ -41,8 +41,33 @@ impl Drop for Guard { } } -/// Type to record cache performance in a tracing span field. -#[derive(Clone, Copy, AsRefStr)] +/// A kind of data that gets looked up +/// +/// See also [`Metrics::record_lookup`]. +// Keep variants sorted +#[allow(clippy::missing_docs_in_private_items)] +#[derive(Clone, Copy, AsRefStr, IntoStaticStr)] +pub(crate) enum Lookup { + AppserviceInRoom, + AuthChain, + CreateEventIdToShort, + CreateStateKeyToShort, + FederationDestination, + LastTimelineCount, + OurRealUsers, + Pdu, + ShortToEventId, + ShortToStateKey, + StateInfo, + StateKeyToShort, + VisibilityForServer, + VisibilityForUser, +} + +/// Locations where a [`Lookup`] value may be found +/// +/// Not all of these variants are used for each value of [`Lookup`]. +#[derive(Clone, Copy, AsRefStr, IntoStaticStr)] pub(crate) enum FoundIn { /// Found in cache Cache, @@ -55,14 +80,6 @@ pub(crate) enum FoundIn { Nothing, } -impl FoundIn { - /// Record the current value to the current [`tracing::Span`] - // TODO: use tracing::Value instead if it ever becomes accessible - pub(crate) fn record(self, field: &str) { - tracing::Span::current().record(field, self.as_ref()); - } -} - /// Initialize observability pub(crate) fn init(config: &Config) -> Result { let config_filter_layer = || EnvFilter::try_new(&config.log); @@ -137,6 +154,9 @@ pub(crate) struct Metrics { /// Histogram of HTTP requests http_requests_histogram: opentelemetry::metrics::Histogram, + + /// Counts where data is found from + lookup: opentelemetry::metrics::Counter, } impl Metrics { @@ -182,9 +202,15 @@ impl Metrics { .with_description("Histogram of HTTP requests") .init(); + let lookup = meter + .u64_counter("lookup") + .with_description("Counts where data is found from") + .init(); + Metrics { otel_state: (registry, provider), http_requests_histogram, + lookup, } } @@ -194,6 +220,17 @@ impl Metrics { .encode_to_string(&self.otel_state.0.gather()) .expect("should be able to encode metrics") } + + /// Record that some data was found in a particular storage location + pub(crate) fn record_lookup(&self, lookup: Lookup, found_in: FoundIn) { + self.lookup.add( + 1, + &[ + KeyValue::new("lookup", <&str>::from(lookup)), + KeyValue::new("found_in", <&str>::from(found_in)), + ], + ); + } } /// Track HTTP metrics by converting this into an [`axum`] layer diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 67ebf8ec..96839c9f 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -28,8 +28,9 @@ use tokio::sync::MutexGuard; use tracing::{error, warn}; use crate::{ - observability::FoundIn, service::pdu::PduBuilder, services, Error, - PduEvent, Result, + observability::{FoundIn, Lookup, METRICS}, + service::pdu::PduBuilder, + services, Error, PduEvent, Result, }; pub(crate) struct Service { @@ -123,13 +124,15 @@ impl Service { /// Whether a server is allowed to see an event through federation, based on /// the room's history_visibility at that event's state. - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] pub(crate) fn server_can_see_event( &self, origin: &ServerName, room_id: &RoomId, event_id: &EventId, ) -> Result { + let lookup = Lookup::VisibilityForServer; + let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else { return Ok(true); }; @@ -140,7 +143,7 @@ impl Service { .unwrap() .get_mut(&(origin.to_owned(), shortstatehash)) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(*visibility); } @@ -191,7 +194,7 @@ impl Service { } }; - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.server_visibility_cache .lock() .unwrap() @@ -202,13 +205,15 @@ impl Service { /// Whether a user is allowed to see an event, based on /// the room's history_visibility at that event's state. - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] pub(crate) fn user_can_see_event( &self, user_id: &UserId, room_id: &RoomId, event_id: &EventId, ) -> Result { + let lookup = Lookup::VisibilityForUser; + let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else { return Ok(true); }; @@ -219,7 +224,7 @@ impl Service { .unwrap() .get_mut(&(user_id.to_owned(), shortstatehash)) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(*visibility); } @@ -262,7 +267,7 @@ impl Service { } }; - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.user_visibility_cache .lock() .unwrap() diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 942dd67c..5cc1f4cd 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -10,7 +10,10 @@ use lru_cache::LruCache; use ruma::{EventId, RoomId}; use self::data::StateDiff; -use crate::{observability::FoundIn, services, utils, Result}; +use crate::{ + observability::{FoundIn, Lookup, METRICS}, + services, utils, Result, +}; #[derive(Clone)] pub(crate) struct CompressedStateLayer { @@ -33,15 +36,17 @@ impl Service { /// Returns a stack with info on shortstatehash, full state, added diff and /// removed diff for the selected shortstatehash and each parent layer. #[allow(clippy::type_complexity)] - #[tracing::instrument(skip(self), fields(cache_result))] + #[tracing::instrument(skip(self))] pub(crate) fn load_shortstatehash_info( &self, shortstatehash: u64, ) -> Result> { + let lookup = Lookup::StateInfo; + if let Some(r) = self.stateinfo_cache.lock().unwrap().get_mut(&shortstatehash) { - FoundIn::Cache.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Cache); return Ok(r.clone()); } @@ -76,7 +81,7 @@ impl Service { }] }; - FoundIn::Database.record("cache_result"); + METRICS.record_lookup(lookup, FoundIn::Database); self.stateinfo_cache .lock() .unwrap() From cc5a9d3440a5769436a98aae158a7cc2e08a91d1 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Wed, 5 Jun 2024 21:52:37 -0700 Subject: [PATCH 217/617] factor search tokenization out into a function This ensures that the tokenization algorithm will remain in sync between querying, indexing, and deindexing. The existing code had slightly different behavior for querying, because it did not discard words with >50 bytes. This was inconsequential, because >50 byte tokens are never present in the index. --- src/database/key_value/rooms/search.rs | 38 ++++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index bd14f312..8975c74d 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -2,6 +2,17 @@ use ruma::RoomId; use crate::{database::KeyValueDatabase, service, services, utils, Result}; +/// Splits a string into tokens used as keys in the search inverted index +/// +/// This may be used to tokenize both message bodies (for indexing) or search +/// queries (for querying). +fn tokenize(body: &str) -> impl Iterator + '_ { + body.split_terminator(|c: char| !c.is_alphanumeric()) + .filter(|s| !s.is_empty()) + .filter(|word| word.len() <= 50) + .map(str::to_lowercase) +} + impl service::rooms::search::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn index_pdu( @@ -10,19 +21,14 @@ impl service::rooms::search::Data for KeyValueDatabase { pdu_id: &[u8], message_body: &str, ) -> Result<()> { - let mut batch = message_body - .split_terminator(|c: char| !c.is_alphanumeric()) - .filter(|s| !s.is_empty()) - .filter(|word| word.len() <= 50) - .map(str::to_lowercase) - .map(|word| { - let mut key = shortroomid.to_be_bytes().to_vec(); - key.extend_from_slice(word.as_bytes()); - key.push(0xFF); - // TODO: currently we save the room id a second time here - key.extend_from_slice(pdu_id); - (key, Vec::new()) - }); + let mut batch = tokenize(message_body).map(|word| { + let mut key = shortroomid.to_be_bytes().to_vec(); + key.extend_from_slice(word.as_bytes()); + key.push(0xFF); + // TODO: currently we save the room id a second time here + key.extend_from_slice(pdu_id); + (key, Vec::new()) + }); self.tokenids.insert_batch(&mut batch) } @@ -43,11 +49,7 @@ impl service::rooms::search::Data for KeyValueDatabase { .to_be_bytes() .to_vec(); - let words: Vec<_> = search_string - .split_terminator(|c: char| !c.is_alphanumeric()) - .filter(|s| !s.is_empty()) - .map(str::to_lowercase) - .collect(); + let words: Vec<_> = tokenize(search_string).collect(); let iterators = words.clone().into_iter().map(move |word| { let mut prefix2 = prefix.clone(); From f74043df9aa59b406b5086c2e9fa2791a31aa41b Mon Sep 17 00:00:00 2001 From: Matthias Ahouansou Date: Wed, 5 Jun 2024 20:17:32 +0100 Subject: [PATCH 218/617] fix: de-index pdus when redacted --- src/database/key_value/rooms/search.rs | 23 +++++++++++++++++++++++ src/service/rooms/search/data.rs | 7 +++++++ src/service/rooms/timeline.rs | 22 ++++++++++++++++++++-- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index 8975c74d..5fb90462 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -33,6 +33,29 @@ impl service::rooms::search::Data for KeyValueDatabase { self.tokenids.insert_batch(&mut batch) } + #[tracing::instrument(skip(self))] + fn deindex_pdu( + &self, + shortroomid: u64, + pdu_id: &[u8], + message_body: &str, + ) -> Result<()> { + let batch = tokenize(message_body).map(|word| { + let mut key = shortroomid.to_be_bytes().to_vec(); + key.extend_from_slice(word.as_bytes()); + key.push(0xFF); + // TODO: currently we save the room id a second time here + key.extend_from_slice(pdu_id); + key + }); + + for token in batch { + self.tokenids.remove(&token)?; + } + + Ok(()) + } + #[tracing::instrument(skip(self))] #[allow(clippy::type_complexity)] fn search_pdus<'a>( diff --git a/src/service/rooms/search/data.rs b/src/service/rooms/search/data.rs index 903a85e6..efc503fe 100644 --- a/src/service/rooms/search/data.rs +++ b/src/service/rooms/search/data.rs @@ -10,6 +10,13 @@ pub(crate) trait Data: Send + Sync { message_body: &str, ) -> Result<()>; + fn deindex_pdu( + &self, + shortroomid: u64, + pdu_id: &[u8], + message_body: &str, + ) -> Result<()>; + #[allow(clippy::type_complexity)] fn search_pdus<'a>( &'a self, diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 53926c3e..95f55819 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -414,7 +414,7 @@ impl Service { &pdu.room_id, false, )? { - self.redact_pdu(redact_id, pdu)?; + self.redact_pdu(redact_id, pdu, shortroomid)?; } } } @@ -435,7 +435,7 @@ impl Service { &pdu.room_id, false, )? { - self.redact_pdu(redact_id, pdu)?; + self.redact_pdu(redact_id, pdu, shortroomid)?; } } } @@ -1178,15 +1178,33 @@ impl Service { &self, event_id: &EventId, reason: &PduEvent, + shortroomid: u64, ) -> Result<()> { // TODO: Don't reserialize, keep original json if let Some(pdu_id) = self.get_pdu_id(event_id)? { + #[derive(Deserialize)] + struct ExtractBody { + body: String, + } + let mut pdu = self.get_pdu_from_id(&pdu_id)?.ok_or_else(|| { Error::bad_database("PDU ID points to invalid PDU.") })?; + + if let Ok(content) = + serde_json::from_str::(pdu.content.get()) + { + services().rooms.search.deindex_pdu( + shortroomid, + &pdu_id, + &content.body, + )?; + } + let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; pdu.redact(room_version_id, reason)?; + self.replace_pdu( &pdu_id, &utils::to_canonical_object(&pdu).expect("PDU is an object"), From 83cdc9c708cd7b50fe1ab40ea6a68dcf252c190b Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 6 Jun 2024 00:23:33 -0700 Subject: [PATCH 219/617] drop redacted events from search results --- src/api/client_server/search.rs | 19 ++++++++++--------- src/service/pdu.rs | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 761f89b7..458d4ae2 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -101,15 +101,16 @@ pub(crate) async fn search_events_route( .get_pdu_from_id(result) .ok()? .filter(|pdu| { - services() - .rooms - .state_accessor - .user_can_see_event( - sender_user, - &pdu.room_id, - &pdu.event_id, - ) - .unwrap_or(false) + !pdu.is_redacted() + && services() + .rooms + .state_accessor + .user_can_see_event( + sender_user, + &pdu.room_id, + &pdu.event_id, + ) + .unwrap_or(false) }) .map(|pdu| pdu.to_room_event()) }) diff --git a/src/service/pdu.rs b/src/service/pdu.rs index 46dafa2e..c538f80c 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -90,6 +90,24 @@ impl PduEvent { Ok(()) } + pub(crate) fn is_redacted(&self) -> bool { + #[derive(Deserialize)] + struct ExtractRedactedBecause { + redacted_because: Option, + } + + let Some(unsigned) = &self.unsigned else { + return false; + }; + + let Ok(unsigned) = ExtractRedactedBecause::deserialize(&**unsigned) + else { + return false; + }; + + unsigned.redacted_because.is_some() + } + pub(crate) fn remove_transaction_id(&mut self) -> crate::Result<()> { if let Some(unsigned) = &self.unsigned { let mut unsigned: BTreeMap> = From da99b0706e683a2d347768efe5b50676abdf7b44 Mon Sep 17 00:00:00 2001 From: Matthias Ahouansou Date: Wed, 12 Jun 2024 10:36:41 -0700 Subject: [PATCH 220/617] fix(edus): ensure sender server is the same as the user in the content Original patch by Matthias. Benjamin modified the logic to include logging when an event was rejected, for consistency with the existing check on device key updates. Co-authored-by: Benjamin Lee --- src/api/server_server.rs | 111 +++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 81ec6c58..cd0386b8 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -812,6 +812,15 @@ pub(crate) async fn send_transaction_message_route( Edu::Receipt(receipt) => { for (room_id, room_updates) in receipt.receipts { for (user_id, user_updates) in room_updates.read { + if user_id.server_name() != sender_servername { + warn!( + %user_id, + %sender_servername, + "Got receipt EDU from incorrect homeserver, \ + ignoring", + ); + continue; + } if let Some((event_id, _)) = user_updates .event_ids .iter() @@ -859,6 +868,14 @@ pub(crate) async fn send_transaction_message_route( } } Edu::Typing(typing) => { + if typing.user_id.server_name() != sender_servername { + warn!( + user_id = %typing.user_id, + %sender_servername, + "Got typing EDU from incorrect homeserver, ignoring", + ); + continue; + } if services() .rooms .state_cache @@ -889,6 +906,15 @@ pub(crate) async fn send_transaction_message_route( user_id, .. }) => { + if user_id.server_name() != sender_servername { + warn!( + %user_id, + %sender_servername, + "Got device list update EDU from incorrect homeserver, \ + ignoring", + ); + continue; + } services().users.mark_device_key_update(&user_id)?; } Edu::DirectToDevice(DirectDeviceContent { @@ -897,22 +923,27 @@ pub(crate) async fn send_transaction_message_route( message_id, messages, }) => { + if sender.server_name() != sender_servername { + warn!( + user_id = %sender, + %sender_servername, + "Got direct-to-device EDU from incorrect homeserver, \ + ignoring", + ); + continue; + } // Check if this is a new transaction id if services() .transaction_ids .existing_txnid(&sender, None, &message_id)? - .is_some() + .is_none() { - continue; - } - - for (target_user_id, map) in &messages { - for (target_device_id_maybe, event) in map { - match target_device_id_maybe { - DeviceIdOrAllDevices::DeviceId( - target_device_id, - ) => { - services().users.add_to_device_event( + for (target_user_id, map) in &messages { + for (target_device_id_maybe, event) in map { + match target_device_id_maybe { + DeviceIdOrAllDevices::DeviceId( + target_device_id, + ) => services().users.add_to_device_event( &sender, target_user_id, target_device_id, @@ -927,41 +958,41 @@ pub(crate) async fn send_transaction_message_route( "Event is invalid", ) })?, - )?; - } + )?, - DeviceIdOrAllDevices::AllDevices => { - for target_device_id in services() - .users - .all_device_ids(target_user_id) - { - services().users.add_to_device_event( - &sender, - target_user_id, - &target_device_id?, - &ev_type.to_string(), - event.deserialize_as().map_err( - |_| { - Error::BadRequest( - ErrorKind::InvalidParam, - "Event is invalid", - ) - }, - )?, - )?; + DeviceIdOrAllDevices::AllDevices => { + for target_device_id in services() + .users + .all_device_ids(target_user_id) + { + services().users.add_to_device_event( + &sender, + target_user_id, + &target_device_id?, + &ev_type.to_string(), + event.deserialize_as().map_err( + |_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Event is invalid", + ) + }, + )?, + )?; + } } } } } - } - // Save transaction id with empty data - services().transaction_ids.add_txnid( - &sender, - None, - &message_id, - &[], - )?; + // Save transaction id with empty data + services().transaction_ids.add_txnid( + &sender, + None, + &message_id, + &[], + )?; + } } Edu::SigningKeyUpdate(SigningKeyUpdateContent { user_id, From 9087da91db8585f34d026a48ba8fdf64865ba14d Mon Sep 17 00:00:00 2001 From: Matthias Ahouansou Date: Sun, 9 Jun 2024 11:15:49 +0100 Subject: [PATCH 221/617] fix(keys): only use keys valid at the time of PDU or transaction, and actually refresh keys Previously, we only fetched keys once, only requesting them again if we have any missing, allowing for ancient keys to be used to sign PDUs and transactions Now we refresh keys that either have or are about to expire, preventing attacks that make use of leaked private keys of a homeserver We also ensure that when validating PDUs or transactions, that they are valid at the origin_server_ts or time of us receiving the transaction respectfully As to not break event authorization for old rooms, we need to keep old keys around We move verify_keys which we no longer see in direct requests to the origin to old_verify_keys We keep old_verify_keys indefinitely as mentioned above, as to not break event authorization (at least until a future MSC addresses this) Original patch by Matthias. Benjamin just rebased it onto grapevine and fixed clippy/rustc warnings. Co-authored-by: Benjamin Lee --- src/api/client_server/membership.rs | 50 +++- src/api/ruma_wrapper/axum.rs | 15 +- src/database/key_value/globals.rs | 145 +++++++---- src/service/admin.rs | 61 +++-- src/service/globals.rs | 103 ++++++-- src/service/globals/data.rs | 87 ++++++- src/service/rooms/event_handler.rs | 359 ++++++++++++++++++++-------- src/service/rooms/timeline.rs | 4 +- 8 files changed, 610 insertions(+), 214 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index e55a601e..52c22414 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -25,9 +25,9 @@ use ruma::{ }, StateEventType, TimelineEventType, }, - serde::Base64, - state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, - OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, RoomVersionId, UserId, + state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, + MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, + OwnedUserId, RoomId, RoomVersionId, UserId, }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; @@ -35,7 +35,10 @@ use tracing::{debug, error, info, warn}; use super::get_alias_helper; use crate::{ - service::pdu::{gen_event_id_canonical_json, PduBuilder}, + service::{ + globals::SigningKeys, + pdu::{gen_event_id_canonical_json, PduBuilder}, + }, services, utils, Ar, Error, PduEvent, Ra, Result, }; @@ -1192,7 +1195,7 @@ async fn make_join_request( async fn validate_and_add_event_id( pdu: &RawJsonValue, room_version: &RoomVersionId, - pub_key_map: &RwLock>>, + pub_key_map: &RwLock>, ) -> Result<(OwnedEventId, CanonicalJsonObject)> { let mut value: CanonicalJsonObject = serde_json::from_str(pdu.get()) .map_err(|e| { @@ -1235,11 +1238,40 @@ async fn validate_and_add_event_id( } } - if let Err(e) = ruma::signatures::verify_event( - &*pub_key_map.read().await, - &value, + let origin_server_ts = value.get("origin_server_ts").ok_or_else(|| { + error!("Invalid PDU, no origin_server_ts field"); + Error::BadRequest( + ErrorKind::MissingParam, + "Invalid PDU, no origin_server_ts field", + ) + })?; + + let origin_server_ts: MilliSecondsSinceUnixEpoch = { + let ts = origin_server_ts.as_integer().ok_or_else(|| { + Error::BadRequest( + ErrorKind::InvalidParam, + "origin_server_ts must be an integer", + ) + })?; + + MilliSecondsSinceUnixEpoch(i64::from(ts).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Time must be after the unix epoch", + ) + })?) + }; + + let unfiltered_keys = (*pub_key_map.read().await).clone(); + + let keys = services().globals.filter_keys_server_map( + unfiltered_keys, + origin_server_ts, room_version, - ) { + ); + + if let Err(e) = ruma::signatures::verify_event(&keys, &value, room_version) + { warn!("Event {} failed verification {:?} {}", event_id, pdu, e); back_off(event_id).await; return Err(Error::BadServerResponse("Event failed verification.")); diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 33744e74..79ac35c2 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -21,7 +21,8 @@ use ruma::{ OutgoingResponse, }, server_util::authorization::XMatrix, - CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId, + CanonicalJsonValue, MilliSecondsSinceUnixEpoch, OwnedDeviceId, + OwnedServerName, OwnedUserId, UserId, }; use serde::Deserialize; use tracing::{debug, error, warn}; @@ -260,6 +261,7 @@ async fn ar_from_request_inner( .fetch_signing_keys( &x_matrix.origin, vec![x_matrix.key.to_string()], + false, ) .await; @@ -274,9 +276,18 @@ async fn ar_from_request_inner( } }; + // Only verify_keys that are currently valid should be used for + // validating requests as per MSC4029 let pub_key_map = BTreeMap::from_iter([( x_matrix.origin.as_str().to_owned(), - keys, + if keys.valid_until_ts > MilliSecondsSinceUnixEpoch::now() { + keys.verify_keys + .into_iter() + .map(|(id, key)| (id, key.key)) + .collect() + } else { + BTreeMap::new() + }, )]); match ruma::signatures::verify_json(&pub_key_map, &request_map) diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 52fde75f..b2594226 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -1,17 +1,18 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use async_trait::async_trait; use futures_util::{stream::FuturesUnordered, StreamExt}; use lru_cache::LruCache; use ruma::{ - api::federation::discovery::{ServerSigningKeys, VerifyKey}, + api::federation::discovery::{OldVerifyKey, ServerSigningKeys}, signatures::Ed25519KeyPair, - DeviceId, MilliSecondsSinceUnixEpoch, OwnedServerSigningKeyId, ServerName, - UserId, + DeviceId, ServerName, UserId, }; use crate::{ - database::KeyValueDatabase, service, services, utils, Error, Result, + database::KeyValueDatabase, + service::{self, globals::SigningKeys}, + services, utils, Error, Result, }; pub(crate) const COUNTER: &[u8] = b"c"; @@ -240,47 +241,97 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" self.global.remove(b"keypair") } - fn add_signing_key( + fn add_signing_key_from_trusted_server( &self, origin: &ServerName, new_keys: ServerSigningKeys, - ) -> Result> { - // Not atomic, but this is not critical - let signingkeys = self.server_signingkeys.get(origin.as_bytes())?; + ) -> Result { + let prev_keys = self.server_signingkeys.get(origin.as_bytes())?; - let mut keys = signingkeys - .and_then(|keys| serde_json::from_slice(&keys).ok()) - .unwrap_or_else(|| { - // Just insert "now", it doesn't matter - ServerSigningKeys::new( - origin.to_owned(), - MilliSecondsSinceUnixEpoch::now(), - ) - }); + Ok( + if let Some(mut prev_keys) = prev_keys.and_then(|keys| { + serde_json::from_slice::(&keys).ok() + }) { + let ServerSigningKeys { + verify_keys, + old_verify_keys, + .. + } = new_keys; - let ServerSigningKeys { - verify_keys, - old_verify_keys, - .. - } = new_keys; + prev_keys.verify_keys.extend(verify_keys); + prev_keys.old_verify_keys.extend(old_verify_keys); + prev_keys.valid_until_ts = new_keys.valid_until_ts; - keys.verify_keys.extend(verify_keys); - keys.old_verify_keys.extend(old_verify_keys); + self.server_signingkeys.insert( + origin.as_bytes(), + &serde_json::to_vec(&prev_keys) + .expect("serversigningkeys can be serialized"), + )?; - self.server_signingkeys.insert( - origin.as_bytes(), - &serde_json::to_vec(&keys) - .expect("serversigningkeys can be serialized"), - )?; + prev_keys.into() + } else { + self.server_signingkeys.insert( + origin.as_bytes(), + &serde_json::to_vec(&new_keys) + .expect("serversigningkeys can be serialized"), + )?; - let mut tree = keys.verify_keys; - tree.extend( - keys.old_verify_keys - .into_iter() - .map(|old| (old.0, VerifyKey::new(old.1.key))), - ); + new_keys.into() + }, + ) + } - Ok(tree) + fn add_signing_key_from_origin( + &self, + origin: &ServerName, + new_keys: ServerSigningKeys, + ) -> Result { + let prev_keys = self.server_signingkeys.get(origin.as_bytes())?; + + Ok( + if let Some(mut prev_keys) = prev_keys.and_then(|keys| { + serde_json::from_slice::(&keys).ok() + }) { + let ServerSigningKeys { + verify_keys, + old_verify_keys, + .. + } = new_keys; + + // Moving `verify_keys` no longer present to `old_verify_keys` + for (key_id, key) in prev_keys.verify_keys { + if !verify_keys.contains_key(&key_id) { + prev_keys.old_verify_keys.insert( + key_id, + OldVerifyKey::new( + prev_keys.valid_until_ts, + key.key, + ), + ); + } + } + + prev_keys.verify_keys = verify_keys; + prev_keys.old_verify_keys.extend(old_verify_keys); + prev_keys.valid_until_ts = new_keys.valid_until_ts; + + self.server_signingkeys.insert( + origin.as_bytes(), + &serde_json::to_vec(&prev_keys) + .expect("serversigningkeys can be serialized"), + )?; + + prev_keys.into() + } else { + self.server_signingkeys.insert( + origin.as_bytes(), + &serde_json::to_vec(&new_keys) + .expect("serversigningkeys can be serialized"), + )?; + + new_keys.into() + }, + ) } /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found @@ -288,21 +339,11 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" fn signing_keys_for( &self, origin: &ServerName, - ) -> Result> { - let signingkeys = self - .server_signingkeys - .get(origin.as_bytes())? - .and_then(|bytes| serde_json::from_slice(&bytes).ok()) - .map(|keys: ServerSigningKeys| { - let mut tree = keys.verify_keys; - tree.extend( - keys.old_verify_keys - .into_iter() - .map(|old| (old.0, VerifyKey::new(old.1.key))), - ); - tree - }) - .unwrap_or_default(); + ) -> Result> { + let signingkeys = + self.server_signingkeys.get(origin.as_bytes())?.and_then(|bytes| { + serde_json::from_slice::(&bytes).ok() + }); Ok(signingkeys) } diff --git a/src/service/admin.rs b/src/service/admin.rs index 00f30e75..69521103 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -28,8 +28,9 @@ use ruma::{ }, TimelineEventType, }, - EventId, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, RoomVersionId, - ServerName, UserId, + signatures::verify_json, + EventId, MilliSecondsSinceUnixEpoch, OwnedRoomAliasId, OwnedRoomId, + RoomAliasId, RoomId, RoomVersionId, ServerName, UserId, }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; @@ -1060,25 +1061,55 @@ impl Service { services() .rooms .event_handler + // Generally we shouldn't be checking against + // expired keys unless required, so in the admin + // room it might be best to not allow expired + // keys .fetch_required_signing_keys( &value, - &pub_key_map, + &pub_key_map ) .await?; - let pub_key_map = pub_key_map.read().await; - match ruma::signatures::verify_json( - &pub_key_map, - &value, - ) { - Ok(()) => RoomMessageEventContent::text_plain( + let mut expired_key_map = BTreeMap::new(); + let mut valid_key_map = BTreeMap::new(); + + for (server, keys) in pub_key_map.into_inner() { + if keys.valid_until_ts + > MilliSecondsSinceUnixEpoch::now() + { + valid_key_map.insert( + server, + keys.verify_keys + .into_iter() + .map(|(id, key)| (id, key.key)) + .collect(), + ); + } else { + expired_key_map.insert( + server, + keys.verify_keys + .into_iter() + .map(|(id, key)| (id, key.key)) + .collect(), + ); + } + } + + if verify_json(&valid_key_map, &value).is_ok() { + RoomMessageEventContent::text_plain( "Signature correct", - ), - Err(e) => RoomMessageEventContent::text_plain( - format!( - "Signature verification failed: {e}" - ), - ), + ) + } else if let Err(e) = + verify_json(&expired_key_map, &value) + { + RoomMessageEventContent::text_plain(format!( + "Signature verification failed: {e}" + )) + } else { + RoomMessageEventContent::text_plain( + "Signature correct (with expired keys)", + ) } } Err(e) => RoomMessageEventContent::text_plain(format!( diff --git a/src/service/globals.rs b/src/service/globals.rs index 45cc9ef5..16bda654 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -15,7 +15,7 @@ use std::{ }; use base64::{engine::general_purpose, Engine as _}; -pub(crate) use data::Data; +pub(crate) use data::{Data, SigningKeys}; use futures_util::FutureExt; use hyper::service::Service as _; use hyper_util::{ @@ -23,10 +23,9 @@ use hyper_util::{ }; use reqwest::dns::{Addrs, Name, Resolve, Resolving}; use ruma::{ - api::federation::discovery::{ServerSigningKeys, VerifyKey}, - serde::Base64, - DeviceId, OwnedEventId, OwnedRoomId, OwnedServerName, - OwnedServerSigningKeyId, RoomVersionId, ServerName, UserId, + api::federation::discovery::ServerSigningKeys, serde::Base64, DeviceId, + MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, + RoomVersionId, ServerName, UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use tracing::{error, info, Instrument}; @@ -379,38 +378,90 @@ impl Service { room_versions } - /// TODO: the key valid until timestamp is only honored in room version > 4 - /// Remove the outdated keys and insert the new ones. - /// /// This doesn't actually check that the keys provided are newer than the /// old set. - pub(crate) fn add_signing_key( + pub(crate) fn add_signing_key_from_trusted_server( &self, origin: &ServerName, new_keys: ServerSigningKeys, - ) -> Result> { - self.db.add_signing_key(origin, new_keys) + ) -> Result { + self.db.add_signing_key_from_trusted_server(origin, new_keys) } - /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found - /// for the server. + /// Same as `from_trusted_server`, except it will move active keys not + /// present in `new_keys` to `old_signing_keys` + pub(crate) fn add_signing_key_from_origin( + &self, + origin: &ServerName, + new_keys: ServerSigningKeys, + ) -> Result { + self.db.add_signing_key_from_origin(origin, new_keys) + } + + /// This returns Ok(None) when there are no keys found for the server. pub(crate) fn signing_keys_for( &self, origin: &ServerName, - ) -> Result> { - let mut keys = self.db.signing_keys_for(origin)?; - if origin == self.server_name() { - keys.insert( - format!("ed25519:{}", services().globals.keypair().version()) - .try_into() - .expect("found invalid server signing keys in DB"), - VerifyKey { - key: Base64::new(self.keypair.public_key().to_vec()), - }, - ); - } + ) -> Result> { + Ok(self.db.signing_keys_for(origin)?.or_else(|| { + (origin == self.server_name()).then(SigningKeys::load_own_keys) + })) + } - Ok(keys) + /// Filters the key map of multiple servers down to keys that should be + /// accepted given the expiry time, room version, and timestamp of the + /// paramters + #[allow(clippy::unused_self)] + pub(crate) fn filter_keys_server_map( + &self, + keys: BTreeMap, + timestamp: MilliSecondsSinceUnixEpoch, + room_version_id: &RoomVersionId, + ) -> BTreeMap> { + keys.into_iter() + .filter_map(|(server, keys)| { + self.filter_keys_single_server(keys, timestamp, room_version_id) + .map(|keys| (server, keys)) + }) + .collect() + } + + /// Filters the keys of a single server down to keys that should be accepted + /// given the expiry time, room version, and timestamp of the paramters + #[allow(clippy::unused_self)] + pub(crate) fn filter_keys_single_server( + &self, + keys: SigningKeys, + timestamp: MilliSecondsSinceUnixEpoch, + room_version_id: &RoomVersionId, + ) -> Option> { + let all_valid = keys.valid_until_ts > timestamp + // valid_until_ts MUST be ignored in room versions 1, 2, 3, and 4. + // https://spec.matrix.org/v1.10/server-server-api/#get_matrixkeyv2server + || matches!(room_version_id, RoomVersionId::V1 + | RoomVersionId::V2 + | RoomVersionId::V4 + | RoomVersionId::V3); + all_valid.then(|| { + // Given that either the room version allows stale keys, or the + // valid_until_ts is in the future, all verify_keys are + // valid + let mut map: BTreeMap<_, _> = keys + .verify_keys + .into_iter() + .map(|(id, key)| (id, key.key)) + .collect(); + + map.extend(keys.old_verify_keys.into_iter().filter_map( + |(id, key)| { + // Even on old room versions, we don't allow old keys if + // they are expired + (key.expired_ts > timestamp).then_some((id, key.key)) + }, + )); + + map + }) } pub(crate) fn database_version(&self) -> Result { diff --git a/src/service/globals/data.rs b/src/service/globals/data.rs index e416530d..6971e2e2 100644 --- a/src/service/globals/data.rs +++ b/src/service/globals/data.rs @@ -1,13 +1,75 @@ -use std::collections::BTreeMap; +use std::{ + collections::BTreeMap, + time::{Duration, SystemTime}, +}; use async_trait::async_trait; use ruma::{ - api::federation::discovery::{ServerSigningKeys, VerifyKey}, + api::federation::discovery::{OldVerifyKey, ServerSigningKeys, VerifyKey}, + serde::Base64, signatures::Ed25519KeyPair, - DeviceId, OwnedServerSigningKeyId, ServerName, UserId, + DeviceId, MilliSecondsSinceUnixEpoch, ServerName, UserId, }; +use serde::Deserialize; -use crate::Result; +use crate::{services, Result}; + +/// Similar to [`ServerSigningKeys`], but drops a few unnecessary fields we +/// don't require post-validation +#[derive(Deserialize, Debug, Clone)] +pub(crate) struct SigningKeys { + pub(crate) verify_keys: BTreeMap, + pub(crate) old_verify_keys: BTreeMap, + pub(crate) valid_until_ts: MilliSecondsSinceUnixEpoch, +} + +impl SigningKeys { + /// Creates the `SigningKeys` struct, using the keys of the current server + pub(crate) fn load_own_keys() -> Self { + let mut keys = Self { + verify_keys: BTreeMap::new(), + old_verify_keys: BTreeMap::new(), + valid_until_ts: MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + Duration::from_secs(7 * 86400), + ) + .expect("Should be valid until year 500,000,000"), + }; + + keys.verify_keys.insert( + format!("ed25519:{}", services().globals.keypair().version()), + VerifyKey { + key: Base64::new( + services().globals.keypair.public_key().to_vec(), + ), + }, + ); + + keys + } +} + +impl From for SigningKeys { + fn from(value: ServerSigningKeys) -> Self { + let ServerSigningKeys { + verify_keys, + old_verify_keys, + valid_until_ts, + .. + } = value; + + Self { + verify_keys: verify_keys + .into_iter() + .map(|(id, key)| (id.to_string(), key)) + .collect(), + old_verify_keys: old_verify_keys + .into_iter() + .map(|(id, key)| (id.to_string(), key)) + .collect(), + valid_until_ts, + } + } +} #[async_trait] pub(crate) trait Data: Send + Sync { @@ -20,18 +82,29 @@ pub(crate) trait Data: Send + Sync { fn clear_caches(&self, amount: u32); fn load_keypair(&self) -> Result; fn remove_keypair(&self) -> Result<()>; - fn add_signing_key( + /// Only extends the cached keys, not moving any verify_keys to + /// old_verify_keys, as if we suddenly recieve requests from the origin + /// server, we want to be able to accept requests from them + fn add_signing_key_from_trusted_server( &self, origin: &ServerName, new_keys: ServerSigningKeys, - ) -> Result>; + ) -> Result; + /// Extends cached keys, as well as moving verify_keys that are not present + /// in these new keys to old_verify_keys, so that potnetially + /// comprimised keys cannot be used to make requests + fn add_signing_key_from_origin( + &self, + origin: &ServerName, + new_keys: ServerSigningKeys, + ) -> Result; /// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found /// for the server. fn signing_keys_for( &self, origin: &ServerName, - ) -> Result>; + ) -> Result>; fn database_version(&self) -> Result; fn bump_database_version(&self, new_version: u64) -> Result<()>; } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 17306f39..6466340a 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -31,7 +31,6 @@ use ruma::{ StateEventType, TimelineEventType, }, int, - serde::Base64, state_res::{self, RoomVersion, StateMap}, uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedServerSigningKeyId, @@ -43,8 +42,10 @@ use tracing::{debug, error, info, trace, warn}; use super::state_compressor::CompressedStateEvent; use crate::{ - service::pdu, services, utils::debug_slice_truncated, Error, PduEvent, - Result, + service::{globals::SigningKeys, pdu}, + services, + utils::debug_slice_truncated, + Error, PduEvent, Result, }; pub(crate) struct Service; @@ -87,7 +88,7 @@ impl Service { room_id: &'a RoomId, value: BTreeMap, is_timeline_event: bool, - pub_key_map: &'a RwLock>>, + pub_key_map: &'a RwLock>, ) -> Result>> { // 0. Check the server is in the room if !services().rooms.metadata.exists(room_id)? { @@ -320,7 +321,7 @@ impl Service { room_id: &'a RoomId, mut value: BTreeMap, auth_events_known: bool, - pub_key_map: &'a RwLock>>, + pub_key_map: &'a RwLock>, ) -> AsyncRecursiveType< 'a, Result<(Arc, BTreeMap)>, @@ -329,12 +330,6 @@ impl Service { // 1.1. Remove unsigned field value.remove("unsigned"); - // TODO: For RoomVersion6 we must check that Raw<..> is canonical do we anywhere?: https://matrix.org/docs/spec/rooms/v6#canonical-json - - // We go through all the signatures we see on the value and fetch - // the corresponding signing keys - self.fetch_required_signing_keys(&value, pub_key_map).await?; - // 2. Check signatures, otherwise drop // 3. check content hash, redact if doesn't match let create_event_content: RoomCreateEventContent = @@ -349,9 +344,56 @@ impl Service { let room_version = RoomVersion::new(room_version_id) .expect("room version is supported"); + // TODO: For RoomVersion6 we must check that Raw<..> is canonical, + // do we anywhere? + // + // https://matrix.org/docs/spec/rooms/v6#canonical-json + + // We go through all the signatures we see on the value and fetch + // the corresponding signing keys + self.fetch_required_signing_keys(&value, pub_key_map).await?; + + let origin_server_ts = + value.get("origin_server_ts").ok_or_else(|| { + error!("Invalid PDU, no origin_server_ts field"); + Error::BadRequest( + ErrorKind::MissingParam, + "Invalid PDU, no origin_server_ts field", + ) + })?; + + let origin_server_ts: MilliSecondsSinceUnixEpoch = { + let ts = origin_server_ts.as_integer().ok_or_else(|| { + Error::BadRequest( + ErrorKind::InvalidParam, + "origin_server_ts must be an integer", + ) + })?; + + MilliSecondsSinceUnixEpoch(i64::from(ts).try_into().map_err( + |_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "Time must be after the unix epoch", + ) + }, + )?) + }; + let guard = pub_key_map.read().await; + + let pkey_map = (*guard).clone(); + + // Removing all the expired keys, unless the room version allows + // stale keys + let filtered_keys = services().globals.filter_keys_server_map( + pkey_map, + origin_server_ts, + room_version_id, + ); + let mut val = match ruma::signatures::verify_event( - &guard, + &filtered_keys, &value, room_version_id, ) { @@ -526,7 +568,7 @@ impl Service { create_event: &PduEvent, origin: &ServerName, room_id: &RoomId, - pub_key_map: &RwLock>>, + pub_key_map: &RwLock>, ) -> Result>> { // Skip the PDU if we already have it as a timeline event if let Ok(Some(pduid)) = @@ -1205,7 +1247,7 @@ impl Service { create_event: &'a PduEvent, room_id: &'a RoomId, room_version_id: &'a RoomVersionId, - pub_key_map: &'a RwLock>>, + pub_key_map: &'a RwLock>, ) -> AsyncRecursiveType< 'a, Vec<(Arc, Option>)>, @@ -1406,7 +1448,7 @@ impl Service { create_event: &PduEvent, room_id: &RoomId, room_version_id: &RoomVersionId, - pub_key_map: &RwLock>>, + pub_key_map: &RwLock>, initial_set: Vec>, ) -> Result<( Vec>, @@ -1509,7 +1551,7 @@ impl Service { pub(crate) async fn fetch_required_signing_keys( &self, event: &BTreeMap, - pub_key_map: &RwLock>>, + pub_key_map: &RwLock>, ) -> Result<()> { let signatures = event .get("signatures") @@ -1541,6 +1583,7 @@ impl Service { ) })?, signature_ids, + true, ) .await; @@ -1570,10 +1613,7 @@ impl Service { BTreeMap, >, room_version: &RoomVersionId, - pub_key_map: &mut RwLockWriteGuard< - '_, - BTreeMap>, - >, + pub_key_map: &mut RwLockWriteGuard<'_, BTreeMap>, ) -> Result<()> { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()) .map_err(|e| { @@ -1626,8 +1666,18 @@ impl Service { let signature_ids = signature_object.keys().cloned().collect::>(); - let contains_all_ids = |keys: &BTreeMap| { - signature_ids.iter().all(|id| keys.contains_key(id)) + let contains_all_ids = |keys: &SigningKeys| { + signature_ids.iter().all(|id| { + keys.verify_keys + .keys() + .map(ToString::to_string) + .any(|key_id| id == &key_id) + || keys + .old_verify_keys + .keys() + .map(ToString::to_string) + .any(|key_id| id == &key_id) + }) }; let origin = <&ServerName>::try_from(signature_server.as_str()) @@ -1646,19 +1696,14 @@ impl Service { trace!("Loading signing keys for {}", origin); - let result: BTreeMap<_, _> = services() - .globals - .signing_keys_for(origin)? - .into_iter() - .map(|(k, v)| (k.to_string(), v.key)) - .collect(); + if let Some(result) = services().globals.signing_keys_for(origin)? { + if !contains_all_ids(&result) { + trace!("Signing key not loaded for {}", origin); + servers.insert(origin.to_owned(), BTreeMap::new()); + } - if !contains_all_ids(&result) { - trace!("Signing key not loaded for {}", origin); - servers.insert(origin.to_owned(), BTreeMap::new()); + pub_key_map.insert(origin.to_string(), result); } - - pub_key_map.insert(origin.to_string(), result); } Ok(()) @@ -1669,7 +1714,7 @@ impl Service { &self, event: &create_join_event::v2::Response, room_version: &RoomVersionId, - pub_key_map: &RwLock>>, + pub_key_map: &RwLock>, ) -> Result<()> { let mut servers: BTreeMap< OwnedServerName, @@ -1741,10 +1786,10 @@ impl Service { let result = services() .globals - .add_signing_key(&k.server_name, k.clone())? - .into_iter() - .map(|(k, v)| (k.to_string(), v.key)) - .collect::>(); + .add_signing_key_from_trusted_server( + &k.server_name, + k.clone(), + )?; pkm.insert(k.server_name.to_string(), result); } @@ -1778,12 +1823,9 @@ impl Service { if let (Ok(get_keys_response), origin) = result { info!("Result is from {origin}"); if let Ok(key) = get_keys_response.server_key.deserialize() { - let result: BTreeMap<_, _> = services() + let result = services() .globals - .add_signing_key(&origin, key)? - .into_iter() - .map(|(k, v)| (k.to_string(), v.key)) - .collect(); + .add_signing_key_from_origin(&origin, key)?; pub_key_map .write() .await @@ -1852,9 +1894,22 @@ impl Service { &self, origin: &ServerName, signature_ids: Vec, - ) -> Result> { - let contains_all_ids = |keys: &BTreeMap| { - signature_ids.iter().all(|id| keys.contains_key(id)) + // Whether to ask for keys from trusted servers. Should be false when + // getting keys for validating requests, as per MSC4029 + query_via_trusted_servers: bool, + ) -> Result { + let contains_all_ids = |keys: &SigningKeys| { + signature_ids.iter().all(|id| { + keys.verify_keys + .keys() + .map(ToString::to_string) + .any(|key_id| id == &key_id) + || keys + .old_verify_keys + .keys() + .map(ToString::to_string) + .any(|key_id| id == &key_id) + }) }; let permit = services() @@ -1921,20 +1976,53 @@ impl Service { trace!("Loading signing keys from database"); - let mut result: BTreeMap<_, _> = services() - .globals - .signing_keys_for(origin)? - .into_iter() - .map(|(k, v)| (k.to_string(), v.key)) - .collect(); + let result = services().globals.signing_keys_for(origin)?; - if contains_all_ids(&result) { - return Ok(result); + let mut expires_soon_or_has_expired = false; + + if let Some(result) = result.clone() { + let ts_threshold = MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + Duration::from_secs(30 * 60), + ) + .expect("Should be valid until year 500,000,000"); + + debug!( + "The threshhold is {:?}, found time is {:?} for server {}", + ts_threshold, result.valid_until_ts, origin + ); + + if contains_all_ids(&result) { + // We want to ensure that the keys remain valid by the time the + // other functions that handle signatures reach them + if result.valid_until_ts > ts_threshold { + debug!( + origin = %origin, + valid_until_ts = %result.valid_until_ts.get(), + "Keys for are deemed as valid, as they expire after threshold", + ); + return Ok(result); + } + + expires_soon_or_has_expired = true; + } } + let mut keys = result.unwrap_or_else(|| SigningKeys { + verify_keys: BTreeMap::new(), + old_verify_keys: BTreeMap::new(), + valid_until_ts: MilliSecondsSinceUnixEpoch::now(), + }); + + // We want to set this to the max, and then lower it whenever we see + // older keys + keys.valid_until_ts = MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + Duration::from_secs(7 * 86400), + ) + .expect("Should be valid until year 500,000,000"); + debug!("Fetching signing keys over federation"); - if let Some(server_key) = services() + if let Some(mut server_key) = services() .sending .send_federation_request( origin, @@ -1944,72 +2032,141 @@ impl Service { .ok() .and_then(|resp| resp.server_key.deserialize().ok()) { - services().globals.add_signing_key(origin, server_key.clone())?; + // Keys should only be valid for a maximum of seven days + server_key.valid_until_ts = server_key.valid_until_ts.min( + MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + Duration::from_secs(7 * 86400), + ) + .expect("Should be valid until year 500,000,000"), + ); - result.extend( + services() + .globals + .add_signing_key_from_origin(origin, server_key.clone())?; + + if keys.valid_until_ts > server_key.valid_until_ts { + keys.valid_until_ts = server_key.valid_until_ts; + } + + keys.verify_keys.extend( server_key .verify_keys .into_iter() - .map(|(k, v)| (k.to_string(), v.key)), + .map(|(id, key)| (id.to_string(), key)), ); - result.extend( + keys.old_verify_keys.extend( server_key .old_verify_keys .into_iter() - .map(|(k, v)| (k.to_string(), v.key)), + .map(|(id, key)| (id.to_string(), key)), ); - if contains_all_ids(&result) { - return Ok(result); + if contains_all_ids(&keys) { + return Ok(keys); } } - for server in services().globals.trusted_servers() { - debug!(trusted_server = %server, "Asking trusted server for signing keys"); - if let Some(server_keys) = services() - .sending - .send_federation_request( - server, - get_remote_server_keys::v2::Request::new( - origin.to_owned(), - MilliSecondsSinceUnixEpoch::from_system_time( - SystemTime::now() - .checked_add(Duration::from_secs(3600)) - .expect("SystemTime to large"), - ) - .expect("time is valid"), - ), - ) - .await - .ok() - .map(|resp| { - resp.server_keys - .into_iter() - .filter_map(|e| e.deserialize().ok()) - .collect::>() - }) - { - trace!(?server_keys, "Got signing keys from trusted server"); - for k in server_keys { - services().globals.add_signing_key(origin, k.clone())?; - result.extend( - k.verify_keys + if query_via_trusted_servers { + for server in services().globals.trusted_servers() { + debug!( + trusted_server = %server, + origin = %origin, + "Asking trusted server for signing keys", + ); + if let Some(server_keys) = services() + .sending + .send_federation_request( + server, + get_remote_server_keys::v2::Request::new( + origin.to_owned(), + MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + .checked_add(Duration::from_secs(3600)) + .expect("SystemTime to large"), + ) + .expect("time is valid"), + ), + ) + .await + .ok() + .map(|resp| { + resp.server_keys .into_iter() - .map(|(k, v)| (k.to_string(), v.key)), + .filter_map(|e| e.deserialize().ok()) + .collect::>() + }) + { + trace!( + ?server_keys, + "Got signing keys from trusted server" ); - result.extend( - k.old_verify_keys - .into_iter() - .map(|(k, v)| (k.to_string(), v.key)), - ); - } + for mut k in server_keys { + // Half an hour should give plenty of time for the + // server to respond with keys that are still + // valid, given we requested keys which are valid at + // least an hour from now + let in_half_hour = + MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + + Duration::from_secs(30 * 60), + ) + .expect("Should be valid until year 500,000,000"); + if k.valid_until_ts < in_half_hour { + // Keys should only be valid for a maximum of seven + // days + k.valid_until_ts = k.valid_until_ts.min( + MilliSecondsSinceUnixEpoch::from_system_time( + SystemTime::now() + + Duration::from_secs(7 * 86400), + ) + .expect( + "Should be valid until year 500,000,000", + ), + ); - if contains_all_ids(&result) { - return Ok(result); + if keys.valid_until_ts > k.valid_until_ts { + keys.valid_until_ts = k.valid_until_ts; + } + + services() + .globals + .add_signing_key_from_trusted_server( + origin, + k.clone(), + )?; + keys.verify_keys.extend( + k.verify_keys + .into_iter() + .map(|(id, key)| (id.to_string(), key)), + ); + keys.old_verify_keys.extend( + k.old_verify_keys + .into_iter() + .map(|(id, key)| (id.to_string(), key)), + ); + } else { + warn!( + origin = %origin, + valid_until = %k.valid_until_ts.get(), + "Server gave us keys older than we \ + requested", + ); + } + + if contains_all_ids(&keys) { + return Ok(keys); + } + } } } } + // We should return these keys if fresher keys were not found + if expires_soon_or_has_expired { + info!(origin = %origin, "Returning stale keys"); + return Ok(keys); + } + drop(permit); back_off(signature_ids).await; diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 95f55819..2a0c8882 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -20,7 +20,6 @@ use ruma::{ GlobalAccountDataEventType, StateEventType, TimelineEventType, }, push::{Action, Ruleset, Tweak}, - serde::Base64, state_res::{self, Event, RoomVersion}, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, @@ -35,6 +34,7 @@ use crate::{ api::server_server, service::{ appservice::NamespaceRegex, + globals::SigningKeys, pdu::{EventHash, PduBuilder}, }, services, utils, Error, PduEvent, Result, @@ -1292,7 +1292,7 @@ impl Service { &self, origin: &ServerName, pdu: Box, - pub_key_map: &RwLock>>, + pub_key_map: &RwLock>, ) -> Result<()> { let (event_id, value, room_id) = server_server::parse_incoming_pdu(&pdu)?; From 95a24c761df8b44b4dfe0e5ae681d181b4f4ee9c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 16:07:47 -0700 Subject: [PATCH 222/617] create admin bot user id once and reuse it This way we don't need to remember to do the conditional everywhere. --- src/service/admin.rs | 103 +++++++++++------------------------------ src/service/globals.rs | 15 +++++- 2 files changed, 42 insertions(+), 76 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 69521103..7fbb5bfe 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -231,16 +231,6 @@ impl Service { let self2 = Arc::clone(self); tokio::spawn(async move { let mut receiver = self2.receiver.lock().await; - let grapevine_user = UserId::parse(format!( - "@{}:{}", - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name() - )) - .expect("admin bot username should be valid"); let Ok(Some(grapevine_room)) = services().admin.get_admin_room() else { @@ -253,23 +243,16 @@ impl Service { .await .expect("admin command channel has been closed"); - Self::handle_event( - &self2, - event, - &grapevine_room, - &grapevine_user, - ) - .await; + Self::handle_event(&self2, event, &grapevine_room).await; } }); } - #[tracing::instrument(skip(self, grapevine_room, grapevine_user))] + #[tracing::instrument(skip(self, grapevine_room))] async fn handle_event( &self, event: AdminRoomEvent, grapevine_room: &OwnedRoomId, - grapevine_user: &ruma::OwnedUserId, ) { let message_content = match event { AdminRoomEvent::SendMessage(content) => content, @@ -302,7 +285,7 @@ impl Service { state_key: None, redacts: None, }, - grapevine_user, + &services().globals.admin_bot_user_id, grapevine_room, &state_lock, ) @@ -716,16 +699,7 @@ impl Service { // Check if the specified user is valid if !services().users.exists(&user_id)? - || user_id - == UserId::parse_with_server_name( - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name(), - ) - .expect("grapevine user exists") + || user_id == services().globals.admin_bot_user_id { return Ok(RoomMessageEventContent::text_plain( "The specified user does not exist!", @@ -1133,11 +1107,7 @@ impl Service { fn usage_to_html(text: &str, server_name: &ServerName) -> String { // Replace `@grapevine:servername:-subcmdname` with // `@grapevine:servername: subcmdname` - let localpart = if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }; + let localpart = services().globals.admin_bot_user_id.localpart(); let text = text.replace( &format!("@{localpart}:{server_name}:-"), @@ -1231,19 +1201,7 @@ impl Service { ); let state_lock = mutex_state.lock().await; - // Create a user for the server - let grapevine_user = UserId::parse(format!( - "@{}:{}", - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name() - )) - .expect("admin bot username should be valid"); - - services().users.create(&grapevine_user, None)?; + services().users.create(&services().globals.admin_bot_user_id, None)?; let room_version = services().globals.default_room_version(); let mut content = match room_version { @@ -1256,9 +1214,9 @@ impl Service { | RoomVersionId::V7 | RoomVersionId::V8 | RoomVersionId::V9 - | RoomVersionId::V10 => { - RoomCreateEventContent::new_v1(grapevine_user.clone()) - } + | RoomVersionId::V10 => RoomCreateEventContent::new_v1( + services().globals.admin_bot_user_id.clone(), + ), RoomVersionId::V11 => RoomCreateEventContent::new_v11(), _ => unreachable!("Validity of room version already checked"), }; @@ -1279,7 +1237,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1304,10 +1262,12 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some(grapevine_user.to_string()), + state_key: Some( + services().globals.admin_bot_user_id.to_string(), + ), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1315,7 +1275,7 @@ impl Service { // 3. Power levels let mut users = BTreeMap::new(); - users.insert(grapevine_user.clone(), 100.into()); + users.insert(services().globals.admin_bot_user_id.clone(), 100.into()); services() .rooms @@ -1332,7 +1292,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1353,7 +1313,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1376,7 +1336,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1397,7 +1357,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1420,7 +1380,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1443,7 +1403,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1470,7 +1430,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1518,16 +1478,6 @@ impl Service { let state_lock = mutex_state.lock().await; // Use the server user to grant the new admin's power level - let grapevine_user = UserId::parse_with_server_name( - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name(), - ) - .expect("admin bot username should be valid"); - // Invite and join the real user services() .rooms @@ -1550,7 +1500,7 @@ impl Service { state_key: Some(user_id.to_string()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) @@ -1584,7 +1534,10 @@ impl Service { // Set power level let mut users = BTreeMap::new(); - users.insert(grapevine_user.clone(), 100.into()); + users.insert( + services().globals.admin_bot_user_id.clone(), + 100.into(), + ); users.insert(user_id.to_owned(), 100.into()); services() @@ -1602,7 +1555,7 @@ impl Service { state_key: Some(String::new()), redacts: None, }, - &grapevine_user, + &services().globals.admin_bot_user_id, &room_id, &state_lock, ) diff --git a/src/service/globals.rs b/src/service/globals.rs index 16bda654..7ef22c63 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -25,7 +25,7 @@ use reqwest::dns::{Addrs, Name, Resolve, Resolving}; use ruma::{ api::federation::discovery::ServerSigningKeys, serde::Base64, DeviceId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, - RoomVersionId, ServerName, UserId, + OwnedUserId, RoomVersionId, ServerName, UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use tracing::{error, info, Instrument}; @@ -52,6 +52,7 @@ pub(crate) struct Service { default_client: reqwest::Client, pub(crate) stable_room_versions: Vec, pub(crate) unstable_room_versions: Vec, + pub(crate) admin_bot_user_id: OwnedUserId, pub(crate) bad_event_ratelimiter: Arc>>, pub(crate) bad_signature_ratelimiter: @@ -206,6 +207,17 @@ impl Service { let unstable_room_versions = vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5]; + let admin_bot_user_id = UserId::parse(format!( + "@{}:{}", + if config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + config.server_name, + )) + .expect("admin bot user ID should be valid"); + let mut s = Self { db, config, @@ -231,6 +243,7 @@ impl Service { jwt_decoding_key, stable_room_versions, unstable_room_versions, + admin_bot_user_id, bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_query_ratelimiter: Arc::new(RwLock::new(HashMap::new())), From 339a869872a823ef6486da61c8c089e4d861f4f2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 16:30:08 -0700 Subject: [PATCH 223/617] create admin bot room alias id once and reuse it --- src/service/admin.rs | 29 +++++++++-------------------- src/service/globals.rs | 11 +++++++++-- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 7fbb5bfe..c825b579 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1,10 +1,4 @@ -use std::{ - collections::BTreeMap, - convert::{TryFrom, TryInto}, - fmt::Write, - sync::Arc, - time::Instant, -}; +use std::{collections::BTreeMap, fmt::Write, sync::Arc, time::Instant}; use clap::Parser; use regex::Regex; @@ -29,8 +23,8 @@ use ruma::{ TimelineEventType, }, signatures::verify_json, - EventId, MilliSecondsSinceUnixEpoch, OwnedRoomAliasId, OwnedRoomId, - RoomAliasId, RoomId, RoomVersionId, ServerName, UserId, + EventId, MilliSecondsSinceUnixEpoch, OwnedRoomId, RoomId, RoomVersionId, + ServerName, UserId, }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; @@ -1410,10 +1404,7 @@ impl Service { .await?; // 6. Room alias - let alias: OwnedRoomAliasId = - format!("#admins:{}", services().globals.server_name()) - .try_into() - .expect("#admins:server_name is a valid alias name"); + let alias = &services().globals.admin_bot_room_alias_id; services() .rooms @@ -1436,7 +1427,7 @@ impl Service { ) .await?; - services().rooms.alias.set_alias(&alias, &room_id)?; + services().rooms.alias.set_alias(alias, &room_id)?; Ok(()) } @@ -1448,12 +1439,10 @@ impl Service { // Allowed because this function uses `services()` #[allow(clippy::unused_self)] pub(crate) fn get_admin_room(&self) -> Result> { - let admin_room_alias: Box = - format!("#admins:{}", services().globals.server_name()) - .try_into() - .expect("#admins:server_name is a valid alias name"); - - services().rooms.alias.resolve_local_alias(&admin_room_alias) + services() + .rooms + .alias + .resolve_local_alias(&services().globals.admin_bot_room_alias_id) } /// Invite the user to the grapevine admin room. diff --git a/src/service/globals.rs b/src/service/globals.rs index 7ef22c63..bed3242b 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -24,8 +24,9 @@ use hyper_util::{ use reqwest::dns::{Addrs, Name, Resolve, Resolving}; use ruma::{ api::federation::discovery::ServerSigningKeys, serde::Base64, DeviceId, - MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, - OwnedUserId, RoomVersionId, ServerName, UserId, + MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomAliasId, OwnedRoomId, + OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId, ServerName, + UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use tracing::{error, info, Instrument}; @@ -53,6 +54,7 @@ pub(crate) struct Service { pub(crate) stable_room_versions: Vec, pub(crate) unstable_room_versions: Vec, pub(crate) admin_bot_user_id: OwnedUserId, + pub(crate) admin_bot_room_alias_id: OwnedRoomAliasId, pub(crate) bad_event_ratelimiter: Arc>>, pub(crate) bad_signature_ratelimiter: @@ -218,6 +220,10 @@ impl Service { )) .expect("admin bot user ID should be valid"); + let admin_bot_room_alias_id = + RoomAliasId::parse(format!("#admins:{}", config.server_name)) + .expect("admin bot room alias ID should be valid"); + let mut s = Self { db, config, @@ -244,6 +250,7 @@ impl Service { stable_room_versions, unstable_room_versions, admin_bot_user_id, + admin_bot_room_alias_id, bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_query_ratelimiter: Arc::new(RwLock::new(HashMap::new())), From 273ab338095c8bad81a28a3d86a50da171ce4fde Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 16:41:52 -0700 Subject: [PATCH 224/617] reintroduce rooms::alias::Serivce struct We're going to need it. --- src/service.rs | 2 +- src/service/rooms/alias.rs | 50 +++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/service.rs b/src/service.rs index b610f6fc..b9eee52b 100644 --- a/src/service.rs +++ b/src/service.rs @@ -61,7 +61,7 @@ impl Services { db, }, rooms: rooms::Service { - alias: db, + alias: rooms::alias::Service::new(db), auth_chain: rooms::auth_chain::Service { db, }, diff --git a/src/service/rooms/alias.rs b/src/service/rooms/alias.rs index 411199f4..3fc2d134 100644 --- a/src/service/rooms/alias.rs +++ b/src/service/rooms/alias.rs @@ -1,4 +1,52 @@ +use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; + +use crate::Result; + mod data; pub(crate) use data::Data; -pub(crate) type Service = &'static dyn Data; + +pub(crate) struct Service { + db: &'static dyn Data, +} + +impl Service { + pub(crate) fn new(db: &'static D) -> Self + where + D: Data, + { + Self { + db, + } + } + + /// Creates or updates the alias to the given room id. + pub(crate) fn set_alias( + &self, + alias: &RoomAliasId, + room_id: &RoomId, + ) -> Result<()> { + self.db.set_alias(alias, room_id) + } + + /// Forgets about an alias. Returns an error if the alias did not exist. + pub(crate) fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { + self.db.remove_alias(alias) + } + + /// Looks up the roomid for the given alias. + pub(crate) fn resolve_local_alias( + &self, + alias: &RoomAliasId, + ) -> Result> { + self.db.resolve_local_alias(alias) + } + + /// Returns all local aliases that point to the given room + pub(crate) fn local_aliases_for_room<'a>( + &'a self, + room_id: &RoomId, + ) -> Box> + 'a> { + self.db.local_aliases_for_room(room_id) + } +} From c7e03a06f7924e9ee551228f32492d059c8cb561 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 17:02:35 -0700 Subject: [PATCH 225/617] refuse admin room alias changes unless admin bot I.e. don't allow the `#admins:example.com` alias to be set or unset by any user other than `@grapevine:example.com`. --- src/api/client_server/alias.rs | 14 ++++++++++++-- src/api/client_server/room.rs | 15 ++++++++++----- src/service/admin.rs | 6 +++++- src/service/rooms/alias.rs | 32 +++++++++++++++++++++++++++++--- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index fb46dc55..c885d5e2 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -19,6 +19,9 @@ use crate::{services, Ar, Error, Ra, Result}; pub(crate) async fn create_alias_route( body: Ar, ) -> Result> { + let sender_user = + body.sender_user.as_deref().expect("user is authenticated"); + if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -44,7 +47,11 @@ pub(crate) async fn create_alias_route( return Err(Error::Conflict("Alias already exists.")); } - services().rooms.alias.set_alias(&body.room_alias, &body.room_id)?; + services().rooms.alias.set_alias( + &body.room_alias, + &body.room_id, + sender_user, + )?; Ok(Ra(create_alias::v3::Response::new())) } @@ -58,6 +65,9 @@ pub(crate) async fn create_alias_route( pub(crate) async fn delete_alias_route( body: Ar, ) -> Result> { + let sender_user = + body.sender_user.as_deref().expect("user is authenticated"); + if body.room_alias.server_name() != services().globals.server_name() { return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -79,7 +89,7 @@ pub(crate) async fn delete_alias_route( )); } - services().rooms.alias.remove_alias(&body.room_alias)?; + services().rooms.alias.remove_alias(&body.room_alias, sender_user)?; // TODO: update alt_aliases? diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 7c4dda90..843d9aea 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -56,7 +56,8 @@ pub(crate) async fn create_room_route( ) -> Result> { use create_room::v3::RoomPreset; - let sender_user = body.sender_user.as_ref().expect("user is authenticated"); + let sender_user = + body.sender_user.as_deref().expect("user is authenticated"); let room_id = RoomId::new(services().globals.server_name()); @@ -194,7 +195,7 @@ pub(crate) async fn create_room_route( | RoomVersionId::V8 | RoomVersionId::V9 | RoomVersionId::V10 => { - RoomCreateEventContent::new_v1(sender_user.clone()) + RoomCreateEventContent::new_v1(sender_user.to_owned()) } RoomVersionId::V11 => RoomCreateEventContent::new_v11(), _ => unreachable!("Validity of room version already checked"), @@ -292,7 +293,7 @@ pub(crate) async fn create_room_route( }); let mut users = BTreeMap::new(); - users.insert(sender_user.clone(), int!(100)); + users.insert(sender_user.to_owned(), int!(100)); if preset == RoomPreset::TrustedPrivateChat { for invite_ in &body.invite { @@ -529,7 +530,7 @@ pub(crate) async fn create_room_route( // Homeserver specific stuff if let Some(alias) = alias { - services().rooms.alias.set_alias(&alias, &room_id)?; + services().rooms.alias.set_alias(&alias, &room_id, sender_user)?; } if body.visibility == room::Visibility::Public { @@ -860,7 +861,11 @@ pub(crate) async fn upgrade_room_route( .local_aliases_for_room(&body.room_id) .filter_map(Result::ok) { - services().rooms.alias.set_alias(&alias, &replacement_room)?; + services().rooms.alias.set_alias( + &alias, + &replacement_room, + sender_user, + )?; } // Get the old room power levels diff --git a/src/service/admin.rs b/src/service/admin.rs index c825b579..e3576576 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1427,7 +1427,11 @@ impl Service { ) .await?; - services().rooms.alias.set_alias(alias, &room_id)?; + services().rooms.alias.set_alias( + alias, + &room_id, + &services().globals.admin_bot_user_id, + )?; Ok(()) } diff --git a/src/service/rooms/alias.rs b/src/service/rooms/alias.rs index 3fc2d134..0b934fe7 100644 --- a/src/service/rooms/alias.rs +++ b/src/service/rooms/alias.rs @@ -1,6 +1,9 @@ -use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId}; +use ruma::{ + api::client::error::ErrorKind, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, + RoomId, UserId, +}; -use crate::Result; +use crate::{services, Error, Result}; mod data; @@ -25,12 +28,35 @@ impl Service { &self, alias: &RoomAliasId, room_id: &RoomId, + user_id: &UserId, ) -> Result<()> { + if alias == services().globals.admin_bot_room_alias_id + && user_id != services().globals.admin_bot_user_id + { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Only the admin bot can modify this alias", + )); + } + self.db.set_alias(alias, room_id) } /// Forgets about an alias. Returns an error if the alias did not exist. - pub(crate) fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { + pub(crate) fn remove_alias( + &self, + alias: &RoomAliasId, + user_id: &UserId, + ) -> Result<()> { + if alias == services().globals.admin_bot_room_alias_id + && user_id != services().globals.admin_bot_user_id + { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Only the admin bot can modify this alias", + )); + } + self.db.remove_alias(alias) } From 0643a3f0810e98261f410551000260535328b68e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:31:22 -0700 Subject: [PATCH 226/617] remove pointless `pub(crate)` and `use self::` And also fix the ordering of things in state_compressor. --- src/config.rs | 2 +- src/main.rs | 4 ++-- src/service/rooms/state_compressor.rs | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 96a4d2fd..e4d197a4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,7 +11,7 @@ use tracing::warn; mod proxy; -use self::proxy::ProxyConfig; +use proxy::ProxyConfig; #[allow(clippy::struct_excessive_bools)] #[derive(Clone, Debug, Deserialize)] diff --git a/src/main.rs b/src/main.rs index 25bc16e6..9bdfd4de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,8 +40,8 @@ use tower_http::{ }; use tracing::{debug, info, info_span, warn, Instrument}; -pub(crate) mod api; -pub(crate) mod clap; +mod api; +mod clap; mod config; mod database; mod error; diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 5cc1f4cd..f6a7d407 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -1,20 +1,22 @@ -pub(crate) mod data; use std::{ collections::HashSet, mem::size_of, sync::{Arc, Mutex}, }; -pub(crate) use data::Data; use lru_cache::LruCache; use ruma::{EventId, RoomId}; -use self::data::StateDiff; use crate::{ observability::{FoundIn, Lookup, METRICS}, services, utils, Result, }; +pub(crate) mod data; + +pub(crate) use data::Data; +use data::StateDiff; + #[derive(Clone)] pub(crate) struct CompressedStateLayer { pub(crate) shortstatehash: u64, From b34e0019974648ac3387283e3707eccb2450d634 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 7 Jun 2024 13:53:16 -0700 Subject: [PATCH 227/617] remove unused Clone derives --- src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index e4d197a4..8c63251d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,7 +14,7 @@ mod proxy; use proxy::ProxyConfig; #[allow(clippy::struct_excessive_bools)] -#[derive(Clone, Debug, Deserialize)] +#[derive(Debug, Deserialize)] pub(crate) struct Config { #[serde(default = "false_fn")] pub(crate) conduit_compat: bool, @@ -89,7 +89,7 @@ pub(crate) struct Config { pub(crate) catchall: BTreeMap, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Debug, Deserialize)] pub(crate) struct TlsConfig { pub(crate) certs: String, pub(crate) key: String, From 3650fde0ac7a9b678afc52f4056fe43def53c559 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:28:51 -0700 Subject: [PATCH 228/617] rename `src/{clap -> args}.rs` --- src/{clap.rs => args.rs} | 0 src/main.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{clap.rs => args.rs} (100%) diff --git a/src/clap.rs b/src/args.rs similarity index 100% rename from src/clap.rs rename to src/args.rs diff --git a/src/main.rs b/src/main.rs index 9bdfd4de..c68d3b29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ use tower_http::{ use tracing::{debug, info, info_span, warn, Instrument}; mod api; -mod clap; +mod args; mod config; mod database; mod error; @@ -108,7 +108,7 @@ async fn main() -> ExitCode { async fn try_main() -> Result<(), error::Main> { use error::Main as Error; - clap::parse(); + args::parse(); // Initialize config let raw_config = Figment::new() From 9b115b411048bb2ac47afa1ea0a25345eddbb413 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:34:51 -0700 Subject: [PATCH 229/617] require config path via cli, remove env var --- nix/modules/default/default.nix | 9 ++------- src/args.rs | 8 +++++++- src/error.rs | 7 ------- src/main.rs | 10 ++-------- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index be66012c..aed8b1b7 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -85,17 +85,12 @@ in systemd.services.grapevine = { description = "Grapevine (Matrix homeserver)"; wantedBy = [ "multi-user.target" ]; - environment = lib.mkMerge [ - { - GRAPEVINE_CONFIG = configFile; - } - cfg.extraEnvironment - ]; + environment = cfg.extraEnvironment; # Keep sorted serviceConfig = { DynamicUser = true; - ExecStart = "${lib.getExe cfg.package}"; + ExecStart = "${lib.getExe cfg.package} --config ${configFile}"; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; diff --git a/src/args.rs b/src/args.rs index 09cd3839..a54589b3 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,11 +1,17 @@ //! Integration with `clap` +use std::path::PathBuf; + use clap::Parser; /// Command line arguments #[derive(Parser)] #[clap(about, version = crate::version())] -pub(crate) struct Args; +pub(crate) struct Args { + /// Path to the configuration file + #[clap(long, short)] + pub(crate) config: PathBuf, +} /// Parse command line arguments into structured data pub(crate) fn parse() -> Args { diff --git a/src/error.rs b/src/error.rs index 33e3bfce..29dca26a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,13 +38,6 @@ impl fmt::Display for DisplayWithSources<'_> { #[allow(missing_docs)] #[derive(Error, Debug)] pub(crate) enum Main { - #[error( - "the `{0}` environment variable must either be set to a configuration \ - file path or set to an empty string to force configuration through \ - environment variables" - )] - ConfigPathUnset(&'static str), - #[error("invalid configuration")] ConfigInvalid(#[from] figment::Error), diff --git a/src/main.rs b/src/main.rs index c68d3b29..bc390f3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,17 +108,11 @@ async fn main() -> ExitCode { async fn try_main() -> Result<(), error::Main> { use error::Main as Error; - args::parse(); + let args = args::parse(); // Initialize config let raw_config = Figment::new() - .merge( - Toml::file({ - let name = "GRAPEVINE_CONFIG"; - Env::var(name).ok_or(Error::ConfigPathUnset(name))? - }) - .nested(), - ) + .merge(Toml::file(&args.config).nested()) .merge(Env::prefixed("GRAPEVINE_").global()); let config = raw_config.extract::()?; From 9a92a8047e2600e5e5ba8d15b0e6783424cca151 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:47:03 -0700 Subject: [PATCH 230/617] drop support for environment variables The configuration file is now the canonical way to, well, configure. This change is desirable because it gives us much more flexibility with how configuration is structured. Environment variables are insufficient because, for example, they're a flat namespace and have no built-in way to represent lists. --- nix/modules/default/default.nix | 10 ---------- src/main.rs | 6 ++---- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index aed8b1b7..91eba6d9 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -25,15 +25,6 @@ in pkgsText = "inputs.grapevine.packages.\${pkgs.system}"; }; - extraEnvironment = lib.mkOption { - type = types.attrsOf types.str; - description = '' - Extra environment variables to set for the process. - ''; - default = {}; - example = { RUST_BACKTRACE="yes"; }; - }; - settings = lib.mkOption { type = types.submodule { freeformType = format.type; @@ -85,7 +76,6 @@ in systemd.services.grapevine = { description = "Grapevine (Matrix homeserver)"; wantedBy = [ "multi-user.target" ]; - environment = cfg.extraEnvironment; # Keep sorted serviceConfig = { diff --git a/src/main.rs b/src/main.rs index bc390f3c..4a661b7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use axum_server::{ bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, }; use figment::{ - providers::{Env, Format, Toml}, + providers::{Format, Toml}, Figment, }; use http::{ @@ -111,9 +111,7 @@ async fn try_main() -> Result<(), error::Main> { let args = args::parse(); // Initialize config - let raw_config = Figment::new() - .merge(Toml::file(&args.config).nested()) - .merge(Env::prefixed("GRAPEVINE_").global()); + let raw_config = Figment::new().merge(Toml::file(&args.config).nested()); let config = raw_config.extract::()?; From a6087e97e12a075dc62c15a69f5b870543de869a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:51:54 -0700 Subject: [PATCH 231/617] remove config deprecation functionality This is a hard fork, we don't need to inherit this cruft. Really, I should've noticed and removed this closer to the beginning of our history. --- src/config.rs | 32 +------------------------------- src/main.rs | 2 -- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/src/config.rs b/src/config.rs index 8c63251d..3b1f0397 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,13 +1,11 @@ use std::{ - collections::BTreeMap, fmt, fmt::Write, net::{IpAddr, Ipv4Addr}, }; use ruma::{OwnedServerName, RoomVersionId}; -use serde::{de::IgnoredAny, Deserialize}; -use tracing::warn; +use serde::Deserialize; mod proxy; @@ -82,11 +80,6 @@ pub(crate) struct Config { pub(crate) turn_ttl: u64, pub(crate) emergency_password: Option, - - #[serde(flatten)] - // This has special meaning to `serde` - #[allow(clippy::zero_sized_map_values)] - pub(crate) catchall: BTreeMap, } #[derive(Debug, Deserialize)] @@ -95,29 +88,6 @@ pub(crate) struct TlsConfig { pub(crate) key: String, } -const DEPRECATED_KEYS: &[&str] = &["cache_capacity"]; - -impl Config { - pub(crate) fn warn_deprecated(&self) { - let mut was_deprecated = false; - for key in self - .catchall - .keys() - .filter(|key| DEPRECATED_KEYS.iter().any(|s| s == key)) - { - warn!("Config parameter {} is deprecated", key); - was_deprecated = true; - } - - if was_deprecated { - warn!( - "Read grapevine documentation and check your configuration if \ - any new configuration parameters should be adjusted" - ); - } - } -} - impl fmt::Display for Config { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Prepare a list of config values to show diff --git a/src/main.rs b/src/main.rs index 4a661b7c..bc877d6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -115,8 +115,6 @@ async fn try_main() -> Result<(), error::Main> { let config = raw_config.extract::()?; - config.warn_deprecated(); - let _guard = observability::init(&config); // This is needed for opening lots of file descriptors, which tends to From 44088852cf1a30498a81b8d28a5bb912907f5731 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:55:19 -0700 Subject: [PATCH 232/617] remove `show-config` admin room command Just `cat` the config file. Also this code would be very annoying to maintain. Getting rid of this also revealed that another config option is specific to RocksDB, so `cfg`s for that have been added. --- src/config.rs | 100 ++----------------------------------------- src/service/admin.rs | 10 ----- 2 files changed, 3 insertions(+), 107 deletions(-) diff --git a/src/config.rs b/src/config.rs index 3b1f0397..990bb865 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,4 @@ -use std::{ - fmt, - fmt::Write, - net::{IpAddr, Ipv4Addr}, -}; +use std::net::{IpAddr, Ipv4Addr}; use ruma::{OwnedServerName, RoomVersionId}; use serde::Deserialize; @@ -25,6 +21,7 @@ pub(crate) struct Config { pub(crate) server_name: OwnedServerName, pub(crate) database_backend: String, pub(crate) database_path: String, + #[cfg(feature = "rocksdb")] #[serde(default = "default_db_cache_capacity_mb")] pub(crate) db_cache_capacity_mb: f64, #[serde(default = "default_cache_capacity_modifier")] @@ -88,98 +85,6 @@ pub(crate) struct TlsConfig { pub(crate) key: String, } -impl fmt::Display for Config { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Prepare a list of config values to show - let lines = [ - ("Server name", self.server_name.host()), - ("Database backend", &self.database_backend), - ("Database path", &self.database_path), - ( - "Database cache capacity (MB)", - &self.db_cache_capacity_mb.to_string(), - ), - ( - "Cache capacity modifier", - &self.cache_capacity_modifier.to_string(), - ), - #[cfg(feature = "rocksdb")] - ( - "Maximum open files for RocksDB", - &self.rocksdb_max_open_files.to_string(), - ), - ("PDU cache capacity", &self.pdu_cache_capacity.to_string()), - ( - "Cleanup interval in seconds", - &self.cleanup_second_interval.to_string(), - ), - ("Maximum request size", &self.max_request_size.to_string()), - ( - "Maximum concurrent requests", - &self.max_concurrent_requests.to_string(), - ), - ("Allow registration", &self.allow_registration.to_string()), - ("Allow encryption", &self.allow_encryption.to_string()), - ("Allow federation", &self.allow_federation.to_string()), - ("Allow room creation", &self.allow_room_creation.to_string()), - ( - "JWT secret", - match self.jwt_secret { - Some(_) => "set", - None => "not set", - }, - ), - ("Trusted servers", { - let mut lst = vec![]; - for server in &self.trusted_servers { - lst.push(server.host()); - } - &lst.join(", ") - }), - ( - "TURN username", - if self.turn_username.is_empty() { - "not set" - } else { - &self.turn_username - }, - ), - ("TURN password", { - if self.turn_password.is_empty() { - "not set" - } else { - "set" - } - }), - ("TURN secret", { - if self.turn_secret.is_empty() { - "not set" - } else { - "set" - } - }), - ("Turn TTL", &self.turn_ttl.to_string()), - ("Turn URIs", { - let mut lst = vec![]; - for item in self.turn_uris.iter().cloned().enumerate() { - let (_, uri): (usize, String) = item; - lst.push(uri); - } - &lst.join(", ") - }), - ]; - - let mut msg: String = "Active config values:\n\n".to_owned(); - - for line in lines.into_iter().enumerate() { - writeln!(msg, "{}: {}", line.1 .0, line.1 .1) - .expect("write to in-memory buffer should succeed"); - } - - write!(f, "{msg}") - } -} - fn false_fn() -> bool { false } @@ -196,6 +101,7 @@ fn default_port() -> u16 { 6167 } +#[cfg(feature = "rocksdb")] fn default_db_cache_capacity_mb() -> f64 { 300.0 } diff --git a/src/service/admin.rs b/src/service/admin.rs index e3576576..76b6dc15 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -156,9 +156,6 @@ enum AdminCommand { amount: u32, }, - /// Show configuration values - ShowConfig, - /// Reset user password ResetPassword { /// Username of the user for whom the password should be reset @@ -659,13 +656,6 @@ impl Service { RoomMessageEventContent::text_plain("Done.") } - AdminCommand::ShowConfig => { - // Construct and send the response - RoomMessageEventContent::text_plain(format!( - "{}", - services().globals.config - )) - } AdminCommand::ResetPassword { username, } => { From 003c0a4928632d481a68f5c4eca3095bb224b8a7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 21:58:33 -0700 Subject: [PATCH 233/617] drop nested config This functionality was never actually used AFAICT, as no way to provide alternate profiles was ever provided. This changes the configuration format to remove the `[global]` section, everything that was previously under that namespace is now at the top level. --- nix/modules/default/default.nix | 14 +++++++------- src/config/proxy.rs | 5 ++--- src/main.rs | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 91eba6d9..9c7d35a8 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -29,21 +29,21 @@ in type = types.submodule { freeformType = format.type; options = { - global.address = lib.mkOption { + address = lib.mkOption { type = types.nonEmptyStr; description = '' The local IP address to bind to. ''; default = "::1"; }; - global.conduit_compat = lib.mkOption { + conduit_compat = lib.mkOption { type = types.bool; description = '' Whether to operate as a drop-in replacement for Conduit. ''; default = false; }; - global.database_path = lib.mkOption { + database_path = lib.mkOption { type = types.nonEmptyStr; readOnly = true; description = '' @@ -52,11 +52,11 @@ in Note that this is read-only because this module makes use of systemd's `StateDirectory` option. ''; - default = if cfg.settings.global.conduit_compat + default = if cfg.settings.conduit_compat then "/var/lib/matrix-conduit" else "/var/lib/grapevine"; }; - global.port = lib.mkOption { + port = lib.mkOption { type = types.port; description = '' The local port to bind to. @@ -98,14 +98,14 @@ in RestrictNamespaces = true; RestrictRealtime = true; StartLimitBurst = 5; - StateDirectory = if cfg.settings.global.conduit_compat + StateDirectory = if cfg.settings.conduit_compat then "matrix-conduit" else "grapevine"; StateDirectoryMode = "0700"; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" ]; UMask = "077"; - User = if cfg.settings.global.conduit_compat + User = if cfg.settings.conduit_compat then "conduit" else "grapevine"; }; diff --git a/src/config/proxy.rs b/src/config/proxy.rs index bf365265..af6df291 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -10,13 +10,12 @@ use crate::Result; /// ``` /// - Global proxy /// ```toml -/// [global.proxy] +/// [proxy] /// global = { url = "socks5h://localhost:9050" } /// ``` /// - Proxy some domains /// ```toml -/// [global.proxy] -/// [[global.proxy.by_domain]] +/// [[proxy.by_domain]] /// url = "socks5h://localhost:9050" /// include = ["*.onion", "matrix.myspecial.onion"] /// exclude = ["*.myspecial.onion"] diff --git a/src/main.rs b/src/main.rs index bc877d6d..4c1c9427 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,7 +111,7 @@ async fn try_main() -> Result<(), error::Main> { let args = args::parse(); // Initialize config - let raw_config = Figment::new().merge(Toml::file(&args.config).nested()); + let raw_config = Figment::new().merge(Toml::file(&args.config)); let config = raw_config.extract::()?; From 2b0bc140cf92192596d0c878827f529de59ee592 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 22:07:32 -0700 Subject: [PATCH 234/617] drop figment Just deserialize directly via the `toml` crate. --- Cargo.lock | 92 ++++------------------------------------------------ Cargo.toml | 2 +- src/error.rs | 7 ++-- src/main.rs | 14 ++++---- 4 files changed, 18 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ab74856..ba8c564c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,15 +113,6 @@ dependencies = [ "syn", ] -[[package]] -name = "atomic" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" -dependencies = [ - "bytemuck", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -686,20 +677,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - [[package]] name = "flate2" version = "1.0.30" @@ -852,7 +829,6 @@ dependencies = [ "base64 0.22.1", "bytes", "clap", - "figment", "futures-util", "hmac", "html-escape", @@ -892,6 +868,7 @@ dependencies = [ "thread_local", "tikv-jemallocator", "tokio", + "toml", "tower", "tower-http", "tracing", @@ -1253,12 +1230,6 @@ dependencies = [ "serde", ] -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - [[package]] name = "ipconfig" version = "0.3.2" @@ -1754,29 +1725,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - [[package]] name = "pem" version = "3.0.4" @@ -1932,19 +1880,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - [[package]] name = "prometheus" version = "0.13.4" @@ -3045,14 +2980,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.14", ] [[package]] @@ -3077,9 +3012,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap 2.2.6", "serde", @@ -3329,15 +3264,6 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -3734,12 +3660,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - [[package]] name = "yap" version = "0.12.0" diff --git a/Cargo.toml b/Cargo.toml index 9fb3466a..ed55a338 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,6 @@ axum-server = { version = "0.6.0", features = ["tls-rustls"] } base64 = "0.22.1" bytes = "1.6.0" clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } -figment = { version = "0.10.19", features = ["env", "toml"] } futures-util = { version = "0.3.30", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" @@ -134,6 +133,7 @@ thiserror = "1.0.61" thread_local = "1.1.8" tikv-jemallocator = { version = "0.5.4", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } tokio = { version = "1.37.0", features = ["fs", "macros", "signal", "sync"] } +toml = "0.8.14" tower = { version = "0.4.13", features = ["util"] } tower-http = { version = "0.5.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.40", features = [] } diff --git a/src/error.rs b/src/error.rs index 29dca26a..f6286c0e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,8 +38,11 @@ impl fmt::Display for DisplayWithSources<'_> { #[allow(missing_docs)] #[derive(Error, Debug)] pub(crate) enum Main { - #[error("invalid configuration")] - ConfigInvalid(#[from] figment::Error), + #[error("failed to read configuration file")] + ConfigRead(#[source] std::io::Error), + + #[error("failed to parse configuration")] + ConfigParse(#[from] toml::de::Error), #[error("failed to initialize observability")] Observability(#[from] Observability), diff --git a/src/main.rs b/src/main.rs index 4c1c9427..e71430c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,10 +16,6 @@ use axum::{ use axum_server::{ bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, }; -use figment::{ - providers::{Format, Toml}, - Figment, -}; use http::{ header::{self, HeaderName}, Method, StatusCode, Uri, @@ -110,10 +106,12 @@ async fn try_main() -> Result<(), error::Main> { let args = args::parse(); - // Initialize config - let raw_config = Figment::new().merge(Toml::file(&args.config)); - - let config = raw_config.extract::()?; + let config = toml::from_str( + &tokio::fs::read_to_string(&args.config) + .await + .map_err(Error::ConfigRead)?, + ) + .map_err(Error::ConfigParse)?; let _guard = observability::init(&config); From 6b819d6f2d41cc14a01922634ce39cad0a695a3e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 6 Jun 2024 22:44:30 -0700 Subject: [PATCH 235/617] move config loading to config module This separates concerns a bit more. We will probably want to extend the logic for config loading in the future, and that stuff should all live in the relevant place. This change points us in the right direction. --- src/config.rs | 24 +++++++++++++++++++++++- src/error.rs | 22 ++++++++++++++++------ src/main.rs | 7 +------ 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/config.rs b/src/config.rs index 990bb865..04e723f4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,13 @@ -use std::net::{IpAddr, Ipv4Addr}; +use std::{ + net::{IpAddr, Ipv4Addr}, + path::Path, +}; use ruma::{OwnedServerName, RoomVersionId}; use serde::Deserialize; +use crate::error; + mod proxy; use proxy::ProxyConfig; @@ -153,3 +158,20 @@ fn default_turn_ttl() -> u64 { pub(crate) fn default_default_room_version() -> RoomVersionId { RoomVersionId::V10 } + +/// Load the configuration from the given path +pub(crate) async fn load

(path: P) -> Result +where + P: AsRef, +{ + use error::Config as Error; + + let path = path.as_ref(); + + toml::from_str( + &tokio::fs::read_to_string(path) + .await + .map_err(|e| Error::Read(e, path.to_owned()))?, + ) + .map_err(|e| Error::Parse(e, path.to_owned())) +} diff --git a/src/error.rs b/src/error.rs index f6286c0e..94d9a847 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ //! Error handling facilities -use std::{fmt, iter}; +use std::{fmt, iter, path::PathBuf}; use thiserror::Error; @@ -38,11 +38,8 @@ impl fmt::Display for DisplayWithSources<'_> { #[allow(missing_docs)] #[derive(Error, Debug)] pub(crate) enum Main { - #[error("failed to read configuration file")] - ConfigRead(#[source] std::io::Error), - - #[error("failed to parse configuration")] - ConfigParse(#[from] toml::de::Error), + #[error("failed to load configuration")] + Config(#[from] Config), #[error("failed to initialize observability")] Observability(#[from] Observability), @@ -74,3 +71,16 @@ pub(crate) enum Observability { #[error("tracing_flame error")] TracingFlame(#[from] tracing_flame::Error), } + +/// Configuration errors +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum Config { + #[error("failed to read configuration file {1:?}")] + Read(#[source] std::io::Error, PathBuf), + + #[error("failed to parse configuration file {1:?}")] + Parse(#[source] toml::de::Error, PathBuf), +} diff --git a/src/main.rs b/src/main.rs index e71430c4..ba1f9e29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,12 +106,7 @@ async fn try_main() -> Result<(), error::Main> { let args = args::parse(); - let config = toml::from_str( - &tokio::fs::read_to_string(&args.config) - .await - .map_err(Error::ConfigRead)?, - ) - .map_err(Error::ConfigParse)?; + let config = config::load(&args.config).await?; let _guard = observability::init(&config); From c46eaed0e04a6c621fa93822fe9cc1fec07aed13 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 7 Jun 2024 13:11:46 -0700 Subject: [PATCH 236/617] parse configured EnvFilter once This allows the error handling to be done upfront instead of for each use. In particular, the `toml` error now points to the span of text in the config file where the misconfigured EnvFilter value is. This is much better than the previous error that did not indicate what was actually causing it to happen. --- src/config.rs | 10 +++++--- src/config/env_filter_clone.rs | 44 ++++++++++++++++++++++++++++++++++ src/error.rs | 3 --- src/observability.rs | 8 +++---- 4 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 src/config/env_filter_clone.rs diff --git a/src/config.rs b/src/config.rs index 04e723f4..c1ffde91 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,8 +8,10 @@ use serde::Deserialize; use crate::error; +mod env_filter_clone; mod proxy; +use env_filter_clone::EnvFilterClone; use proxy::ProxyConfig; #[allow(clippy::struct_excessive_bools)] @@ -69,7 +71,7 @@ pub(crate) struct Config { #[serde(default = "default_trusted_servers")] pub(crate) trusted_servers: Vec, #[serde(default = "default_log")] - pub(crate) log: String, + pub(crate) log: EnvFilterClone, #[serde(default)] pub(crate) turn_username: String, #[serde(default)] @@ -146,8 +148,10 @@ fn default_trusted_servers() -> Vec { vec![OwnedServerName::try_from("matrix.org").unwrap()] } -fn default_log() -> String { - "warn,state_res=warn,_=off".to_owned() +fn default_log() -> EnvFilterClone { + "warn,state_res=warn,_=off" + .parse() + .expect("hardcoded env filter should be valid") } fn default_turn_ttl() -> u64 { diff --git a/src/config/env_filter_clone.rs b/src/config/env_filter_clone.rs new file mode 100644 index 00000000..183c827f --- /dev/null +++ b/src/config/env_filter_clone.rs @@ -0,0 +1,44 @@ +//! A workaround for [`EnvFilter`] not directly implementing [`Clone`] +//! +//! This will be unnecessary after [tokio-rs/tracing#2956][0] is merged. +//! +//! [0]: https://github.com/tokio-rs/tracing/pull/2956 +#![warn(missing_docs, clippy::missing_docs_in_private_items)] + +use std::str::FromStr; + +use serde::{de, Deserialize, Deserializer}; +use tracing_subscriber::EnvFilter; + +/// A workaround for [`EnvFilter`] not directly implementing [`Clone`] +/// +/// Use [`FromStr`] or [`Deserialize`] to construct this type, then [`From`] or +/// [`Into`] to convert it into an [`EnvFilter`] when needed. +#[derive(Debug)] +pub(crate) struct EnvFilterClone(String); + +impl FromStr for EnvFilterClone { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + EnvFilter::from_str(s)?; + Ok(Self(s.to_owned())) + } +} + +impl From<&EnvFilterClone> for EnvFilter { + fn from(other: &EnvFilterClone) -> Self { + EnvFilter::from_str(&other.0) + .expect("env filter syntax should have been validated already") + } +} + +impl<'de> Deserialize<'de> for EnvFilterClone { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_str(&s).map_err(de::Error::custom) + } +} diff --git a/src/error.rs b/src/error.rs index 94d9a847..c8e9d8e2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,9 +61,6 @@ pub(crate) enum Observability { #[error("opentelemetry error")] Otel(#[from] opentelemetry::trace::TraceError), - #[error("invalid log filter syntax")] - EnvFilter(#[from] tracing_subscriber::filter::ParseError), - #[error("failed to install global default tracing subscriber")] SetSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError), diff --git a/src/observability.rs b/src/observability.rs index f182219f..b6ded983 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -82,8 +82,6 @@ pub(crate) enum FoundIn { /// Initialize observability pub(crate) fn init(config: &Config) -> Result { - let config_filter_layer = || EnvFilter::try_new(&config.log); - let jaeger_layer = config .allow_jaeger .then(|| { @@ -101,7 +99,7 @@ pub(crate) fn init(config: &Config) -> Result { let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); Ok::<_, error::Observability>( - telemetry.with_filter(config_filter_layer()?), + telemetry.with_filter(EnvFilter::from(&config.log)), ) }) .transpose()?; @@ -114,7 +112,7 @@ pub(crate) fn init(config: &Config) -> Result { let flame_layer = flame_layer.with_empty_samples(false); Ok::<_, error::Observability>(( - flame_layer.with_filter(config_filter_layer()?), + flame_layer.with_filter(EnvFilter::from(&config.log)), guard, )) }) @@ -122,7 +120,7 @@ pub(crate) fn init(config: &Config) -> Result { .unzip(); let fmt_layer = tracing_subscriber::fmt::Layer::new() - .with_filter(config_filter_layer()?); + .with_filter(EnvFilter::from(&config.log)); let subscriber = Registry::default() .with(jaeger_layer) From 85e77832e92a832b9cf6f107a888fa7542847871 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 11 Jun 2024 20:41:05 -0700 Subject: [PATCH 237/617] follow xdg base dirs spec by default --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/args.rs | 22 +++++++++++++++++++--- src/config.rs | 28 +++++++++++++++++++++++++--- src/error.rs | 16 ++++++++++++++++ src/main.rs | 2 +- 6 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba8c564c..0ec85d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -876,6 +876,7 @@ dependencies = [ "tracing-opentelemetry", "tracing-subscriber", "trust-dns-resolver", + "xdg", ] [[package]] @@ -3660,6 +3661,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + [[package]] name = "yap" version = "0.12.0" diff --git a/Cargo.toml b/Cargo.toml index ed55a338..c2da8680 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,6 +141,7 @@ tracing-flame = "0.2.0" tracing-opentelemetry = "0.24.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } trust-dns-resolver = "0.23.2" +xdg = "2.5.2" [target.'cfg(unix)'.dependencies] nix = { version = "0.29", features = ["resource"] } diff --git a/src/args.rs b/src/args.rs index a54589b3..33862de2 100644 --- a/src/args.rs +++ b/src/args.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; -use clap::Parser; +use clap::{CommandFactory as _, FromArgMatches as _, Parser}; /// Command line arguments #[derive(Parser)] @@ -10,10 +10,26 @@ use clap::Parser; pub(crate) struct Args { /// Path to the configuration file #[clap(long, short)] - pub(crate) config: PathBuf, + pub(crate) config: Option, } /// Parse command line arguments into structured data pub(crate) fn parse() -> Args { - Args::parse() + let mut command = Args::command().mut_arg("config", |x| { + let help = "Set the path to the configuration file"; + x.help(help).long_help(format!( + "{}\n\nIf this option is specified, the provided value is used \ + as-is.\n\nIf this option is not specified, then the XDG Base \ + Directory Specification is followed, searching for the path `{}` \ + in the configuration directories. + ", + help, + crate::config::DEFAULT_PATH.display(), + )) + }); + + match Args::from_arg_matches(&command.get_matches_mut()) { + Ok(x) => x, + Err(e) => e.format(&mut command).exit(), + } } diff --git a/src/config.rs b/src/config.rs index c1ffde91..88399a46 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,10 @@ use std::{ + borrow::Cow, net::{IpAddr, Ipv4Addr}, - path::Path, + path::{Path, PathBuf}, }; +use once_cell::sync::Lazy; use ruma::{OwnedServerName, RoomVersionId}; use serde::Deserialize; @@ -14,6 +16,10 @@ mod proxy; use env_filter_clone::EnvFilterClone; use proxy::ProxyConfig; +/// The default configuration file path +pub(crate) static DEFAULT_PATH: Lazy = + Lazy::new(|| [env!("CARGO_PKG_NAME"), "config.toml"].iter().collect()); + #[allow(clippy::struct_excessive_bools)] #[derive(Debug, Deserialize)] pub(crate) struct Config { @@ -163,13 +169,29 @@ pub(crate) fn default_default_room_version() -> RoomVersionId { RoomVersionId::V10 } -/// Load the configuration from the given path -pub(crate) async fn load

(path: P) -> Result +/// Search default locations for a configuration file +/// +/// If one isn't found, the list of tried paths is returned. +fn search() -> Result { + use error::ConfigSearch as Error; + + xdg::BaseDirectories::new()? + .find_config_file(&*DEFAULT_PATH) + .ok_or(Error::NotFound) +} + +/// Load the configuration from the given path or XDG Base Directories +pub(crate) async fn load

(path: Option

) -> Result where P: AsRef, { use error::Config as Error; + let path = match path.as_ref().map(AsRef::as_ref) { + Some(x) => Cow::Borrowed(x), + None => Cow::Owned(search()?), + }; + let path = path.as_ref(); toml::from_str( diff --git a/src/error.rs b/src/error.rs index c8e9d8e2..2c57b021 100644 --- a/src/error.rs +++ b/src/error.rs @@ -75,9 +75,25 @@ pub(crate) enum Observability { #[allow(missing_docs)] #[derive(Error, Debug)] pub(crate) enum Config { + #[error("failed to find configuration file")] + Search(#[from] ConfigSearch), + #[error("failed to read configuration file {1:?}")] Read(#[source] std::io::Error, PathBuf), #[error("failed to parse configuration file {1:?}")] Parse(#[source] toml::de::Error, PathBuf), } + +/// Errors that can occur while searching for a config file +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum ConfigSearch { + #[error("XDG Base Directory error")] + Xdg(#[from] xdg::BaseDirectoriesError), + + #[error("no relevant configuration files found in XDG Base Directories")] + NotFound, +} diff --git a/src/main.rs b/src/main.rs index ba1f9e29..bc318479 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,7 +106,7 @@ async fn try_main() -> Result<(), error::Main> { let args = args::parse(); - let config = config::load(&args.config).await?; + let config = config::load(args.config.as_ref()).await?; let _guard = observability::init(&config); From 70fa17dde0801227af58c16141cebdae18d3af7a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 11 Jun 2024 21:14:59 -0700 Subject: [PATCH 238/617] enable clap's wrap_help feature This makes the output easier to read. --- Cargo.lock | 40 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0ec85d7c..66d91f80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,6 +449,7 @@ checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstyle", "clap_lex", + "terminal_size", ] [[package]] @@ -650,6 +651,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1391,6 +1402,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -2360,6 +2377,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.21.12" @@ -2787,6 +2817,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "thiserror" version = "1.0.61" diff --git a/Cargo.toml b/Cargo.toml index c2da8680..4e71bdb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,7 @@ axum-extra = { version = "0.9.3", features = ["typed-header"] } axum-server = { version = "0.6.0", features = ["tls-rustls"] } base64 = "0.22.1" bytes = "1.6.0" -clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string"] } +clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } futures-util = { version = "0.3.30", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" From d4b5f62bfe8f972f8dbbee5fcd8ea541b0621b10 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 20:31:35 -0700 Subject: [PATCH 239/617] simplify `is_admin` --- src/service/users.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/service/users.rs b/src/service/users.rs index fe0ac1ac..47277600 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -9,7 +9,6 @@ pub(crate) use data::Data; use ruma::{ api::client::{ device::Device, - error::ErrorKind, filter::FilterDefinition, sync::sync_events::{ self, @@ -20,7 +19,7 @@ use ruma::{ events::AnyToDeviceEvent, serde::Raw, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, - OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, UInt, UserId, + OwnedMxcUri, OwnedRoomId, OwnedUserId, UInt, UserId, }; use crate::{services, Error, Result}; @@ -259,20 +258,9 @@ impl Service { // Allowed because this function uses `services()` #[allow(clippy::unused_self)] pub(crate) fn is_admin(&self, user_id: &UserId) -> Result { - let admin_room_alias_id = RoomAliasId::parse(format!( - "#admins:{}", - services().globals.server_name() - )) - .map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias.") - })?; - let admin_room_id = services() - .rooms - .alias - .resolve_local_alias(&admin_room_alias_id)? - .unwrap(); - - services().rooms.state_cache.is_joined(user_id, &admin_room_id) + services().admin.get_admin_room()?.map_or(Ok(false), |admin_room_id| { + services().rooms.state_cache.is_joined(user_id, &admin_room_id) + }) } /// Create a new user account on this homeserver. From b0d85bb575b17ad74125220de42b72591cb938f2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 20:38:29 -0700 Subject: [PATCH 240/617] use `admin_bot_user_id` more Also change some terminology to be less weird. --- src/database.rs | 31 +++++++++---------------------- src/service/rooms/timeline.rs | 32 ++++++++++++-------------------- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/src/database.rs b/src/database.rs index a9f2abc6..f115fd14 100644 --- a/src/database.rs +++ b/src/database.rs @@ -529,21 +529,12 @@ impl KeyValueDatabase { // Matrix resource ownership is based on the server name; changing it // requires recreating the database from scratch. if services().users.count()? > 0 { - let grapevine_user = UserId::parse_with_server_name( - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name(), - ) - .expect("admin bot username should be valid"); - - if !services().users.exists(&grapevine_user)? { + let admin_bot = services().globals.admin_bot_user_id.as_ref(); + if !services().users.exists(admin_bot)? { error!( - "The {} server user does not exist, and the database is \ - not new.", - grapevine_user + "The {} admin bot does not exist, and the database is not \ + new.", + admin_bot ); return Err(Error::bad_database( "Cannot reuse an existing database after changing the \ @@ -1221,25 +1212,21 @@ impl KeyValueDatabase { /// Sets the emergency password and push rules for the @grapevine account in /// case emergency password is set fn set_emergency_access() -> Result { - let grapevine_user = UserId::parse_with_server_name( - "grapevine", - services().globals.server_name(), - ) - .expect("@grapevine:server_name is a valid UserId"); + let admin_bot = services().globals.admin_bot_user_id.as_ref(); services().users.set_password( - &grapevine_user, + admin_bot, services().globals.emergency_password().as_deref(), )?; let (ruleset, res) = match services().globals.emergency_password() { - Some(_) => (Ruleset::server_default(&grapevine_user), Ok(true)), + Some(_) => (Ruleset::server_default(admin_bot), Ok(true)), None => (Ruleset::new(), Ok(false)), }; services().account_data.update( None, - &grapevine_user, + admin_bot, GlobalAccountDataEventType::PushRules.to_string().into(), &serde_json::to_value(&GlobalAccountDataEvent { content: PushRulesEventContent { diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 2a0c8882..1cf176a5 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -516,33 +516,25 @@ impl Service { &body, )?; - let server_user = format!( - "@{}:{}", - if services().globals.config.conduit_compat { - "conduit" - } else { - "grapevine" - }, - services().globals.server_name() - ); + let admin_bot = &services().globals.admin_bot_user_id; - let to_grapevine = body - .starts_with(&format!("{server_user}: ")) - || body.starts_with(&format!("{server_user} ")) - || body == format!("{server_user}:") - || body == server_user; + let to_admin_bot = body + .starts_with(&format!("{admin_bot}: ")) + || body.starts_with(&format!("{admin_bot} ")) + || body == format!("{admin_bot}:") + || body == admin_bot.as_str(); - // This will evaluate to false if the emergency password is - // set up so that the administrator can - // execute commands as grapevine - let from_grapevine = pdu.sender == server_user + // This will evaluate to false if the emergency password + // is set up so that the administrator can execute commands + // as the admin bot + let from_admin_bot = &pdu.sender == admin_bot && services().globals.emergency_password().is_none(); if let Some(admin_room) = services().admin.get_admin_room()? { - if to_grapevine - && !from_grapevine + if to_admin_bot + && !from_admin_bot && admin_room == pdu.room_id { services().admin.process_message(body); From 76b060aa84d27b23c4931cbdfaf468444bfd59f4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 12 Jun 2024 20:48:06 -0700 Subject: [PATCH 241/617] only process admin commands if bot is in room This isn't exploitable in any way, it just meant that the bot could receive and run commands from users who were already in the admin room despite the bot not being in the room, which also means the bot would be unable to send response messages. Now, the bot will simply ignore admin room messages if the bot isn't in the admin room. --- src/service/rooms/timeline.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 1cf176a5..3fc590c4 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -536,6 +536,11 @@ impl Service { if to_admin_bot && !from_admin_bot && admin_room == pdu.room_id + && services() + .rooms + .state_cache + .is_joined(admin_bot, &admin_room) + .unwrap_or(false) { services().admin.process_message(body); } From b7ad00ef6e8891ea59146da0be747b74fe8ed96a Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 8 Jun 2024 12:21:07 -0700 Subject: [PATCH 242/617] distinct error types for running server This is in preparation for the config change to allow specifying multiple listeners, which will add several other possible error conditions. --- src/error.rs | 20 +++++++++++++++++++- src/main.rs | 20 +++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/error.rs b/src/error.rs index 2c57b021..bf09b836 100644 --- a/src/error.rs +++ b/src/error.rs @@ -48,7 +48,7 @@ pub(crate) enum Main { DatabaseError(#[source] crate::utils::error::Error), #[error("failed to serve requests")] - Serve(#[source] std::io::Error), + Serve(#[from] Serve), } /// Observability initialization errors @@ -97,3 +97,21 @@ pub(crate) enum ConfigSearch { #[error("no relevant configuration files found in XDG Base Directories")] NotFound, } + +/// Errors serving traffic +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum Serve { + #[error("failed to read TLS cert and key files at {certs:?} and {key:?}")] + LoadCerts { + certs: String, + key: String, + #[source] + err: std::io::Error, + }, + + #[error("failed to run request listener")] + Listen(#[source] std::io::Error), +} diff --git a/src/main.rs b/src/main.rs index bc318479..96fdbeef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ use std::{ future::Future, - io, net::SocketAddr, process::ExitCode, sync::{atomic, RwLock}, @@ -127,12 +126,14 @@ async fn try_main() -> Result<(), error::Main> { .map_err(Error::DatabaseError)?; info!("Starting server"); - run_server().await.map_err(Error::Serve)?; + run_server().await?; Ok(()) } -async fn run_server() -> io::Result<()> { +async fn run_server() -> Result<(), error::Serve> { + use error::Serve as Error; + let config = &services().globals.config; let addr = SocketAddr::from((config.address, config.port)); @@ -189,15 +190,20 @@ async fn run_server() -> io::Result<()> { match &config.tls { Some(tls) => { - let conf = - RustlsConfig::from_pem_file(&tls.certs, &tls.key).await?; + let conf = RustlsConfig::from_pem_file(&tls.certs, &tls.key) + .await + .map_err(|err| Error::LoadCerts { + certs: tls.certs.clone(), + key: tls.key.clone(), + err, + })?; let server = bind_rustls(addr, conf).handle(handle).serve(app); #[cfg(feature = "systemd")] sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) .expect("should be able to notify systemd"); - server.await?; + server.await.map_err(Error::Listen)?; } None => { let server = bind(addr).handle(handle).serve(app); @@ -206,7 +212,7 @@ async fn run_server() -> io::Result<()> { sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) .expect("should be able to notify systemd"); - server.await?; + server.await.map_err(Error::Listen)?; } } From f7d7952f9b49c2e0d8ab02eef927a923dfe088ad Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 7 Jun 2024 19:49:59 -0700 Subject: [PATCH 243/617] allow listening on multiple ports in config This is a config compatibility break. The ability to listen on multiple ports, including both TLS and non-TLS, is necessary for running complement against grapevine. --- nix/modules/default/default.nix | 21 ++++---- src/config.rs | 27 ++++++++-- src/error.rs | 9 ++++ src/main.rs | 87 +++++++++++++++++++++------------ 4 files changed, 99 insertions(+), 45 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 9c7d35a8..9118cfea 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -29,13 +29,6 @@ in type = types.submodule { freeformType = format.type; options = { - address = lib.mkOption { - type = types.nonEmptyStr; - description = '' - The local IP address to bind to. - ''; - default = "::1"; - }; conduit_compat = lib.mkOption { type = types.bool; description = '' @@ -56,12 +49,18 @@ in then "/var/lib/matrix-conduit" else "/var/lib/grapevine"; }; - port = lib.mkOption { - type = types.port; + listen = lib.mkOption { + type = types.listOf format.type; description = '' - The local port to bind to. + List of places to listen for incoming connections. ''; - default = 6167; + default = [ + { + type = "tcp"; + address = "::1"; + port = 6167; + } + ]; }; }; }; diff --git a/src/config.rs b/src/config.rs index 88399a46..bf5de8f9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,10 +25,8 @@ pub(crate) static DEFAULT_PATH: Lazy = pub(crate) struct Config { #[serde(default = "false_fn")] pub(crate) conduit_compat: bool, - #[serde(default = "default_address")] - pub(crate) address: IpAddr, - #[serde(default = "default_port")] - pub(crate) port: u16, + #[serde(default = "default_listen")] + pub(crate) listen: Vec, pub(crate) tls: Option, pub(crate) server_name: OwnedServerName, @@ -98,6 +96,19 @@ pub(crate) struct TlsConfig { pub(crate) key: String, } +#[derive(Debug, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub(crate) enum ListenConfig { + Tcp { + #[serde(default = "default_address")] + address: IpAddr, + #[serde(default = "default_port")] + port: u16, + #[serde(default = "false_fn")] + tls: bool, + }, +} + fn false_fn() -> bool { false } @@ -106,6 +117,14 @@ fn true_fn() -> bool { true } +fn default_listen() -> Vec { + vec![ListenConfig::Tcp { + address: default_address(), + port: default_port(), + tls: false, + }] +} + fn default_address() -> IpAddr { Ipv4Addr::LOCALHOST.into() } diff --git a/src/error.rs b/src/error.rs index bf09b836..c63f743b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -104,6 +104,15 @@ pub(crate) enum ConfigSearch { #[allow(missing_docs)] #[derive(Error, Debug)] pub(crate) enum Serve { + #[error("no listeners were specified in the configuration file")] + NoListeners, + + #[error( + "listener requested TLS, but no TLS cert was specified in the \ + configuration file. Please set 'tls.certs' and 'tls.key'" + )] + NoTlsCerts, + #[error("failed to read TLS cert and key files at {certs:?} and {key:?}")] LoadCerts { certs: String, diff --git a/src/main.rs b/src/main.rs index 96fdbeef..9f732360 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ use axum::{ use axum_server::{ bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, }; +use futures_util::FutureExt; use http::{ header::{self, HeaderName}, Method, StatusCode, Uri, @@ -26,14 +27,14 @@ use ruma::api::{ }, IncomingRequest, }; -use tokio::signal; +use tokio::{signal, task::JoinSet}; use tower::ServiceBuilder; use tower_http::{ cors::{self, CorsLayer}, trace::TraceLayer, ServiceBuilderExt as _, }; -use tracing::{debug, info, info_span, warn, Instrument}; +use tracing::{debug, error, info, info_span, warn, Instrument}; mod api; mod args; @@ -46,7 +47,7 @@ mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; use api::{client_server, server_server}; -pub(crate) use config::Config; +pub(crate) use config::{Config, ListenConfig}; pub(crate) use database::KeyValueDatabase; pub(crate) use service::{pdu::PduEvent, Services}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] @@ -135,7 +136,6 @@ async fn run_server() -> Result<(), error::Serve> { use error::Serve as Error; let config = &services().globals.config; - let addr = SocketAddr::from((config.address, config.port)); let x_requested_with = HeaderName::from_static("x-requested-with"); @@ -184,36 +184,60 @@ async fn run_server() -> Result<(), error::Serve> { .layer(axum::middleware::from_fn(observability::http_metrics_layer)); let app = routes(config).layer(middlewares).into_make_service(); - let handle = ServerHandle::new(); + let mut handles = Vec::new(); + let mut servers = JoinSet::new(); - tokio::spawn(shutdown_signal(handle.clone())); + let tls_config = if let Some(tls) = &config.tls { + Some(RustlsConfig::from_pem_file(&tls.certs, &tls.key).await.map_err( + |err| Error::LoadCerts { + certs: tls.certs.clone(), + key: tls.key.clone(), + err, + }, + )?) + } else { + None + }; - match &config.tls { - Some(tls) => { - let conf = RustlsConfig::from_pem_file(&tls.certs, &tls.key) - .await - .map_err(|err| Error::LoadCerts { - certs: tls.certs.clone(), - key: tls.key.clone(), - err, - })?; - let server = bind_rustls(addr, conf).handle(handle).serve(app); + if config.listen.is_empty() { + return Err(Error::NoListeners); + } - #[cfg(feature = "systemd")] - sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) - .expect("should be able to notify systemd"); - - server.await.map_err(Error::Listen)?; + for listen in &config.listen { + match listen { + ListenConfig::Tcp { + address, + port, + tls, + } => { + let addr = SocketAddr::from((*address, *port)); + let handle = ServerHandle::new(); + handles.push(handle.clone()); + let server = if *tls { + let tls_config = + tls_config.clone().ok_or(Error::NoTlsCerts)?; + bind_rustls(addr, tls_config) + .handle(handle) + .serve(app.clone()) + .left_future() + } else { + bind(addr).handle(handle).serve(app.clone()).right_future() + }; + servers.spawn(server); + } } - None => { - let server = bind(addr).handle(handle).serve(app); + } - #[cfg(feature = "systemd")] - sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) - .expect("should be able to notify systemd"); + #[cfg(feature = "systemd")] + sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) + .expect("should be able to notify systemd"); - server.await.map_err(Error::Listen)?; - } + tokio::spawn(shutdown_signal(handles)); + + while let Some(result) = servers.join_next().await { + result + .expect("should be able to join server task") + .map_err(Error::Listen)?; } Ok(()) @@ -456,7 +480,7 @@ fn routes(config: &Config) -> Router { } } -async fn shutdown_signal(handle: ServerHandle) { +async fn shutdown_signal(handles: Vec) { let ctrl_c = async { signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); }; @@ -480,10 +504,13 @@ async fn shutdown_signal(handle: ServerHandle) { } warn!("Received {}, shutting down...", sig); - handle.graceful_shutdown(Some(Duration::from_secs(30))); services().globals.shutdown(); + for handle in handles { + handle.graceful_shutdown(Some(Duration::from_secs(30))); + } + #[cfg(feature = "systemd")] sd_notify::notify(true, &[sd_notify::NotifyState::Stopping]) .expect("should be able to notify systemd"); From 4f041f91533c23f889b70aef39410a7c7bb612c0 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 8 Jun 2024 12:52:13 -0700 Subject: [PATCH 244/617] specify listener in error messages and logs The "listening for incoming traffic on ..." log line is new, but something I've wanted even when we only supported one listener. I considered getting rid of `clippy::too_many_lines` by factoring out the construction of `app` to a separate function, but found that specifying it's type (or relevant traits) got quite hairy. --- src/config.rs | 20 +++++++++++++++++++- src/error.rs | 10 ++++++---- src/main.rs | 17 +++++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/config.rs b/src/config.rs index bf5de8f9..af01282a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use std::{ borrow::Cow, + fmt::{self, Display}, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, }; @@ -96,7 +97,7 @@ pub(crate) struct TlsConfig { pub(crate) key: String, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub(crate) enum ListenConfig { Tcp { @@ -109,6 +110,23 @@ pub(crate) enum ListenConfig { }, } +impl Display for ListenConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ListenConfig::Tcp { + address, + port, + tls: false, + } => write!(f, "http://{address}:{port}"), + ListenConfig::Tcp { + address, + port, + tls: true, + } => write!(f, "https://{address}:{port}"), + } + } +} + fn false_fn() -> bool { false } diff --git a/src/error.rs b/src/error.rs index c63f743b..70f0124e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use std::{fmt, iter, path::PathBuf}; use thiserror::Error; +use crate::config::ListenConfig; + /// Formats an [`Error`][0] and its [`source`][1]s with a separator /// /// [0]: std::error::Error @@ -108,10 +110,10 @@ pub(crate) enum Serve { NoListeners, #[error( - "listener requested TLS, but no TLS cert was specified in the \ + "listener {0} requested TLS, but no TLS cert was specified in the \ configuration file. Please set 'tls.certs' and 'tls.key'" )] - NoTlsCerts, + NoTlsCerts(ListenConfig), #[error("failed to read TLS cert and key files at {certs:?} and {key:?}")] LoadCerts { @@ -121,6 +123,6 @@ pub(crate) enum Serve { err: std::io::Error, }, - #[error("failed to run request listener")] - Listen(#[source] std::io::Error), + #[error("failed to run request listener on {1}")] + Listen(#[source] std::io::Error, ListenConfig), } diff --git a/src/main.rs b/src/main.rs index 9f732360..33bbed81 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,6 +132,7 @@ async fn try_main() -> Result<(), error::Main> { Ok(()) } +#[allow(clippy::too_many_lines)] async fn run_server() -> Result<(), error::Serve> { use error::Serve as Error; @@ -204,6 +205,7 @@ async fn run_server() -> Result<(), error::Serve> { } for listen in &config.listen { + info!("Listening for incoming traffic on {listen}"); match listen { ListenConfig::Tcp { address, @@ -214,8 +216,9 @@ async fn run_server() -> Result<(), error::Serve> { let handle = ServerHandle::new(); handles.push(handle.clone()); let server = if *tls { - let tls_config = - tls_config.clone().ok_or(Error::NoTlsCerts)?; + let tls_config = tls_config + .clone() + .ok_or_else(|| Error::NoTlsCerts(listen.clone()))?; bind_rustls(addr, tls_config) .handle(handle) .serve(app.clone()) @@ -223,7 +226,9 @@ async fn run_server() -> Result<(), error::Serve> { } else { bind(addr).handle(handle).serve(app.clone()).right_future() }; - servers.spawn(server); + servers.spawn( + server.then(|result| async { (listen.clone(), result) }), + ); } } } @@ -235,9 +240,9 @@ async fn run_server() -> Result<(), error::Serve> { tokio::spawn(shutdown_signal(handles)); while let Some(result) = servers.join_next().await { - result - .expect("should be able to join server task") - .map_err(Error::Listen)?; + let (listen, result) = + result.expect("should be able to join server task"); + result.map_err(|err| Error::Listen(err, listen))?; } Ok(()) From a909e2079bd00af7e0cde55ad1752d56138a1d05 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 13 Jun 2024 19:01:37 -0700 Subject: [PATCH 245/617] config options for log format and color usage We want to be able to disable colors for complement logs (since they are likely to be opened in a text editor). There's no pressing need for alternative log formats, but I'm interested in whether the 'pretty' format will be easier for debugging. I chose to add 'log_*' options rather than making a separate 'log' section for now. There's been some discussion about trying to separate the tracing/logging stuff into more structured sections, but that can happen later. --- Cargo.lock | 13 +++++++++++++ Cargo.toml | 2 +- src/config.rs | 18 ++++++++++++++++++ src/observability.rs | 17 ++++++++++++++--- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66d91f80..29beb49a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3214,6 +3214,16 @@ dependencies = [ "web-time", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -3224,12 +3234,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4e71bdb4..d7682651 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,7 +139,7 @@ tower-http = { version = "0.5.2", features = ["add-extension", "cors", "sensitiv tracing = { version = "0.1.40", features = [] } tracing-flame = "0.2.0" tracing-opentelemetry = "0.24.0" -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } trust-dns-resolver = "0.23.2" xdg = "2.5.2" diff --git a/src/config.rs b/src/config.rs index af01282a..89b91dec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -77,6 +77,10 @@ pub(crate) struct Config { pub(crate) trusted_servers: Vec, #[serde(default = "default_log")] pub(crate) log: EnvFilterClone, + #[serde(default = "true_fn")] + pub(crate) log_colors: bool, + #[serde(default)] + pub(crate) log_format: LogFormat, #[serde(default)] pub(crate) turn_username: String, #[serde(default)] @@ -110,6 +114,20 @@ pub(crate) enum ListenConfig { }, } +#[derive(Copy, Clone, Default, Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum LogFormat { + /// Use the [`tracing_subscriber::fmt::format::Pretty`] formatter + Pretty, + /// Use the [`tracing_subscriber::fmt::format::Full`] formatter + #[default] + Full, + /// Use the [`tracing_subscriber::fmt::format::Compact`] formatter + Compact, + /// Use the [`tracing_subscriber::fmt::format::Json`] formatter + Json, +} + impl Display for ListenConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/observability.rs b/src/observability.rs index b6ded983..0204de0e 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -23,7 +23,11 @@ use tokio::time::Instant; use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; -use crate::{config::Config, error, utils::error::Result}; +use crate::{ + config::{Config, LogFormat}, + error, + utils::error::Result, +}; /// Globally accessible metrics state pub(crate) static METRICS: Lazy = Lazy::new(Metrics::new); @@ -119,8 +123,15 @@ pub(crate) fn init(config: &Config) -> Result { .transpose()? .unzip(); - let fmt_layer = tracing_subscriber::fmt::Layer::new() - .with_filter(EnvFilter::from(&config.log)); + let fmt_layer = + tracing_subscriber::fmt::Layer::new().with_ansi(config.log_colors); + let fmt_layer = match config.log_format { + LogFormat::Pretty => fmt_layer.pretty().boxed(), + LogFormat::Full => fmt_layer.boxed(), + LogFormat::Compact => fmt_layer.compact().boxed(), + LogFormat::Json => fmt_layer.json().boxed(), + }; + let fmt_layer = fmt_layer.with_filter(EnvFilter::from(&config.log)); let subscriber = Registry::default() .with(jaeger_layer) From 0b5a07d192c07f81afb9dec6fb410dd5d2651e2a Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 13 Jun 2024 00:31:35 -0700 Subject: [PATCH 246/617] add alternate !admin prefix for admin commands This is supported on conduwuit, and I liked it because it's faster for me to type than tab-completing the bot user, and because it makes it a little easier to copy-paste admin commands. --- src/service/rooms/timeline.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 3fc590c4..6f224e5a 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -522,7 +522,9 @@ impl Service { .starts_with(&format!("{admin_bot}: ")) || body.starts_with(&format!("{admin_bot} ")) || body == format!("{admin_bot}:") - || body == admin_bot.as_str(); + || body == admin_bot.as_str() + || body.starts_with("!admin ") + || body == "!admin"; // This will evaluate to false if the emergency password // is set up so that the administrator can execute commands From e318dfcb3da1f19f3c7838ff71500d66b9c2a74a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 14 Jun 2024 14:04:46 -0700 Subject: [PATCH 247/617] drop oci image outputs We don't support this anyway. OCI images will be needed for testing with Complement, but there are a bunch of other special requirements that has so these oci image outputs won't be very useful for that anyway. --- flake.nix | 9 --------- nix/pkgs/oci-image/default.nix | 25 ------------------------- 2 files changed, 34 deletions(-) delete mode 100644 nix/pkgs/oci-image/default.nix diff --git a/flake.nix b/flake.nix index 1d45525b..51c50679 100644 --- a/flake.nix +++ b/flake.nix @@ -21,8 +21,6 @@ inherit inputs; - oci-image = self.callPackage ./nix/pkgs/oci-image {}; - # Return a new scope with overrides applied to the 'default' package overrideDefaultPackage = args: self.overrideScope (final: prev: { default = prev.default.override args; @@ -50,7 +48,6 @@ { packages = { default = (mkScope pkgs).default; - oci-image = (mkScope pkgs).oci-image; } // builtins.listToAttrs @@ -73,12 +70,6 @@ name = binaryName; value = (mkScope pkgsCrossStatic).default; } - - # An output for an OCI image based on that binary - { - name = "oci-image-${crossSystem}"; - value = (mkScope pkgsCrossStatic).oci-image; - } ] ) [ diff --git a/nix/pkgs/oci-image/default.nix b/nix/pkgs/oci-image/default.nix deleted file mode 100644 index 8b359ce4..00000000 --- a/nix/pkgs/oci-image/default.nix +++ /dev/null @@ -1,25 +0,0 @@ -# Keep sorted -{ default -, dockerTools -, lib -, tini -}: - -dockerTools.buildImage { - name = default.pname; - tag = "next"; - copyToRoot = [ - dockerTools.caCertificates - ]; - config = { - # Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT) - # are handled as expected - Entrypoint = [ - "${lib.getExe' tini "tini"}" - "--" - ]; - Cmd = [ - "${lib.getExe default}" - ]; - }; -} From 9a5e552ca0f879dcb6c089bfe7607280086ae25d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 9 Jun 2024 12:52:45 -0700 Subject: [PATCH 248/617] set up mdbook This adds some new tools in CI for lint the book and also sets up automated deployment to GitLab Pages. Also adds a readme that suggests reading the book, since that's where all the information will be. --- .gitignore | 3 +++ .gitlab-ci.yml | 12 ++++++++++++ .lycheeignore | 1 + .markdownlintignore | 1 + README.md | 9 +++++++++ book.toml | 12 ++++++++++++ book/SUMMARY.md | 3 +++ book/introduction.md | 1 + engage.toml | 20 ++++++++++++++++++++ nix/shell.nix | 6 ++++++ 10 files changed, 68 insertions(+) create mode 120000 .lycheeignore create mode 120000 .markdownlintignore create mode 100644 README.md create mode 100644 book.toml create mode 100644 book/SUMMARY.md create mode 100644 book/introduction.md diff --git a/.gitignore b/.gitignore index a17d5fe5..4fba1aea 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ result* # GitLab CI cache /.gitlab-ci.d + +# mdbook artifacts +/public diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aba71d2e..71632f4a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ stages: - ci - artifacts + - deploy variables: # Makes some things print in color @@ -48,3 +49,14 @@ artifacts: image: nixos/nix:2.18.2 script: - ./bin/nix-build-and-cache packages + +pages: + stage: deploy + image: nixos/nix:2.18.2 + script: + - direnv exec . mdbook build + artifacts: + paths: + - public + only: + - main diff --git a/.lycheeignore b/.lycheeignore new file mode 120000 index 00000000..3e4e48b0 --- /dev/null +++ b/.lycheeignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.markdownlintignore b/.markdownlintignore new file mode 120000 index 00000000..3e4e48b0 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..3f4c2c9a --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Grapevine + +A Matrix homeserver. + +## Read the book + +[Click here to read the latest version.][0] + +[0]: https://matrix.pages.gitlab.computer.surgery/grapevine-fork/ diff --git a/book.toml b/book.toml new file mode 100644 index 00000000..f71ac6e0 --- /dev/null +++ b/book.toml @@ -0,0 +1,12 @@ +[book] +title = "Grapevine" +language = "en" +multilingual = false +src = "book" + +[build] +build-dir = "public" + +[output.html] +git-repository-icon = "fa-git-square" +git-repository-url = "https://gitlab.computer.surgery/matrix/grapevine-fork" diff --git a/book/SUMMARY.md b/book/SUMMARY.md new file mode 100644 index 00000000..6ed14891 --- /dev/null +++ b/book/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +* [Introduction](./introduction.md) diff --git a/book/introduction.md b/book/introduction.md new file mode 100644 index 00000000..e10b99d0 --- /dev/null +++ b/book/introduction.md @@ -0,0 +1 @@ +# Introduction diff --git a/engage.toml b/engage.toml index 19acec13..3e5d4417 100644 --- a/engage.toml +++ b/engage.toml @@ -30,6 +30,26 @@ name = "cargo-clippy" group = "versions" script = "cargo clippy -- --version" +[[task]] +name = "lychee" +group = "versions" +script = "lychee --version" + +[[task]] +name = "markdownlint" +group = "versions" +script = "markdownlint --version" + +[[task]] +name = "lychee" +group = "lints" +script = "lychee --offline ." + +[[task]] +name = "markdownlint" +group = "lints" +script = "markdownlint ." + [[task]] name = "cargo-fmt" group = "lints" diff --git a/nix/shell.nix b/nix/shell.nix index ebfe1dc6..354edf7a 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -3,6 +3,9 @@ , engage , inputs , jq +, lychee +, markdownlint-cli +, mdbook , mkShell , system , toolchain @@ -27,6 +30,9 @@ mkShell { # Keep sorted engage jq + lychee + markdownlint-cli + mdbook toolchain ] ++ From cfd3ca0d336b5973d4fcc8e96c87e3bc2fcb9234 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 16 Jun 2024 12:53:07 -0700 Subject: [PATCH 249/617] fill in the introduction section --- book/introduction.md | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/book/introduction.md b/book/introduction.md index e10b99d0..fa739d48 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -1 +1,80 @@ # Introduction + +Grapevine is a [Matrix][matrix] homeserver that was originally forked from +[Conduit 0.7.0][conduit]. + +[matrix]: https://matrix.org/ +[conduit]: https://gitlab.com/famedly/conduit/-/tree/v0.7.0?ref_type=tags + +## Goals + +Our goal is to provide a robust and reliable Matrix homeserver implementation. +In order to accomplish this, we aim to do the following: + +* Optimize for maintainability +* Implement automated testing to ensure correctness +* Improve instrumentation to provide real-world data to aid decision-making + +## Non-goals + +We also have some things we specifically want to avoid as we feel they inhibit +our ability to accomplish our goals: + +* macOS or Windows support + * These operating systems are very uncommon in the hobbyist server space, and + we feel our effort is better spent elsewhere. +* Docker support + * Docker tends to generate a high volume of support requests that are solely + due to Docker itself or how users are using Docker. In attempt to mitigate + this, we will not provide first-party Docker images. Instead, we'd recommend + avoiding Docker and either using our pre-built statically-linked binaries + or building from source. However, if your deployment mechanism *requires* + Docker, it should be straightforward to build your own Docker image. +* Configuration via environment variables + * Environment variables restrict the options for structuring configuration and + support for them would increase the maintenance burden. If your deployment + mechanism requires this, consider using an external tool like + [`envsubst`][envsubst]. +* Configuration compatibility with Conduit + * To provide a secure and ergonomic configuration experience, breaking changes + are required. However, we do intend to provide a migration tool to ease + migration; this feature is tracked [here][migration-tool]. +* Perfect database compatibility with Conduit + * The current database compatibility status can be tracked [here][db-compat]. + In the long run, it's inevitable that changes will be made to Conduit that + we won't want to pull in, or that we need to make changes that Conduit won't + want to pull in. + +[envsubst]: https://github.com/a8m/envsubst +[migration-tool]: https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/38 +[db-compat]: https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/17 + +## Project management + +The project's current maintainers[^1] are: + +| Matrix username | GitLab username | +|-|-| +| `@charles:computer.surgery` | `charles` | +| `@benjamin:computer.surgery` | `benjamin` | +| `@xiretza:xiretza.xyz` | `Lambda` | + +We would like to expand this list in the future as social trust is built and +technical competence is demonstrated by other contributors. + +We require at least 1 approving code review from a maintainer[^2] before changes +can be merged. This number may increase in the future as the list of maintainers +grows. + +## Expectations management + +This project is run and maintained entirely by volunteers who are doing their +best. Additionally, due to our goals, the development of new features may be +slower than alternatives. We find this to be an acceptable tradeoff considering +the importance of the reliability of a project like this. + +--- + +[^1]: A "maintainer" is someone who has the ability to close issues opened by + someone else and merge changes. +[^2]: A maintainer approving their own change doesn't count. From 9517cdc483a471e0721116094b327a60543eab36 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 16 Jun 2024 13:02:10 -0700 Subject: [PATCH 250/617] add a code of conduct --- book/SUMMARY.md | 1 + book/code-of-conduct.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 book/code-of-conduct.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 6ed14891..0ce8b211 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -1,3 +1,4 @@ # Summary * [Introduction](./introduction.md) +* [Code of conduct](./code-of-conduct.md) diff --git a/book/code-of-conduct.md b/book/code-of-conduct.md new file mode 100644 index 00000000..80239ce9 --- /dev/null +++ b/book/code-of-conduct.md @@ -0,0 +1,12 @@ +# Code of conduct + +We follow the [Rust Code of Conduct][rust-coc] with some extra points: + +* In the absence of evidence to suggest otherwise, assume good faith when + engaging with others +* Moderation actions may be taken for behavior observed outside of + project-specific spaces +* We have limited patience, so violations may skip the warning and directly + result in a ban + +[rust-coc]: https://www.rust-lang.org/policies/code-of-conduct From d07394a840f67c14825507a0b175c305450b011b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 16 Jun 2024 13:47:07 -0700 Subject: [PATCH 251/617] add a page about contributing in general I think it'd also be good to have a document explaining how to set up and work with the development environment for contributing code, but I think that should probably live on a separate, dedicated page. --- book/SUMMARY.md | 1 + book/contributing.md | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 book/contributing.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 0ce8b211..00f35581 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -2,3 +2,4 @@ * [Introduction](./introduction.md) * [Code of conduct](./code-of-conduct.md) +* [Contributing](./contributing.md) diff --git a/book/contributing.md b/book/contributing.md new file mode 100644 index 00000000..6eb64d38 --- /dev/null +++ b/book/contributing.md @@ -0,0 +1,47 @@ +# Contributing + +## On Matrix + +Currently, the Matrix room at [#grapevine:computer.surgery][room] serves +multiple purposes: + +* General discussion about the project, such as answering questions about it +* Reporting issues with Grapevine, if getting GitLab access is too much trouble + for you +* Providing support to users running Grapevine +* Discussing the development of Grapevine + +If you'd like to engage in or observe any of those things, please join! + +[room]: https://matrix.to/#/#grapevine:computer.surgery + +## On GitLab + +Instructions for getting GitLab access can be found on the [sign-in][sign-in] +page. + +GitLab access is primarily useful if you'd like to open issues, engage in +discussions on issues or merge requests, or submit your own merge requests. + +Note that if the sign-up process is too much trouble and you'd just +like to report an issue, feel free to report it in the Matrix room at +[#grapevine:computer.surgery][room]; someone with GitLab access can open an +issue on your behalf. + +[sign-in]: https://gitlab.computer.surgery/users/sign_in + +## Information about a vulnerability + +If you find a security vulnerability in Grapevine, please privately report it to +the Grapevine maintainers in one of the following ways: + +* Open a GitLab issue that's marked as confidential +* Create a private, invite-only, E2EE Matrix room and invite the following + users: + * `@benjamin:computer.surgery` + * `@charles:computer.surgery` + * `@xiretza:xiretza.xyz` + +If the maintainers determine that the vulnerability is shared with Conduit or +other forks, we'll work with their teams to ensure that all affected projects +can release a fix at the same time. From 08cd8f19e3158b4c407cf87f400940684e0cd15c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 16 Jun 2024 17:45:35 -0700 Subject: [PATCH 252/617] add and backfill changelog --- book/SUMMARY.md | 1 + book/changelog.md | 170 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 book/changelog.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 00f35581..478d6135 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -3,3 +3,4 @@ * [Introduction](./introduction.md) * [Code of conduct](./code-of-conduct.md) * [Contributing](./contributing.md) +* [Changelog](./changelog.md) diff --git a/book/changelog.md b/book/changelog.md new file mode 100644 index 00000000..faf1b2fc --- /dev/null +++ b/book/changelog.md @@ -0,0 +1,170 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog][keep-a-changelog], and this project +adheres to [Semantic Versioning][semver]. + +[keep-a-changelog]: https://keepachangelog.com/en/1.0.0/ +[semver]: https://semver.org/spec/v2.0.0.html + + + +## Unreleased + + + +This will be the first release of Grapevine since it was forked from Conduit +0.7.0. + +### Security + +1. Prevent XSS via user-uploaded media. + ([!8](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/8)) +2. Switch from incorrect, hand-rolled `X-Matrix` `Authorization` parser to the + much better implementation provided by Ruma. + ([!31](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/31)) + * This is not practically exploitable to our knowledge, but this change does + reduce risk. +3. Switch to a more trustworthy password hashing library. + ([!29](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/29)) + * This is not practically exploitable to our knowledge, but this change does + reduce risk. +4. Don't return redacted events from the search endpoint. + ([!41 (f74043d)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=f74043df9aa59b406b5086c2e9fa2791a31aa41b), + [!41 (83cdc9c)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=83cdc9c708cd7b50fe1ab40ea6a68dcf252c190b)) +5. Prevent impersonation in EDUs. + ([!41 (da99b07)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=da99b0706e683a2d347768efe5b50676abdf7b44)) + * `m.signing_key_update` was not affected by this bug. +6. Verify PDUs and transactions against the temporally-correct signing keys. + ([!41 (9087da9)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=9087da91db8585f34d026a48ba8fdf64865ba14d)) +7. Only allow the admin bot to change the room ID that the admin room alias + points to. + ([!42](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/42)) + +### Removed + +1. Remove update checker. + ([17a0b34](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/17a0b3430934fbb8370066ee9dc3506102c5b3f6)) +2. Remove optional automatic display name emoji for newly registered users. + ([cddf699](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/cddf6991f280008b5af5acfab6a9719bb0cfb7f1)) +3. Remove admin room welcome message on first startup. + ([c9945f6](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/c9945f6bbac6e22af6cf955cfa99826d4b04fe8c)) +4. Remove incomplete presence implementation. + ([f27941d](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/f27941d5108acda250921c6a58499a46568fd030)) +5. Remove Debian packaging. + ([d41f0fb](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/d41f0fbf72dae6562358173f425d23bb0e174ca2)) +6. Remove Docker packaging. + ([!48](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/48)) + +### Changed + +1. **BREAKING:** Rename `conduit_cache_capacity_modifier` configuration option + to `cache_capacity_modifier`. + ([5619d7e](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/5619d7e3180661731800e253b558b88b407d2ae7)) + * If you are explicitly setting this configuration option, make sure to + change its name before updating. +2. **BREAKING:** Rename Conduit to Grapevine. + ([360e020](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/360e020b644bd012ed438708b661a25fbd124f68)) + * The `CONDUIT_VERSION_EXTRA` build-time environment variable has been + renamed to `GRAPEVINE_VERSION_EXTRA`. This change only affects distribution + packagers or non-Nix users who are building from source. If you fall into + one of those categories *and* were explicitly setting this environment + variable, make sure to change its name before building Grapevine. +3. **BREAKING:** Change the default port from 8000 to 6167. + ([f205280](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/f2052805201f0685d850592b1c96f4861c58fb22)) + * If you relied on the default port being 8000, either update your other + configuration to use the new port, or explicitly configure Grapevine's port + to 8000. +4. Improve tracing spans and events. + ([!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) + (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), + [!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) + (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), + [!11 (f556fce)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=f556fce73eb7beec2ed7b1781df0acdf47920d9c) + (merged as [ac42e0b](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/ac42e0bfff6af8677636a3dc1a56701a3255071d)), + [!18](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/18), + [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26)) +5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. + ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) +6. **BREAKING:** Allow federation by default. + ([!24](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/24)) + * If you relied on federation being disabled by default, make sure to + explicitly disable it before upgrading. +7. **BREAKING:** Remove the `[global]` section from the configuration file. + ([!38](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/38)) + * Details on how to migrate can be found in the merge request's description. +8. **BREAKING:** Allow specifying multiple transport listeners in the + configuration file. + ([!39](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/39)) + * Details on how to migrate can be found in the merge request's description. + +### Fixed + +1. Fix questionable numeric conversions. + ([71c48f6](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/71c48f66c4922813c2dc30b7b875200e06ce4b75)) +2. Stop sending no-longer-valid cached responses from the + `/_matrix/client/{r0,v3}/sync` endpoints. + ([!7](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/7)) +3. Stop returning extra E2EE device updates from `/_matrix/client/{r0,v3}/sync` + as that violates the specification. + ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) +4. Make certain membership state transitions work correctly again. + ([!16](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/16)) + * For example, it was previously impossible to unban users from rooms. +5. Ensure that `tracing-flame` flushes all its data before the process exits. + ([!20 (263edcc)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/20/diffs?commit_id=263edcc8a127ad2a541a3bb6ad35a8a459ea5616)) +6. Reduce the likelihood of locking up the async runtime. + ([!19](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/19)) +7. Fix dynamically linked jemalloc builds. + ([!23](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/23)) +8. Fix search results not including subsequent pages in certain situations. + ([!35 (0cdf032)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/35/diffs?commit_id=0cdf03288ab8fa363c313bd929c8b5183d14ab77)) +9. Fix search results missing events in subsequent pages in certain situations. + ([!35 (3551a6e)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/35/diffs?commit_id=3551a6ef7a29219b9b30f50a7e8c92b92debcdcf)) +10. Only process admin commands if the admin bot is in the admin room. + ([!43](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/43)) + +### Added + +1. Add various conveniences for users of the Nix package. + ([51f9650](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/51f9650ca7bc9378690d331192c85fea3c151b58), + [bbb1a6f](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/bbb1a6fea45b16e8d4f94c1afbf7fa22c9281f37)) +2. Add a NixOS module. + ([33e7a46](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/33e7a46b5385ea9035c9d13c6775d63e5626a4c7)) +3. Add a Conduit compat mode. + ([a25f2ec](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/a25f2ec95045c5620c98eead88197a0bf13e6bb3)) + * **BREAKING:** If you're migrating from Conduit, this option must be enabled + or else your homeserver will refuse to start. +4. Include `GRAPEVINE_VERSION_EXTRA` information in the + `/_matrix/federation/v1/version` endpoint. + ([509b70b](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/509b70bd827fec23b88e223b57e0df3b42cede34)) +5. Allow multiple tracing subscribers to be active at once. + ([!20 (7a154f74)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/20/diffs?commit_id=7a154f74166c1309ca5752149e02bbe44cd91431)) +6. Allow configuring the filter for `tracing-flame`. + ([!20 (507de06)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/20/diffs?commit_id=507de063f53f52e0cf8e2c1a67215a5ad87bb35a)) +7. Collect HTTP response time metrics via OpenTelemetry and optionally expose + them as Prometheus metrics. This functionality is disabled by default. + ([!22](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/22)) +8. Collect metrics for lookup results (e.g. cache hits/misses). + ([!15](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/15), + [!36](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/36)) +9. Add configuration options for controlling the log format and colors. + ([!46](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/46)) +10. Recognize the `!admin` prefix to invoke admin commands. + ([!45](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/45)) From 6aca12854760cd5c1b75beeed2b1a875f2f5db1a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 23 Jun 2024 19:59:06 -0700 Subject: [PATCH 253/617] generalize documentation, make the name shorter It's more useful than just *debug* logs, but yes, should not be relied upon for logic reasons. --- src/service/admin.rs | 8 ++++---- src/utils.rs | 30 ++++++++++++------------------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 76b6dc15..669c9f49 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -34,7 +34,7 @@ use super::pdu::PduBuilder; use crate::{ api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH}, services, - utils::{self, truncate_str_for_debug}, + utils::{self, dbg_truncate_str}, Error, PduEvent, Result, }; @@ -287,7 +287,7 @@ impl Service { #[tracing::instrument( skip(self, room_message), fields( - room_message = truncate_str_for_debug(&room_message, 50).as_ref(), + room_message = dbg_truncate_str(&room_message, 50).as_ref(), ), )] pub(crate) fn process_message(&self, room_message: String) { @@ -306,7 +306,7 @@ impl Service { #[tracing::instrument( skip(self, room_message), fields( - room_message = truncate_str_for_debug(&room_message, 50).as_ref(), + room_message = dbg_truncate_str(&room_message, 50).as_ref(), ), )] async fn process_admin_message( @@ -357,7 +357,7 @@ impl Service { #[tracing::instrument( skip(command_line), fields( - command_line = truncate_str_for_debug(command_line, 50).as_ref(), + command_line = dbg_truncate_str(command_line, 50).as_ref(), ), )] fn parse_admin_command( diff --git a/src/utils.rs b/src/utils.rs index 5b59c8aa..3bd0319b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -228,11 +228,8 @@ pub(crate) fn debug_slice_truncated( /// Truncates a string to an approximate maximum length, replacing any extra /// text with an ellipsis. /// -/// Only to be used for debug logging, exact semantics are unspecified. -pub(crate) fn truncate_str_for_debug( - s: &str, - mut max_len: usize, -) -> Cow<'_, str> { +/// Only to be used for informational purposes, exact semantics are unspecified. +pub(crate) fn dbg_truncate_str(s: &str, mut max_len: usize) -> Cow<'_, str> { while max_len < s.len() && !s.is_char_boundary(max_len) { max_len += 1; } @@ -247,21 +244,18 @@ pub(crate) fn truncate_str_for_debug( #[cfg(test)] mod tests { - use crate::utils::truncate_str_for_debug; + use crate::utils::dbg_truncate_str; #[test] - fn test_truncate_str_for_debug() { - assert_eq!(truncate_str_for_debug("short", 10), "short"); - assert_eq!( - truncate_str_for_debug("very long string", 10), - "very long ..." - ); - assert_eq!(truncate_str_for_debug("no info, only dots", 0), "..."); - assert_eq!(truncate_str_for_debug("", 0), ""); - assert_eq!(truncate_str_for_debug("unicöde", 5), "unicö..."); + fn test_truncate_str() { + assert_eq!(dbg_truncate_str("short", 10), "short"); + assert_eq!(dbg_truncate_str("very long string", 10), "very long ..."); + assert_eq!(dbg_truncate_str("no info, only dots", 0), "..."); + assert_eq!(dbg_truncate_str("", 0), ""); + assert_eq!(dbg_truncate_str("unicöde", 5), "unicö..."); let ok_hand = "👌🏽"; - assert_eq!(truncate_str_for_debug(ok_hand, 1), "👌..."); - assert_eq!(truncate_str_for_debug(ok_hand, ok_hand.len() - 1), "👌🏽"); - assert_eq!(truncate_str_for_debug(ok_hand, ok_hand.len()), "👌🏽"); + assert_eq!(dbg_truncate_str(ok_hand, 1), "👌..."); + assert_eq!(dbg_truncate_str(ok_hand, ok_hand.len() - 1), "👌🏽"); + assert_eq!(dbg_truncate_str(ok_hand, ok_hand.len()), "👌🏽"); } } From 12b0fb7f91e8483e9a018d1e3b8b8d6f6659328c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 23 Jun 2024 16:16:59 -0700 Subject: [PATCH 254/617] don't write KBs of html to the logs Handing this to tracing as a String makes it automatically escape newlines and such. --- src/api/server_server.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index cd0386b8..f1fdd412 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -69,7 +69,9 @@ use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, observability::{FoundIn, Lookup, METRICS}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, Ar, Error, PduEvent, Ra, Result, + services, utils, + utils::dbg_truncate_str, + Ar, Error, PduEvent, Ra, Result, }; /// Wraps either an literal IP address plus port, or a hostname plus complement @@ -281,10 +283,11 @@ where if status != 200 { warn!( status = u16::from(status), - response = String::from_utf8_lossy(&body) - .lines() - .collect::>() - .join(" "), + response = dbg_truncate_str( + String::from_utf8_lossy(&body).as_ref(), + 100, + ) + .into_owned(), "Received error over federation", ); } From 601c2ed3e557f958cee6cea150a6cf69ee7c91d2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 18 Jun 2024 22:54:33 -0700 Subject: [PATCH 255/617] clean up shutdown events The shutdown function is called in exactly one place, and that place has a better log message, so we'll just delete the extra one. --- src/main.rs | 2 +- src/service/globals.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 33bbed81..4f1d4b7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -508,7 +508,7 @@ async fn shutdown_signal(handles: Vec) { () = terminate => { sig = "SIGTERM"; }, } - warn!("Received {}, shutting down...", sig); + warn!(signal = %sig, "shutting down due to signal"); services().globals.shutdown(); diff --git a/src/service/globals.rs b/src/service/globals.rs index bed3242b..f83f1f79 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -29,7 +29,7 @@ use ruma::{ UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; -use tracing::{error, info, Instrument}; +use tracing::{error, Instrument}; use trust_dns_resolver::TokioAsyncResolver; use crate::{api::server_server::FedDest, services, Config, Error, Result}; @@ -509,8 +509,6 @@ impl Service { pub(crate) fn shutdown(&self) { self.shutdown.store(true, atomic::Ordering::Relaxed); - // On shutdown - info!(target: "shutdown-sync", "Received shutdown notification, notifying sync helpers..."); services().globals.rotate.fire(); } } From 230172718ff435e31d87b0f8c69b893b5df42848 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 22 Jun 2024 11:32:37 -0700 Subject: [PATCH 256/617] demote event from info to debug And also make it structured instead of stringified. --- src/service/rooms/event_handler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 6466340a..1880f997 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -588,7 +588,10 @@ impl Service { )); } - info!("Upgrading {} to timeline pdu", incoming_pdu.event_id); + debug!( + event_id = %incoming_pdu.event_id, + "Upgrading event to timeline pdu", + ); let create_event_content: RoomCreateEventContent = serde_json::from_str(create_event.content.get()).map_err(|e| { From 573fac553c74b50eddac4b00a5f6b96a0742d15a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 22 Jun 2024 16:21:39 -0700 Subject: [PATCH 257/617] promote request error event from info to warn And also make it structured. --- src/utils/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/error.rs b/src/utils/error.rs index 3b165767..eabfd943 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -9,7 +9,7 @@ use ruma::{ OwnedServerName, }; use thiserror::Error; -use tracing::{error, info}; +use tracing::{error, warn}; use crate::Ra; @@ -149,7 +149,7 @@ impl Error { _ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR), }; - info!("Returning an error: {}: {}", status_code, message); + warn!(%status_code, error = %message, "Responding with an error"); Ra(UiaaResponse::MatrixError(RumaError { body: ErrorBody::Standard { From 82cc605b5f7abdef070b5f85d30e62ae93f41900 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 22 Jun 2024 16:53:23 -0700 Subject: [PATCH 258/617] reflow macro --- src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 4f1d4b7d..2128f1e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -153,7 +153,12 @@ async fn run_server() -> Result<(), error::Serve> { request.uri().path() }; - tracing::info_span!("http_request", otel.name = path, %path, method = %request.method()) + tracing::info_span!( + "http_request", + otel.name = path, + %path, + method = %request.method(), + ) }, )) .layer(axum::middleware::from_fn(unrecognized_method)) From e83a30af4ba0c5e5a729733b3cfae1b5ec2513ce Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 23 Jun 2024 17:41:53 -0700 Subject: [PATCH 259/617] reduce duplicate events I hate `log_error`. A better way to do this would be to not reuse the same error type literally everywhere, so you could distinguish, in `crate::service::sending::Service::handle_response`, whether to emit an event based on which function created the error. Fixing that is a lot more work, though. --- src/api/server_server.rs | 13 ++++++++----- src/service/sending.rs | 7 ++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index f1fdd412..b3f07300 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -130,10 +130,11 @@ impl FedDest { } } -#[tracing::instrument(skip(request), fields(url))] +#[tracing::instrument(skip(request, log_error), fields(url))] pub(crate) async fn send_request( destination: &ServerName, request: T, + log_error: bool, ) -> Result where T: OutgoingRequest + Debug, @@ -330,10 +331,12 @@ where } } Err(e) => { - warn!( - error = %e, - "Could not send request", - ); + if log_error { + warn!( + error = %e, + "Could not send request", + ); + } Err(e.into()) } } diff --git a/src/service/sending.rs b/src/service/sending.rs index 03593208..c46f26d7 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -286,8 +286,8 @@ impl Service { })) } } - Err(_err) => { - warn!("Marking transaction as failed"); + Err(error) => { + warn!(%error, "Marking transaction as failed"); current_transaction_status.entry(destination).and_modify(|e| { *e = match e { TransactionStatus::Running => { @@ -684,7 +684,7 @@ impl Service { debug!("Got permit"); let response = tokio::time::timeout( Duration::from_secs(2 * 60), - server_server::send_request(destination, request), + server_server::send_request(destination, request, true), ) .await .map_err(|_| { @@ -920,6 +920,7 @@ async fn handle_federation_event( )) .into(), }, + false, ) .await?; From 32e6b3b039a95fa3af3e90be680a0d610edbefd2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 24 Jun 2024 12:43:00 -0700 Subject: [PATCH 260/617] don't log twice, make event structured --- src/service/rooms/timeline.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 6f224e5a..038f71e9 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -786,9 +786,9 @@ impl Service { None::, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) - .map_err(|e| { - error!("{:?}", e); - Error::bad_database("Auth check failed.") + .map_err(|error| { + error!(%error, "Auth check failed"); + Error::BadDatabase("Auth check failed.") })?; if !auth_check { From 1b51e0beecbf09867d27f57f86081abd4ecc61b2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 18 Jun 2024 22:17:20 -0700 Subject: [PATCH 261/617] increase the default log level The vast majority of spans are at the info level, so increasing the log level to info will greatly increase the amount of (useful!) inforamtion included in the logs. However, `ruma_state_res` generates a substantial amount of logs, so that one gets to stay fixed at `warn` for now. --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 89b91dec..5792b8b8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -210,7 +210,7 @@ fn default_trusted_servers() -> Vec { } fn default_log() -> EnvFilterClone { - "warn,state_res=warn,_=off" + "info,ruma_state_res=warn" .parse() .expect("hardcoded env filter should be valid") } From f2e5b281c9bc47b7a7701be35ab24ec0d5d4bd1a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 22 Jun 2024 16:55:37 -0700 Subject: [PATCH 262/617] include method in otel.name for incoming requests Also change the name of the field from path to endpoint since it's not the exact request path, and swap the order of method and endpoint. --- src/main.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2128f1e0..657a7a36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,19 +145,21 @@ async fn run_server() -> Result<(), error::Serve> { .layer(axum::middleware::from_fn(spawn_task)) .layer(TraceLayer::new_for_http().make_span_with( |request: &http::Request<_>| { - let path = if let Some(path) = + let endpoint = if let Some(endpoint) = request.extensions().get::() { - path.as_str() + endpoint.as_str() } else { request.uri().path() }; + let method = request.method(); + tracing::info_span!( "http_request", - otel.name = path, - %path, - method = %request.method(), + otel.name = format!("{method} {endpoint}"), + %method, + %endpoint, ) }, )) From e13db834ed9e94cc2e860a504f77fba06fbd3bec Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 23 Jun 2024 17:56:05 -0700 Subject: [PATCH 263/617] refactor handle_response in service/sending Early returns good. --- src/service/sending.rs | 86 +++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index c46f26d7..095f5801 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -256,57 +256,47 @@ impl Service { Span::current().record("error", e.to_string()); } - match result { - Ok(()) => { - self.db.delete_all_active_requests_for(&destination)?; + if let Err(error) = result { + warn!(%error, "Marking transaction as failed"); + current_transaction_status.entry(destination).and_modify(|e| { + use TransactionStatus::{Failed, Retrying, Running}; - // Find events that have been added since starting the - // last request - let new_events = self - .db - .queued_requests(&destination) - .filter_map(Result::ok) - .take(30) - .collect::>(); - - if new_events.is_empty() { - current_transaction_status.remove(&destination); - Ok(None) - } else { - // Insert pdus we found - self.db.mark_as_active(&new_events)?; - - Ok(Some(HandlerInputs { - destination: destination.clone(), - events: new_events - .into_iter() - .map(|(event, _)| event) - .collect(), - requester_span: None, - })) - } - } - Err(error) => { - warn!(%error, "Marking transaction as failed"); - current_transaction_status.entry(destination).and_modify(|e| { - *e = match e { - TransactionStatus::Running => { - TransactionStatus::Failed(1, Instant::now()) - } - TransactionStatus::Retrying(n) => { - TransactionStatus::Failed(*n + 1, Instant::now()) - } - TransactionStatus::Failed(..) => { - error!( - "Request that was not even running failed?!" - ); - return; - } + *e = match e { + Running => Failed(1, Instant::now()), + Retrying(n) => Failed(*n + 1, Instant::now()), + Failed(..) => { + error!("Request that was not even running failed?!"); + return; } - }); - Ok(None) - } + } + }); + return Ok(None); } + + self.db.delete_all_active_requests_for(&destination)?; + + // Find events that have been added since starting the + // last request + let new_events = self + .db + .queued_requests(&destination) + .filter_map(Result::ok) + .take(30) + .collect::>(); + + if new_events.is_empty() { + current_transaction_status.remove(&destination); + return Ok(None); + } + + // Insert pdus we found + self.db.mark_as_active(&new_events)?; + + Ok(Some(HandlerInputs { + destination: destination.clone(), + events: new_events.into_iter().map(|(event, _)| event).collect(), + requester_span: None, + })) } #[tracing::instrument( From cb036593eac18e97ad0dc1873913f0ba0582bbcb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 23 Jun 2024 18:20:21 -0700 Subject: [PATCH 264/617] refactor send_request in api/server_server Seriously, what is going on with the control flow in this codebase? --- src/api/server_server.rs | 135 +++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 78 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index b3f07300..1f9353ab 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -258,88 +258,67 @@ where let response = services().globals.federation_client().execute(reqwest_request).await; - match response { - Ok(mut response) => { - // reqwest::Response -> http::Response conversion - let status = response.status(); - debug!(status = u16::from(status), "Received response"); - let mut http_response_builder = http::Response::builder() - .status(status) - .version(response.version()); - mem::swap( - response.headers_mut(), - http_response_builder - .headers_mut() - .expect("http::response::Builder is usable"), - ); + let mut response = response.inspect_err(|error| { + if log_error { + warn!(%error, "Could not send request"); + } + })?; - debug!("Getting response bytes"); - // TODO: handle timeout - let body = response.bytes().await.unwrap_or_else(|e| { - warn!("server error {}", e); - Vec::new().into() - }); - debug!("Got response bytes"); + // reqwest::Response -> http::Response conversion + let status = response.status(); + debug!(status = u16::from(status), "Received response"); + let mut http_response_builder = + http::Response::builder().status(status).version(response.version()); + mem::swap( + response.headers_mut(), + http_response_builder + .headers_mut() + .expect("http::response::Builder is usable"), + ); - if status != 200 { - warn!( - status = u16::from(status), - response = dbg_truncate_str( - String::from_utf8_lossy(&body).as_ref(), - 100, - ) + debug!("Getting response bytes"); + // TODO: handle timeout + let body = response.bytes().await.unwrap_or_else(|e| { + warn!("server error {}", e); + Vec::new().into() + }); + debug!("Got response bytes"); + + if status != 200 { + warn!( + status = u16::from(status), + response = + dbg_truncate_str(String::from_utf8_lossy(&body).as_ref(), 100) .into_owned(), - "Received error over federation", - ); - } - - let http_response = http_response_builder - .body(body) - .expect("reqwest body is valid http body"); - - if status == 200 { - debug!("Parsing response bytes"); - let response = - T::IncomingResponse::try_from_http_response(http_response); - if response.is_ok() && write_destination_to_cache { - METRICS.record_lookup( - Lookup::FederationDestination, - FoundIn::Remote, - ); - services() - .globals - .actual_destination_cache - .write() - .await - .insert( - OwnedServerName::from(destination), - (actual_destination, host), - ); - } - - response.map_err(|e| { - warn!(error = %e, "Invalid 200 response",); - Error::BadServerResponse( - "Server returned bad 200 response.", - ) - }) - } else { - Err(Error::Federation( - destination.to_owned(), - RumaError::from_http_response(http_response), - )) - } - } - Err(e) => { - if log_error { - warn!( - error = %e, - "Could not send request", - ); - } - Err(e.into()) - } + "Received error over federation", + ); } + + let http_response = http_response_builder + .body(body) + .expect("reqwest body is valid http body"); + + if status != 200 { + return Err(Error::Federation( + destination.to_owned(), + RumaError::from_http_response(http_response), + )); + } + + debug!("Parsing response bytes"); + let response = T::IncomingResponse::try_from_http_response(http_response); + if response.is_ok() && write_destination_to_cache { + METRICS.record_lookup(Lookup::FederationDestination, FoundIn::Remote); + services().globals.actual_destination_cache.write().await.insert( + OwnedServerName::from(destination), + (actual_destination, host), + ); + } + + response.map_err(|e| { + warn!(error = %e, "Invalid 200 response"); + Error::BadServerResponse("Server returned bad 200 response.") + }) } fn get_ip_with_port(destination_str: &str) -> Option { From 027ff907db0a66c887e27d533bfcde67cf3a917b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 22 Jun 2024 17:09:53 -0700 Subject: [PATCH 265/617] fix changelog indentation Whoops. --- book/changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index faf1b2fc..c86da4d3 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -107,12 +107,12 @@ This will be the first release of Grapevine since it was forked from Conduit * If you relied on federation being disabled by default, make sure to explicitly disable it before upgrading. 7. **BREAKING:** Remove the `[global]` section from the configuration file. - ([!38](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/38)) - * Details on how to migrate can be found in the merge request's description. + ([!38](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/38)) + * Details on how to migrate can be found in the merge request's description. 8. **BREAKING:** Allow specifying multiple transport listeners in the - configuration file. - ([!39](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/39)) - * Details on how to migrate can be found in the merge request's description. + configuration file. + ([!39](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/39)) + * Details on how to migrate can be found in the merge request's description. ### Fixed From 6fb9abed6218b3ced6c14cedfaca5c4094e88bc8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 22 Jun 2024 17:11:17 -0700 Subject: [PATCH 266/617] update changelog --- book/changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index c86da4d3..b932a22f 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -99,7 +99,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!11 (f556fce)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=f556fce73eb7beec2ed7b1781df0acdf47920d9c) (merged as [ac42e0b](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/ac42e0bfff6af8677636a3dc1a56701a3255071d)), [!18](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/18), - [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26)) + [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26), + [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. @@ -113,6 +114,8 @@ This will be the first release of Grapevine since it was forked from Conduit configuration file. ([!39](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/39)) * Details on how to migrate can be found in the merge request's description. +9. Increase default log level so that span information is included. + ([!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50)) ### Fixed From b11cbb699111d5a82e0bc0dde87c9569a83c36e6 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 24 Jun 2024 19:59:11 +0000 Subject: [PATCH 267/617] Instrument rocksdb functions at TRACE level This allows e.g. aggregate time statistics if you really care about it by adding grapevine::database::abstraction::rocksdb=trace to the tracing filter. --- book/changelog.md | 3 ++- src/database/abstraction/rocksdb.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index b932a22f..88dbd54c 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -100,7 +100,8 @@ This will be the first release of Grapevine since it was forked from Conduit (merged as [ac42e0b](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/ac42e0bfff6af8677636a3dc1a56701a3255071d)), [!18](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/18), [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26), - [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50)) + [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50), + [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 9abfc44d..afcc4ef5 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -11,6 +11,7 @@ use rocksdb::{ DBRecoveryMode, DBWithThreadMode, Direction, IteratorMode, MultiThreaded, Options, ReadOptions, WriteOptions, }; +use tracing::Level; use super::{ super::Config, watchers::Watchers, KeyValueDatabaseEngine, KvTree, @@ -165,12 +166,14 @@ impl RocksDbEngineTree<'_> { } impl KvTree for RocksDbEngineTree<'_> { + #[tracing::instrument(level = Level::TRACE, skip_all)] fn get(&self, key: &[u8]) -> Result>> { let readoptions = ReadOptions::default(); Ok(self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> { let writeoptions = WriteOptions::default(); let lock = self.write_lock.read().unwrap(); @@ -182,6 +185,7 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(()) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn insert_batch( &self, iter: &mut dyn Iterator, Vec)>, @@ -194,11 +198,13 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(()) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn remove(&self, key: &[u8]) -> Result<()> { let writeoptions = WriteOptions::default(); Ok(self.db.rocks.delete_cf_opt(&self.cf(), key, &writeoptions)?) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn iter<'a>(&'a self) -> Box, Vec)> + 'a> { let readoptions = ReadOptions::default(); @@ -211,6 +217,7 @@ impl KvTree for RocksDbEngineTree<'_> { ) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn iter_from<'a>( &'a self, from: &[u8], @@ -238,6 +245,7 @@ impl KvTree for RocksDbEngineTree<'_> { ) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn increment(&self, key: &[u8]) -> Result> { let readoptions = ReadOptions::default(); let writeoptions = WriteOptions::default(); @@ -252,6 +260,7 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(new) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn increment_batch( &self, iter: &mut dyn Iterator>, @@ -273,6 +282,7 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(()) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn scan_prefix<'a>( &'a self, prefix: Vec, @@ -293,6 +303,7 @@ impl KvTree for RocksDbEngineTree<'_> { ) } + #[tracing::instrument(level = Level::TRACE, skip_all)] fn watch_prefix<'a>( &'a self, prefix: &[u8], From 3ca6d7776b795c3923299492f5991896dbe7fce8 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 17 Jun 2024 20:18:30 -0700 Subject: [PATCH 268/617] better log message for auth chain room id mismatch The previous "Evil event in db" message does not indicate what's going on, and does not help identify *which* events are causing the problem. --- src/service/rooms/auth_chain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 4a551953..98d19662 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -154,9 +154,10 @@ impl Service { match services().rooms.timeline.get_pdu(&event_id) { Ok(Some(pdu)) => { if pdu.room_id != room_id { + warn!(bad_room_id = %pdu.room_id, "Event referenced in auth chain has incorrect room id"); return Err(Error::BadRequest( ErrorKind::forbidden(), - "Evil event in db", + "Event has incorrect room id", )); } for auth_event in &pdu.auth_events { From 9c44aa877ca86dff72504cc14e0d34e946dd2a52 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 25 Jun 2024 23:03:38 -0700 Subject: [PATCH 269/617] changelog entry for auth chain room id mismatch logging --- book/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index 88dbd54c..fb88793c 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -101,7 +101,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!18](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/18), [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26), [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50), - [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52)) + [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52), + [!54](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/54)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. From b171f7c123ba8d42bf22b350d1042b617c67a6cb Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 16 Jun 2024 13:15:04 +0000 Subject: [PATCH 270/617] config: fix order of items --- src/config.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5792b8b8..074ee33f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -114,20 +114,6 @@ pub(crate) enum ListenConfig { }, } -#[derive(Copy, Clone, Default, Debug, Deserialize)] -#[serde(rename_all = "snake_case")] -pub(crate) enum LogFormat { - /// Use the [`tracing_subscriber::fmt::format::Pretty`] formatter - Pretty, - /// Use the [`tracing_subscriber::fmt::format::Full`] formatter - #[default] - Full, - /// Use the [`tracing_subscriber::fmt::format::Compact`] formatter - Compact, - /// Use the [`tracing_subscriber::fmt::format::Json`] formatter - Json, -} - impl Display for ListenConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -145,6 +131,20 @@ impl Display for ListenConfig { } } +#[derive(Copy, Clone, Default, Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum LogFormat { + /// Use the [`tracing_subscriber::fmt::format::Pretty`] formatter + Pretty, + /// Use the [`tracing_subscriber::fmt::format::Full`] formatter + #[default] + Full, + /// Use the [`tracing_subscriber::fmt::format::Compact`] formatter + Compact, + /// Use the [`tracing_subscriber::fmt::format::Json`] formatter + Json, +} + fn false_fn() -> bool { false } From e911518aaceb400f232e1615e17425e678b0e042 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 16 Jun 2024 13:22:01 +0000 Subject: [PATCH 271/617] config: make db_cache_capacity_mb field unconditional This is also used by sqlite, causing builds with `--no-default-features --features sqlite` to fail. --- src/config.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index 074ee33f..03a5673c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -33,7 +33,6 @@ pub(crate) struct Config { pub(crate) server_name: OwnedServerName, pub(crate) database_backend: String, pub(crate) database_path: String, - #[cfg(feature = "rocksdb")] #[serde(default = "default_db_cache_capacity_mb")] pub(crate) db_cache_capacity_mb: f64, #[serde(default = "default_cache_capacity_modifier")] @@ -169,7 +168,6 @@ fn default_port() -> u16 { 6167 } -#[cfg(feature = "rocksdb")] fn default_db_cache_capacity_mb() -> f64 { 300.0 } From 79d5d306cc66ca6d7f1296dddbd9b55ba63973cc Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 7 Jun 2024 09:18:08 +0000 Subject: [PATCH 272/617] Move TURN config to separate config section This renames: turn_username -> turn.username turn_password -> turn.password turn_uris -> turn.uris turn_secret -> turn.secret turn_ttl -> turn.ttl --- src/config.rs | 36 +++++++++++++++++++++++------------- src/service/globals.rs | 10 +++++----- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/config.rs b/src/config.rs index 03a5673c..806e3770 100644 --- a/src/config.rs +++ b/src/config.rs @@ -81,15 +81,7 @@ pub(crate) struct Config { #[serde(default)] pub(crate) log_format: LogFormat, #[serde(default)] - pub(crate) turn_username: String, - #[serde(default)] - pub(crate) turn_password: String, - #[serde(default = "Vec::new")] - pub(crate) turn_uris: Vec, - #[serde(default)] - pub(crate) turn_secret: String, - #[serde(default = "default_turn_ttl")] - pub(crate) turn_ttl: u64, + pub(crate) turn: TurnConfig, pub(crate) emergency_password: Option, } @@ -144,6 +136,28 @@ pub(crate) enum LogFormat { Json, } +#[derive(Clone, Debug, Deserialize)] +#[serde(default)] +pub(crate) struct TurnConfig { + pub(crate) username: String, + pub(crate) password: String, + pub(crate) uris: Vec, + pub(crate) secret: String, + pub(crate) ttl: u64, +} + +impl Default for TurnConfig { + fn default() -> Self { + Self { + username: String::new(), + password: String::new(), + uris: Vec::new(), + secret: String::new(), + ttl: 60 * 60 * 24, + } + } +} + fn false_fn() -> bool { false } @@ -213,10 +227,6 @@ fn default_log() -> EnvFilterClone { .expect("hardcoded env filter should be valid") } -fn default_turn_ttl() -> u64 { - 60 * 60 * 24 -} - // I know, it's a great name pub(crate) fn default_default_room_version() -> RoomVersionId { RoomVersionId::V10 diff --git a/src/service/globals.rs b/src/service/globals.rs index f83f1f79..8a73f089 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -366,23 +366,23 @@ impl Service { } pub(crate) fn turn_password(&self) -> &String { - &self.config.turn_password + &self.config.turn.password } pub(crate) fn turn_ttl(&self) -> u64 { - self.config.turn_ttl + self.config.turn.ttl } pub(crate) fn turn_uris(&self) -> &[String] { - &self.config.turn_uris + &self.config.turn.uris } pub(crate) fn turn_username(&self) -> &String { - &self.config.turn_username + &self.config.turn.username } pub(crate) fn turn_secret(&self) -> &String { - &self.config.turn_secret + &self.config.turn.secret } pub(crate) fn emergency_password(&self) -> &Option { From d26b87a2f24cc03406e0e2b8d9ac7e02e052f84b Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 16 Jun 2024 13:24:28 +0000 Subject: [PATCH 273/617] Move database config to separate section This renames: database_backend -> database.backend database_path -> database.path db_cache_capacity_mb -> database.cache_capacity_mb rocksdb_max_open_files -> database.rocksdb_max_open_files Charles updated the NixOS module. Co-authored-by: Charles Hall --- nix/modules/default/default.nix | 2 +- src/config.rs | 20 +++++++++++++------- src/database.rs | 17 +++++++++-------- src/database/abstraction/rocksdb.rs | 16 ++++++++++------ src/database/abstraction/sqlite.rs | 8 ++++---- src/service/globals.rs | 4 ++-- 6 files changed, 39 insertions(+), 28 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 9118cfea..034e7024 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -36,7 +36,7 @@ in ''; default = false; }; - database_path = lib.mkOption { + database.path = lib.mkOption { type = types.nonEmptyStr; readOnly = true; description = '' diff --git a/src/config.rs b/src/config.rs index 806e3770..9589078f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,15 +31,10 @@ pub(crate) struct Config { pub(crate) tls: Option, pub(crate) server_name: OwnedServerName, - pub(crate) database_backend: String, - pub(crate) database_path: String, - #[serde(default = "default_db_cache_capacity_mb")] - pub(crate) db_cache_capacity_mb: f64, + pub(crate) database: DatabaseConfig, + #[serde(default = "default_cache_capacity_modifier")] pub(crate) cache_capacity_modifier: f64, - #[cfg(feature = "rocksdb")] - #[serde(default = "default_rocksdb_max_open_files")] - pub(crate) rocksdb_max_open_files: i32, #[serde(default = "default_pdu_cache_capacity")] pub(crate) pdu_cache_capacity: u32, #[serde(default = "default_cleanup_second_interval")] @@ -158,6 +153,17 @@ impl Default for TurnConfig { } } +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct DatabaseConfig { + pub(crate) backend: String, + pub(crate) path: String, + #[serde(default = "default_db_cache_capacity_mb")] + pub(crate) cache_capacity_mb: f64, + #[cfg(feature = "rocksdb")] + #[serde(default = "default_rocksdb_max_open_files")] + pub(crate) rocksdb_max_open_files: i32, +} + fn false_fn() -> bool { false } diff --git a/src/database.rs b/src/database.rs index f115fd14..8f6836db 100644 --- a/src/database.rs +++ b/src/database.rs @@ -250,7 +250,7 @@ pub(crate) struct KeyValueDatabase { impl KeyValueDatabase { fn check_db_setup(config: &Config) -> Result<()> { - let path = Path::new(&config.database_path); + let path = Path::new(&config.database.path); let sqlite_exists = path .join(format!( @@ -279,14 +279,14 @@ impl KeyValueDatabase { return Ok(()); } - if sqlite_exists && config.database_backend != "sqlite" { + if sqlite_exists && config.database.backend != "sqlite" { return Err(Error::bad_config( "Found sqlite at database_path, but is not specified in \ config.", )); } - if rocksdb_exists && config.database_backend != "rocksdb" { + if rocksdb_exists && config.database.backend != "rocksdb" { return Err(Error::bad_config( "Found rocksdb at database_path, but is not specified in \ config.", @@ -305,8 +305,8 @@ impl KeyValueDatabase { pub(crate) async fn load_or_create(config: Config) -> Result<()> { Self::check_db_setup(&config)?; - if !Path::new(&config.database_path).exists() { - fs::create_dir_all(&config.database_path).map_err(|_| { + if !Path::new(&config.database.path).exists() { + fs::create_dir_all(&config.database.path).map_err(|_| { Error::BadConfig( "Database folder doesn't exists and couldn't be created \ (e.g. due to missing permissions). Please create the \ @@ -320,7 +320,8 @@ impl KeyValueDatabase { allow(unused_variables) )] let builder: Arc = match &*config - .database_backend + .database + .backend { #[cfg(feature = "sqlite")] "sqlite" => { @@ -1106,7 +1107,7 @@ impl KeyValueDatabase { info!( "Loaded {} database with version {}", - services().globals.config.database_backend, + services().globals.config.database.backend, latest_database_version ); } else { @@ -1119,7 +1120,7 @@ impl KeyValueDatabase { warn!( "Created new {} database with version {}", - services().globals.config.database_backend, + services().globals.config.database.backend, latest_database_version ); } diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index afcc4ef5..72e676a1 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -78,32 +78,36 @@ impl KeyValueDatabaseEngine for Arc { clippy::cast_possible_truncation )] let cache_capacity_bytes = - (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize; + (config.database.cache_capacity_mb * 1024.0 * 1024.0) as usize; let rocksdb_cache = Cache::new_lru_cache(cache_capacity_bytes); - let db_opts = db_options(config.rocksdb_max_open_files, &rocksdb_cache); + let db_opts = + db_options(config.database.rocksdb_max_open_files, &rocksdb_cache); let cfs = DBWithThreadMode::::list_cf( &db_opts, - &config.database_path, + &config.database.path, ) .map(|x| x.into_iter().collect::>()) .unwrap_or_default(); let db = DBWithThreadMode::::open_cf_descriptors( &db_opts, - &config.database_path, + &config.database.path, cfs.iter().map(|name| { ColumnFamilyDescriptor::new( name, - db_options(config.rocksdb_max_open_files, &rocksdb_cache), + db_options( + config.database.rocksdb_max_open_files, + &rocksdb_cache, + ), ) }), )?; Ok(Arc::new(Engine { rocks: db, - max_open_files: config.rocksdb_max_open_files, + max_open_files: config.database.rocksdb_max_open_files, cache: rocksdb_cache, old_cfs: cfs, new_cfs: Mutex::default(), diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 871e2af4..7b41fc79 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -110,7 +110,7 @@ impl Engine { impl KeyValueDatabaseEngine for Arc { fn open(config: &Config) -> Result { - let path = Path::new(&config.database_path).join(format!( + let path = Path::new(&config.database.path).join(format!( "{}.db", if config.conduit_compat { "conduit" @@ -130,9 +130,9 @@ impl KeyValueDatabaseEngine for Arc { clippy::cast_precision_loss, clippy::cast_sign_loss )] - let cache_size_per_thread = ((config.db_cache_capacity_mb * 1024.0) - / ((num_cpus::get() as f64 * 2.0) + 1.0)) - as u32; + let cache_size_per_thread = + ((config.database.cache_capacity_mb * 1024.0) + / ((num_cpus::get() as f64 * 2.0) + 1.0)) as u32; let writer = Mutex::new(Engine::prepare_conn(&path, cache_size_per_thread)?); diff --git a/src/service/globals.rs b/src/service/globals.rs index 8a73f089..e92cb0b1 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -494,14 +494,14 @@ impl Service { pub(crate) fn get_media_folder(&self) -> PathBuf { let mut r = PathBuf::new(); - r.push(self.config.database_path.clone()); + r.push(self.config.database.path.clone()); r.push("media"); r } pub(crate) fn get_media_file(&self, key: &[u8]) -> PathBuf { let mut r = PathBuf::new(); - r.push(self.config.database_path.clone()); + r.push(self.config.database.path.clone()); r.push("media"); r.push(general_purpose::URL_SAFE_NO_PAD.encode(key)); r From 8a30817930b1c5522e8d5411c9ea8822b666878d Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 16 Jun 2024 14:31:59 +0000 Subject: [PATCH 274/617] config: convert database backend to enum This reports a nice error when the config is loaded, rather than later when the database is initialized. --- src/config.rs | 22 +++++++++++++++++++++- src/database.rs | 25 +++++++++++++++---------- src/main.rs | 4 ++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9589078f..43dd487f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -153,9 +153,29 @@ impl Default for TurnConfig { } } +#[derive(Clone, Copy, Debug, Deserialize)] +#[serde(rename_all = "lowercase")] +pub(crate) enum DatabaseBackend { + #[cfg(feature = "rocksdb")] + Rocksdb, + #[cfg(feature = "sqlite")] + Sqlite, +} + +impl Display for DatabaseBackend { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + #[cfg(feature = "rocksdb")] + DatabaseBackend::Rocksdb => write!(f, "RocksDB"), + #[cfg(feature = "sqlite")] + DatabaseBackend::Sqlite => write!(f, "SQLite"), + } + } +} + #[derive(Clone, Debug, Deserialize)] pub(crate) struct DatabaseConfig { - pub(crate) backend: String, + pub(crate) backend: DatabaseBackend, pub(crate) path: String, #[serde(default = "default_db_cache_capacity_mb")] pub(crate) cache_capacity_mb: f64, diff --git a/src/database.rs b/src/database.rs index 8f6836db..0e495475 100644 --- a/src/database.rs +++ b/src/database.rs @@ -25,8 +25,8 @@ use ruma::{ use tracing::{debug, error, info, info_span, warn, Instrument}; use crate::{ - service::rooms::timeline::PduCount, services, utils, Config, Error, - PduEvent, Result, Services, SERVICES, + config::DatabaseBackend, service::rooms::timeline::PduCount, services, + utils, Config, Error, PduEvent, Result, Services, SERVICES, }; pub(crate) struct KeyValueDatabase { @@ -279,14 +279,22 @@ impl KeyValueDatabase { return Ok(()); } - if sqlite_exists && config.database.backend != "sqlite" { + let (backend_is_rocksdb, backend_is_sqlite): (bool, bool) = + match config.database.backend { + #[cfg(feature = "rocksdb")] + DatabaseBackend::Rocksdb => (true, false), + #[cfg(feature = "sqlite")] + DatabaseBackend::Sqlite => (false, true), + }; + + if sqlite_exists && !backend_is_sqlite { return Err(Error::bad_config( "Found sqlite at database_path, but is not specified in \ config.", )); } - if rocksdb_exists && config.database.backend != "rocksdb" { + if rocksdb_exists && !backend_is_rocksdb { return Err(Error::bad_config( "Found rocksdb at database_path, but is not specified in \ config.", @@ -319,21 +327,18 @@ impl KeyValueDatabase { not(any(feature = "rocksdb", feature = "sqlite")), allow(unused_variables) )] - let builder: Arc = match &*config + let builder: Arc = match config .database .backend { #[cfg(feature = "sqlite")] - "sqlite" => { + DatabaseBackend::Sqlite => { Arc::new(Arc::::open(&config)?) } #[cfg(feature = "rocksdb")] - "rocksdb" => { + DatabaseBackend::Rocksdb => { Arc::new(Arc::::open(&config)?) } - _ => { - return Err(Error::BadConfig("Database backend not found.")); - } }; if config.registration_token == Some(String::new()) { diff --git a/src/main.rs b/src/main.rs index 657a7a36..7d7f2448 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +// Avoid spurious warnings with --no-default-features, which isn't expected to +// work anyway +#![cfg_attr(not(any(feature = "sqlite", feature = "rocksdb")), allow(unused))] + use std::{ future::Future, net::SocketAddr, From 98d49554ce4643b4cb0a09a3a199b19a938f4863 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 7 Jun 2024 10:03:06 +0000 Subject: [PATCH 275/617] Move observability config to separate config section This renames: allow_prometheus -> observability.metrics.enable allow_jaeger -> observability.traces.enable tracing_flame -> observability.flame.enable log -> observability.logs.filter log_colors -> observability.logs.colors log_format -> observability.logs.format New config values in these sections will follow. --- src/config.rs | 61 ++++++++++++++++++++++++++++++++++++-------- src/main.rs | 2 +- src/observability.rs | 27 ++++++++++++-------- 3 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/config.rs b/src/config.rs index 43dd487f..d0eb50d7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -58,23 +58,13 @@ pub(crate) struct Config { pub(crate) allow_unstable_room_versions: bool, #[serde(default = "default_default_room_version")] pub(crate) default_room_version: RoomVersionId, - #[serde(default = "false_fn")] - pub(crate) allow_jaeger: bool, - #[serde(default = "false_fn")] - pub(crate) allow_prometheus: bool, - #[serde(default = "false_fn")] - pub(crate) tracing_flame: bool, #[serde(default)] pub(crate) proxy: ProxyConfig, pub(crate) jwt_secret: Option, #[serde(default = "default_trusted_servers")] pub(crate) trusted_servers: Vec, - #[serde(default = "default_log")] - pub(crate) log: EnvFilterClone, - #[serde(default = "true_fn")] - pub(crate) log_colors: bool, #[serde(default)] - pub(crate) log_format: LogFormat, + pub(crate) observability: ObservabilityConfig, #[serde(default)] pub(crate) turn: TurnConfig, @@ -184,6 +174,55 @@ pub(crate) struct DatabaseConfig { pub(crate) rocksdb_max_open_files: i32, } +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(default)] +pub(crate) struct MetricsConfig { + pub(crate) enable: bool, +} + +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(default)] +pub(crate) struct OtelTraceConfig { + pub(crate) enable: bool, +} + +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(default)] +pub(crate) struct FlameConfig { + pub(crate) enable: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(default)] +pub(crate) struct LogConfig { + pub(crate) filter: EnvFilterClone, + pub(crate) colors: bool, + pub(crate) format: LogFormat, +} + +impl Default for LogConfig { + fn default() -> Self { + Self { + filter: default_log(), + colors: true, + format: LogFormat::default(), + } + } +} + +#[derive(Debug, Default, Deserialize)] +#[serde(default)] +pub(crate) struct ObservabilityConfig { + /// Prometheus metrics + pub(crate) metrics: MetricsConfig, + /// OpenTelemetry traces + pub(crate) traces: OtelTraceConfig, + /// Folded inferno stack traces + pub(crate) flame: FlameConfig, + /// Logging to stdout + pub(crate) logs: LogConfig, +} + fn false_fn() -> bool { false } diff --git a/src/main.rs b/src/main.rs index 7d7f2448..f68264a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -442,7 +442,7 @@ fn routes(config: &Config) -> Router { .put(c2s::send_state_event_for_empty_key_route), ); - let router = if config.allow_prometheus { + let router = if config.observability.metrics.enable { router.route( "/metrics", get(|| async { observability::METRICS.export() }), diff --git a/src/observability.rs b/src/observability.rs index 0204de0e..3b3b1764 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -87,7 +87,9 @@ pub(crate) enum FoundIn { /// Initialize observability pub(crate) fn init(config: &Config) -> Result { let jaeger_layer = config - .allow_jaeger + .observability + .traces + .enable .then(|| { opentelemetry::global::set_text_map_propagator( opentelemetry_jaeger_propagator::Propagator::new(), @@ -102,36 +104,41 @@ pub(crate) fn init(config: &Config) -> Result { .install_batch(opentelemetry_sdk::runtime::Tokio)?; let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - Ok::<_, error::Observability>( - telemetry.with_filter(EnvFilter::from(&config.log)), - ) + Ok::<_, error::Observability>(telemetry.with_filter( + EnvFilter::from(&config.observability.logs.filter), + )) }) .transpose()?; let (flame_layer, flame_guard) = config - .tracing_flame + .observability + .flame + .enable .then(|| { let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded")?; let flame_layer = flame_layer.with_empty_samples(false); Ok::<_, error::Observability>(( - flame_layer.with_filter(EnvFilter::from(&config.log)), + flame_layer.with_filter(EnvFilter::from( + &config.observability.logs.filter, + )), guard, )) }) .transpose()? .unzip(); - let fmt_layer = - tracing_subscriber::fmt::Layer::new().with_ansi(config.log_colors); - let fmt_layer = match config.log_format { + let fmt_layer = tracing_subscriber::fmt::Layer::new() + .with_ansi(config.observability.logs.colors); + let fmt_layer = match config.observability.logs.format { LogFormat::Pretty => fmt_layer.pretty().boxed(), LogFormat::Full => fmt_layer.boxed(), LogFormat::Compact => fmt_layer.compact().boxed(), LogFormat::Json => fmt_layer.json().boxed(), }; - let fmt_layer = fmt_layer.with_filter(EnvFilter::from(&config.log)); + let fmt_layer = fmt_layer + .with_filter(EnvFilter::from(&config.observability.logs.filter)); let subscriber = Registry::default() .with(jaeger_layer) From df571818f156df6262a6d41eae6cbeb0e8d237c6 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 7 Jun 2024 10:14:05 +0000 Subject: [PATCH 276/617] Make tracing filters configurable per backend --- src/config.rs | 30 ++++++++++++--- src/observability.rs | 91 +++++++++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/config.rs b/src/config.rs index d0eb50d7..9cef6383 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,7 +14,7 @@ use crate::error; mod env_filter_clone; mod proxy; -use env_filter_clone::EnvFilterClone; +pub(crate) use env_filter_clone::EnvFilterClone; use proxy::ProxyConfig; /// The default configuration file path @@ -180,16 +180,36 @@ pub(crate) struct MetricsConfig { pub(crate) enable: bool, } -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(default)] pub(crate) struct OtelTraceConfig { pub(crate) enable: bool, + pub(crate) filter: EnvFilterClone, } -#[derive(Clone, Debug, Default, Deserialize)] +impl Default for OtelTraceConfig { + fn default() -> Self { + Self { + enable: false, + filter: default_tracing_filter(), + } + } +} + +#[derive(Debug, Deserialize)] #[serde(default)] pub(crate) struct FlameConfig { pub(crate) enable: bool, + pub(crate) filter: EnvFilterClone, +} + +impl Default for FlameConfig { + fn default() -> Self { + Self { + enable: false, + filter: default_tracing_filter(), + } + } } #[derive(Debug, Deserialize)] @@ -203,7 +223,7 @@ pub(crate) struct LogConfig { impl Default for LogConfig { fn default() -> Self { Self { - filter: default_log(), + filter: default_tracing_filter(), colors: true, format: LogFormat::default(), } @@ -286,7 +306,7 @@ fn default_trusted_servers() -> Vec { vec![OwnedServerName::try_from("matrix.org").unwrap()] } -fn default_log() -> EnvFilterClone { +fn default_tracing_filter() -> EnvFilterClone { "info,ruma_state_res=warn" .parse() .expect("hardcoded env filter should be valid") diff --git a/src/observability.rs b/src/observability.rs index 3b3b1764..ff1a68fc 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -24,7 +24,7 @@ use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; use crate::{ - config::{Config, LogFormat}, + config::{Config, EnvFilterClone, LogFormat}, error, utils::error::Result, }; @@ -84,13 +84,37 @@ pub(crate) enum FoundIn { Nothing, } +/// Wrapper for the creation of a `tracing` [`Layer`] and any associated opaque +/// data. +/// +/// Returns a no-op `None` layer if `enable` is `false`, otherwise calls the +/// given closure to construct the layer and associated data, then applies the +/// filter to the layer. +fn make_backend( + enable: bool, + filter: &EnvFilterClone, + init: impl FnOnce() -> Result<(L, T), error::Observability>, +) -> Result<(impl Layer, Option), error::Observability> +where + L: Layer, + S: tracing::Subscriber + + for<'span> tracing_subscriber::registry::LookupSpan<'span>, +{ + enable + .then(|| { + let (layer, data) = init()?; + Ok((layer.with_filter(EnvFilter::from(filter)), data)) + }) + .transpose() + .map(Option::unzip) +} + /// Initialize observability pub(crate) fn init(config: &Config) -> Result { - let jaeger_layer = config - .observability - .traces - .enable - .then(|| { + let (jaeger_layer, _) = make_backend( + config.observability.traces.enable, + &config.observability.traces.filter, + || { opentelemetry::global::set_text_map_propagator( opentelemetry_jaeger_propagator::Propagator::new(), ); @@ -102,43 +126,32 @@ pub(crate) fn init(config: &Config) -> Result { ) .with_exporter(opentelemetry_otlp::new_exporter().tonic()) .install_batch(opentelemetry_sdk::runtime::Tokio)?; - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + Ok((tracing_opentelemetry::layer().with_tracer(tracer), ())) + }, + )?; - Ok::<_, error::Observability>(telemetry.with_filter( - EnvFilter::from(&config.observability.logs.filter), - )) - }) - .transpose()?; - - let (flame_layer, flame_guard) = config - .observability - .flame - .enable - .then(|| { + let (flame_layer, flame_guard) = make_backend( + config.observability.flame.enable, + &config.observability.flame.filter, + || { let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded")?; - let flame_layer = flame_layer.with_empty_samples(false); + Ok((flame_layer.with_empty_samples(false), guard)) + }, + )?; - Ok::<_, error::Observability>(( - flame_layer.with_filter(EnvFilter::from( - &config.observability.logs.filter, - )), - guard, - )) - }) - .transpose()? - .unzip(); - - let fmt_layer = tracing_subscriber::fmt::Layer::new() - .with_ansi(config.observability.logs.colors); - let fmt_layer = match config.observability.logs.format { - LogFormat::Pretty => fmt_layer.pretty().boxed(), - LogFormat::Full => fmt_layer.boxed(), - LogFormat::Compact => fmt_layer.compact().boxed(), - LogFormat::Json => fmt_layer.json().boxed(), - }; - let fmt_layer = fmt_layer - .with_filter(EnvFilter::from(&config.observability.logs.filter)); + let (fmt_layer, _) = + make_backend(true, &config.observability.logs.filter, || { + let fmt_layer = tracing_subscriber::fmt::Layer::new() + .with_ansi(config.observability.logs.colors); + let fmt_layer = match config.observability.logs.format { + LogFormat::Pretty => fmt_layer.pretty().boxed(), + LogFormat::Full => fmt_layer.boxed(), + LogFormat::Compact => fmt_layer.compact().boxed(), + LogFormat::Json => fmt_layer.json().boxed(), + }; + Ok((fmt_layer, ())) + })?; let subscriber = Registry::default() .with(jaeger_layer) From e0e7d8fd9190ced5890414a860f22ddefc5d951c Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 7 Jun 2024 11:28:33 +0000 Subject: [PATCH 277/617] Make observability more configurable The following are now configurable: - the OpenTelemetry endpoint, - the tracing-flame filename, and - whether the log output should include timestamps (useful to disable if it goes straight into journald). --- src/config.rs | 6 ++++++ src/observability.rs | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9cef6383..78308c7f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -185,6 +185,7 @@ pub(crate) struct MetricsConfig { pub(crate) struct OtelTraceConfig { pub(crate) enable: bool, pub(crate) filter: EnvFilterClone, + pub(crate) endpoint: Option, } impl Default for OtelTraceConfig { @@ -192,6 +193,7 @@ impl Default for OtelTraceConfig { Self { enable: false, filter: default_tracing_filter(), + endpoint: None, } } } @@ -201,6 +203,7 @@ impl Default for OtelTraceConfig { pub(crate) struct FlameConfig { pub(crate) enable: bool, pub(crate) filter: EnvFilterClone, + pub(crate) filename: String, } impl Default for FlameConfig { @@ -208,6 +211,7 @@ impl Default for FlameConfig { Self { enable: false, filter: default_tracing_filter(), + filename: "./tracing.folded".to_owned(), } } } @@ -218,6 +222,7 @@ pub(crate) struct LogConfig { pub(crate) filter: EnvFilterClone, pub(crate) colors: bool, pub(crate) format: LogFormat, + pub(crate) timestamp: bool, } impl Default for LogConfig { @@ -226,6 +231,7 @@ impl Default for LogConfig { filter: default_tracing_filter(), colors: true, format: LogFormat::default(), + timestamp: true, } } } diff --git a/src/observability.rs b/src/observability.rs index ff1a68fc..678546ba 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -14,6 +14,7 @@ use opentelemetry::{ metrics::{MeterProvider, Unit}, KeyValue, }; +use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::{ metrics::{new_view, Aggregation, Instrument, SdkMeterProvider, Stream}, Resource, @@ -118,13 +119,17 @@ pub(crate) fn init(config: &Config) -> Result { opentelemetry::global::set_text_map_propagator( opentelemetry_jaeger_propagator::Propagator::new(), ); + let mut exporter = opentelemetry_otlp::new_exporter().tonic(); + if let Some(endpoint) = &config.observability.traces.endpoint { + exporter = exporter.with_endpoint(endpoint); + } let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_trace_config( opentelemetry_sdk::trace::config() .with_resource(standard_resource()), ) - .with_exporter(opentelemetry_otlp::new_exporter().tonic()) + .with_exporter(exporter) .install_batch(opentelemetry_sdk::runtime::Tokio)?; Ok((tracing_opentelemetry::layer().with_tracer(tracer), ())) }, @@ -135,15 +140,41 @@ pub(crate) fn init(config: &Config) -> Result { &config.observability.flame.filter, || { let (flame_layer, guard) = - FlameLayer::with_file("./tracing.folded")?; + FlameLayer::with_file(&config.observability.flame.filename)?; Ok((flame_layer.with_empty_samples(false), guard)) }, )?; let (fmt_layer, _) = make_backend(true, &config.observability.logs.filter, || { + /// Time format selection for `tracing_subscriber` at runtime + #[allow(clippy::missing_docs_in_private_items)] + enum TimeFormat { + SystemTime, + NoTime, + } + impl tracing_subscriber::fmt::time::FormatTime for TimeFormat { + fn format_time( + &self, + w: &mut tracing_subscriber::fmt::format::Writer<'_>, + ) -> std::fmt::Result { + match self { + TimeFormat::SystemTime => { + tracing_subscriber::fmt::time::SystemTime + .format_time(w) + } + TimeFormat::NoTime => Ok(()), + } + } + } + let fmt_layer = tracing_subscriber::fmt::Layer::new() - .with_ansi(config.observability.logs.colors); + .with_ansi(config.observability.logs.colors) + .with_timer(if config.observability.logs.timestamp { + TimeFormat::SystemTime + } else { + TimeFormat::NoTime + }); let fmt_layer = match config.observability.logs.format { LogFormat::Pretty => fmt_layer.pretty().boxed(), LogFormat::Full => fmt_layer.boxed(), From 5a6e4fac738d7e43f3338a8ba5125afc424fa7ca Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 26 Jun 2024 06:45:23 +0000 Subject: [PATCH 278/617] Move federation config to separate config section This renames: allow_federation -> federation.enable trusted_servers -> federation.trusted_servers max_fetch_prev_events -> federation.max_fetch_prev_events max_concurrent_requests -> federation.max_concurrent_requests --- src/config.rs | 44 +++++++++++++++++++++++------------------- src/main.rs | 2 +- src/service/globals.rs | 6 +++--- src/service/sending.rs | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/config.rs b/src/config.rs index 78308c7f..6fe3a43e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -32,6 +32,8 @@ pub(crate) struct Config { pub(crate) server_name: OwnedServerName, pub(crate) database: DatabaseConfig, + #[serde(default)] + pub(crate) federation: FederationConfig, #[serde(default = "default_cache_capacity_modifier")] pub(crate) cache_capacity_modifier: f64, @@ -41,18 +43,12 @@ pub(crate) struct Config { pub(crate) cleanup_second_interval: u32, #[serde(default = "default_max_request_size")] pub(crate) max_request_size: u32, - #[serde(default = "default_max_concurrent_requests")] - pub(crate) max_concurrent_requests: u16, - #[serde(default = "default_max_fetch_prev_events")] - pub(crate) max_fetch_prev_events: u16, #[serde(default = "false_fn")] pub(crate) allow_registration: bool, pub(crate) registration_token: Option, #[serde(default = "true_fn")] pub(crate) allow_encryption: bool, #[serde(default = "true_fn")] - pub(crate) allow_federation: bool, - #[serde(default = "true_fn")] pub(crate) allow_room_creation: bool, #[serde(default = "true_fn")] pub(crate) allow_unstable_room_versions: bool, @@ -61,8 +57,6 @@ pub(crate) struct Config { #[serde(default)] pub(crate) proxy: ProxyConfig, pub(crate) jwt_secret: Option, - #[serde(default = "default_trusted_servers")] - pub(crate) trusted_servers: Vec, #[serde(default)] pub(crate) observability: ObservabilityConfig, #[serde(default)] @@ -249,6 +243,28 @@ pub(crate) struct ObservabilityConfig { pub(crate) logs: LogConfig, } +#[derive(Debug, Deserialize)] +#[serde(default)] +pub(crate) struct FederationConfig { + pub(crate) enable: bool, + pub(crate) trusted_servers: Vec, + pub(crate) max_fetch_prev_events: u16, + pub(crate) max_concurrent_requests: u16, +} + +impl Default for FederationConfig { + fn default() -> Self { + Self { + enable: true, + trusted_servers: vec![ + OwnedServerName::try_from("matrix.org").unwrap() + ], + max_fetch_prev_events: 100, + max_concurrent_requests: 100, + } + } +} + fn false_fn() -> bool { false } @@ -300,18 +316,6 @@ fn default_max_request_size() -> u32 { 20 * 1024 * 1024 } -fn default_max_concurrent_requests() -> u16 { - 100 -} - -fn default_max_fetch_prev_events() -> u16 { - 100_u16 -} - -fn default_trusted_servers() -> Vec { - vec![OwnedServerName::try_from("matrix.org").unwrap()] -} - fn default_tracing_filter() -> EnvFilterClone { "info,ruma_state_res=warn" .parse() diff --git a/src/main.rs b/src/main.rs index f68264a9..385dc470 100644 --- a/src/main.rs +++ b/src/main.rs @@ -463,7 +463,7 @@ fn routes(config: &Config) -> Router { .route("/", get(it_works)) .fallback(not_found); - if config.allow_federation { + if config.federation.enable { router .ruma_route(s2s::get_server_version_route) .route("/_matrix/key/v2/server", get(s2s::get_server_keys_route)) diff --git a/src/service/globals.rs b/src/service/globals.rs index e92cb0b1..681ba44a 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -324,7 +324,7 @@ impl Service { } pub(crate) fn max_fetch_prev_events(&self) -> u16 { - self.config.max_fetch_prev_events + self.config.federation.max_fetch_prev_events } pub(crate) fn allow_registration(&self) -> bool { @@ -336,7 +336,7 @@ impl Service { } pub(crate) fn allow_federation(&self) -> bool { - self.config.allow_federation + self.config.federation.enable } pub(crate) fn allow_room_creation(&self) -> bool { @@ -352,7 +352,7 @@ impl Service { } pub(crate) fn trusted_servers(&self) -> &[OwnedServerName] { - &self.config.trusted_servers + &self.config.federation.trusted_servers } pub(crate) fn dns_resolver(&self) -> &TokioAsyncResolver { diff --git a/src/service/sending.rs b/src/service/sending.rs index 095f5801..32573cc3 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -159,7 +159,7 @@ impl Service { sender, receiver: Mutex::new(receiver), maximum_requests: Arc::new(Semaphore::new( - config.max_concurrent_requests.into(), + config.federation.max_concurrent_requests.into(), )), }) } From f576aff7eb4dfe7063ff1005156f37ceb2bae9ef Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 15 Jun 2024 13:43:43 +0000 Subject: [PATCH 279/617] Don't accidentally ignore observability errors --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 385dc470..71197409 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,7 +112,7 @@ async fn try_main() -> Result<(), error::Main> { let config = config::load(args.config.as_ref()).await?; - let _guard = observability::init(&config); + let _guard = observability::init(&config)?; // This is needed for opening lots of file descriptors, which tends to // happen more often when using RocksDB and making lots of federation From f89e1c7dfccf7bd698e9998d88fb993e09bdab2f Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 7 Jun 2024 16:45:52 +0000 Subject: [PATCH 280/617] Allow tracing filters to be changed at runtime ReloadHandle is taken from conduwuit commit 8a5599adf9eafe9111f3d1597f8fb333b8b76849, authored by Benjamin. Co-authored-by: Benjamin Lee --- book/changelog.md | 3 ++ src/database.rs | 13 +++++-- src/main.rs | 4 +- src/observability.rs | 88 +++++++++++++++++++++++++++++++++--------- src/service.rs | 5 ++- src/service/admin.rs | 48 ++++++++++++++++++++++- src/service/globals.rs | 13 ++++++- 7 files changed, 145 insertions(+), 29 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index fb88793c..007cf51c 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -173,3 +173,6 @@ This will be the first release of Grapevine since it was forked from Conduit ([!46](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/46)) 10. Recognize the `!admin` prefix to invoke admin commands. ([!45](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/45)) +11. Add the `set-tracing-filter` admin command to change log/metrics/flame + filters dynamically at runtime. + ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) diff --git a/src/database.rs b/src/database.rs index 0e495475..19ab1f23 100644 --- a/src/database.rs +++ b/src/database.rs @@ -25,8 +25,9 @@ use ruma::{ use tracing::{debug, error, info, info_span, warn, Instrument}; use crate::{ - config::DatabaseBackend, service::rooms::timeline::PduCount, services, - utils, Config, Error, PduEvent, Result, Services, SERVICES, + config::DatabaseBackend, observability::FilterReloadHandles, + service::rooms::timeline::PduCount, services, utils, Config, Error, + PduEvent, Result, Services, SERVICES, }; pub(crate) struct KeyValueDatabase { @@ -310,7 +311,10 @@ impl KeyValueDatabase { allow(unreachable_code) )] #[allow(clippy::too_many_lines)] - pub(crate) async fn load_or_create(config: Config) -> Result<()> { + pub(crate) async fn load_or_create( + config: Config, + reload_handles: FilterReloadHandles, + ) -> Result<()> { Self::check_db_setup(&config)?; if !Path::new(&config.database.path).exists() { @@ -527,7 +531,8 @@ impl KeyValueDatabase { let db = Box::leak(db_raw); - let services_raw = Box::new(Services::build(db, config)?); + let services_raw = + Box::new(Services::build(db, config, reload_handles)?); // This is the first and only time we initialize the SERVICE static *SERVICES.write().unwrap() = Some(Box::leak(services_raw)); diff --git a/src/main.rs b/src/main.rs index 71197409..4f2dc417 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,7 +112,7 @@ async fn try_main() -> Result<(), error::Main> { let config = config::load(args.config.as_ref()).await?; - let _guard = observability::init(&config)?; + let (_guard, reload_handles) = observability::init(&config)?; // This is needed for opening lots of file descriptors, which tends to // happen more often when using RocksDB and making lots of federation @@ -126,7 +126,7 @@ async fn try_main() -> Result<(), error::Main> { .expect("should be able to increase the soft limit to the hard limit"); info!("Loading database"); - KeyValueDatabase::load_or_create(config) + KeyValueDatabase::load_or_create(config, reload_handles) .await .map_err(Error::DatabaseError)?; diff --git a/src/observability.rs b/src/observability.rs index 678546ba..7add9ce4 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -22,7 +22,9 @@ use opentelemetry_sdk::{ use strum::{AsRefStr, IntoStaticStr}; use tokio::time::Instant; use tracing_flame::{FlameLayer, FlushGuard}; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; +use tracing_subscriber::{ + layer::SubscriberExt, reload, EnvFilter, Layer, Registry, +}; use crate::{ config::{Config, EnvFilterClone, LogFormat}, @@ -46,6 +48,44 @@ impl Drop for Guard { } } +/// We need to store a [`reload::Handle`] value, but can't name it's type +/// explicitly because the S type parameter depends on the subscriber's previous +/// layers. In our case, this includes unnameable 'impl Trait' types. +/// +/// This is fixed[1] in the unreleased tracing-subscriber from the master +/// branch, which removes the S parameter. Unfortunately can't use it without +/// pulling in a version of tracing that's incompatible with the rest of our +/// deps. +/// +/// To work around this, we define an trait without the S paramter that forwards +/// to the [`reload::Handle::reload`] method, and then store the handle as a +/// trait object. +/// +/// [1]: https://github.com/tokio-rs/tracing/pull/1035/commits/8a87ea52425098d3ef8f56d92358c2f6c144a28f +pub(crate) trait ReloadHandle { + /// Replace the layer with a new value. See [`reload::Handle::reload`]. + fn reload(&self, new_value: L) -> Result<(), reload::Error>; +} + +impl ReloadHandle for reload::Handle { + fn reload(&self, new_value: L) -> Result<(), reload::Error> { + reload::Handle::reload(self, new_value) + } +} + +/// A type-erased [reload handle][reload::Handle] for an [`EnvFilter`]. +pub(crate) type FilterReloadHandle = Box + Sync>; + +/// Collection of [`FilterReloadHandle`]s, allowing the filters for tracing +/// backends to be changed dynamically. Handles may be [`None`] if the backend +/// is disabled in the config. +#[allow(clippy::missing_docs_in_private_items)] +pub(crate) struct FilterReloadHandles { + pub(crate) traces: Option, + pub(crate) flame: Option, + pub(crate) log: Option, +} + /// A kind of data that gets looked up /// /// See also [`Metrics::record_lookup`]. @@ -95,24 +135,29 @@ fn make_backend( enable: bool, filter: &EnvFilterClone, init: impl FnOnce() -> Result<(L, T), error::Observability>, -) -> Result<(impl Layer, Option), error::Observability> +) -> Result< + (impl Layer, Option, Option), + error::Observability, +> where L: Layer, S: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>, { - enable - .then(|| { - let (layer, data) = init()?; - Ok((layer.with_filter(EnvFilter::from(filter)), data)) - }) - .transpose() - .map(Option::unzip) + if !enable { + return Ok((None, None, None)); + } + + let (filter, handle) = reload::Layer::new(EnvFilter::from(filter)); + let (layer, data) = init()?; + Ok((Some(layer.with_filter(filter)), Some(Box::new(handle)), Some(data))) } /// Initialize observability -pub(crate) fn init(config: &Config) -> Result { - let (jaeger_layer, _) = make_backend( +pub(crate) fn init( + config: &Config, +) -> Result<(Guard, FilterReloadHandles), error::Observability> { + let (traces_layer, traces_filter, _) = make_backend( config.observability.traces.enable, &config.observability.traces.filter, || { @@ -135,7 +180,7 @@ pub(crate) fn init(config: &Config) -> Result { }, )?; - let (flame_layer, flame_guard) = make_backend( + let (flame_layer, flame_filter, flame_guard) = make_backend( config.observability.flame.enable, &config.observability.flame.filter, || { @@ -145,7 +190,7 @@ pub(crate) fn init(config: &Config) -> Result { }, )?; - let (fmt_layer, _) = + let (log_layer, log_filter, _) = make_backend(true, &config.observability.logs.filter, || { /// Time format selection for `tracing_subscriber` at runtime #[allow(clippy::missing_docs_in_private_items)] @@ -185,14 +230,21 @@ pub(crate) fn init(config: &Config) -> Result { })?; let subscriber = Registry::default() - .with(jaeger_layer) + .with(traces_layer) .with(flame_layer) - .with(fmt_layer); + .with(log_layer); tracing::subscriber::set_global_default(subscriber)?; - Ok(Guard { - flame_guard, - }) + Ok(( + Guard { + flame_guard, + }, + FilterReloadHandles { + traces: traces_filter, + flame: flame_filter, + log: log_filter, + }, + )) } /// Construct the standard [`Resource`] value to use for this service diff --git a/src/service.rs b/src/service.rs index b9eee52b..61216a61 100644 --- a/src/service.rs +++ b/src/service.rs @@ -6,7 +6,7 @@ use std::{ use lru_cache::LruCache; use tokio::sync::{broadcast, Mutex, RwLock}; -use crate::{Config, Result}; +use crate::{observability::FilterReloadHandles, Config, Result}; pub(crate) mod account_data; pub(crate) mod admin; @@ -54,6 +54,7 @@ impl Services { >( db: &'static D, config: Config, + reload_handles: FilterReloadHandles, ) -> Result { Ok(Self { appservice: appservice::Service::build(db)?, @@ -149,7 +150,7 @@ impl Services { }, sending: sending::Service::build(db, &config), - globals: globals::Service::load(db, config)?, + globals: globals::Service::load(db, config, reload_handles)?, }) } diff --git a/src/service/admin.rs b/src/service/admin.rs index 669c9f49..b1edf1a9 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, fmt::Write, sync::Arc, time::Instant}; -use clap::Parser; +use clap::{Parser, ValueEnum}; use regex::Regex; use ruma::{ api::appservice::Registration, @@ -196,6 +196,12 @@ enum AdminCommand { // Allowed because the doc comment gets parsed by our code later #[allow(clippy::doc_markdown)] VerifyJson, + + /// Dynamically change a tracing backend's filter string + SetTracingFilter { + backend: TracingBackend, + filter: String, + }, } #[derive(Debug)] @@ -209,6 +215,13 @@ pub(crate) struct Service { receiver: Mutex>, } +#[derive(Debug, Clone, ValueEnum)] +enum TracingBackend { + Log, + Flame, + Traces, +} + impl Service { pub(crate) fn build() -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); @@ -1081,6 +1094,39 @@ impl Service { ) } } + AdminCommand::SetTracingFilter { + backend, + filter, + } => { + let handles = &services().globals.reload_handles; + let handle = match backend { + TracingBackend::Log => &handles.log, + TracingBackend::Flame => &handles.flame, + TracingBackend::Traces => &handles.traces, + }; + let Some(handle) = handle else { + return Ok(RoomMessageEventContent::text_plain( + "Backend is disabled", + )); + }; + let filter = match filter.parse() { + Ok(filter) => filter, + Err(e) => { + return Ok(RoomMessageEventContent::text_plain( + format!("Invalid filter string: {e}"), + )); + } + }; + if let Err(e) = handle.reload(filter) { + return Ok(RoomMessageEventContent::text_plain(format!( + "Failed to reload filter: {e}" + ))); + }; + + return Ok(RoomMessageEventContent::text_plain( + "Filter reloaded", + )); + } }; Ok(reply_message_content) diff --git a/src/service/globals.rs b/src/service/globals.rs index 681ba44a..02ae4e93 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -32,7 +32,10 @@ use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use tracing::{error, Instrument}; use trust_dns_resolver::TokioAsyncResolver; -use crate::{api::server_server::FedDest, services, Config, Error, Result}; +use crate::{ + api::server_server::FedDest, observability::FilterReloadHandles, services, + Config, Error, Result, +}; type WellKnownMap = HashMap; type TlsNameMap = HashMap, u16)>; @@ -41,6 +44,7 @@ type RateLimitState = (Instant, u32); pub(crate) struct Service { pub(crate) db: &'static dyn Data, + pub(crate) reload_handles: FilterReloadHandles, // actual_destination, host pub(crate) actual_destination_cache: Arc>, @@ -173,7 +177,11 @@ impl Resolve for Resolver { impl Service { #[tracing::instrument(skip_all)] - pub(crate) fn load(db: &'static dyn Data, config: Config) -> Result { + pub(crate) fn load( + db: &'static dyn Data, + config: Config, + reload_handles: FilterReloadHandles, + ) -> Result { let keypair = db.load_keypair(); let keypair = match keypair { @@ -227,6 +235,7 @@ impl Service { let mut s = Self { db, config, + reload_handles, keypair: Arc::new(keypair), dns_resolver: TokioAsyncResolver::tokio_from_system_conf() .map_err(|e| { From b05c91b13e215816da1d7b45862952c2c73b8d55 Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 26 Jun 2024 06:50:09 +0000 Subject: [PATCH 281/617] Update changelog for config organization changes Charles wrote item 12 in the "Added" section. Co-authored-by: Charles Hall --- book/changelog.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 007cf51c..4a2aea2b 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -118,6 +118,9 @@ This will be the first release of Grapevine since it was forked from Conduit * Details on how to migrate can be found in the merge request's description. 9. Increase default log level so that span information is included. ([!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50)) +10. **BREAKING:** Reorganize config into sections. + ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) + * Details on how to migrate can be found in the merge request's description. ### Fixed @@ -176,3 +179,13 @@ This will be the first release of Grapevine since it was forked from Conduit 11. Add the `set-tracing-filter` admin command to change log/metrics/flame filters dynamically at runtime. ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) +12. Add more configuration options. + ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) + * `observability.traces.filter`: The `tracing` filter to use for + OpenTelemetry traces. + * `observability.traces.endpoint`: Where OpenTelemetry should send traces. + * `observability.flame.filter`: The `tracing` filter for `tracing-flame`. + * `observability.flame.filename`: Where `tracing-flame` will write its + output. + * `observability.logs.timestamp`: Whether timestamps should be included in + the logs. From ca4f780c93f56ea5c4d2ce5add41efd5900cdafb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 8 Jul 2024 13:43:01 -0700 Subject: [PATCH 282/617] fix api/client_server/membership events --- src/api/client_server/membership.rs | 64 +++++++++++++++++------------ 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 52c22414..0a2725f2 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -518,6 +518,7 @@ pub(crate) async fn joined_members_route( } #[allow(clippy::too_many_lines)] +#[tracing::instrument(skip(reason, _third_party_signed))] async fn join_room_by_id_helper( sender_user: Option<&UserId>, room_id: &RoomId, @@ -557,8 +558,8 @@ async fn join_room_by_id_helper( .as_ref() .map(|join_rules_event| { serde_json::from_str(join_rules_event.content.get()) - .map_err(|e| { - warn!("Invalid join rules event: {}", e); + .map_err(|error| { + warn!(%error, "Invalid join rules event"); Error::bad_database( "Invalid join rules event in db.", ) @@ -811,7 +812,7 @@ async fn join_room_by_id_helper( ) .await?; } else { - info!("Joining {room_id} over federation."); + info!("Joining over federation."); let (make_join_response, remote_server) = make_join_request(sender_user, room_id, servers).await?; @@ -915,7 +916,7 @@ async fn join_room_by_id_helper( // It has enough fields to be called a proper event now let mut join_event = join_event_stub; - info!("Asking {remote_server} for send_join"); + info!(server = %remote_server, "Asking other server for send_join"); let send_join_response = services() .sending .send_federation_request( @@ -975,10 +976,13 @@ async fn join_room_by_id_helper( .expect("we created a valid pdu") .insert(remote_server.to_string(), signature.clone()); } - Err(e) => { + Err(error) => { warn!( - "Server {remote_server} sent invalid signature in \ - sendjoin signatures for event {signed_value:?}: {e:?}", + %error, + server = %remote_server, + event = ?signed_value, + "Other server sent invalid signature in sendjoin \ + signatures for event", ); } } @@ -1015,10 +1019,11 @@ async fn join_room_by_id_helper( }; let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err( - |e| { + |error| { warn!( - "Invalid PDU in send_join response: {} {:?}", - e, value + %error, + object = ?value, + "Invalid PDU in send_join response", ); Error::BadServerResponse( "Invalid PDU in send_join response.", @@ -1076,8 +1081,8 @@ async fn join_room_by_id_helper( .ok()? }, ) - .map_err(|e| { - warn!("Auth check failed: {e}"); + .map_err(|error| { + warn!(%error, "Auth check failed"); Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed") })?; @@ -1168,7 +1173,7 @@ async fn make_join_request( if remote_server == services().globals.server_name() { continue; } - info!("Asking {remote_server} for make_join"); + info!(server = %remote_server, "Asking other server for make_join"); let make_join_response = services() .sending .send_federation_request( @@ -1198,8 +1203,8 @@ async fn validate_and_add_event_id( pub_key_map: &RwLock>, ) -> Result<(OwnedEventId, CanonicalJsonObject)> { let mut value: CanonicalJsonObject = serde_json::from_str(pdu.get()) - .map_err(|e| { - error!("Invalid PDU in server response: {:?}: {:?}", pdu, e); + .map_err(|error| { + error!(%error, object = ?pdu, "Invalid PDU in server response"); Error::BadServerResponse("Invalid PDU in server response") })?; let event_id = EventId::parse(format!( @@ -1231,7 +1236,7 @@ async fn validate_and_add_event_id( } if time.elapsed() < min_elapsed_duration { - debug!("Backing off from {}", event_id); + debug!(%event_id, "Backing off from event"); return Err(Error::BadServerResponse( "bad event, still backing off", )); @@ -1270,9 +1275,15 @@ async fn validate_and_add_event_id( room_version, ); - if let Err(e) = ruma::signatures::verify_event(&keys, &value, room_version) + if let Err(error) = + ruma::signatures::verify_event(&keys, &value, room_version) { - warn!("Event {} failed verification {:?} {}", event_id, pdu, e); + warn!( + %event_id, + %error, + ?pdu, + "Event failed verification", + ); back_off(event_id).await; return Err(Error::BadServerResponse("Event failed verification.")); } @@ -1375,11 +1386,11 @@ pub(crate) async fn invite_helper( if *pdu.event_id != *event_id { warn!( - "Server {} changed invite event, that's not allowed in the \ - spec: ours: {:?}, theirs: {:?}", - user_id.server_name(), - pdu_json, - value + server = %user_id.server_name(), + our_object = ?pdu_json, + their_object = ?value, + "Other server changed invite event, that's not allowed in the \ + spec", ); } @@ -1500,13 +1511,14 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> { }; if let Err(error) = leave_room(user_id, &room_id, None).await { - warn!(%user_id, %room_id, %error, "failed to leave room"); + warn!(%user_id, %room_id, %error, "Failed to leave room"); } } Ok(()) } +#[tracing::instrument(skip(reason))] pub(crate) async fn leave_room( user_id: &UserId, room_id: &RoomId, @@ -1580,8 +1592,8 @@ pub(crate) async fn leave_room( ) .await?; } else { - if let Err(e) = remote_leave_room(user_id, room_id).await { - warn!("Failed to leave room {} remotely: {}", user_id, e); + if let Err(error) = remote_leave_room(user_id, room_id).await { + warn!(%error, "Failed to leave room remotely"); // Don't tell the client about this error } From f5e10f5a8f65e01bfe697cbddaed8037e21c8255 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 15:49:37 -0700 Subject: [PATCH 283/617] fix api/ruma_wrapper/axum events --- src/api/ruma_wrapper/axum.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 79ac35c2..79fba3a5 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -25,7 +25,7 @@ use ruma::{ OwnedServerName, OwnedUserId, UserId, }; use serde::Deserialize; -use tracing::{debug, error, warn}; +use tracing::{error, warn}; use super::{Ar, Ra}; use crate::{service::appservice::RegistrationInfo, services, Error, Result}; @@ -81,8 +81,8 @@ async fn ar_from_request_inner( let query = parts.uri.query().unwrap_or_default(); let query_params: QueryParams = match serde_html_form::from_str(query) { Ok(params) => params, - Err(e) => { - error!(%query, "Failed to deserialize query parameters: {}", e); + Err(error) => { + error!(%error, %query, "Failed to deserialize query parameters"); return Err(Error::BadRequest( ErrorKind::Unknown, "Failed to read query parameters", @@ -181,10 +181,10 @@ async fn ar_from_request_inner( let TypedHeader(Authorization(x_matrix)) = parts .extract::>>() .await - .map_err(|e| { - warn!("Missing or invalid Authorization header: {}", e); + .map_err(|error| { + warn!(%error, "Missing or invalid Authorization header"); - let msg = match e.reason() { + let msg = match error.reason() { TypedHeaderRejectionReason::Missing => { "Missing Authorization header." } @@ -267,8 +267,8 @@ async fn ar_from_request_inner( let keys = match keys_result { Ok(b) => b, - Err(e) => { - warn!("Failed to fetch signing keys: {}", e); + Err(error) => { + warn!(%error, "Failed to fetch signing keys"); return Err(Error::BadRequest( ErrorKind::forbidden(), "Failed to fetch signing keys.", @@ -293,10 +293,12 @@ async fn ar_from_request_inner( match ruma::signatures::verify_json(&pub_key_map, &request_map) { Ok(()) => (None, None, Some(x_matrix.origin), None), - Err(e) => { + Err(error) => { warn!( - "Failed to verify json request from {}: {}\n{:?}", - x_matrix.origin, e, request_map + %error, + origin = %x_matrix.origin, + object = ?request_map, + "Failed to verify JSON request" ); if parts.uri.to_string().contains('@') { @@ -402,9 +404,12 @@ where let body = T::try_from_http_request(pieces.http_request, &pieces.path_params) - .map_err(|e| { - warn!("try_from_http_request failed: {:?}", e); - debug!("JSON body: {:?}", pieces.json_body); + .map_err(|error| { + warn!( + %error, + body = ?pieces.json_body, + "Request body JSON structure is incorrect" + ); Error::BadRequest( ErrorKind::BadJson, "Failed to deserialize request.", From 37beaea48b415af259c8b7b78abddcfef0af65fb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 16:12:36 -0700 Subject: [PATCH 284/617] fix main events --- src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4f2dc417..69ba7023 100644 --- a/src/main.rs +++ b/src/main.rs @@ -216,7 +216,7 @@ async fn run_server() -> Result<(), error::Serve> { } for listen in &config.listen { - info!("Listening for incoming traffic on {listen}"); + info!(listener = %listen, "Listening for incoming traffic"); match listen { ListenConfig::Tcp { address, @@ -283,7 +283,7 @@ async fn unrecognized_method( let uri = req.uri().clone(); let inner = next.run(req).await; if inner.status() == StatusCode::METHOD_NOT_ALLOWED { - warn!("Method not allowed: {method} {uri}"); + warn!(%method, %uri, "Method not allowed"); return Ok(Ra(UiaaResponse::MatrixError(RumaError { body: ErrorBody::Standard { kind: ErrorKind::Unrecognized, @@ -519,7 +519,7 @@ async fn shutdown_signal(handles: Vec) { () = terminate => { sig = "SIGTERM"; }, } - warn!(signal = %sig, "shutting down due to signal"); + warn!(signal = %sig, "Shutting down due to signal"); services().globals.shutdown(); @@ -537,7 +537,7 @@ async fn federation_disabled(_: Uri) -> impl IntoResponse { } async fn not_found(method: Method, uri: Uri) -> impl IntoResponse { - debug!(%method, %uri, "unknown route"); + debug!(%method, %uri, "Unknown route"); Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request") } @@ -657,11 +657,11 @@ fn maximize_fd_limit() -> Result<(), nix::errno::Errno> { let (soft_limit, hard_limit) = getrlimit(res)?; - debug!("Current nofile soft limit: {soft_limit}"); + debug!(soft_limit, "Current nofile soft limit"); setrlimit(res, hard_limit, hard_limit)?; - debug!("Increased nofile soft limit to {hard_limit}"); + debug!(hard_limit, "Increased nofile soft limit to the hard limit"); Ok(()) } From 162d01f6152f395ec51c6da567b647f950038b51 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 16:15:04 -0700 Subject: [PATCH 285/617] fix service/uiaa events --- src/service/uiaa.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 109e214d..5ca95ed1 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -118,7 +118,7 @@ impl Service { AuthData::Dummy(_) => { uiaainfo.completed.push(AuthType::Dummy); } - k => error!("type not supported: {:?}", k), + kind => error!(?kind, "Auth kind not supported"), } // Check if a flow now succeeds From e49fe04f10007b196046b94e5a6b5c692f9de0e0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 16:25:42 -0700 Subject: [PATCH 286/617] fix api/appservice_server events --- src/api/appservice_server.rs | 41 +++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index b6ef0465..117d1914 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -57,21 +57,19 @@ where *reqwest_request.timeout_mut() = Some(Duration::from_secs(30)); let url = reqwest_request.url().clone(); - let mut response = match services() + let mut response = services() .globals .default_client() .execute(reqwest_request) .await - { - Ok(r) => r, - Err(e) => { + .inspect_err(|error| { warn!( - "Could not send request to appservice {:?} at {}: {}", - registration.id, destination, e + %error, + appservice = registration.id, + %destination, + "Could not send request to appservice", ); - return Err(e.into()); - } - }; + })?; // reqwest::Response -> http::Response conversion let status = response.status(); @@ -85,18 +83,21 @@ where ); // TODO: handle timeout - let body = response.bytes().await.unwrap_or_else(|e| { - warn!("server error: {}", e); + let body = response.bytes().await.unwrap_or_else(|error| { + warn!(%error, "Server error"); Vec::new().into() }); if status != 200 { warn!( - "Appservice returned bad response {} {}\n{}\n{:?}", - destination, - status, - url, - utils::string_from_bytes(&body) + appservice = %destination, + %status, + %url, + body = %utils::dbg_truncate_str( + String::from_utf8_lossy(&body).as_ref(), + 100, + ), + "Appservice returned bad response", ); } @@ -106,10 +107,12 @@ where .expect("reqwest body is valid http body"), ); - response.map(Some).map_err(|_| { + response.map(Some).map_err(|error| { warn!( - "Appservice returned invalid response bytes {}\n{}", - destination, url + %error, + appservice = %destination, + %url, + "Appservice returned invalid response bytes", ); Error::BadServerResponse("Server returned bad response.") }) From a7cdbab44ad99741b6e1689352bf3a5db2b1b98d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 16:32:27 -0700 Subject: [PATCH 287/617] fix database events --- src/database.rs | 52 +++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/database.rs b/src/database.rs index 19ab1f23..13f989a2 100644 --- a/src/database.rs +++ b/src/database.rs @@ -350,7 +350,10 @@ impl KeyValueDatabase { } if config.max_request_size < 1024 { - error!(?config.max_request_size, "Max request size is less than 1KB. Please increase it."); + error!( + ?config.max_request_size, + "Max request size is less than 1KB. Please increase it.", + ); } let db_raw = Box::new(Self { @@ -543,9 +546,8 @@ impl KeyValueDatabase { let admin_bot = services().globals.admin_bot_user_id.as_ref(); if !services().users.exists(admin_bot)? { error!( - "The {} admin bot does not exist, and the database is not \ - new.", - admin_bot + user_id = %admin_bot, + "The admin bot does not exist and the database is not new", ); return Err(Error::bad_database( "Cannot reuse an existing database after changing the \ @@ -962,8 +964,12 @@ impl KeyValueDatabase { services().globals.server_name(), ) { Ok(u) => u, - Err(e) => { - warn!("Invalid username {username}: {e}"); + Err(error) => { + warn!( + %error, + user_localpart = %username, + "Invalid username", + ); continue; } }; @@ -1063,8 +1069,12 @@ impl KeyValueDatabase { services().globals.server_name(), ) { Ok(u) => u, - Err(e) => { - warn!("Invalid username {username}: {e}"); + Err(error) => { + warn!( + %error, + user_localpart = %username, + "Invalid username", + ); continue; } }; @@ -1116,9 +1126,9 @@ impl KeyValueDatabase { ); info!( - "Loaded {} database with version {}", - services().globals.config.database.backend, - latest_database_version + backend = %services().globals.config.database.backend, + version = latest_database_version, + "Loaded database", ); } else { services() @@ -1128,10 +1138,10 @@ impl KeyValueDatabase { // Create the admin room and server user on first run services().admin.create_admin_room().await?; - warn!( - "Created new {} database with version {}", - services().globals.config.database.backend, - latest_database_version + info!( + backend = %services().globals.config.database.backend, + version = latest_database_version, + "Created new database", ); } @@ -1155,11 +1165,11 @@ impl KeyValueDatabase { ); } } - Err(e) => { + Err(error) => { error!( + %error, "Could not set the configured emergency password for the \ - grapevine user: {}", - e + Grapevine user", ); } }; @@ -1207,10 +1217,10 @@ impl KeyValueDatabase { async { msg(); let start = Instant::now(); - if let Err(e) = services().globals.cleanup() { - error!("cleanup: Errored: {}", e); + if let Err(error) = services().globals.cleanup() { + error!(%error, "cleanup: Error"); } else { - debug!("cleanup: Finished in {:?}", start.elapsed()); + debug!(elapsed = ?start.elapsed(), "cleanup: Finished"); } } .instrument(info_span!("database_cleanup")) From b6cba0c4ae8293aad0b06a9a055967937001b2fb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 16:46:14 -0700 Subject: [PATCH 288/617] extract closure into a function This was mostly written by using rust-analyzer's "extract to function" and "extract to variable" functionality. --- src/api/client_server/directory.rs | 319 +++++++++++++---------------- 1 file changed, 148 insertions(+), 171 deletions(-) diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 9aea4127..3b010bec 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -197,177 +197,9 @@ pub(crate) async fn get_public_rooms_filtered_helper( .rooms .directory .public_rooms() - .map(|room_id| { - let room_id = room_id?; - - let chunk = PublicRoomsChunk { - canonical_alias: services() - .rooms - .state_accessor - .room_state_get( - &room_id, - &StateEventType::RoomCanonicalAlias, - "", - )? - .map_or(Ok(None), |s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomCanonicalAliasEventContent| c.alias) - .map_err(|_| { - Error::bad_database( - "Invalid canonical alias event in \ - database.", - ) - }) - })?, - name: services().rooms.state_accessor.get_name(&room_id)?, - num_joined_members: services() - .rooms - .state_cache - .room_joined_count(&room_id)? - .unwrap_or_else(|| { - warn!("Room {} has no member count", room_id); - 0 - }) - .try_into() - .expect("user count should not be that big"), - topic: services() - .rooms - .state_accessor - .room_state_get(&room_id, &StateEventType::RoomTopic, "")? - .map_or(Ok(None), |s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomTopicEventContent| Some(c.topic)) - .map_err(|_| { - error!( - "Invalid room topic event in database for \ - room {}", - room_id - ); - Error::bad_database( - "Invalid room topic event in database.", - ) - }) - })?, - world_readable: services() - .rooms - .state_accessor - .room_state_get( - &room_id, - &StateEventType::RoomHistoryVisibility, - "", - )? - .map_or(Ok(false), |s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomHistoryVisibilityEventContent| { - c.history_visibility - == HistoryVisibility::WorldReadable - }) - .map_err(|_| { - Error::bad_database( - "Invalid room history visibility event in \ - database.", - ) - }) - })?, - guest_can_join: services() - .rooms - .state_accessor - .room_state_get( - &room_id, - &StateEventType::RoomGuestAccess, - "", - )? - .map_or(Ok(false), |s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomGuestAccessEventContent| { - c.guest_access == GuestAccess::CanJoin - }) - .map_err(|_| { - Error::bad_database( - "Invalid room guest access event in \ - database.", - ) - }) - })?, - avatar_url: services() - .rooms - .state_accessor - .room_state_get(&room_id, &StateEventType::RoomAvatar, "")? - .map(|s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomAvatarEventContent| c.url) - .map_err(|_| { - Error::bad_database( - "Invalid room avatar event in database.", - ) - }) - }) - .transpose()? - .flatten(), - join_rule: services() - .rooms - .state_accessor - .room_state_get( - &room_id, - &StateEventType::RoomJoinRules, - "", - )? - .map(|s| { - serde_json::from_str(s.content.get()) - .map(|c: RoomJoinRulesEventContent| { - match c.join_rule { - JoinRule::Public => { - Some(PublicRoomJoinRule::Public) - } - JoinRule::Knock => { - Some(PublicRoomJoinRule::Knock) - } - _ => None, - } - }) - .map_err(|e| { - error!( - "Invalid room join rule event in \ - database: {}", - e - ); - Error::BadDatabase( - "Invalid room join rule event in database.", - ) - }) - }) - .transpose()? - .flatten() - .ok_or_else(|| { - Error::bad_database( - "Missing room join rule event for room.", - ) - })?, - room_type: services() - .rooms - .state_accessor - .room_state_get(&room_id, &StateEventType::RoomCreate, "")? - .map(|s| { - serde_json::from_str::( - s.content.get(), - ) - .map_err(|e| { - error!( - "Invalid room create event in database: {}", - e - ); - Error::BadDatabase( - "Invalid room create event in database.", - ) - }) - }) - .transpose()? - .and_then(|e| e.room_type), - room_id, - }; - Ok(chunk) - }) - .filter_map(Result::<_>::ok) + .filter_map(Result::ok) + .map(room_id_to_chunk) + .filter_map(Result::ok) .filter(|chunk| { if let Some(query) = filter.generic_search_term.as_ref().map(|q| q.to_lowercase()) @@ -430,3 +262,148 @@ pub(crate) async fn get_public_rooms_filtered_helper( total_room_count_estimate: Some(total_room_count_estimate), }) } + +#[allow(clippy::too_many_lines)] +fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { + let canonical_alias = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomCanonicalAlias, "")? + .map_or(Ok(None), |s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomCanonicalAliasEventContent| c.alias) + .map_err(|_| { + Error::bad_database( + "Invalid canonical alias event in database.", + ) + }) + })?; + + let name = services().rooms.state_accessor.get_name(&room_id)?; + + let num_joined_members = services() + .rooms + .state_cache + .room_joined_count(&room_id)? + .unwrap_or_else(|| { + warn!("Room {} has no member count", room_id); + 0 + }) + .try_into() + .expect("user count should not be that big"); + + let topic = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomTopic, "")? + .map_or(Ok(None), |s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomTopicEventContent| Some(c.topic)) + .map_err(|_| { + error!( + "Invalid room topic event in database for room {}", + room_id + ); + Error::bad_database("Invalid room topic event in database.") + }) + })?; + + let world_readable = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomHistoryVisibility, "")? + .map_or(Ok(false), |s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomHistoryVisibilityEventContent| { + c.history_visibility == HistoryVisibility::WorldReadable + }) + .map_err(|_| { + Error::bad_database( + "Invalid room history visibility event in database.", + ) + }) + })?; + + let guest_can_join = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomGuestAccess, "")? + .map_or(Ok(false), |s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomGuestAccessEventContent| { + c.guest_access == GuestAccess::CanJoin + }) + .map_err(|_| { + Error::bad_database( + "Invalid room guest access event in database.", + ) + }) + })?; + + let avatar_url = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomAvatar, "")? + .map(|s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomAvatarEventContent| c.url) + .map_err(|_| { + Error::bad_database( + "Invalid room avatar event in database.", + ) + }) + }) + .transpose()? + .flatten(); + + let join_rule = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomJoinRules, "")? + .map(|s| { + serde_json::from_str(s.content.get()) + .map(|c: RoomJoinRulesEventContent| match c.join_rule { + JoinRule::Public => Some(PublicRoomJoinRule::Public), + JoinRule::Knock => Some(PublicRoomJoinRule::Knock), + _ => None, + }) + .map_err(|e| { + error!("Invalid room join rule event in database: {}", e); + Error::BadDatabase( + "Invalid room join rule event in database.", + ) + }) + }) + .transpose()? + .flatten() + .ok_or_else(|| { + Error::bad_database("Missing room join rule event for room.") + })?; + + let room_type = services() + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomCreate, "")? + .map(|s| { + serde_json::from_str::(s.content.get()) + .map_err(|e| { + error!("Invalid room create event in database: {}", e); + Error::BadDatabase("Invalid room create event in database.") + }) + }) + .transpose()? + .and_then(|e| e.room_type); + + Ok(PublicRoomsChunk { + canonical_alias, + name, + num_joined_members, + room_id, + topic, + world_readable, + guest_can_join, + avatar_url, + join_rule, + room_type, + }) +} From db666fe9031d795ed491afad6a79b5873bdd5317 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 16:50:12 -0700 Subject: [PATCH 289/617] fix api/client_server/directory events --- src/api/client_server/directory.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 3b010bec..3d4e1e06 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -93,7 +93,11 @@ pub(crate) async fn set_room_visibility_route( match &body.visibility { room::Visibility::Public => { services().rooms.directory.set_public(&body.room_id)?; - info!("{} made {} public", sender_user, body.room_id); + info!( + user_id = %sender_user, + room_id = %body.room_id, + "User made room public", + ); } room::Visibility::Private => { services().rooms.directory.set_not_public(&body.room_id)?; @@ -264,6 +268,7 @@ pub(crate) async fn get_public_rooms_filtered_helper( } #[allow(clippy::too_many_lines)] +#[tracing::instrument] fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { let canonical_alias = services() .rooms @@ -286,7 +291,7 @@ fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { .state_cache .room_joined_count(&room_id)? .unwrap_or_else(|| { - warn!("Room {} has no member count", room_id); + warn!("Room has no member count"); 0 }) .try_into() @@ -300,10 +305,7 @@ fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { serde_json::from_str(s.content.get()) .map(|c: RoomTopicEventContent| Some(c.topic)) .map_err(|_| { - error!( - "Invalid room topic event in database for room {}", - room_id - ); + error!("Invalid room topic event in database for room",); Error::bad_database("Invalid room topic event in database.") }) })?; @@ -367,8 +369,8 @@ fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { JoinRule::Knock => Some(PublicRoomJoinRule::Knock), _ => None, }) - .map_err(|e| { - error!("Invalid room join rule event in database: {}", e); + .map_err(|error| { + error!(%error, "Invalid room join rule event in database"); Error::BadDatabase( "Invalid room join rule event in database.", ) @@ -386,8 +388,8 @@ fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { .room_state_get(&room_id, &StateEventType::RoomCreate, "")? .map(|s| { serde_json::from_str::(s.content.get()) - .map_err(|e| { - error!("Invalid room create event in database: {}", e); + .map_err(|error| { + error!(%error, "Invalid room create event in database"); Error::BadDatabase("Invalid room create event in database.") }) }) From 9d8e1a14904fca0a6afb76a7cd09aa9104d15bc8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:11:50 -0700 Subject: [PATCH 290/617] fix api/server_server events --- src/api/server_server.rs | 122 +++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 57 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 1f9353ab..e2841acb 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -180,10 +180,11 @@ where SendAccessToken::IfRequired(""), &[MatrixVersion::V1_4], ) - .map_err(|e| { + .map_err(|error| { warn!( - "Failed to find destination {}: {}", - actual_destination_str, e + %error, + actual_destination = actual_destination_str, + "Failed to find destination", ); Error::BadServerResponse("Invalid destination") })?; @@ -278,8 +279,8 @@ where debug!("Getting response bytes"); // TODO: handle timeout - let body = response.bytes().await.unwrap_or_else(|e| { - warn!("server error {}", e); + let body = response.bytes().await.unwrap_or_else(|error| { + warn!(%error, "Server error"); Vec::new().into() }); debug!("Got response bytes"); @@ -344,11 +345,11 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { /// Numbers in comments below refer to bullet points in linked section of /// specification #[allow(clippy::too_many_lines)] -#[tracing::instrument(skip(destination), ret(level = "debug"))] +#[tracing::instrument(ret(level = "debug"))] async fn find_actual_destination( destination: &'_ ServerName, ) -> (FedDest, FedDest) { - debug!("Finding actual destination for {destination}"); + debug!("Finding actual destination"); let destination_str = destination.as_str().to_owned(); let mut hostname = destination_str.clone(); let actual_destination = match get_ip_with_port(&destination_str) { @@ -362,7 +363,7 @@ async fn find_actual_destination( let (host, port) = destination_str.split_at(pos); FedDest::Named(host.to_owned(), port.to_owned()) } else { - debug!("Requesting well known for {destination}"); + debug!(%destination, "Requesting well known"); if let Some(delegated_hostname) = request_well_known(destination.as_str()).await { @@ -471,7 +472,7 @@ async fn find_actual_destination( } } }; - debug!("Actual destination: {actual_destination:?}"); + debug!(?actual_destination, "Resolved actual destination"); // Can't use get_ip_with_port here because we don't want to add a port // to an IP address if it wasn't specified @@ -532,8 +533,8 @@ async fn request_well_known(destination: &str) -> Option { .send() .await; debug!("Got well known response"); - if let Err(e) = &response { - debug!("Well known error: {e:?}"); + if let Err(error) = &response { + debug!(%error, "Failed to request .well-known"); return None; } let text = response.ok()?.text().await; @@ -670,8 +671,8 @@ pub(crate) fn parse_incoming_pdu( pdu: &RawJsonValue, ) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> { let value: CanonicalJsonObject = - serde_json::from_str(pdu.get()).map_err(|e| { - warn!("Error parsing incoming event {:?}: {:?}", pdu, e); + serde_json::from_str(pdu.get()).map_err(|error| { + warn!(%error, object = ?pdu, "Error parsing incoming event"); Error::BadServerResponse("Invalid PDU in server response") })?; @@ -713,8 +714,8 @@ pub(crate) async fn send_transaction_message_route( for pdu in &body.pdus { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()) - .map_err(|e| { - warn!("Error parsing incoming event {:?}: {:?}", pdu, e); + .map_err(|error| { + warn!(%error, object = ?pdu, "Error parsing incoming event"); Error::BadServerResponse("Invalid PDU in server response") })?; let room_id: OwnedRoomId = value @@ -726,16 +727,15 @@ pub(crate) async fn send_transaction_message_route( ))?; if services().rooms.state.get_room_version(&room_id).is_err() { - debug!("Server is not in room {room_id}"); + debug!(%room_id, "This server is not in the room"); continue; } let r = parse_incoming_pdu(pdu); let (event_id, value, room_id) = match r { Ok(t) => t, - Err(e) => { - warn!("Could not parse PDU: {e}"); - warn!("Full PDU: {:?}", &pdu); + Err(error) => { + warn!(%error, object = ?pdu, "Error parsing incoming event"); continue; } }; @@ -771,19 +771,17 @@ pub(crate) async fn send_transaction_message_route( ); drop(mutex_lock); - let elapsed = start_time.elapsed(); debug!( - "Handling transaction of event {} took {}m{}s", - event_id, - elapsed.as_secs() / 60, - elapsed.as_secs() % 60 + %event_id, + elapsed = ?start_time.elapsed(), + "Finished handling event", ); } for pdu in &resolved_map { - if let Err(e) = pdu.1 { - if matches!(e, Error::BadRequest(ErrorKind::NotFound, _)) { - warn!("Incoming PDU failed {:?}", pdu); + if let (event_id, Err(error)) = pdu { + if matches!(error, Error::BadRequest(ErrorKind::NotFound, _)) { + warn!(%error, %event_id, "Incoming PDU failed"); } } } @@ -845,8 +843,8 @@ pub(crate) async fn send_transaction_message_route( } else { // TODO fetch missing events debug!( - "No known event ids in read receipt: {:?}", - user_updates + ?user_updates, + "No known event ids in read receipt", ); } } @@ -933,16 +931,19 @@ pub(crate) async fn send_transaction_message_route( target_user_id, target_device_id, &ev_type.to_string(), - event.deserialize_as().map_err(|e| { - warn!( - "To-Device event is invalid: \ - {event:?} {e}" - ); - Error::BadRequest( - ErrorKind::InvalidParam, - "Event is invalid", - ) - })?, + event.deserialize_as().map_err( + |error| { + warn!( + %error, + object = ?event.json(), + "To-Device event is invalid", + ); + Error::BadRequest( + ErrorKind::InvalidParam, + "Event is invalid", + ) + }, + )?, )?, DeviceIdOrAllDevices::AllDevices => { @@ -985,7 +986,12 @@ pub(crate) async fn send_transaction_message_route( self_signing_key, }) => { if user_id.server_name() != sender_servername { - warn!(%user_id, %sender_servername, "Got signing key update from incorrect homeserver, ignoring"); + warn!( + %user_id, + %sender_servername, + "Got signing key update from incorrect homeserver, \ + ignoring", + ); continue; } if let Some(master_key) = master_key { @@ -1025,7 +1031,7 @@ pub(crate) async fn get_event_route( let event = services().rooms.timeline.get_pdu_json(&body.event_id)?.ok_or_else( || { - warn!("Event not found, event ID: {:?}", &body.event_id); + warn!(event_id = %body.event_id, "Event not found"); Error::BadRequest(ErrorKind::NotFound, "Event not found.") }, )?; @@ -1078,7 +1084,7 @@ pub(crate) async fn get_backfill_route( let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); - debug!("Got backfill request from: {}", sender_servername); + debug!(server = %sender_servername, "Got backfill request"); if !services() .rooms @@ -1188,9 +1194,10 @@ pub(crate) async fn get_missing_events_route( if event_room_id != body.room_id { warn!( - "Evil event detected: Event {} found while searching in \ - room {}", - queued_events[i], body.room_id + event_id = %queued_events[i], + expected_room_id = %body.room_id, + actual_room_id = %event_room_id, + "Evil event detected" ); return Err(Error::BadRequest( ErrorKind::InvalidParam, @@ -1269,7 +1276,7 @@ pub(crate) async fn get_event_authorization_route( let event = services().rooms.timeline.get_pdu_json(&body.event_id)?.ok_or_else( || { - warn!("Event not found, event ID: {:?}", &body.event_id); + warn!(event_id = %body.event_id, "Event not found"); Error::BadRequest(ErrorKind::NotFound, "Event not found.") }, )?; @@ -1354,13 +1361,13 @@ pub(crate) async fn get_room_state_route( Ok(Ra(get_room_state::v1::Response { auth_chain: auth_chain_ids - .filter_map(|id| { + .filter_map(|event_id| { if let Some(json) = - services().rooms.timeline.get_pdu_json(&id).ok()? + services().rooms.timeline.get_pdu_json(&event_id).ok()? { Some(PduEvent::convert_to_outgoing_federation_event(json)) } else { - error!("Could not find event json for {id} in db."); + error!(%event_id, "Could not find event JSON for event"); None } }) @@ -1469,8 +1476,8 @@ pub(crate) async fn create_join_event_template_route( .as_ref() .map(|join_rules_event| { serde_json::from_str(join_rules_event.content.get()).map_err( - |e| { - warn!("Invalid join rules event: {}", e); + |error| { + warn!(%error, "Invalid join rules event"); Error::bad_database("Invalid join rules event in db.") }, ) @@ -1565,8 +1572,8 @@ async fn create_join_event( .as_ref() .map(|join_rules_event| { serde_json::from_str(join_rules_event.content.get()).map_err( - |e| { - warn!("Invalid join rules event: {}", e); + |error| { + warn!(%error, "Invalid join rules event"); Error::bad_database("Invalid join rules event in db.") }, ) @@ -1829,10 +1836,11 @@ pub(crate) async fn create_invite_route( event.insert("event_id".to_owned(), "$dummy".into()); - let pdu: PduEvent = serde_json::from_value(event.into()).map_err(|e| { - warn!("Invalid invite event: {}", e); - Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event.") - })?; + let pdu: PduEvent = + serde_json::from_value(event.into()).map_err(|error| { + warn!(%error, "Invalid invite event"); + Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event.") + })?; invite_state.push(pdu.to_stripped_state_event()); From 52decf0ceafef6023679d1fe8e2d4cecdbfd8215 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:43:38 -0700 Subject: [PATCH 291/617] fix service/pusher events --- src/service/pusher.rs | 46 ++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 94cb7fb4..adc56c2f 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -25,9 +25,9 @@ use ruma::{ serde::Raw, uint, RoomId, UInt, UserId, }; -use tracing::{info, warn}; +use tracing::warn; -use crate::{services, Error, PduEvent, Result}; +use crate::{services, utils, Error, PduEvent, Result}; pub(crate) struct Service { pub(crate) db: &'static dyn Data, @@ -78,8 +78,8 @@ impl Service { SendAccessToken::IfRequired(""), &[MatrixVersion::V1_0], ) - .map_err(|e| { - warn!("Failed to find destination {}: {}", destination, e); + .map_err(|error| { + warn!(%error, %destination, "Failed to find destination"); Error::BadServerResponse("Invalid destination") })? .map(BytesMut::freeze); @@ -105,18 +105,21 @@ impl Service { ); // TODO: handle timeout - let body = response.bytes().await.unwrap_or_else(|e| { - warn!("server error {}", e); + let body = response.bytes().await.unwrap_or_else(|error| { + warn!(%error, "Server error"); Vec::new().into() }); if status != 200 { - info!( - "Push gateway returned bad response {} {}\n{}\n{:?}", - destination, - status, - url, - crate::utils::string_from_bytes(&body) + warn!( + push_gateway = %destination, + %status, + %url, + body = %utils::dbg_truncate_str( + String::from_utf8_lossy(&body).as_ref(), + 100, + ), + "Push gateway returned bad response", ); } @@ -125,22 +128,25 @@ impl Service { .body(body) .expect("reqwest body is valid http body"), ); - response.map_err(|_| { - info!( - "Push gateway returned invalid response bytes {}\n{}", - destination, url + response.map_err(|error| { + warn!( + %error, + appservice = %destination, + %url, + "Push gateway returned invalid response bytes", ); Error::BadServerResponse( "Push gateway returned bad response.", ) }) } - Err(e) => { + Err(error) => { warn!( - "Could not send request to pusher {}: {}", - destination, e + %error, + %destination, + "Could not send request to push gateway", ); - Err(e.into()) + Err(error.into()) } } } From 5a376ceb0a74b4a4deae39251164dc009da7e6ea Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:47:28 -0700 Subject: [PATCH 292/617] fix database/key_value/rooms/timeline events --- src/database/key_value/rooms/timeline.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 5e64cd7c..0633e1d1 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -31,11 +31,12 @@ impl service::rooms::timeline::Data for KeyValueDatabase { hash_map::Entry::Vacant(v) => { if let Some(last_count) = self .pdus_until(sender_user, room_id, PduCount::MAX)? - .find_map(|r| { - if r.is_err() { - error!("Bad pdu in pdus_since: {:?}", r); + .find_map(|x| match x { + Ok(x) => Some(x), + Err(error) => { + error!(%error, "Bad pdu in pdus_since"); + None } - r.ok() }) { METRICS.record_lookup(lookup, FoundIn::Database); From 7392880bf8622eab39f345373ebdff9e800a9cbb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:50:19 -0700 Subject: [PATCH 293/617] fix utils/error events --- src/utils/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/error.rs b/src/utils/error.rs index eabfd943..776c1a3e 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -86,12 +86,12 @@ pub(crate) enum Error { impl Error { pub(crate) fn bad_database(message: &'static str) -> Self { - error!("BadDatabase: {}", message); + error!(message, "Bad database"); Self::BadDatabase(message) } pub(crate) fn bad_config(message: &'static str) -> Self { - error!("BadConfig: {}", message); + error!(message, "Bad config"); Self::BadConfig(message) } From 42e397203a95775cdbdc4d15afa26a6f49fa027d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:51:28 -0700 Subject: [PATCH 294/617] fix service/pdu events --- src/service/pdu.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service/pdu.rs b/src/service/pdu.rs index c538f80c..b6052a4c 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -474,8 +474,8 @@ pub(crate) fn gen_event_id_canonical_json( room_version_id: &RoomVersionId, ) -> crate::Result<(OwnedEventId, CanonicalJsonObject)> { let value: CanonicalJsonObject = - serde_json::from_str(pdu.get()).map_err(|e| { - warn!("Error parsing incoming event {:?}: {:?}", pdu, e); + serde_json::from_str(pdu.get()).map_err(|error| { + warn!(%error, object = ?pdu, "Error parsing incoming event"); Error::BadServerResponse("Invalid PDU in server response") })?; From 0aef00c58bc50df8c5e7e58f654ffbdd8e84bb12 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:52:16 -0700 Subject: [PATCH 295/617] fix api/client_server/keys events --- src/api/client_server/keys.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 1c21e40b..2f14733a 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -414,7 +414,7 @@ pub(crate) async fn get_keys_helper bool>( } if time.elapsed() < min_elapsed_duration { - debug!("Backing off query from {:?}", server); + debug!(%server, "Backing off from server"); return ( server, Err(Error::BadServerResponse( From 60b89aba78667bf689605972f0fd1e1caa4a9f53 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 17:56:43 -0700 Subject: [PATCH 296/617] fix api/client_server/session events And also add a new event for logging out to match the one for logging in because why not. --- src/api/client_server/session.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 123a66c7..188397cf 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -79,7 +79,7 @@ pub(crate) async fn login_route( } else if let Some(user) = user { UserId::parse(user) } else { - warn!("Bad login type: {:?}", &body.login_info); + warn!(kind = ?body.login_info, "Bad login kind"); return Err(Error::BadRequest( ErrorKind::forbidden(), "Bad login type.", @@ -184,7 +184,7 @@ pub(crate) async fn login_route( } else if let Some(user) = user { UserId::parse(user) } else { - warn!("Bad login type: {:?}", &body.login_info); + warn!(kind = ?body.login_info, "Bad login kind"); return Err(Error::BadRequest( ErrorKind::forbidden(), "Bad login type.", @@ -214,7 +214,7 @@ pub(crate) async fn login_route( user_id } _ => { - warn!("Unsupported or unknown login type: {:?}", &body.login_info); + warn!(kind = ?body.login_info, "Unsupported or unknown login kind"); return Err(Error::BadRequest( ErrorKind::Unknown, "Unsupported login type.", @@ -250,7 +250,7 @@ pub(crate) async fn login_route( )?; } - info!("{} logged in", user_id); + info!(%user_id, %device_id, "User logged in"); // Homeservers are still required to send the `home_server` field #[allow(deprecated)] @@ -292,6 +292,8 @@ pub(crate) async fn logout_route( services().users.remove_device(sender_user, sender_device)?; + info!(user_id = %sender_user, device_id = %sender_device, "User logged out"); + Ok(Ra(logout::v3::Response::new())) } From e7087444dbeb59ac4b32dd2da05858d1cb42f87c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 20:40:31 -0700 Subject: [PATCH 297/617] fix service/sending events --- src/service/sending.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 32573cc3..832380c9 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -190,8 +190,10 @@ impl Service { if entry.len() > 30 { warn!( - "Dropping some current events: {:?} {:?} {:?}", - key, destination, event + ?key, + ?destination, + ?event, + "Dropping some current events", ); self.db.delete_active_request(key)?; continue; @@ -324,7 +326,7 @@ impl Service { current_transaction_status, ) { Ok(SelectedEvents::Retries(events)) => { - debug!("retrying old events"); + debug!("Retrying old events"); Some(HandlerInputs { destination, events, @@ -332,7 +334,7 @@ impl Service { }) } Ok(SelectedEvents::New(events)) => { - debug!("sending new event"); + debug!("Sending new event"); Some(HandlerInputs { destination, events, @@ -340,7 +342,7 @@ impl Service { }) } Ok(SelectedEvents::None) => { - debug!("holding off from sending any events"); + debug!("Holding off from sending any events"); None } Err(error) => { @@ -678,7 +680,7 @@ impl Service { ) .await .map_err(|_| { - warn!("Timeout waiting for server response of {destination}"); + warn!("Timeout waiting for server response"); Error::BadServerResponse("Timeout waiting for server response") })?; drop(permit); @@ -873,7 +875,7 @@ async fn handle_federation_event( .timeline .get_pdu_json_from_id(pdu_id)? .ok_or_else(|| { - error!("event not found: {server} {pdu_id:?}"); + error!(pdu_id = ?pdu_id, "PDU not found"); Error::bad_database( "[Normal] Event in servernamevent_datas not \ found in db.", @@ -915,8 +917,8 @@ async fn handle_federation_event( .await?; for pdu in response.pdus { - if pdu.1.is_err() { - warn!("Failed to send to {}: {:?}", server, pdu); + if let (event_id, Err(error)) = pdu { + warn!(%server, %event_id, %error, "Failed to send event to server"); } } From 1283dc64775b7e4c473cdbfed5a562fbd0d046f8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 20:44:16 -0700 Subject: [PATCH 298/617] fix service/rooms/state_accessor events --- src/service/rooms/state_accessor.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 96839c9f..99502700 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -188,8 +188,8 @@ impl Service { current_server_members .any(|member| self.user_was_joined(shortstatehash, &member)) } - _ => { - error!("Unknown history visibility {history_visibility}"); + other => { + error!(kind = %other, "Unknown history visibility"); false } }; @@ -261,8 +261,8 @@ impl Service { // Allow if any member on requested server was joined, else deny self.user_was_joined(shortstatehash, user_id) } - _ => { - error!("Unknown history visibility {history_visibility}"); + other => { + error!(kind = %other, "Unknown history visibility"); false } }; @@ -358,13 +358,9 @@ impl Service { |s| { serde_json::from_str(s.content.get()) .map(|c: RoomNameEventContent| Some(c.name)) - .map_err(|e| { - error!( - "Invalid room name event in database for room {}. \ - {}", - room_id, e - ); - Error::bad_database( + .map_err(|error| { + error!(%error, "Invalid room name event in database"); + Error::BadDatabase( "Invalid room name event in database.", ) }) From d2fc2fad1fee376879d020927ff8943279e8c0cd Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 20:45:10 -0700 Subject: [PATCH 299/617] fix service/rooms/state events --- src/service/rooms/state.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 32037633..709c70d2 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -348,10 +348,12 @@ impl Service { let create_event_content: RoomCreateEventContent = create_event .as_ref() .map(|create_event| { - serde_json::from_str(create_event.content.get()).map_err(|e| { - warn!("Invalid create event: {}", e); - Error::bad_database("Invalid create event in db.") - }) + serde_json::from_str(create_event.content.get()).map_err( + |error| { + warn!(%error, "Invalid create event"); + Error::BadDatabase("Invalid create event in db.") + }, + ) }) .transpose()? .ok_or_else(|| { From 196c923a5aaee60baee177a2e5bc23a0e585833f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 20:47:19 -0700 Subject: [PATCH 300/617] fix api/client_server/account events --- src/api/client_server/account.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 1b180766..25202750 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -276,7 +276,7 @@ pub(crate) async fn register_route( body.initial_device_display_name.clone(), )?; - info!("New user {} registered on this server.", user_id); + info!(%user_id, "New user registered on this server"); if body.appservice_info.is_none() && !is_guest { services().admin.send_message(RoomMessageEventContent::notice_plain( format!("New user {user_id} registered on this server."), @@ -293,8 +293,8 @@ pub(crate) async fn register_route( services().admin.make_user_admin(&user_id, displayname).await?; warn!( - "Granting {} admin privileges as the first user", - user_id + %user_id, + "Granting admin privileges to the first user", ); } } @@ -376,7 +376,7 @@ pub(crate) async fn change_password_route( } } - info!("User {} changed their password.", sender_user); + info!(user_id = %sender_user, "User changed their password"); services().admin.send_message(RoomMessageEventContent::notice_plain( format!("User {sender_user} changed their password."), )); @@ -456,7 +456,7 @@ pub(crate) async fn deactivate_route( // Remove devices and mark account as deactivated services().users.deactivate_account(sender_user)?; - info!("User {} deactivated their account.", sender_user); + info!(user_id = %sender_user, "User deactivated their account"); services().admin.send_message(RoomMessageEventContent::notice_plain( format!("User {sender_user} deactivated their account."), )); From 4b9c6a754c832360d28ad11b5a6191cc4e6282d7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 20:51:15 -0700 Subject: [PATCH 301/617] fix api/client_server/room events --- src/api/client_server/room.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 843d9aea..b73af16f 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -442,8 +442,8 @@ pub(crate) async fn create_room_route( // 6. Events listed in initial_state for event in &body.initial_state { let mut pdu_builder = - event.deserialize_as::().map_err(|e| { - warn!("Invalid initial state event: {:?}", e); + event.deserialize_as::().map_err(|error| { + warn!(%error, "Invalid initial state event"); Error::BadRequest( ErrorKind::InvalidParam, "Invalid initial state event.", @@ -524,7 +524,7 @@ pub(crate) async fn create_room_route( invite_helper(sender_user, user_id, &room_id, None, body.is_direct) .await { - warn!(%error, "invite helper failed"); + warn!(%error, "Invite helper failed"); }; } @@ -537,7 +537,7 @@ pub(crate) async fn create_room_route( services().rooms.directory.set_public(&room_id)?; } - info!("{} created a room", sender_user); + info!(user_id = %sender_user, room_id = %room_id, "User created a room"); Ok(Ra(create_room::v3::Response::new(room_id))) } @@ -555,7 +555,7 @@ pub(crate) async fn get_room_event_route( let event = services().rooms.timeline.get_pdu(&body.event_id)?.ok_or_else( || { - warn!("Event not found, event ID: {:?}", &body.event_id); + warn!(event_id = %body.event_id, "Event not found"); Error::BadRequest(ErrorKind::NotFound, "Event not found.") }, )?; From dacb2ae7dd8ad3b2ff62ae48aab248652a06d9ac Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 20:52:41 -0700 Subject: [PATCH 302/617] fix api/client_server/context events --- src/api/client_server/context.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 989b1c66..76f3d2f0 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -159,19 +159,21 @@ pub(crate) async fn get_context_route( let mut state = Vec::new(); - for (shortstatekey, id) in state_ids { + for (shortstatekey, event_id) in state_ids { let (event_type, state_key) = services().rooms.short.get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { - error!("Pdu in state not found: {}", id); + let Some(pdu) = services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); continue; }; state.push(pdu.to_state_event()); } else if !lazy_load_enabled || lazy_loaded.contains(&state_key) { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { - error!("Pdu in state not found: {}", id); + let Some(pdu) = services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); continue; }; state.push(pdu.to_state_event()); From ee95b6be3c4e25ef700c14205e0697181cb19bee Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 21:00:19 -0700 Subject: [PATCH 303/617] fix api/client_server/sync events Some duplicated-ish code from src/database/key_value/rooms/timeline.rs about handling errors from `pdus_since`/`pdus_until`, it seems like the error message was actually directly copy-pasted from there because it referred to the wrong function lol --- src/api/client_server/sync.rs | 68 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index ae779f92..64069f66 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -276,8 +276,8 @@ pub(crate) async fn sync_events_route( left_state_ids.insert(leave_shortstatekey, left_event_id); let mut i = 0; - for (key, id) in left_state_ids { - if full_state || since_state_ids.get(&key) != Some(&id) { + for (key, event_id) in left_state_ids { + if full_state || since_state_ids.get(&key) != Some(&event_id) { let (event_type, state_key) = services().rooms.short.get_statekey_from_short(key)?; @@ -287,9 +287,10 @@ pub(crate) async fn sync_events_route( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? else { - error!("Pdu in state not found: {}", id); + error!(%event_id, "Event in state not found"); continue; }; @@ -455,7 +456,7 @@ pub(crate) async fn sync_events_route( } match tokio::time::timeout(duration, watcher).await { Ok(x) => x.expect("watcher should succeed"), - Err(error) => debug!(%error, "timed out"), + Err(error) => debug!(%error, "Timed out"), }; } Ok(Ra(response)) @@ -524,7 +525,7 @@ async fn load_joined_room( let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else { - error!("Room {} has no state", room_id); + error!("Room has no state"); return Err(Error::BadDatabase("Room has no state")); }; @@ -672,16 +673,17 @@ async fn load_joined_room( let mut lazy_loaded = HashSet::new(); let mut i = 0; - for (shortstatekey, id) in current_state_ids { + for (shortstatekey, event_id) in current_state_ids { let (event_type, state_key) = services() .rooms .short .get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? else { - error!("Pdu in state not found: {}", id); + error!(%event_id, "Event in state not found"); continue; }; state_events.push(pdu); @@ -696,9 +698,10 @@ async fn load_joined_room( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let Some(pdu) = services().rooms.timeline.get_pdu(&id)? + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? else { - error!("Pdu in state not found: {}", id); + error!(%event_id, "Event in state not found"); continue; }; @@ -762,12 +765,14 @@ async fn load_joined_room( .state_full_ids(since_shortstatehash) .await?; - for (key, id) in current_state_ids { - if full_state || since_state_ids.get(&key) != Some(&id) { + for (key, event_id) in current_state_ids { + if full_state + || since_state_ids.get(&key) != Some(&event_id) + { let Some(pdu) = - services().rooms.timeline.get_pdu(&id)? + services().rooms.timeline.get_pdu(&event_id)? else { - error!("Pdu in state not found: {}", id); + error!(%event_id, "Event in state not found"); continue; }; @@ -895,8 +900,12 @@ async fn load_joined_room( Ok(state_key_userid) => { lazy_loaded.insert(state_key_userid); } - Err(e) => { - error!("Invalid state key for member event: {}", e); + Err(error) => { + error!( + event_id = %pdu.event_id, + %error, + "Invalid state key for member event", + ); } } } @@ -974,7 +983,7 @@ async fn load_joined_room( |(pdu_count, _)| { Ok(Some(match pdu_count { PduCount::Backfilled(_) => { - error!("timeline in backfill state?!"); + error!("Timeline in backfill state?!"); "0".to_owned() } PduCount::Normal(c) => c.to_string(), @@ -1074,11 +1083,12 @@ fn load_timeline( .rooms .timeline .pdus_until(sender_user, room_id, PduCount::MAX)? - .filter_map(|r| { - if r.is_err() { - error!("Bad pdu in pdus_since: {:?}", r); + .filter_map(|x| match x { + Ok(x) => Some(x), + Err(error) => { + error!(%error, "Bad PDU in pdus_since"); + None } - r.ok() }) .take_while(|(pducount, _)| pducount > &roomsincecount); @@ -1195,7 +1205,7 @@ pub(crate) async fn sync_events_v4_route( let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else { - error!("Room {} has no state", room_id); + error!(%room_id, "Room has no state"); continue; }; @@ -1268,12 +1278,12 @@ pub(crate) async fn sync_events_v4_route( .state_full_ids(since_shortstatehash) .await?; - for (key, id) in current_state_ids { - if since_state_ids.get(&key) != Some(&id) { + for (key, event_id) in current_state_ids { + if since_state_ids.get(&key) != Some(&event_id) { let Some(pdu) = - services().rooms.timeline.get_pdu(&id)? + services().rooms.timeline.get_pdu(&event_id)? else { - error!("Pdu in state not found: {}", id); + error!(%event_id, "Event in state not found"); continue; }; if pdu.kind == TimelineEventType::RoomMember { @@ -1552,7 +1562,7 @@ pub(crate) async fn sync_events_v4_route( .map_or(Ok::<_, Error>(None), |(pdu_count, _)| { Ok(Some(match pdu_count { PduCount::Backfilled(_) => { - error!("timeline in backfill state?!"); + error!("Timeline in backfill state?!"); "0".to_owned() } PduCount::Normal(c) => c.to_string(), @@ -1704,7 +1714,7 @@ pub(crate) async fn sync_events_v4_route( } match tokio::time::timeout(duration, watcher).await { Ok(x) => x.expect("watcher should succeed"), - Err(error) => debug!(%error, "timed out"), + Err(error) => debug!(%error, "Timed out"), }; } From 592e42ba7de1847ab5d2f23c25a5fa757c8d1436 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 21:04:35 -0700 Subject: [PATCH 304/617] fix service/rooms/timeline events --- src/service/rooms/timeline.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 038f71e9..f850057d 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -244,7 +244,7 @@ impl Service { } } } else { - error!("Invalid unsigned type in pdu."); + error!("Invalid unsigned type in pdu"); } } @@ -1251,7 +1251,7 @@ impl Service { // Request backfill for backfill_server in admin_servers { - info!("Asking {backfill_server} for backfill"); + info!(server = %backfill_server, "Asking server for backfill"); let response = services() .sending .send_federation_request( @@ -1267,17 +1267,21 @@ impl Service { Ok(response) => { let pub_key_map = RwLock::new(BTreeMap::new()); for pdu in response.pdus { - if let Err(e) = self + if let Err(error) = self .backfill_pdu(backfill_server, pdu, &pub_key_map) .await { - warn!("Failed to add backfilled pdu: {e}"); + warn!(%error, "Failed to add backfilled pdu"); } } return Ok(()); } - Err(e) => { - warn!("{backfill_server} could not provide backfill: {e}"); + Err(error) => { + warn!( + server = %backfill_server, + %error, + "Server could not provide backfill", + ); } } } @@ -1310,7 +1314,7 @@ impl Service { // Skip the PDU if we already have it as a timeline event if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(&event_id)? { - info!("We already know {event_id} at {pdu_id:?}"); + info!(%event_id, ?pdu_id, "We already know this event"); return Ok(()); } From 4b5d1273680d24b169ebc245cc3fc8034a539352 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 21:06:27 -0700 Subject: [PATCH 305/617] fix service/rooms/state_cache events --- src/service/rooms/state_cache.rs | 68 ++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index af6c7848..372cfd3f 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -91,24 +91,31 @@ impl Service { // ) // .ok(); + let event_kind = RoomAccountDataEventType::Tag; + // Copy old tags to new room if let Some(tag_event) = services() .account_data .get( Some(&predecessor.room_id), user_id, - RoomAccountDataEventType::Tag, + event_kind.clone(), )? .map(|event| { - serde_json::from_str(event.get()).map_err(|e| { - warn!( - "Invalid account data event in db: \ - {e:?}" - ); - Error::BadDatabase( - "Invalid account data event in db.", - ) - }) + serde_json::from_str(event.get()).map_err( + |error| { + warn!( + %error, + predecessor_room_id = + %predecessor.room_id, + %event_kind, + "Invalid account data event", + ); + Error::BadDatabase( + "Invalid account data event.", + ) + }, + ) }) { services() @@ -122,25 +129,24 @@ impl Service { .ok(); }; + let event_kind = RoomAccountDataEventType::from( + GlobalAccountDataEventType::Direct.to_string(), + ); + // Copy direct chat flag if let Some(direct_event) = services() .account_data - .get( - None, - user_id, - GlobalAccountDataEventType::Direct - .to_string() - .into(), - )? + .get(None, user_id, event_kind.clone())? .map(|event| { serde_json::from_str::(event.get()) - .map_err(|e| { + .map_err(|error| { warn!( - "Invalid account data event in \ - db: {e:?}" + %error, + %event_kind, + "Invalid account data event", ); Error::BadDatabase( - "Invalid account data event in db.", + "Invalid account data event.", ) }) }) @@ -177,6 +183,10 @@ impl Service { self.db.mark_as_joined(user_id, room_id)?; } MembershipState::Invite => { + let event_kind = RoomAccountDataEventType::from( + GlobalAccountDataEventType::IgnoredUserList.to_string(), + ); + // We want to know if the sender is ignored by the receiver let is_ignored = services() .account_data @@ -185,19 +195,19 @@ impl Service { None, // Receiver user_id, - GlobalAccountDataEventType::IgnoredUserList - .to_string() - .into(), + event_kind.clone(), )? .map(|event| { serde_json::from_str::( event.get(), ) - .map_err(|e| { - warn!("Invalid account data event in db: {e:?}"); - Error::BadDatabase( - "Invalid account data event in db.", - ) + .map_err(|error| { + warn!( + %error, + %event_kind, + "Invalid account data event", + ); + Error::BadDatabase("Invalid account data event.") }) }) .transpose()? From b4d81bb0676e6eab73d95185cf0eae965ad50e9e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 22:05:34 -0700 Subject: [PATCH 306/617] fix service/rooms/event_handler events --- src/service/rooms/event_handler.rs | 212 ++++++++++++++++------------- 1 file changed, 118 insertions(+), 94 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 1880f997..427e65e2 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -121,10 +121,12 @@ impl Service { })?; let create_event_content: RoomCreateEventContent = - serde_json::from_str(create_event.content.get()).map_err(|e| { - error!("Invalid create event: {}", e); - Error::BadDatabase("Invalid create event in db") - })?; + serde_json::from_str(create_event.content.get()).map_err( + |error| { + error!(%error, "Invalid create event"); + Error::BadDatabase("Invalid create event.") + }, + )?; let room_version_id = &create_event_content.room_version; let first_pdu_in_room = @@ -195,7 +197,7 @@ impl Service { } if time.elapsed() < min_elapsed_duration { - info!("Backing off from {}", prev_id); + info!(event_id = %prev_id, "Backing off from prev event"); continue; } } @@ -236,7 +238,7 @@ impl Service { ((*prev_id).to_owned(), start_time), ); - if let Err(e) = self + if let Err(error) = self .upgrade_outlier_to_timeline_pdu( pdu, json, @@ -248,7 +250,7 @@ impl Service { .await { errors += 1; - warn!("Prev event {} failed: {}", prev_id, e); + warn!(%error, event_id = %prev_id, "Prev event failed"); match services() .globals .bad_event_ratelimiter @@ -264,7 +266,6 @@ impl Service { } } } - let elapsed = start_time.elapsed(); services() .globals .roomid_federationhandletime @@ -272,10 +273,9 @@ impl Service { .await .remove(&room_id.to_owned()); debug!( - "Handling prev event {} took {}m{}s", - prev_id, - elapsed.as_secs() / 60, - elapsed.as_secs() % 60 + elapsed = ?start_time.elapsed(), + event_id = %prev_id, + "Finished handling prev event", ); } } @@ -334,8 +334,8 @@ impl Service { // 3. check content hash, redact if doesn't match let create_event_content: RoomCreateEventContent = serde_json::from_str(create_event.content.get()).map_err( - |e| { - error!("Invalid create event: {}", e); + |error| { + error!(%error, "Invalid create event"); Error::BadDatabase("Invalid create event in db") }, )?; @@ -397,9 +397,9 @@ impl Service { &value, room_version_id, ) { - Err(e) => { + Err(error) => { // Drop - warn!("Dropping bad event {}: {}", event_id, e,); + warn!(%event_id, %error, "Dropping bad event"); return Err(Error::BadRequest( ErrorKind::InvalidParam, "Signature verification failed", @@ -407,7 +407,7 @@ impl Service { } Ok(ruma::signatures::Verified::Signatures) => { // Redact - warn!("Calculated hash does not match: {}", event_id); + warn!(%event_id, "Calculated hash does not match"); let Ok(obj) = ruma::canonical_json::redact( value, room_version_id, @@ -481,16 +481,17 @@ impl Service { // 6. Reject "due to auth events" if the event doesn't pass auth // based on the auth events debug!( - "Auth check for {} based on auth events", - incoming_pdu.event_id + event_id = %incoming_pdu.event_id, + "Starting auth check for event based on auth events", ); // Build map of auth events let mut auth_events = HashMap::new(); - for id in &incoming_pdu.auth_events { - let Some(auth_event) = services().rooms.timeline.get_pdu(id)? + for event_id in &incoming_pdu.auth_events { + let Some(auth_event) = + services().rooms.timeline.get_pdu(event_id)? else { - warn!("Could not find auth event {}", id); + warn!(%event_id, "Could not find auth event"); continue; }; @@ -543,7 +544,7 @@ impl Service { )); } - debug!("Validation successful."); + debug!("Validation successful"); // 7. Persist the event as an outlier. services() @@ -551,7 +552,7 @@ impl Service { .outlier .add_pdu_outlier(&incoming_pdu.event_id, &val)?; - debug!("Added pdu as outlier."); + debug!("Added pdu as outlier"); Ok((Arc::new(incoming_pdu), val)) }) @@ -594,10 +595,12 @@ impl Service { ); let create_event_content: RoomCreateEventContent = - serde_json::from_str(create_event.content.get()).map_err(|e| { - warn!("Invalid create event: {}", e); - Error::BadDatabase("Invalid create event in db") - })?; + serde_json::from_str(create_event.content.get()).map_err( + |error| { + warn!(%error, "Invalid create event"); + Error::BadDatabase("Invalid create event in db") + }, + )?; let room_version_id = &create_event_content.room_version; let room_version = RoomVersion::new(room_version_id) @@ -728,7 +731,7 @@ impl Service { id.clone(), ); } else { - warn!("Failed to get_statekey_from_short."); + warn!("Failed to get_statekey_from_short"); } starting_events.push(id); } @@ -751,10 +754,10 @@ impl Service { room_version_id, &fork_states, auth_chain_sets, - |id| { - let res = services().rooms.timeline.get_pdu(id); - if let Err(e) = &res { - error!("LOOK AT ME Failed to fetch event: {}", e); + |event_id| { + let res = services().rooms.timeline.get_pdu(event_id); + if let Err(error) = &res { + error!(%error, %event_id, "Failed to fetch event"); } res.ok().flatten() }, @@ -777,12 +780,11 @@ impl Service { }) .collect::>()?, ), - Err(e) => { + Err(error) => { warn!( + %error, "State resolution on prev events failed, either \ - an event could not be found or deserialization: \ - {}", - e + an event could not be found or deserialization" ); None } @@ -807,7 +809,7 @@ impl Service { .await { Ok(res) => { - debug!("Fetching state events at event."); + debug!("Fetching state events at event"); let collect = res .pdu_ids .iter() @@ -871,9 +873,9 @@ impl Service { state_at_incoming_event = Some(state); } - Err(e) => { - warn!("Fetching state for event failed: {}", e); - return Err(e); + Err(error) => { + warn!(%error, "Fetching state for event failed"); + return Err(error); } }; } @@ -1092,7 +1094,7 @@ impl Service { // Soft fail, we keep the event as an outlier but don't add it to // the timeline - warn!("Event was soft failed: {:?}", incoming_pdu); + warn!("Event was soft failed"); services() .rooms .pdu_metadata @@ -1191,8 +1193,8 @@ impl Service { let fetch_event = |id: &_| { let res = services().rooms.timeline.get_pdu(id); - if let Err(e) = &res { - error!("LOOK AT ME Failed to fetch event: {}", e); + if let Err(error) = &res { + error!(%error, "Failed to fetch event"); } res.ok().flatten() }; @@ -1212,7 +1214,7 @@ impl Service { drop(lock); - debug!("State resolution done. Compressing state"); + debug!("State resolution done; compressing state"); let new_room_state = state .into_iter() @@ -1274,14 +1276,14 @@ impl Service { }; let mut pdus = vec![]; - for id in events { + for event_id in events { // a. Look in the main timeline (pduid_pdu tree) // b. Look at outlier pdu tree // (get_pdu_json checks both) if let Ok(Some(local_pdu)) = - services().rooms.timeline.get_pdu(id) + services().rooms.timeline.get_pdu(event_id) { - trace!("Found {} in db", id); + trace!(%event_id, "Found event locally"); pdus.push((local_pdu, None)); continue; } @@ -1289,7 +1291,7 @@ impl Service { // c. Ask origin server over federation // We also handle its auth chain here so we don't get a stack // overflow in handle_outlier_pdu. - let mut todo_auth_events = vec![Arc::clone(id)]; + let mut todo_auth_events = vec![Arc::clone(event_id)]; let mut events_in_reverse_order = Vec::new(); let mut events_all = HashSet::new(); let mut i = 0; @@ -1312,7 +1314,10 @@ impl Service { } if time.elapsed() < min_elapsed_duration { - info!("Backing off from {}", next_id); + info!( + event_id = %next_id, + "Backing off from event", + ); continue; } } @@ -1329,11 +1334,14 @@ impl Service { if let Ok(Some(_)) = services().rooms.timeline.get_pdu(&next_id) { - trace!("Found {} in db", next_id); + trace!(event_id = %next_id, "Found event locally"); continue; } - info!("Fetching {} over federation.", next_id); + info!( + event_id = %next_id, + "Fetching event over federation", + ); if let Ok(res) = services() .sending .send_federation_request( @@ -1344,7 +1352,7 @@ impl Service { ) .await { - info!("Got {} over federation", next_id); + info!(event_id = %next_id, "Got event over federation"); let Ok((calculated_event_id, value)) = pdu::gen_event_id_canonical_json( &res.pdu, @@ -1357,9 +1365,10 @@ impl Service { if calculated_event_id != *next_id { warn!( - "Server didn't return event id we requested: \ - requested: {}, we got {}. Event: {:?}", - next_id, calculated_event_id, &res.pdu + expected_event_id = %next_id, + actual_event_id = %calculated_event_id, + "Server returned an event with a different ID \ + than requested", ); } @@ -1383,7 +1392,7 @@ impl Service { events_in_reverse_order.push((next_id.clone(), value)); events_all.insert(next_id); } else { - warn!("Failed to fetch event: {}", next_id); + warn!(event_id = %next_id, "Failed to fetch event"); back_off((*next_id).to_owned()).await; } } @@ -1407,7 +1416,10 @@ impl Service { } if time.elapsed() < min_elapsed_duration { - info!("Backing off from {}", next_id); + info!( + event_id = %next_id, + "Backing off from event", + ); continue; } } @@ -1425,14 +1437,15 @@ impl Service { .await { Ok((pdu, json)) => { - if next_id == id { + if next_id == event_id { pdus.push((pdu, Some(json))); } } - Err(e) => { + Err(error) => { warn!( - "Authentication of event {} failed: {:?}", - next_id, e + event_id = %next_id, + %error, + "Event failed auth checks", ); back_off((**next_id).to_owned()).await; } @@ -1591,10 +1604,7 @@ impl Service { .await; let Ok(keys) = fetch_res else { - warn!( - "Signature verification failed: Could not fetch signing \ - key.", - ); + warn!("Failed to fetch signing key"); continue; }; @@ -1619,8 +1629,8 @@ impl Service { pub_key_map: &mut RwLockWriteGuard<'_, BTreeMap>, ) -> Result<()> { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()) - .map_err(|e| { - error!("Invalid PDU in server response: {:?}: {:?}", pdu, e); + .map_err(|error| { + error!(%error, ?pdu, "Invalid PDU in server response"); Error::BadServerResponse("Invalid PDU in server response") })?; @@ -1643,9 +1653,9 @@ impl Service { } if time.elapsed() < min_elapsed_duration { - debug!("Backing off from {}", event_id); + debug!(%event_id, "Backing off from event"); return Err(Error::BadServerResponse( - "bad event, still backing off", + "Bad event, still backing off", )); } } @@ -1697,11 +1707,14 @@ impl Service { continue; } - trace!("Loading signing keys for {}", origin); + trace!(server = %origin, "Loading signing keys for other server"); if let Some(result) = services().globals.signing_keys_for(origin)? { if !contains_all_ids(&result) { - trace!("Signing key not loaded for {}", origin); + trace!( + server = %origin, + "Signing key not loaded for server", + ); servers.insert(origin.to_owned(), BTreeMap::new()); } @@ -1744,7 +1757,7 @@ impl Service { ) .await { - debug!(%error, "failed to get server keys from cache"); + debug!(%error, "Failed to get server keys from cache"); }; } @@ -1757,7 +1770,7 @@ impl Service { } for server in services().globals.trusted_servers() { - info!("Asking batch signing keys from trusted server {}", server); + info!(%server, "Asking batch signing keys from trusted server"); if let Ok(keys) = services() .sending .send_federation_request( @@ -1768,18 +1781,18 @@ impl Service { ) .await { - trace!("Got signing keys: {:?}", keys); + trace!(signing_keys = ?keys, "Got signing keys"); let mut pkm = pub_key_map.write().await; for k in keys.server_keys { let k = match k.deserialize() { Ok(key) => key, - Err(e) => { + Err(error) => { warn!( - "Received error {} while fetching keys from \ - trusted server {}", - e, server + %error, + %server, + object = ?k.json(), + "Failed to fetch keys from trusted server", ); - warn!("{}", k.into_json()); continue; } }; @@ -1804,7 +1817,7 @@ impl Service { } } - info!("Asking individual servers for signing keys: {servers:?}"); + info!(?servers, "Asking individual servers for signing keys"); let mut futures: FuturesUnordered<_> = servers .into_keys() .map(|server| async move { @@ -1822,9 +1835,8 @@ impl Service { .collect(); while let Some(result) = futures.next().await { - info!("Received new result"); if let (Ok(get_keys_response), origin) = result { - info!("Result is from {origin}"); + info!(server = %origin, "Received new result from server"); if let Ok(key) = get_keys_response.server_key.deserialize() { let result = services() .globals @@ -1861,11 +1873,15 @@ impl Service { return Ok(()); }; - let Ok(acl_event_content) = serde_json::from_str::< + let acl_event_content = match serde_json::from_str::< RoomServerAclEventContent, - >(acl_event.content.get()) else { - warn!("Invalid ACL event"); - return Ok(()); + >(acl_event.content.get()) + { + Ok(x) => x, + Err(error) => { + warn!(%error, "Invalid ACL event"); + return Ok(()); + } }; if acl_event_content.allow.is_empty() { @@ -1877,8 +1893,9 @@ impl Service { Ok(()) } else { info!( - "Server {} was denied by room ACL in {}", - server_name, room_id + server = %server_name, + %room_id, + "Other server was denied by room ACL", ); Err(Error::BadRequest( ErrorKind::forbidden(), @@ -1970,7 +1987,7 @@ impl Service { } if time.elapsed() < min_elapsed_duration { - debug!("Backing off from {:?}", signature_ids); + debug!(?signature_ids, "Backing off from signatures"); return Err(Error::BadServerResponse( "bad signature, still backing off", )); @@ -1990,8 +2007,10 @@ impl Service { .expect("Should be valid until year 500,000,000"); debug!( - "The threshhold is {:?}, found time is {:?} for server {}", - ts_threshold, result.valid_until_ts, origin + server = %origin, + ts_threshold = %ts_threshold.get(), + ts_valid_until = %result.valid_until_ts.get(), + "Loaded signing keys for server", ); if contains_all_ids(&result) { @@ -2001,7 +2020,7 @@ impl Service { debug!( origin = %origin, valid_until_ts = %result.valid_until_ts.get(), - "Keys for are deemed as valid, as they expire after threshold", + "Keys are valid because they expire after threshold", ); return Ok(result); } @@ -2181,7 +2200,12 @@ impl Service { #[tracing::instrument(skip_all)] fn check_room_id(room_id: &RoomId, pdu: &PduEvent) -> Result<()> { if pdu.room_id != room_id { - warn!("Found event from room {} in room {}", pdu.room_id, room_id); + warn!( + event_id = %pdu.event_id, + expected_room_id = %pdu.room_id, + actual_room_id = %room_id, + "Event has wrong room ID", + ); return Err(Error::BadRequest( ErrorKind::InvalidParam, "Event has wrong room id", From 96e6ac9563d66d4cd5eca97fc4d7fabcd277b987 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 22:18:16 -0700 Subject: [PATCH 307/617] fix service/rooms/spaces events --- src/service/rooms/spaces.rs | 80 ++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 03485f20..57655564 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -179,11 +179,10 @@ impl Service { .map(|s| { serde_json::from_str(s.content.get()) .map(|c: RoomJoinRulesEventContent| c.join_rule) - .map_err(|e| { + .map_err(|error| { error!( - "Invalid room join rule event in \ - database: {}", - e + %error, + "Invalid room join rule event" ); Error::BadDatabase( "Invalid room join rule event in \ @@ -218,7 +217,7 @@ impl Service { // Early return so the client can see some data already break; } - debug!("Asking {server} for /hierarchy"); + debug!(%server, "Asking other server for /hierarchy"); if let Ok(response) = services() .sending .send_federation_request( @@ -231,8 +230,9 @@ impl Service { .await { warn!( - "Got response from {server} for \ - /hierarchy\n{response:?}" + %server, + ?response, + "Got response from other server for /hierarchy", ); let chunk = SpaceHierarchyRoomsChunk { canonical_alias: response.room.canonical_alias, @@ -327,7 +327,7 @@ impl Service { } #[allow(clippy::too_many_lines)] - #[tracing::instrument(skip(self, sender_user, children))] + #[tracing::instrument(skip(self, children))] fn get_room_chunk( &self, sender_user: &UserId, @@ -346,8 +346,13 @@ impl Service { .map_or(Ok(None), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomCanonicalAliasEventContent| c.alias) - .map_err(|_| { - Error::bad_database( + .map_err(|error| { + error!( + %error, + event_id = %s.event_id, + "Invalid room canonical alias event" + ); + Error::BadDatabase( "Invalid canonical alias event in database.", ) }) @@ -358,7 +363,7 @@ impl Service { .state_cache .room_joined_count(room_id)? .unwrap_or_else(|| { - warn!("Room {} has no member count", room_id); + warn!("Room has no member count"); 0 }) .try_into() @@ -371,13 +376,13 @@ impl Service { .map_or(Ok(None), |s| { serde_json::from_str(s.content.get()) .map(|c: RoomTopicEventContent| Some(c.topic)) - .map_err(|_| { + .map_err(|error| { error!( - "Invalid room topic event in database for \ - room {}", - room_id + %error, + event_id = %s.event_id, + "Invalid room topic event" ); - Error::bad_database( + Error::BadDatabase( "Invalid room topic event in database.", ) }) @@ -396,8 +401,13 @@ impl Service { c.history_visibility == HistoryVisibility::WorldReadable }) - .map_err(|_| { - Error::bad_database( + .map_err(|error| { + error!( + %error, + event_id = %s.event_id, + "Invalid room history visibility event" + ); + Error::BadDatabase( "Invalid room history visibility event in \ database.", ) @@ -412,8 +422,13 @@ impl Service { .map(|c: RoomGuestAccessEventContent| { c.guest_access == GuestAccess::CanJoin }) - .map_err(|_| { - Error::bad_database( + .map_err(|error| { + error!( + %error, + event_id = %s.event_id, + "Invalid room guest access event" + ); + Error::BadDatabase( "Invalid room guest access event in database.", ) }) @@ -425,7 +440,12 @@ impl Service { .map(|s| { serde_json::from_str(s.content.get()) .map(|c: RoomAvatarEventContent| c.url) - .map_err(|_| { + .map_err(|error| { + error!( + %error, + event_id = %s.event_id, + "Invalid room avatar event" + ); Error::bad_database( "Invalid room avatar event in database.", ) @@ -445,11 +465,11 @@ impl Service { .map(|s| { serde_json::from_str(s.content.get()) .map(|c: RoomJoinRulesEventContent| c.join_rule) - .map_err(|e| { + .map_err(|error| { error!( - "Invalid room join rule event in \ - database: {}", - e + %error, + event_id = %s.event_id, + "Invalid room join rule event", ); Error::BadDatabase( "Invalid room join rule event in database.", @@ -460,7 +480,7 @@ impl Service { .unwrap_or(JoinRule::Invite); if !self.handle_join_rule(&join_rule, sender_user, room_id)? { - debug!("User is not allowed to see room {room_id}"); + debug!("User is not allowed to see room"); // This error will be caught later return Err(Error::BadRequest( ErrorKind::forbidden(), @@ -478,8 +498,12 @@ impl Service { serde_json::from_str::( s.content.get(), ) - .map_err(|e| { - error!("Invalid room create event in database: {}", e); + .map_err(|error| { + error!( + %error, + event_id = %s.event_id, + "Invalid room create event", + ); Error::BadDatabase( "Invalid room create event in database.", ) From bf799c1fa1fb9592b8792c747594662c903068db Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 15 Jul 2024 22:24:14 -0700 Subject: [PATCH 308/617] update changelog --- book/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index 4a2aea2b..5362ed90 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -102,7 +102,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26), [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50), [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52), - [!54](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/54)) + [!54](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/54), + [!56](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/56)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. From c70cfd3d254924d42b2c3b4a7c271526bd5c29ba Mon Sep 17 00:00:00 2001 From: tranquillity-codes Date: Fri, 28 Jun 2024 10:01:41 +0200 Subject: [PATCH 309/617] Return 504 when a file is missing to be by-spec The spec defines that the media endpoints should return 504 when a file is not-yet-uploaded, which has been interpreted to include when a file was deleted. Modifies the /media/v3/download/ and /media/r0/thumbnail endpoints. --- src/api/client_server/media.rs | 4 ++-- src/service/media.rs | 14 ++++++++------ src/utils/error.rs | 7 ++++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index aed354a0..95c31609 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -250,7 +250,7 @@ async fn get_content_route_ruma( cross_origin_resource_policy: Some("cross-origin".to_owned()), }) } else { - Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) + Err(Error::BadRequest(ErrorKind::NotYetUploaded, "Media not found.")) } } @@ -415,6 +415,6 @@ async fn get_content_thumbnail_route_ruma( cross_origin_resource_policy: Some("cross-origin".to_owned()), }) } else { - Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) + Err(Error::BadRequest(ErrorKind::NotYetUploaded, "Media not found.")) } } diff --git a/src/service/media.rs b/src/service/media.rs index 91b5d135..052c726c 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use image::imageops::FilterType; use tokio::{ fs::File, - io::{AsyncReadExt, AsyncWriteExt, BufReader}, + io::{AsyncReadExt, AsyncWriteExt}, }; use tracing::{debug, warn}; @@ -88,15 +88,17 @@ impl Service { self.db.search_file_metadata(mxc, 0, 0) { let path = services().globals.get_media_file(&key); - let mut file = Vec::new(); - BufReader::new(File::open(path).await?) - .read_to_end(&mut file) - .await?; + let mut file_data = Vec::new(); + let Ok(mut file) = File::open(path).await else { + return Ok(None); + }; + + file.read_to_end(&mut file_data).await?; Ok(Some(FileMeta { content_disposition, content_type, - file, + file: file_data, })) } else { Ok(None) diff --git a/src/utils/error.rs b/src/utils/error.rs index 776c1a3e..7b87b5f5 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -98,9 +98,9 @@ impl Error { pub(crate) fn to_response(&self) -> Ra { use ErrorKind::{ Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, - NotFound, ThreepidAuthFailed, ThreepidDenied, TooLarge, - Unauthorized, Unknown, UnknownToken, Unrecognized, UserDeactivated, - WrongRoomKeysVersion, + NotFound, NotYetUploaded, ThreepidAuthFailed, ThreepidDenied, + TooLarge, Unauthorized, Unknown, UnknownToken, Unrecognized, + UserDeactivated, WrongRoomKeysVersion, }; if let Self::Uiaa(uiaainfo) = self { @@ -142,6 +142,7 @@ impl Error { .. } => StatusCode::TOO_MANY_REQUESTS, TooLarge => StatusCode::PAYLOAD_TOO_LARGE, + NotYetUploaded => StatusCode::GATEWAY_TIMEOUT, _ => StatusCode::BAD_REQUEST, }, ), From ef1b6fe111472e11db338244d97ad51e23acb09e Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 23 Jun 2024 18:11:22 +0000 Subject: [PATCH 310/617] upgrade_room_route: give more descriptive names to locks This makes the next commit, which is an actual fix, much easier to read. --- src/api/client_server/room.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index b73af16f..11c01a67 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -644,7 +644,7 @@ pub(crate) async fn upgrade_room_route( .entry(body.room_id.clone()) .or_default(), ); - let state_lock = mutex_state.lock().await; + let original_state_lock = mutex_state.lock().await; // Send a m.room.tombstone event to the old room to indicate that it is not // intended to be used any further Fail if the sender does not have the @@ -666,12 +666,12 @@ pub(crate) async fn upgrade_room_route( }, sender_user, &body.room_id, - &state_lock, + &original_state_lock, ) .await?; // Change lock to replacement room - drop(state_lock); + drop(original_state_lock); let mutex_state = Arc::clone( services() .globals @@ -681,7 +681,7 @@ pub(crate) async fn upgrade_room_route( .entry(replacement_room.clone()) .or_default(), ); - let state_lock = mutex_state.lock().await; + let replacement_state_lock = mutex_state.lock().await; // Get the old room creation event let mut create_event_content = serde_json::from_str::( @@ -779,7 +779,7 @@ pub(crate) async fn upgrade_room_route( }, sender_user, &replacement_room, - &state_lock, + &replacement_state_lock, ) .await?; @@ -807,7 +807,7 @@ pub(crate) async fn upgrade_room_route( }, sender_user, &replacement_room, - &state_lock, + &replacement_state_lock, ) .await?; @@ -849,7 +849,7 @@ pub(crate) async fn upgrade_room_route( }, sender_user, &replacement_room, - &state_lock, + &replacement_state_lock, ) .await?; } @@ -912,11 +912,11 @@ pub(crate) async fn upgrade_room_route( }, sender_user, &body.room_id, - &state_lock, + &replacement_state_lock, ) .await?; - drop(state_lock); + drop(replacement_state_lock); // Return the replacement room id Ok(Ra(upgrade_room::v3::Response { From aea6019c0afd70988c76fe7b2df0c1dcae48e5c1 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 23 Jun 2024 18:11:41 +0000 Subject: [PATCH 311/617] upgrade_room_route: fix state lock This was using the lock for the replacement room to send events to the original room, which may or may not cause problems. --- src/api/client_server/room.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 11c01a67..76ed6b9d 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -671,7 +671,6 @@ pub(crate) async fn upgrade_room_route( .await?; // Change lock to replacement room - drop(original_state_lock); let mutex_state = Arc::clone( services() .globals @@ -912,7 +911,7 @@ pub(crate) async fn upgrade_room_route( }, sender_user, &body.room_id, - &replacement_state_lock, + &original_state_lock, ) .await?; From 2c19abc5359b628d3157b7c86864ecdebc22e1a4 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 27 May 2024 18:12:02 +0000 Subject: [PATCH 312/617] Add OnDemandHashMap and TokenSet --- src/observability.rs | 26 ++- src/utils.rs | 1 + src/utils/on_demand_hashmap.rs | 304 +++++++++++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 src/utils/on_demand_hashmap.rs diff --git a/src/observability.rs b/src/observability.rs index 7add9ce4..177001aa 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -1,7 +1,7 @@ //! Facilities for observing runtime behavior #![warn(missing_docs, clippy::missing_docs_in_private_items)] -use std::{collections::HashSet, fs::File, io::BufWriter}; +use std::{collections::HashSet, fs::File, io::BufWriter, sync::Arc}; use axum::{ extract::{MatchedPath, Request}, @@ -269,6 +269,10 @@ pub(crate) struct Metrics { /// Counts where data is found from lookup: opentelemetry::metrics::Counter, + + /// Number of entries in an + /// [`OnDemandHashMap`](crate::utils::on_demand_hashmap::OnDemandHashMap) + on_demand_hashmap_size: opentelemetry::metrics::Gauge, } impl Metrics { @@ -319,10 +323,16 @@ impl Metrics { .with_description("Counts where data is found from") .init(); + let on_demand_hashmap_size = meter + .u64_gauge("on_demand_hashmap_size") + .with_description("Number of entries in OnDemandHashMap") + .init(); + Metrics { otel_state: (registry, provider), http_requests_histogram, lookup, + on_demand_hashmap_size, } } @@ -343,6 +353,20 @@ impl Metrics { ], ); } + + /// Record size of [`OnDemandHashMap`] + /// + /// [`OnDemandHashMap`]: crate::utils::on_demand_hashmap::OnDemandHashMap + pub(crate) fn record_on_demand_hashmap_size( + &self, + name: Arc, + size: usize, + ) { + self.on_demand_hashmap_size.record( + size.try_into().unwrap_or(u64::MAX), + &[KeyValue::new("name", name)], + ); + } } /// Track HTTP metrics by converting this into an [`axum`] layer diff --git a/src/utils.rs b/src/utils.rs index 3bd0319b..52956bf6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ pub(crate) mod error; +pub(crate) mod on_demand_hashmap; use std::{ borrow::Cow, diff --git a/src/utils/on_demand_hashmap.rs b/src/utils/on_demand_hashmap.rs new file mode 100644 index 00000000..69e8bac9 --- /dev/null +++ b/src/utils/on_demand_hashmap.rs @@ -0,0 +1,304 @@ +use std::{ + collections::HashMap, + fmt, + hash::Hash, + marker::PhantomData, + ops::Deref, + sync::{Arc, Weak}, +}; + +use tokio::sync::{mpsc, Mutex, OwnedMutexGuard, RwLock}; +use tracing::{trace, warn, Level}; + +use crate::observability::METRICS; + +/// Data shared between [`OnDemandHashMap`] and the cleanup task +/// +/// Importantly it does not contain the `cleanup_sender`, since it getting +/// dropped signals the cleanup task to exit. If the cleanup task had an owned +/// reference to it, the only way for it to exit would be for every [`Entry`] to +/// be dropped, we don't want to rely on that. +struct SharedData { + name: Arc, + /// Values are owned by their [entries][Entry] + entries: RwLock>>, +} + +impl SharedData +where + K: Hash + Eq + Clone + fmt::Debug, +{ + #[tracing::instrument( + level = Level::TRACE, + skip(self), + fields(name = self.name.as_ref()), + )] + async fn try_cleanup_entry(&self, key: K) { + let mut map = self.entries.write().await; + + let Some(weak) = map.get(&key) else { + trace!("Entry has already been cleaned up"); + return; + }; + + if weak.strong_count() != 0 { + trace!("Entry is in use"); + return; + } + + trace!("Cleaning up unused entry"); + map.remove(&key); + METRICS.record_on_demand_hashmap_size(self.name.clone(), map.len()); + } + + #[tracing::instrument(level = Level::TRACE, skip(map))] + fn try_get_live_value( + pass: usize, + map: &HashMap>, + key: &K, + ) -> Option> { + if let Some(value) = map.get(key) { + if let Some(value) = value.upgrade() { + trace!(pass, "Using existing value"); + return Some(value); + } + + trace!( + pass, + "Existing value is stale and needs cleanup, creating new" + ); + } else { + trace!(pass, "No existing value, creating new"); + } + + None + } + + /// Either returns an existing live value, or creates a new one and inserts + /// it into the map. + #[tracing::instrument(level = Level::TRACE, skip(self, create))] + async fn get_or_insert_with(&self, key: &K, create: F) -> Arc + where + F: FnOnce() -> V, + { + { + // first, take a read lock and try to get an existing value + + // TODO check if this fast path actually makes it faster, possibly + // make it configurable per OnDemandHashMap depending on contention + // and how expensive create() is + let map = self.entries.read().await; + if let Some(v) = Self::try_get_live_value(1, &map, key) { + return v; + } + } + + // no entry or it has died, create a new one + let value = Arc::new(create()); + let weak = Arc::downgrade(&value); + + // take a write lock, try again, otherwise insert our new value + let mut map = self.entries.write().await; + if let Some(v) = Self::try_get_live_value(2, &map, key) { + // another entry showed up while we had let go of the lock, + // use that + drop(value); + drop(weak); + return v; + } + + map.insert(key.clone(), weak); + METRICS.record_on_demand_hashmap_size(self.name.clone(), map.len()); + + value + } +} + +/// A [`HashMap`] whose entries are automatically removed once they are no +/// longer referenced. +pub(crate) struct OnDemandHashMap { + /// The data shared between the [`OnDemandHashMap`] and the cleanup task. + shared: Arc>, + /// This is the only non-[weak][mpsc::WeakUnboundedSender] `Sender`, which + /// means that dropping the `OnDemandHashMap` causes the cleanup + /// process to exit. + cleanup_sender: mpsc::UnboundedSender, +} + +impl OnDemandHashMap +where + K: Hash + Eq + Clone + fmt::Debug + Send + Sync + 'static, + V: Send + Sync + 'static, +{ + /// Creates a new `OnDemandHashMap`. The `name` is used for metrics and + /// should be unique to this instance. + pub(crate) fn new(name: String) -> Self { + let (cleanup_sender, mut receiver) = mpsc::unbounded_channel(); + + let shared = Arc::new(SharedData { + name: name.into(), + entries: RwLock::new(HashMap::new()), + }); + + { + let shared = Arc::clone(&shared); + tokio::task::spawn(async move { + loop { + let Some(key) = receiver.recv().await else { + trace!( + name = shared.name.as_ref(), + "Channel has died, exiting cleanup task" + ); + return; + }; + + shared.try_cleanup_entry(key).await; + } + }); + } + + Self { + shared, + cleanup_sender, + } + } + + #[tracing::instrument(level = Level::TRACE, skip(self, create))] + pub(crate) async fn get_or_insert_with( + &self, + key: K, + create: F, + ) -> Entry + where + F: FnOnce() -> V, + { + let value = self.shared.get_or_insert_with(&key, create).await; + + Entry { + drop_guard: EntryDropGuard { + cleanup_sender: self.cleanup_sender.downgrade(), + key: Some(key), + }, + value, + } + } +} + +struct EntryDropGuard { + cleanup_sender: mpsc::WeakUnboundedSender, + /// Only `None` during `drop()` + key: Option, +} + +impl Drop for EntryDropGuard { + fn drop(&mut self) { + let Some(cleanup_sender) = self.cleanup_sender.upgrade() else { + trace!("Backing map has already been dropped"); + return; + }; + + if let Err(error) = cleanup_sender + .send(self.key.take().expect("drop should only be called once")) + { + warn!(%error, "Failed to send cleanup message"); + }; + } +} + +/// A wrapper around a key/value pair inside an [`OnDemandHashMap`] +/// +/// If every `Entry` for a specific key is dropped, the value is removed from +/// the map. +pub(crate) struct Entry { + drop_guard: EntryDropGuard, + value: Arc, +} + +impl Deref for Entry { + type Target = V; + + fn deref(&self) -> &Self::Target { + self.value.as_ref() + } +} + +/// Internal zero-sized type used to swallow the [`TokenSet`]'s marker type +struct TokenMarker(PhantomData T>); + +/// A collection of dynamically-created locks, one for each value of `K`. +/// +/// A given key can be locked using [`TokenSet::lock_key()`], which will either +/// return an ownership token immediately if the key is not currently locked, or +/// wait until the previous lock has been released. +/// +/// The marker type `M` can be used to disambiguate different `TokenSet` +/// instances to avoid misuse of tokens. +pub(crate) struct TokenSet { + inner: OnDemandHashMap>>, +} + +impl TokenSet +where + K: Hash + Eq + Clone + fmt::Debug + Send + Sync + 'static, + M: 'static, +{ + /// Creates a new `TokenSet`. The `name` is used for metrics and should be + /// unique to this instance. + pub(crate) fn new(name: String) -> Self { + Self { + inner: OnDemandHashMap::new(name), + } + } + + /// Locks this key in the `TokenSet`, returning a token proving + /// unique access. + #[tracing::instrument(level = Level::TRACE, skip(self))] + pub(crate) async fn lock_key(&self, key: K) -> KeyToken { + let Entry { + drop_guard, + value, + } = self + .inner + .get_or_insert_with(key, || Mutex::new(TokenMarker(PhantomData))) + .await; + + KeyToken { + drop_guard, + _mutex_guard: value.lock_owned().await, + } + } +} + +/// Unique token for a given key in a [`TokenSet`]. +/// +/// Ownership of this token proves that no other [`KeyToken`] for this key in +/// this [`TokenSet`] currently exists. +/// +/// Access to the underlying key is provided by a [`Deref`] impl. +pub(crate) struct KeyToken { + drop_guard: EntryDropGuard, + _mutex_guard: OwnedMutexGuard>, +} + +impl Deref for KeyToken { + type Target = K; + + fn deref(&self) -> &Self::Target { + self.drop_guard + .key + .as_ref() + .expect("key should only be None during Drop") + } +} + +impl fmt::Debug for KeyToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", &**self) + } +} + +impl fmt::Display for KeyToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &**self) + } +} From 07b52339809aae4616c1db8e10a6a5962b160cee Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 27 May 2024 19:27:08 +0000 Subject: [PATCH 313/617] Use OnDemandHashMap for servername_ratelimiter This way, semaphores are actually cleaned up eventually. --- src/service/globals.rs | 8 +++++--- src/service/rooms/event_handler.rs | 22 +++------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/service/globals.rs b/src/service/globals.rs index 02ae4e93..bf82d4e1 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -34,7 +34,7 @@ use trust_dns_resolver::TokioAsyncResolver; use crate::{ api::server_server::FedDest, observability::FilterReloadHandles, services, - Config, Error, Result, + utils::on_demand_hashmap::OnDemandHashMap, Config, Error, Result, }; type WellKnownMap = HashMap; @@ -66,7 +66,7 @@ pub(crate) struct Service { pub(crate) bad_query_ratelimiter: Arc>>, pub(crate) servername_ratelimiter: - Arc>>>, + OnDemandHashMap, pub(crate) roomid_mutex_insert: RwLock>>>, pub(crate) roomid_mutex_state: RwLock>>>, @@ -263,7 +263,9 @@ impl Service { bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_query_ratelimiter: Arc::new(RwLock::new(HashMap::new())), - servername_ratelimiter: Arc::new(RwLock::new(HashMap::new())), + servername_ratelimiter: OnDemandHashMap::new( + "servername_ratelimiter".to_owned(), + ), roomid_mutex_state: RwLock::new(HashMap::new()), roomid_mutex_insert: RwLock::new(HashMap::new()), roomid_mutex_federation: RwLock::new(HashMap::new()), diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 427e65e2..be1c3276 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1935,25 +1935,9 @@ impl Service { let permit = services() .globals .servername_ratelimiter - .read() - .await - .get(origin) - .map(|s| Arc::clone(s).acquire_owned()); - - let permit = if let Some(p) = permit { - p - } else { - let mut write = - services().globals.servername_ratelimiter.write().await; - let s = Arc::clone( - write - .entry(origin.to_owned()) - .or_insert_with(|| Arc::new(Semaphore::new(1))), - ); - - s.acquire_owned() - } - .await; + .get_or_insert_with(origin.to_owned(), || Semaphore::new(1)) + .await; + let permit = permit.acquire().await; let back_off = |id| async { match services() From 34ccb2cd06448cb398b7042908b02b55ebab29ed Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 23 Jun 2024 18:39:56 +0000 Subject: [PATCH 314/617] Use TokenSet for roomid_mutex_state --- src/api/client_server/membership.rs | 160 +++++++++----------------- src/api/client_server/message.rs | 25 ++-- src/api/client_server/profile.rs | 46 ++------ src/api/client_server/redact.rs | 22 ++-- src/api/client_server/room.rs | 97 +++++----------- src/api/client_server/state.rs | 18 +-- src/api/server_server.rs | 20 ++-- src/database/key_value/rooms/state.rs | 18 +-- src/service/admin.rs | 85 +++++--------- src/service/globals.rs | 20 +++- src/service/rooms/event_handler.rs | 23 ++-- src/service/rooms/state.rs | 35 +++--- src/service/rooms/state/data.rs | 15 +-- src/service/rooms/state_accessor.rs | 16 +-- src/service/rooms/timeline.rs | 72 +++++------- 15 files changed, 243 insertions(+), 429 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 0a2725f2..993aedd1 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -226,16 +226,11 @@ pub(crate) async fn kick_user_route( event.membership = MembershipState::Leave; event.reason.clone_from(&body.reason); - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; services() .rooms @@ -250,12 +245,11 @@ pub(crate) async fn kick_user_route( redacts: None, }, sender_user, - &body.room_id, - &state_lock, + &room_token, ) .await?; - drop(state_lock); + drop(room_token); Ok(Ra(kick_user::v3::Response::new())) } @@ -302,16 +296,11 @@ pub(crate) async fn ban_user_route( }, )?; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; services() .rooms @@ -326,12 +315,11 @@ pub(crate) async fn ban_user_route( redacts: None, }, sender_user, - &body.room_id, - &state_lock, + &room_token, ) .await?; - drop(state_lock); + drop(room_token); Ok(Ra(ban_user::v3::Response::new())) } @@ -365,16 +353,11 @@ pub(crate) async fn unban_user_route( event.membership = MembershipState::Leave; event.reason.clone_from(&body.reason); - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; services() .rooms @@ -389,12 +372,11 @@ pub(crate) async fn unban_user_route( redacts: None, }, sender_user, - &body.room_id, - &state_lock, + &room_token, ) .await?; - drop(state_lock); + drop(room_token); Ok(Ra(unban_user::v3::Response::new())) } @@ -528,16 +510,11 @@ async fn join_room_by_id_helper( ) -> Result { let sender_user = sender_user.expect("user is authenticated"); - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.to_owned()) + .await; // Ask a remote server if we are not participating in this room if services() @@ -601,10 +578,9 @@ async fn join_room_by_id_helper( { if user.server_name() == services().globals.server_name() && services().rooms.state_accessor.user_can_invite( - room_id, + &room_token, &user, sender_user, - &state_lock, ) { auth_user = Some(user); @@ -641,8 +617,7 @@ async fn join_room_by_id_helper( redacts: None, }, sender_user, - room_id, - &state_lock, + &room_token, ) .await { @@ -797,7 +772,7 @@ async fn join_room_by_id_helper( )); } - drop(state_lock); + drop(room_token); let pub_key_map = RwLock::new(BTreeMap::new()); services() .rooms @@ -1113,13 +1088,7 @@ async fn join_room_by_id_helper( services() .rooms .state - .force_state( - room_id, - statehash_before_join, - new, - removed, - &state_lock, - ) + .force_state(&room_token, statehash_before_join, new, removed) .await?; info!("Updating joined counts for new room"); @@ -1139,7 +1108,7 @@ async fn join_room_by_id_helper( &parsed_join_pdu, join_event, vec![(*parsed_join_pdu.event_id).to_owned()], - &state_lock, + &room_token, ) .await?; @@ -1147,11 +1116,10 @@ async fn join_room_by_id_helper( // We set the room state after inserting the pdu, so that we never have // a moment in time where events in the current room state do // not exist - services().rooms.state.set_room_state( - room_id, - statehash_after_join, - &state_lock, - )?; + services() + .rooms + .state + .set_room_state(&room_token, statehash_after_join)?; } Ok(join_room_by_id::v3::Response::new(room_id.to_owned())) @@ -1306,16 +1274,11 @@ pub(crate) async fn invite_helper( ) -> Result<()> { if user_id.server_name() != services().globals.server_name() { let (pdu, pdu_json, invite_room_state) = { - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.to_owned()) + .await; let content = to_raw_value(&RoomMemberEventContent { avatar_url: None, @@ -1339,14 +1302,13 @@ pub(crate) async fn invite_helper( redacts: None, }, sender_user, - room_id, - &state_lock, + &room_token, )?; let invite_room_state = services().rooms.state.calculate_invite_state(&pdu)?; - drop(state_lock); + drop(room_token); (pdu, pdu_json, invite_room_state) }; @@ -1447,16 +1409,11 @@ pub(crate) async fn invite_helper( )); } - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.to_owned()) + .await; services() .rooms @@ -1480,12 +1437,11 @@ pub(crate) async fn invite_helper( redacts: None, }, sender_user, - room_id, - &state_lock, + &room_token, ) .await?; - drop(state_lock); + drop(room_token); Ok(()) } @@ -1530,16 +1486,11 @@ pub(crate) async fn leave_room( .state_cache .server_in_room(services().globals.server_name(), room_id)? { - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.to_owned()) + .await; let member_event = services().rooms.state_accessor.room_state_get( room_id, @@ -1587,8 +1538,7 @@ pub(crate) async fn leave_room( redacts: None, }, user_id, - room_id, - &state_lock, + &room_token, ) .await?; } else { diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 162c2d63..6f29205f 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{BTreeMap, HashSet}, - sync::Arc, -}; +use std::collections::{BTreeMap, HashSet}; use ruma::{ api::client::{ @@ -32,16 +29,11 @@ pub(crate) async fn send_message_event_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_device = body.sender_device.as_deref(); - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; // Forbid m.room.encrypted if encryption is disabled if TimelineEventType::RoomEncrypted == body.event_type.to_string().into() @@ -104,8 +96,7 @@ pub(crate) async fn send_message_event_route( redacts: None, }, sender_user, - &body.room_id, - &state_lock, + &room_token, ) .await?; @@ -116,7 +107,7 @@ pub(crate) async fn send_message_event_route( event_id.as_bytes(), )?; - drop(state_lock); + drop(room_token); Ok(Ra(send_message_event::v3::Response::new((*event_id).to_owned()))) } diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index 8557baab..8a84f743 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use ruma::{ api::{ client::{ @@ -81,26 +79,16 @@ pub(crate) async fn set_displayname_route( .collect(); for (pdu_builder, room_id) in all_rooms_joined { - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.clone()) + .await; if let Err(error) = services() .rooms .timeline - .build_and_append_pdu( - pdu_builder, - sender_user, - &room_id, - &state_lock, - ) + .build_and_append_pdu(pdu_builder, sender_user, &room_token) .await { warn!(%error, "failed to add PDU"); @@ -203,26 +191,16 @@ pub(crate) async fn set_avatar_url_route( .collect(); for (pdu_builder, room_id) in all_joined_rooms { - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.clone()) + .await; if let Err(error) = services() .rooms .timeline - .build_and_append_pdu( - pdu_builder, - sender_user, - &room_id, - &state_lock, - ) + .build_and_append_pdu(pdu_builder, sender_user, &room_token) .await { warn!(%error, "failed to add PDU"); diff --git a/src/api/client_server/redact.rs b/src/api/client_server/redact.rs index 5604bfb0..46af6d8c 100644 --- a/src/api/client_server/redact.rs +++ b/src/api/client_server/redact.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use ruma::{ api::client::redact::redact_event, events::{room::redaction::RoomRedactionEventContent, TimelineEventType}, @@ -19,16 +17,11 @@ pub(crate) async fn redact_event_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; let event_id = services() .rooms @@ -46,12 +39,11 @@ pub(crate) async fn redact_event_route( redacts: Some(body.event_id.into()), }, sender_user, - &body.room_id, - &state_lock, + &room_token, ) .await?; - drop(state_lock); + drop(room_token); let event_id = (*event_id).to_owned(); Ok(Ra(redact_event::v3::Response { diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 76ed6b9d..c02526b3 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -1,4 +1,4 @@ -use std::{cmp::max, collections::BTreeMap, sync::Arc}; +use std::{cmp::max, collections::BTreeMap}; use ruma::{ api::client::{ @@ -63,16 +63,8 @@ pub(crate) async fn create_room_route( services().rooms.short.get_or_create_shortroomid(&room_id)?; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = + services().globals.roomid_mutex_state.lock_key(room_id.clone()).await; if !services().globals.allow_room_creation() && body.appservice_info.is_none() @@ -250,8 +242,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -278,8 +269,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -338,8 +328,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -361,8 +350,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; } @@ -389,8 +377,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -410,8 +397,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -434,8 +420,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -463,12 +448,7 @@ pub(crate) async fn create_room_route( services() .rooms .timeline - .build_and_append_pdu( - pdu_builder, - sender_user, - &room_id, - &state_lock, - ) + .build_and_append_pdu(pdu_builder, sender_user, &room_token) .await?; } @@ -489,8 +469,7 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; } @@ -511,14 +490,13 @@ pub(crate) async fn create_room_route( redacts: None, }, sender_user, - &room_id, - &state_lock, + &room_token, ) .await?; } // 8. Events implied by invite (and TODO: invite_3pid) - drop(state_lock); + drop(room_token); for user_id in &body.invite { if let Err(error) = invite_helper(sender_user, user_id, &room_id, None, body.is_direct) @@ -635,16 +613,11 @@ pub(crate) async fn upgrade_room_route( let replacement_room = RoomId::new(services().globals.server_name()); services().rooms.short.get_or_create_shortroomid(&replacement_room)?; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let original_state_lock = mutex_state.lock().await; + let original_room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; // Send a m.room.tombstone event to the old room to indicate that it is not // intended to be used any further Fail if the sender does not have the @@ -665,22 +638,16 @@ pub(crate) async fn upgrade_room_route( redacts: None, }, sender_user, - &body.room_id, - &original_state_lock, + &original_room_token, ) .await?; // Change lock to replacement room - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(replacement_room.clone()) - .or_default(), - ); - let replacement_state_lock = mutex_state.lock().await; + let replacement_room_token = services() + .globals + .roomid_mutex_state + .lock_key(replacement_room.clone()) + .await; // Get the old room creation event let mut create_event_content = serde_json::from_str::( @@ -777,8 +744,7 @@ pub(crate) async fn upgrade_room_route( redacts: None, }, sender_user, - &replacement_room, - &replacement_state_lock, + &replacement_room_token, ) .await?; @@ -805,8 +771,7 @@ pub(crate) async fn upgrade_room_route( redacts: None, }, sender_user, - &replacement_room, - &replacement_state_lock, + &replacement_room_token, ) .await?; @@ -847,8 +812,7 @@ pub(crate) async fn upgrade_room_route( redacts: None, }, sender_user, - &replacement_room, - &replacement_state_lock, + &replacement_room_token, ) .await?; } @@ -910,13 +874,10 @@ pub(crate) async fn upgrade_room_route( redacts: None, }, sender_user, - &body.room_id, - &original_state_lock, + &original_room_token, ) .await?; - drop(replacement_state_lock); - // Return the replacement room id Ok(Ra(upgrade_room::v3::Response { replacement_room, diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 9da197b9..b0e5809a 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -240,16 +240,11 @@ async fn send_state_event_for_key_helper( } } - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.to_owned()) + .await; let event_id = services() .rooms @@ -264,8 +259,7 @@ async fn send_state_event_for_key_helper( redacts: None, }, sender_user, - room_id, - &state_lock, + &room_token, ) .await?; diff --git a/src/api/server_server.rs b/src/api/server_server.rs index e2841acb..2208c693 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1452,16 +1452,11 @@ pub(crate) async fn create_join_event_template_route( .event_handler .acl_check(sender_servername, &body.room_id)?; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(body.room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(body.room_id.clone()) + .await; // TODO: Grapevine does not implement restricted join rules yet, we always // reject @@ -1529,11 +1524,10 @@ pub(crate) async fn create_join_event_template_route( redacts: None, }, &body.user_id, - &body.room_id, - &state_lock, + &room_token, )?; - drop(state_lock); + drop(room_token); pdu_json.remove("event_id"); diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index 78863a75..a3246878 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -1,9 +1,13 @@ use std::{collections::HashSet, sync::Arc}; -use ruma::{EventId, OwnedEventId, RoomId}; -use tokio::sync::MutexGuard; +use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId}; -use crate::{database::KeyValueDatabase, service, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, + service::{self, globals::marker}, + utils::{self, on_demand_hashmap::KeyToken}, + Error, Result, +}; impl service::rooms::state::Data for KeyValueDatabase { fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result> { @@ -21,10 +25,8 @@ impl service::rooms::state::Data for KeyValueDatabase { fn set_room_state( &self, - room_id: &RoomId, + room_id: &KeyToken, new_shortstatehash: u64, - // Take mutex guard to make sure users get the room state mutex - _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { self.roomid_shortstatehash .insert(room_id.as_bytes(), &new_shortstatehash.to_be_bytes())?; @@ -71,10 +73,8 @@ impl service::rooms::state::Data for KeyValueDatabase { fn set_forward_extremities( &self, - room_id: &RoomId, + room_id: &KeyToken, event_ids: Vec, - // Take mutex guard to make sure users get the room state mutex - _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { let mut prefix = room_id.as_bytes().to_vec(); prefix.push(0xFF); diff --git a/src/service/admin.rs b/src/service/admin.rs index b1edf1a9..e18a2039 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -265,17 +265,11 @@ impl Service { } }; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(grapevine_room.clone()) - .or_default(), - ); - - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(grapevine_room.clone()) + .await; services() .rooms @@ -290,8 +284,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - grapevine_room, - &state_lock, + &room_token, ) .await .unwrap(); @@ -1220,16 +1213,11 @@ impl Service { services().rooms.short.get_or_create_shortroomid(&room_id)?; - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.clone()) + .await; services().users.create(&services().globals.admin_bot_user_id, None)?; @@ -1268,8 +1256,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1298,8 +1285,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1323,8 +1309,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1344,8 +1329,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1367,8 +1351,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1388,8 +1371,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1411,8 +1393,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1434,8 +1415,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1458,8 +1438,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1495,16 +1474,11 @@ impl Service { displayname: String, ) -> Result<()> { if let Some(room_id) = services().admin.get_admin_room()? { - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.clone()) + .await; // Use the server user to grant the new admin's power level // Invite and join the real user @@ -1530,8 +1504,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; services() @@ -1556,8 +1529,7 @@ impl Service { redacts: None, }, user_id, - &room_id, - &state_lock, + &room_token, ) .await?; @@ -1585,8 +1557,7 @@ impl Service { redacts: None, }, &services().globals.admin_bot_user_id, - &room_id, - &state_lock, + &room_token, ) .await?; } diff --git a/src/service/globals.rs b/src/service/globals.rs index bf82d4e1..e8fe08d1 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -33,8 +33,11 @@ use tracing::{error, Instrument}; use trust_dns_resolver::TokioAsyncResolver; use crate::{ - api::server_server::FedDest, observability::FilterReloadHandles, services, - utils::on_demand_hashmap::OnDemandHashMap, Config, Error, Result, + api::server_server::FedDest, + observability::FilterReloadHandles, + services, + utils::on_demand_hashmap::{OnDemandHashMap, TokenSet}, + Config, Error, Result, }; type WellKnownMap = HashMap; @@ -42,6 +45,15 @@ type TlsNameMap = HashMap, u16)>; // Time if last failed try, number of failed tries type RateLimitState = (Instant, u32); +// Markers for +// [`Service::roomid_mutex_state`]/[`Service::roomid_mutex_insert`]/ +// [`Service::roomid_mutex_federation`] +pub(crate) mod marker { + pub(crate) enum State {} + pub(crate) enum Insert {} + pub(crate) enum Federation {} +} + pub(crate) struct Service { pub(crate) db: &'static dyn Data, pub(crate) reload_handles: FilterReloadHandles, @@ -69,7 +81,7 @@ pub(crate) struct Service { OnDemandHashMap, pub(crate) roomid_mutex_insert: RwLock>>>, - pub(crate) roomid_mutex_state: RwLock>>>, + pub(crate) roomid_mutex_state: TokenSet, // this lock will be held longer pub(crate) roomid_mutex_federation: @@ -266,7 +278,7 @@ impl Service { servername_ratelimiter: OnDemandHashMap::new( "servername_ratelimiter".to_owned(), ), - roomid_mutex_state: RwLock::new(HashMap::new()), + roomid_mutex_state: TokenSet::new("roomid_mutex_state".to_owned()), roomid_mutex_insert: RwLock::new(HashMap::new()), roomid_mutex_federation: RwLock::new(HashMap::new()), roomid_federationhandletime: RwLock::new(HashMap::new()), diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index be1c3276..3817fb90 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -992,16 +992,11 @@ impl Service { // 13. Use state resolution to find new room state // We start looking at current room state now, so lets lock the room - let mutex_state = Arc::clone( - services() - .globals - .roomid_mutex_state - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let state_lock = mutex_state.lock().await; + let room_token = services() + .globals + .roomid_mutex_state + .lock_key(room_id.to_owned()) + .await; // Now we calculate the set of extremities this room has after the // incoming event has been applied. We start with the previous @@ -1070,7 +1065,7 @@ impl Service { services() .rooms .state - .force_state(room_id, sstatehash, new, removed, &state_lock) + .force_state(&room_token, sstatehash, new, removed) .await?; } @@ -1088,7 +1083,7 @@ impl Service { extremities.iter().map(|e| (**e).to_owned()).collect(), state_ids_compressed, soft_fail, - &state_lock, + &room_token, ) .await?; @@ -1121,14 +1116,14 @@ impl Service { extremities.iter().map(|e| (**e).to_owned()).collect(), state_ids_compressed, soft_fail, - &state_lock, + &room_token, ) .await?; debug!("Appended incoming pdu"); // Event has passed all auth/stateres checks - drop(state_lock); + drop(room_token); Ok(pdu_id) } diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 709c70d2..f2ab7b36 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -13,16 +13,18 @@ use ruma::{ }, serde::Raw, state_res::{self, StateMap}, - EventId, OwnedEventId, RoomId, RoomVersionId, UserId, + EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId, }; use serde::Deserialize; -use tokio::sync::MutexGuard; use tracing::warn; use super::state_compressor::CompressedStateEvent; use crate::{ + service::globals::marker, services, - utils::{calculate_hash, debug_slice_truncated}, + utils::{ + calculate_hash, debug_slice_truncated, on_demand_hashmap::KeyToken, + }, Error, PduEvent, Result, }; @@ -32,20 +34,13 @@ pub(crate) struct Service { impl Service { /// Set the room to the given statehash and update caches. - #[tracing::instrument(skip( - self, - statediffnew, - _statediffremoved, - state_lock - ))] + #[tracing::instrument(skip(self, statediffnew, _statediffremoved))] pub(crate) async fn force_state( &self, - room_id: &RoomId, + room_id: &KeyToken, shortstatehash: u64, statediffnew: Arc>, _statediffremoved: Arc>, - // Take mutex guard to make sure users get the room state mutex - state_lock: &MutexGuard<'_, ()>, ) -> Result<()> { for event_id in statediffnew.iter().filter_map(|new| { services() @@ -116,7 +111,7 @@ impl Service { services().rooms.state_cache.update_joined_count(room_id)?; - self.db.set_room_state(room_id, shortstatehash, state_lock)?; + self.db.set_room_state(room_id, shortstatehash)?; Ok(()) } @@ -325,12 +320,10 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) fn set_room_state( &self, - room_id: &RoomId, + room_id: &KeyToken, shortstatehash: u64, - // Take mutex guard to make sure users get the room state mutex - mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()> { - self.db.set_room_state(room_id, shortstatehash, mutex_lock) + self.db.set_room_state(room_id, shortstatehash) } /// Returns the room's version. @@ -383,17 +376,15 @@ impl Service { } #[tracing::instrument( - skip(self, event_ids, state_lock), + skip(self, event_ids), fields(event_ids = debug_slice_truncated(&event_ids, 5)), )] pub(crate) fn set_forward_extremities( &self, - room_id: &RoomId, + room_id: &KeyToken, event_ids: Vec, - // Take mutex guard to make sure users get the room state mutex - state_lock: &MutexGuard<'_, ()>, ) -> Result<()> { - self.db.set_forward_extremities(room_id, event_ids, state_lock) + self.db.set_forward_extremities(room_id, event_ids) } /// This fetches auth events from the current state. diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index 2ed50def..6f15b004 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -1,9 +1,10 @@ use std::{collections::HashSet, sync::Arc}; -use ruma::{EventId, OwnedEventId, RoomId}; -use tokio::sync::MutexGuard; +use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId}; -use crate::Result; +use crate::{ + service::globals::marker, utils::on_demand_hashmap::KeyToken, Result, +}; pub(crate) trait Data: Send + Sync { /// Returns the last state hash key added to the db for the given room. @@ -12,10 +13,8 @@ pub(crate) trait Data: Send + Sync { /// Set the state hash to a new version, but does not update `state_cache`. fn set_room_state( &self, - room_id: &RoomId, + room_id: &KeyToken, new_shortstatehash: u64, - // Take mutex guard to make sure users get the room state mutex - _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()>; /// Associates a state with an event. @@ -34,9 +33,7 @@ pub(crate) trait Data: Send + Sync { /// Replace the forward extremities of the room. fn set_forward_extremities( &self, - room_id: &RoomId, + room_id: &KeyToken, event_ids: Vec, - // Take mutex guard to make sure users get the room state mutex - _mutex_lock: &MutexGuard<'_, ()>, ) -> Result<()>; } diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 99502700..b9e9f702 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -20,17 +20,18 @@ use ruma::{ StateEventType, }, state_res::Event, - EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, - UserId, + EventId, JsOption, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, + ServerName, UserId, }; use serde_json::value::to_raw_value; -use tokio::sync::MutexGuard; use tracing::{error, warn}; use crate::{ observability::{FoundIn, Lookup, METRICS}, - service::pdu::PduBuilder, - services, Error, PduEvent, Result, + service::{globals::marker, pdu::PduBuilder}, + services, + utils::on_demand_hashmap::KeyToken, + Error, PduEvent, Result, }; pub(crate) struct Service { @@ -390,10 +391,9 @@ impl Service { #[tracing::instrument(skip(self), ret(level = "trace"))] pub(crate) fn user_can_invite( &self, - room_id: &RoomId, + room_id: &KeyToken, sender: &UserId, target_user: &UserId, - state_lock: &MutexGuard<'_, ()>, ) -> bool { let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite)) @@ -410,7 +410,7 @@ impl Service { services() .rooms .timeline - .create_hash_and_sign_event(new_event, sender, room_id, state_lock) + .create_hash_and_sign_event(new_event, sender, room_id) .is_ok() } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index f850057d..cc8e42d3 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -22,11 +22,12 @@ use ruma::{ push::{Action, Ruleset, Tweak}, state_res::{self, Event, RoomVersion}, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, - OwnedEventId, OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, + OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId, + ServerName, UserId, }; use serde::Deserialize; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; -use tokio::sync::{MutexGuard, RwLock}; +use tokio::sync::RwLock; use tracing::{error, info, warn}; use super::state_compressor::CompressedStateEvent; @@ -34,10 +35,12 @@ use crate::{ api::server_server, service::{ appservice::NamespaceRegex, - globals::SigningKeys, + globals::{marker, SigningKeys}, pdu::{EventHash, PduBuilder}, }, - services, utils, Error, PduEvent, Result, + services, + utils::{self, on_demand_hashmap::KeyToken}, + Error, PduEvent, Result, }; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] @@ -197,9 +200,10 @@ impl Service { pdu: &PduEvent, mut pdu_json: CanonicalJsonObject, leaves: Vec, - // Take mutex guard to make sure users get the room state mutex - state_lock: &MutexGuard<'_, ()>, + room_id: &KeyToken, ) -> Result> { + assert_eq!(*pdu.room_id, **room_id, "Token for incorrect room passed"); + let shortroomid = services() .rooms .short @@ -253,11 +257,7 @@ impl Service { .rooms .pdu_metadata .mark_as_referenced(&pdu.room_id, &pdu.prev_events)?; - services().rooms.state.set_forward_extremities( - &pdu.room_id, - leaves, - state_lock, - )?; + services().rooms.state.set_forward_extremities(room_id, leaves)?; let mutex_insert = Arc::clone( services() @@ -669,9 +669,7 @@ impl Service { &self, pdu_builder: PduBuilder, sender: &UserId, - room_id: &RoomId, - // Take mutex guard to make sure users get the room state mutex - _mutex_lock: &MutexGuard<'_, ()>, + room_id: &KeyToken, ) -> Result<(PduEvent, CanonicalJsonObject)> { let PduBuilder { event_type, @@ -702,7 +700,7 @@ impl Service { } else { Err(Error::InconsistentRoomState( "non-create event for room of unknown version", - room_id.to_owned(), + (**room_id).clone(), )) } })?; @@ -751,7 +749,7 @@ impl Service { let mut pdu = PduEvent { event_id: ruma::event_id!("$thiswillbefilledinlater").into(), - room_id: room_id.to_owned(), + room_id: (**room_id).clone(), sender: sender.to_owned(), origin_server_ts: utils::millis_since_unix_epoch() .try_into() @@ -855,24 +853,18 @@ impl Service { /// Creates a new persisted data unit and adds it to a room. This function /// takes a roomid_mutex_state, meaning that only this function is able /// to mutate the room state. - #[tracing::instrument(skip(self, state_lock))] + #[tracing::instrument(skip(self))] pub(crate) async fn build_and_append_pdu( &self, pdu_builder: PduBuilder, sender: &UserId, - room_id: &RoomId, - // Take mutex guard to make sure users get the room state mutex - state_lock: &MutexGuard<'_, ()>, + room_id: &KeyToken, ) -> Result> { - let (pdu, pdu_json) = self.create_hash_and_sign_event( - pdu_builder, - sender, - room_id, - state_lock, - )?; + let (pdu, pdu_json) = + self.create_hash_and_sign_event(pdu_builder, sender, room_id)?; if let Some(admin_room) = services().admin.get_admin_room()? { - if admin_room == room_id { + if admin_room == **room_id { match pdu.event_type() { TimelineEventType::RoomEncryption => { warn!("Encryption is not allowed in the admins room"); @@ -1052,18 +1044,14 @@ impl Service { // Since this PDU references all pdu_leaves we can update the // leaves of the room vec![(*pdu.event_id).to_owned()], - state_lock, + room_id, ) .await?; // We set the room state after inserting the pdu, so that we never have // a moment in time where events in the current room state do // not exist - services().rooms.state.set_room_state( - room_id, - statehashid, - state_lock, - )?; + services().rooms.state.set_room_state(room_id, statehashid)?; let mut servers: HashSet = services() .rooms @@ -1103,9 +1091,10 @@ impl Service { new_room_leaves: Vec, state_ids_compressed: Arc>, soft_fail: bool, - // Take mutex guard to make sure users get the room state mutex - state_lock: &MutexGuard<'_, ()>, + room_id: &KeyToken, ) -> Result>> { + assert_eq!(*pdu.room_id, **room_id, "Token for incorrect room passed"); + // We append to state before appending the pdu, so we don't have a // moment in time with the pdu without it's state. This is okay // because append_pdu can't fail. @@ -1119,19 +1108,18 @@ impl Service { services() .rooms .pdu_metadata - .mark_as_referenced(&pdu.room_id, &pdu.prev_events)?; - services().rooms.state.set_forward_extremities( - &pdu.room_id, - new_room_leaves, - state_lock, - )?; + .mark_as_referenced(room_id, &pdu.prev_events)?; + services() + .rooms + .state + .set_forward_extremities(room_id, new_room_leaves)?; return Ok(None); } let pdu_id = services() .rooms .timeline - .append_pdu(pdu, pdu_json, new_room_leaves, state_lock) + .append_pdu(pdu, pdu_json, new_room_leaves, room_id) .await?; Ok(Some(pdu_id)) From 4893c54f4f6048f4be2215455eec9e09be51417f Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 23 Jun 2024 20:07:54 +0000 Subject: [PATCH 315/617] Use TokenSet for roomid_mutex_insert --- src/api/client_server/sync.rs | 52 ++++++++++++----------------------- src/service/globals.rs | 7 +++-- src/service/rooms/timeline.rs | 34 ++++++++--------------- 3 files changed, 34 insertions(+), 59 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 64069f66..f2985301 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1,6 +1,5 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - sync::Arc, time::Duration, }; @@ -161,17 +160,12 @@ pub(crate) async fn sync_events_route( { // Get and drop the lock to wait for remaining operations to finish - let mutex_insert = Arc::clone( - services() - .globals - .roomid_mutex_insert - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let insert_lock = mutex_insert.lock().await; - drop(insert_lock); + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; + drop(room_token); } let left_count = services() @@ -330,17 +324,12 @@ pub(crate) async fn sync_events_route( { // Get and drop the lock to wait for remaining operations to finish - let mutex_insert = Arc::clone( - services() - .globals - .roomid_mutex_insert - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let insert_lock = mutex_insert.lock().await; - drop(insert_lock); + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; + drop(room_token); } let invite_count = services() @@ -481,17 +470,12 @@ async fn load_joined_room( { // Get and drop the lock to wait for remaining operations to finish // This will make sure the we have all events until next_batch - let mutex_insert = Arc::clone( - services() - .globals - .roomid_mutex_insert - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let insert_lock = mutex_insert.lock().await; - drop(insert_lock); + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.to_owned()) + .await; + drop(room_token); } let (timeline_pdus, limited) = diff --git a/src/service/globals.rs b/src/service/globals.rs index e8fe08d1..662d6da4 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -79,8 +79,7 @@ pub(crate) struct Service { Arc>>, pub(crate) servername_ratelimiter: OnDemandHashMap, - pub(crate) roomid_mutex_insert: - RwLock>>>, + pub(crate) roomid_mutex_insert: TokenSet, pub(crate) roomid_mutex_state: TokenSet, // this lock will be held longer @@ -279,7 +278,9 @@ impl Service { "servername_ratelimiter".to_owned(), ), roomid_mutex_state: TokenSet::new("roomid_mutex_state".to_owned()), - roomid_mutex_insert: RwLock::new(HashMap::new()), + roomid_mutex_insert: TokenSet::new( + "roomid_mutex_insert".to_owned(), + ), roomid_mutex_federation: RwLock::new(HashMap::new()), roomid_federationhandletime: RwLock::new(HashMap::new()), stateres_mutex: Arc::new(Mutex::new(())), diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index cc8e42d3..a8472811 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -259,16 +259,11 @@ impl Service { .mark_as_referenced(&pdu.room_id, &pdu.prev_events)?; services().rooms.state.set_forward_extremities(room_id, leaves)?; - let mutex_insert = Arc::clone( - services() - .globals - .roomid_mutex_insert - .write() - .await - .entry(pdu.room_id.clone()) - .or_default(), - ); - let insert_lock = mutex_insert.lock().await; + let insert_token = services() + .globals + .roomid_mutex_insert + .lock_key(pdu.room_id.clone()) + .await; let count1 = services().globals.next_count()?; // Mark as read first so the sending client doesn't get a notification @@ -290,7 +285,7 @@ impl Service { // Insert pdu self.db.append_pdu(&pdu_id, pdu, &pdu_json, count2)?; - drop(insert_lock); + drop(insert_token); // See if the event matches any known pushers let power_levels: RoomPowerLevelsEventContent = services() @@ -1328,16 +1323,11 @@ impl Service { .get_shortroomid(&room_id)? .expect("room exists"); - let mutex_insert = Arc::clone( - services() - .globals - .roomid_mutex_insert - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let insert_lock = mutex_insert.lock().await; + let insert_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; let count = services().globals.next_count()?; let mut pdu_id = shortroomid.to_be_bytes().to_vec(); @@ -1347,7 +1337,7 @@ impl Service { // Insert pdu self.db.prepend_backfill_pdu(&pdu_id, &event_id, &value)?; - drop(insert_lock); + drop(insert_token); if pdu.kind == TimelineEventType::RoomMessage { #[derive(Deserialize)] From dd24a441121b94d389fb46f08c7ec51886d5aa32 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 23 Jun 2024 20:12:38 +0000 Subject: [PATCH 316/617] Use TokenSet for roomid_mutex_federation --- src/api/server_server.rs | 34 ++++++++++++---------------------- src/service/globals.rs | 6 ++++-- src/service/rooms/timeline.rs | 17 ++++++----------- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 2208c693..890d9997 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -742,16 +742,11 @@ pub(crate) async fn send_transaction_message_route( // We do not add the event_id field to the pdu here because of signature // and hashes checks - let mutex = Arc::clone( - services() - .globals - .roomid_mutex_federation - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let mutex_lock = mutex.lock().await; + let federation_token = services() + .globals + .roomid_mutex_federation + .lock_key(room_id.clone()) + .await; let start_time = Instant::now(); resolved_map.insert( event_id.clone(), @@ -769,7 +764,7 @@ pub(crate) async fn send_transaction_message_route( .await .map(|_| ()), ); - drop(mutex_lock); + drop(federation_token); debug!( %event_id, @@ -1619,16 +1614,11 @@ async fn create_join_event( Error::BadRequest(ErrorKind::InvalidParam, "Origin field is invalid.") })?; - let mutex = Arc::clone( - services() - .globals - .roomid_mutex_federation - .write() - .await - .entry(room_id.to_owned()) - .or_default(), - ); - let mutex_lock = mutex.lock().await; + let federation_token = services() + .globals + .roomid_mutex_federation + .lock_key(room_id.to_owned()) + .await; let pdu_id: Vec = services() .rooms .event_handler @@ -1645,7 +1635,7 @@ async fn create_join_event( ErrorKind::InvalidParam, "Could not accept incoming PDU as timeline event.", ))?; - drop(mutex_lock); + drop(federation_token); let state_ids = services().rooms.state_accessor.state_full_ids(shortstatehash).await?; diff --git a/src/service/globals.rs b/src/service/globals.rs index 662d6da4..7ef6fbb7 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -84,7 +84,7 @@ pub(crate) struct Service { // this lock will be held longer pub(crate) roomid_mutex_federation: - RwLock>>>, + TokenSet, pub(crate) roomid_federationhandletime: RwLock>, pub(crate) stateres_mutex: Arc>, @@ -281,7 +281,9 @@ impl Service { roomid_mutex_insert: TokenSet::new( "roomid_mutex_insert".to_owned(), ), - roomid_mutex_federation: RwLock::new(HashMap::new()), + roomid_mutex_federation: TokenSet::new( + "roomid_mutex_federation".to_owned(), + ), roomid_federationhandletime: RwLock::new(HashMap::new()), stateres_mutex: Arc::new(Mutex::new(())), rotate: RotationHandler::new(), diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index a8472811..b325f198 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -1284,16 +1284,11 @@ impl Service { server_server::parse_incoming_pdu(&pdu)?; // Lock so we cannot backfill the same pdu twice at the same time - let mutex = Arc::clone( - services() - .globals - .roomid_mutex_federation - .write() - .await - .entry(room_id.clone()) - .or_default(), - ); - let mutex_lock = mutex.lock().await; + let federation_token = services() + .globals + .roomid_mutex_federation + .lock_key(room_id.clone()) + .await; // Skip the PDU if we already have it as a timeline event if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(&event_id)? { @@ -1359,7 +1354,7 @@ impl Service { )?; } } - drop(mutex_lock); + drop(federation_token); info!("Prepended backfill pdu"); Ok(()) From 51dd75c3dca00b5d80932bf2278e9dbe68b3ccf0 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 25 Jun 2024 15:21:55 -0700 Subject: [PATCH 317/617] factor account data upgrade logic into functions The implementation inside each function is unchanged from the original. I intend to clean up and fix bugs in later commits, so the diff will be more clear. --- src/service/rooms/state_cache.rs | 240 ++++++++++++++++--------------- 1 file changed, 128 insertions(+), 112 deletions(-) diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 372cfd3f..7fc35247 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -65,118 +65,11 @@ impl Service { content.predecessor }) { - // Copy user settings from predecessor to the current - // room: - // - Push rules - // - // TODO: finish this once push rules are implemented. - // - // let mut push_rules_event_content: PushRulesEvent = - // account_data .get( - // None, - // user_id, - // EventType::PushRules, - // )?; - // - // NOTE: find where `predecessor.room_id` match - // and update to `room_id`. - // - // account_data - // .update( - // None, - // user_id, - // EventType::PushRules, - // &push_rules_event_content, - // globals, - // ) - // .ok(); - - let event_kind = RoomAccountDataEventType::Tag; - - // Copy old tags to new room - if let Some(tag_event) = services() - .account_data - .get( - Some(&predecessor.room_id), - user_id, - event_kind.clone(), - )? - .map(|event| { - serde_json::from_str(event.get()).map_err( - |error| { - warn!( - %error, - predecessor_room_id = - %predecessor.room_id, - %event_kind, - "Invalid account data event", - ); - Error::BadDatabase( - "Invalid account data event.", - ) - }, - ) - }) - { - services() - .account_data - .update( - Some(room_id), - user_id, - RoomAccountDataEventType::Tag, - &tag_event?, - ) - .ok(); - }; - - let event_kind = RoomAccountDataEventType::from( - GlobalAccountDataEventType::Direct.to_string(), - ); - - // Copy direct chat flag - if let Some(direct_event) = services() - .account_data - .get(None, user_id, event_kind.clone())? - .map(|event| { - serde_json::from_str::(event.get()) - .map_err(|error| { - warn!( - %error, - %event_kind, - "Invalid account data event", - ); - Error::BadDatabase( - "Invalid account data event.", - ) - }) - }) - { - let mut direct_event = direct_event?; - let mut room_ids_updated = false; - - for room_ids in direct_event.content.0.values_mut() - { - if room_ids - .iter() - .any(|r| r == &predecessor.room_id) - { - room_ids.push(room_id.to_owned()); - room_ids_updated = true; - } - } - - if room_ids_updated { - services().account_data.update( - None, - user_id, - GlobalAccountDataEventType::Direct - .to_string() - .into(), - &serde_json::to_value(&direct_event) - .expect("to json always works"), - )?; - } - }; + self.copy_upgraded_account_data( + user_id, + &predecessor.room_id, + room_id, + )?; } } @@ -238,6 +131,129 @@ impl Service { Ok(()) } + /// Copy all account data references from the predecessor to a new room when + /// joining an upgraded room. + /// + /// References to the predecessor room are not removed. + #[tracing::instrument(skip(self))] + fn copy_upgraded_account_data( + &self, + user_id: &UserId, + from_room_id: &RoomId, + to_room_id: &RoomId, + ) -> Result<()> { + // - Push rules + // + // TODO: finish this once push rules are implemented. + // + // let mut push_rules_event_content: PushRulesEvent = + // account_data .get( + // None, + // user_id, + // EventType::PushRules, + // )?; + // + // NOTE: find where `predecessor.room_id` match + // and update to `room_id`. + // + // account_data + // .update( + // None, + // user_id, + // EventType::PushRules, + // &push_rules_event_content, + // globals, + // ) + // .ok(); + + self.copy_upgraded_account_data_tag(user_id, from_room_id, to_room_id)?; + self.copy_upgraded_account_data_direct( + user_id, + from_room_id, + to_room_id, + )?; + Ok(()) + } + + /// Copy `m.tag` account data to an upgraded room. + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] + fn copy_upgraded_account_data_tag( + &self, + user_id: &UserId, + from_room_id: &RoomId, + to_room_id: &RoomId, + ) -> Result<()> { + let event_kind = RoomAccountDataEventType::Tag; + if let Some(tag_event) = services() + .account_data + .get(Some(from_room_id), user_id, event_kind.clone())? + .map(|event| { + serde_json::from_str(event.get()).map_err(|error| { + warn!(%error, %event_kind, "Invalid account data event"); + Error::BadDatabase("Invalid account data event.") + }) + }) + { + services() + .account_data + .update(Some(to_room_id), user_id, event_kind, &tag_event?) + .ok(); + } + + Ok(()) + } + + /// Copy references in `m.direct` account data events to an upgraded room. + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] + fn copy_upgraded_account_data_direct( + &self, + user_id: &UserId, + from_room_id: &RoomId, + to_room_id: &RoomId, + ) -> Result<()> { + let event_kind = RoomAccountDataEventType::from( + GlobalAccountDataEventType::Direct.to_string(), + ); + if let Some(direct_event) = services() + .account_data + .get( + None, + user_id, + event_kind.clone(), + )? + .map(|event| { + serde_json::from_str::(event.get()) + .map_err(|error| { + warn!(%error, %event_kind, "Invalid account data event"); + Error::BadDatabase("Invalid account data event.") + }) + }) + { + let mut direct_event = direct_event?; + let mut room_ids_updated = false; + + for room_ids in direct_event.content.0.values_mut() { + if room_ids.iter().any(|r| r == from_room_id) { + room_ids.push(to_room_id.to_owned()); + room_ids_updated = true; + } + } + + if room_ids_updated { + services().account_data.update( + None, + user_id, + event_kind, + &serde_json::to_value(&direct_event) + .expect("to json always works"), + )?; + } + } + Ok(()) + } + #[tracing::instrument(skip(self, room_id))] pub(crate) fn update_joined_count(&self, room_id: &RoomId) -> Result<()> { self.db.update_joined_count(room_id) From 18360cd3f94efb5c861694238f07dc415f2c4979 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 25 Jun 2024 16:03:43 -0700 Subject: [PATCH 318/617] refactor error handling in copy_upgraded_account_data_tag The new error handling logic is semantically different from the old, but I don't know of any cases you could hit in practice where it would matter. --- src/service/rooms/state_cache.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 7fc35247..135a80f5 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -184,21 +184,23 @@ impl Service { from_room_id: &RoomId, to_room_id: &RoomId, ) -> Result<()> { - let event_kind = RoomAccountDataEventType::Tag; - if let Some(tag_event) = services() - .account_data - .get(Some(from_room_id), user_id, event_kind.clone())? - .map(|event| { - serde_json::from_str(event.get()).map_err(|error| { - warn!(%error, %event_kind, "Invalid account data event"); - Error::BadDatabase("Invalid account data event.") - }) - }) - { - services() - .account_data - .update(Some(to_room_id), user_id, event_kind, &tag_event?) - .ok(); + let Some(event) = services().account_data.get( + Some(from_room_id), + user_id, + RoomAccountDataEventType::Tag, + )? + else { + return Ok(()); + }; + let event = serde_json::from_str::(event.get()) + .expect("RawValue -> Value should always succeed"); + if let Err(error) = services().account_data.update( + Some(to_room_id), + user_id, + RoomAccountDataEventType::Tag, + &event, + ) { + warn!(%error, "error writing m.tag account data to upgraded room"); } Ok(()) From a32dc1a3eeb864eaa91c7c0bc08137f4ae4576b0 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 25 Jun 2024 17:11:39 -0700 Subject: [PATCH 319/617] tolerate invalid m.direct events when upgrading rooms Previous behavior was causing us to error out of the entire state_cache::update_membership function when it saw an invalid m.direct, making it impossible for affected users to join upgraded rooms. This bug is especially bad if an affected user attempts to upgrade a room, because we will fail to create their join event in the new room, leaving both rooms permanently bricked. The new behavior should never prevent users from joining a room based on the contents of their account data, and should migrate the `m.direct` event in a reasonable way even if it is partially corrupted by the element bug. This also fixes a bug where the previous implementation will unintentionally remove any keys that aren't part of the expected m.direct schema. I don't know of any cases where this came up in practice. Fixes: #46 --- src/service/rooms/state_cache.rs | 86 ++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 135a80f5..a2b1aab2 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -4,7 +4,6 @@ use std::{collections::HashSet, sync::Arc}; pub(crate) use data::Data; use ruma::{ events::{ - direct::DirectEvent, ignored_user_list::IgnoredUserListEvent, room::{create::RoomCreateEventContent, member::MembershipState}, AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, @@ -218,39 +217,64 @@ impl Service { let event_kind = RoomAccountDataEventType::from( GlobalAccountDataEventType::Direct.to_string(), ); - if let Some(direct_event) = services() - .account_data - .get( + let Some(event) = + services().account_data.get(None, user_id, event_kind.clone())? + else { + return Ok(()); + }; + + let mut event = serde_json::from_str::(event.get()) + .expect("RawValue -> Value should always succeed"); + + // As a server, we should try not to assume anything about the schema + // of this event. Account data may be arbitrary JSON. + // + // In particular, there is an element bug[1] that causes it to store + // m.direct events that don't match the schema from the spec. + // + // [1]: https://github.com/element-hq/element-web/issues/27630 + // + // A valid m.direct event looks like this: + // + // { + // "type": "m.account_data", + // "content": { + // "@userid1": [ "!roomid1", "!roomid2" ], + // "@userid2": [ "!roomid3" ], + // } + // } + // + // We want to find userid keys where the value contains from_room_id, + // and insert a new entry for to_room_id. This should work even if some + // of the userid keys do not conform to the spec. If parts of the object + // do not match the expected schema, we should prefer to skip just those + // parts. + + let mut event_updated = false; + let Some(direct_user_ids) = event.get_mut("content") else { + return Ok(()); + }; + let Some(direct_user_ids) = direct_user_ids.as_object_mut() else { + return Ok(()); + }; + for room_ids in direct_user_ids.values_mut() { + let Some(room_ids) = room_ids.as_array_mut() else { + continue; + }; + if room_ids.iter().any(|room_id| room_id == from_room_id.as_str()) { + room_ids.push(to_room_id.to_string().into()); + event_updated = true; + } + } + + if event_updated { + if let Err(error) = services().account_data.update( None, user_id, event_kind.clone(), - )? - .map(|event| { - serde_json::from_str::(event.get()) - .map_err(|error| { - warn!(%error, %event_kind, "Invalid account data event"); - Error::BadDatabase("Invalid account data event.") - }) - }) - { - let mut direct_event = direct_event?; - let mut room_ids_updated = false; - - for room_ids in direct_event.content.0.values_mut() { - if room_ids.iter().any(|r| r == from_room_id) { - room_ids.push(to_room_id.to_owned()); - room_ids_updated = true; - } - } - - if room_ids_updated { - services().account_data.update( - None, - user_id, - event_kind, - &serde_json::to_value(&direct_event) - .expect("to json always works"), - )?; + &event, + ) { + warn!(%event_kind, %error, "error writing account data event after upgrading room"); } } Ok(()) From 827f670c54eb116885fd54014f57ab08c3fd2128 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 25 Jun 2024 19:40:53 -0700 Subject: [PATCH 320/617] add changelog entries for the account data validation fixes --- book/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 5362ed90..2d04d10d 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -148,6 +148,13 @@ This will be the first release of Grapevine since it was forked from Conduit ([!35 (3551a6e)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/35/diffs?commit_id=3551a6ef7a29219b9b30f50a7e8c92b92debcdcf)) 10. Only process admin commands if the admin bot is in the admin room. ([!43](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/43)) +11. Fix bug where invalid account data from a client could prevent a user from + joining any upgraded rooms and brick rooms that affected users attempted to + upgrade. + ([!53](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/53)) +12. Fix bug where unexpected keys were deleted from `m.direct` account data + events when joining an upgraded room. + ([!53](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/53)) ### Added From 1551833501c9c8b8a9879f6f5b89e5e2b6e4915b Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 10 Aug 2024 12:46:50 -0700 Subject: [PATCH 321/617] log failed remote device key requests --- src/api/client_server/keys.rs | 73 +++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 2f14733a..35db42b4 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -440,48 +440,55 @@ pub(crate) async fn get_keys_helper bool>( ), ) .await - .map_err(|_e| Error::BadServerResponse("Query took too long")), + .map_err(|_e| Error::BadServerResponse("Query took too long")) + // TODO: switch to .flatten() when stable + // + .and_then(|result| result), ) }) .collect(); while let Some((server, response)) = futures.next().await { - if let Ok(Ok(response)) = response { - for (user, masterkey) in response.master_keys { - let (master_key_id, mut master_key) = - services().users.parse_master_key(&user, &masterkey)?; - - if let Some(our_master_key) = services().users.get_key( - &master_key_id, - sender_user, - &user, - &allowed_signatures, - )? { - let (_, our_master_key) = services() - .users - .parse_master_key(&user, &our_master_key)?; - master_key.signatures.extend(our_master_key.signatures); - } - let json = serde_json::to_value(master_key) - .expect("to_value always works"); - let raw = serde_json::from_value(json) - .expect("Raw::from_value always works"); - services().users.add_cross_signing_keys( - &user, &raw, &None, &None, - // Dont notify. A notification would trigger another key - // request resulting in an endless loop - false, - )?; - master_keys.insert(user, raw); + let response = match response { + Ok(response) => response, + Err(error) => { + back_off(server.to_owned()).await; + debug!(%server, %error, "remote device key query failed"); + failures.insert(server.to_string(), json!({})); + continue; } + }; - self_signing_keys.extend(response.self_signing_keys); - device_keys.extend(response.device_keys); - } else { - back_off(server.to_owned()).await; + for (user, masterkey) in response.master_keys { + let (master_key_id, mut master_key) = + services().users.parse_master_key(&user, &masterkey)?; - failures.insert(server.to_string(), json!({})); + if let Some(our_master_key) = services().users.get_key( + &master_key_id, + sender_user, + &user, + &allowed_signatures, + )? { + let (_, our_master_key) = services() + .users + .parse_master_key(&user, &our_master_key)?; + master_key.signatures.extend(our_master_key.signatures); + } + let json = serde_json::to_value(master_key) + .expect("to_value always works"); + let raw = serde_json::from_value(json) + .expect("Raw::from_value always works"); + services().users.add_cross_signing_keys( + &user, &raw, &None, &None, + // Dont notify. A notification would trigger another key + // request resulting in an endless loop + false, + )?; + master_keys.insert(user, raw); } + + self_signing_keys.extend(response.self_signing_keys); + device_keys.extend(response.device_keys); } Ok(get_keys::v3::Response { From 141c60e4a35341d983c2da736ac51ad067c425fe Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 10 Aug 2024 12:49:46 -0700 Subject: [PATCH 322/617] log more detailed backoff state for remote device key requests --- src/api/client_server/keys.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 35db42b4..9d2f2d81 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -413,8 +413,10 @@ pub(crate) async fn get_keys_helper bool>( min_elapsed_duration = Duration::from_secs(60 * 60 * 24); } - if time.elapsed() < min_elapsed_duration { - debug!(%server, "Backing off from server"); + if let Some(remaining) = + min_elapsed_duration.checked_sub(time.elapsed()) + { + debug!(%server, %tries, ?remaining, "Backing off from server"); return ( server, Err(Error::BadServerResponse( From 91739899e7d30f6ee83528243e50c01fae6d0148 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 10 Aug 2024 15:00:12 -0700 Subject: [PATCH 323/617] changelog entry for remote device key query logging --- book/changelog.md | 3 ++- src/api/client_server/keys.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 2d04d10d..f8e94d62 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -103,7 +103,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50), [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52), [!54](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/54), - [!56](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/56)) + [!56](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/56), + [!69](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/69)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 9d2f2d81..0e423e72 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -430,6 +430,8 @@ pub(crate) async fn get_keys_helper bool>( for (user_id, keys) in vec { device_keys_input_fed.insert(user_id.to_owned(), keys.clone()); } + // TODO: switch .and_then(|result| result) to .flatten() when stable + // ( server, tokio::time::timeout( @@ -443,8 +445,6 @@ pub(crate) async fn get_keys_helper bool>( ) .await .map_err(|_e| Error::BadServerResponse("Query took too long")) - // TODO: switch to .flatten() when stable - // .and_then(|result| result), ) }) From 00b77144c192618c75e6c5c0106e021f18f2bbac Mon Sep 17 00:00:00 2001 From: avdb13 Date: Mon, 22 Jul 2024 13:38:29 +0200 Subject: [PATCH 324/617] chore: deprecate support for unstable room versions --- src/api/client_server/capabilities.rs | 3 -- src/api/client_server/room.rs | 53 +++++++++------------------ src/config.rs | 2 - src/service/admin.rs | 25 ++++++------- src/service/globals.rs | 27 ++------------ src/service/rooms/event_handler.rs | 15 ++------ src/service/rooms/timeline.rs | 17 +++------ 7 files changed, 41 insertions(+), 101 deletions(-) diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index 8a7d6b10..515ebfc1 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -14,9 +14,6 @@ pub(crate) async fn get_capabilities_route( _body: Ar, ) -> Result> { let mut available = BTreeMap::new(); - for room_version in &services().globals.unstable_room_versions { - available.insert(room_version.clone(), RoomVersionStability::Unstable); - } for room_version in &services().globals.stable_room_versions { available.insert(room_version.clone(), RoomVersionStability::Stable); } diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index c02526b3..835a57f4 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -138,17 +138,8 @@ pub(crate) async fn create_room_route( .deserialize_as::() .expect("Invalid creation content"); - match room_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { + match &room_version { + room_version if *room_version < RoomVersionId::V11 => { content.insert( "creator".into(), json!(&sender_user).try_into().map_err(|_| { @@ -161,7 +152,11 @@ pub(crate) async fn create_room_route( } // V11 removed the "creator" key RoomVersionId::V11 => {} - _ => unreachable!("Validity of room version already checked"), + _ => { + return Err(Error::BadServerResponse( + "Unsupported room version.", + )) + } } content.insert( @@ -176,21 +171,16 @@ pub(crate) async fn create_room_route( content } None => { - let content = match room_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { + let content = match &room_version { + room_version if *room_version < RoomVersionId::V11 => { RoomCreateEventContent::new_v1(sender_user.to_owned()) } RoomVersionId::V11 => RoomCreateEventContent::new_v11(), - _ => unreachable!("Validity of room version already checked"), + _ => { + return Err(Error::BadServerResponse( + "Unsupported room version.", + )) + } }; let mut content = serde_json::from_str::( to_raw_value(&content) @@ -671,17 +661,8 @@ pub(crate) async fn upgrade_room_route( // Send a m.room.create event containing a predecessor field and the // applicable room_version - match body.new_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { + match &body.new_version { + room_version if *room_version < RoomVersionId::V11 => { create_event_content.insert( "creator".into(), json!(&sender_user).try_into().map_err(|_| { @@ -696,7 +677,7 @@ pub(crate) async fn upgrade_room_route( // "creator" key no longer exists in V11 rooms create_event_content.remove("creator"); } - _ => unreachable!("Validity of room version already checked"), + _ => return Err(Error::BadServerResponse("Unsupported room version.")), } create_event_content.insert( "room_version".into(), diff --git a/src/config.rs b/src/config.rs index 6fe3a43e..90b047c8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,8 +50,6 @@ pub(crate) struct Config { pub(crate) allow_encryption: bool, #[serde(default = "true_fn")] pub(crate) allow_room_creation: bool, - #[serde(default = "true_fn")] - pub(crate) allow_unstable_room_versions: bool, #[serde(default = "default_default_room_version")] pub(crate) default_room_version: RoomVersionId, #[serde(default)] diff --git a/src/service/admin.rs b/src/service/admin.rs index e18a2039..43c9753d 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1222,21 +1222,18 @@ impl Service { services().users.create(&services().globals.admin_bot_user_id, None)?; let room_version = services().globals.default_room_version(); - let mut content = match room_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => RoomCreateEventContent::new_v1( - services().globals.admin_bot_user_id.clone(), - ), + let mut content = match &room_version { + room_version if *room_version < RoomVersionId::V11 => { + RoomCreateEventContent::new_v1( + services().globals.admin_bot_user_id.clone(), + ) + } RoomVersionId::V11 => RoomCreateEventContent::new_v11(), - _ => unreachable!("Validity of room version already checked"), + _ => { + return Err(Error::BadServerResponse( + "Unsupported room version.", + )) + } }; content.federate = true; content.predecessor = None; diff --git a/src/service/globals.rs b/src/service/globals.rs index 7ef6fbb7..2bdad38c 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -68,7 +68,6 @@ pub(crate) struct Service { federation_client: reqwest::Client, default_client: reqwest::Client, pub(crate) stable_room_versions: Vec, - pub(crate) unstable_room_versions: Vec, pub(crate) admin_bot_user_id: OwnedUserId, pub(crate) admin_bot_room_alias_id: OwnedRoomAliasId, pub(crate) bad_event_ratelimiter: @@ -224,9 +223,6 @@ impl Service { RoomVersionId::V10, RoomVersionId::V11, ]; - // Experimental, partially supported room versions - let unstable_room_versions = - vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5]; let admin_bot_user_id = UserId::parse(format!( "@{}:{}", @@ -268,7 +264,6 @@ impl Service { default_client, jwt_decoding_key, stable_room_versions, - unstable_room_versions, admin_bot_user_id, admin_bot_room_alias_id, bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), @@ -369,10 +364,6 @@ impl Service { self.config.allow_room_creation } - pub(crate) fn allow_unstable_room_versions(&self) -> bool { - self.config.allow_unstable_room_versions - } - pub(crate) fn default_room_version(&self) -> RoomVersionId { self.config.default_room_version.clone() } @@ -416,12 +407,7 @@ impl Service { } pub(crate) fn supported_room_versions(&self) -> Vec { - let mut room_versions: Vec = vec![]; - room_versions.extend(self.stable_room_versions.clone()); - if self.allow_unstable_room_versions() { - room_versions.extend(self.unstable_room_versions.clone()); - }; - room_versions + self.stable_room_versions.clone() } /// This doesn't actually check that the keys provided are newer than the @@ -479,15 +465,10 @@ impl Service { &self, keys: SigningKeys, timestamp: MilliSecondsSinceUnixEpoch, - room_version_id: &RoomVersionId, + _room_version_id: &RoomVersionId, ) -> Option> { - let all_valid = keys.valid_until_ts > timestamp - // valid_until_ts MUST be ignored in room versions 1, 2, 3, and 4. - // https://spec.matrix.org/v1.10/server-server-api/#get_matrixkeyv2server - || matches!(room_version_id, RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V4 - | RoomVersionId::V3); + let all_valid = keys.valid_until_ts > timestamp; + all_valid.then(|| { // Given that either the room version allows stale keys, or the // valid_until_ts is in the future, all verify_keys are diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 3817fb90..1970ea3e 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -942,16 +942,7 @@ impl Service { })? || incoming_pdu.kind == TimelineEventType::RoomRedaction && match room_version_id { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { + room_version if *room_version < RoomVersionId::V11 => { if let Some(redact_id) = &incoming_pdu.redacts { !services().rooms.state_accessor.user_can_redact( redact_id, @@ -985,7 +976,9 @@ impl Service { } } _ => { - unreachable!("Validity of room version already checked") + return Err(Error::BadServerResponse( + "Unsupported room version.", + )) } }; diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index b325f198..f1b9364f 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -391,17 +391,8 @@ impl Service { TimelineEventType::RoomRedaction => { let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; - match room_version_id { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { + match &room_version_id { + room_version if *room_version < RoomVersionId::V11 => { if let Some(redact_id) = &pdu.redacts { if services().rooms.state_accessor.user_can_redact( redact_id, @@ -435,7 +426,9 @@ impl Service { } } _ => { - unreachable!("Validity of room version already checked") + return Err(Error::BadServerResponse( + "Unsupported room version.", + )); } }; } From b45c0afe3716bef7b56db4d2d185fb3b60dfe02e Mon Sep 17 00:00:00 2001 From: avdb13 Date: Sun, 11 Aug 2024 22:57:22 +0200 Subject: [PATCH 325/617] update changelog --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index f8e94d62..5b1c23f5 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -71,6 +71,8 @@ This will be the first release of Grapevine since it was forked from Conduit ([d41f0fb](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/d41f0fbf72dae6562358173f425d23bb0e174ca2)) 6. Remove Docker packaging. ([!48](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/48)) +7. **BREAKING:** Remove unstable room versions. + ([!59](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/59)) ### Changed From 992e6b945a9bb9fbed94c732a171cdbec0662354 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 12 Aug 2024 21:22:05 -0700 Subject: [PATCH 326/617] remove usage of IFD in nix packages Lix builders have IFD disabled, and the changes needed to support this are minimal. We do need to do slightly more manual work to keep rust-toolchain.toml in sync with flake.nix, but it's not significant. --- flake.lock | 15 ++++++++++++++- flake.nix | 37 ++++++++++++++++++++++++++---------- nix/pkgs/default/default.nix | 10 ++++------ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/flake.lock b/flake.lock index f719ccbd..3a58f74d 100644 --- a/flake.lock +++ b/flake.lock @@ -226,7 +226,8 @@ "flake-compat": "flake-compat_2", "flake-utils": "flake-utils_2", "nix-filter": "nix-filter", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_2", + "rust-manifest": "rust-manifest" } }, "rust-analyzer-src": { @@ -246,6 +247,18 @@ "type": "github" } }, + "rust-manifest": { + "flake": false, + "locked": { + "narHash": "sha256-aZFye4UrtlcvLHrISldx4g9uGt3thDbVlLMK5keBSj0=", + "type": "file", + "url": "https://static.rust-lang.org/dist/channel-rust-1.78.0.toml" + }, + "original": { + "type": "file", + "url": "https://static.rust-lang.org/dist/channel-rust-1.78.0.toml" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index 51c50679..3a31a02f 100644 --- a/flake.nix +++ b/flake.nix @@ -8,6 +8,12 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + + rust-manifest = { + # Keep version in sync with rust-toolchain.toml + url = "https://static.rust-lang.org/dist/channel-rust-1.78.0.toml"; + flake = false; + }; }; outputs = inputs: @@ -29,16 +35,27 @@ shell = self.callPackage ./nix/shell.nix {}; # The Rust toolchain to use - toolchain = inputs - .fenix - .packages - .${pkgs.pkgsBuildHost.system} - .fromToolchainFile { - file = ./rust-toolchain.toml; - - # See also `rust-toolchain.toml` - sha256 = "sha256-opUgs6ckUQCyDxcB9Wy51pqhd0MPGHUVbwRKKPGiwZU="; - }; + # Using fromManifestFile and parsing the toolchain file with importTOML + # instead of fromToolchainFile to avoid IFD + toolchain = let + toolchainFile = pkgs.lib.importTOML ./rust-toolchain.toml; + defaultProfileComponents = [ + "rustc" + "cargo" + "rust-docs" + "rustfmt" + "clippy" + ]; + components = defaultProfileComponents ++ + toolchainFile.toolchain.components; + targets = toolchainFile.toolchain.targets; + fenix = inputs.fenix.packages.${pkgs.pkgsBuildHost.system}; + in + fenix.combine (builtins.map + (target: + (fenix.targets.${target}.fromManifestFile inputs.rust-manifest) + .withComponents components) + targets); }); in inputs.flake-utils.lib.eachDefaultSystem (system: diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index b03c2c50..fe31cc74 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -71,12 +71,10 @@ let } // buildDepsOnlyEnv; commonAttrs = { - inherit - (craneLib.crateNameFromCargoToml { - cargoToml = "${inputs.self}/Cargo.toml"; - }) - pname - version; + # Reading from cargoManifest directly instead of using + # createNameFromCargoToml to avoid IFD + pname = cargoManifest.package.name; + version = cargoManifest.package.version; src = let filter = inputs.nix-filter.lib; in filter { root = inputs.self; From e9c0b3ef7660bf424b73774b652b56e06a4bb810 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 12 Aug 2024 21:26:24 -0700 Subject: [PATCH 327/617] disable IFD in CI We want to know if future changes break builds without IFD. --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 71632f4a..08bee17e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,9 @@ before_script: # Enable nix-command and flakes - if command -v nix > /dev/null; then echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf; fi + # Disable IFD, to ensure we are able to build without it + - if command -v nix > /dev/null; then echo "allow-import-from-derivation = false" >> /etc/nix/nix.conf; fi + # Add our own binary cache - if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ] && [ -n "$ATTIC_CACHE" ]; then echo "extra-substituters = $ATTIC_ENDPOINT/$ATTIC_CACHE" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi From 3b99032456700d06dd937db6a85976a8be9d4fa7 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 12 Aug 2024 21:35:16 -0700 Subject: [PATCH 328/617] add changelog entry for non-IFD nix build support --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 5b1c23f5..deacaca3 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -200,3 +200,5 @@ This will be the first release of Grapevine since it was forked from Conduit output. * `observability.logs.timestamp`: Whether timestamps should be included in the logs. +13. Support building nix packages without IFD + ([!73](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/73)) From 6685f8cd14dc7473706cb75445c4ba8783883bd5 Mon Sep 17 00:00:00 2001 From: K900 Date: Sat, 17 Aug 2024 11:16:15 -0700 Subject: [PATCH 329/617] remove uses of aliases --- flake.nix | 2 +- nix/modules/default/default.nix | 4 ++-- nix/shell.nix | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index 3a31a02f..3f65fb2d 100644 --- a/flake.nix +++ b/flake.nix @@ -49,7 +49,7 @@ components = defaultProfileComponents ++ toolchainFile.toolchain.components; targets = toolchainFile.toolchain.targets; - fenix = inputs.fenix.packages.${pkgs.pkgsBuildHost.system}; + fenix = inputs.fenix.packages.${pkgs.stdenv.buildPlatform.system}; in fenix.combine (builtins.map (target: diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 034e7024..d6d8ac65 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -18,11 +18,11 @@ in options.services.grapevine = { enable = lib.mkEnableOption "grapevine"; package = lib.mkPackageOption - inputs.self.packages.${pkgs.system} + inputs.self.packages.${pkgs.hostPlatform.system} "grapevine" { default = "default"; - pkgsText = "inputs.grapevine.packages.\${pkgs.system}"; + pkgsText = "inputs.grapevine.packages.\${pkgs.hostPlatform.system}"; }; settings = lib.mkOption { diff --git a/nix/shell.nix b/nix/shell.nix index 354edf7a..130d63e6 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,5 +1,6 @@ # Keep sorted -{ default +{ buildPlatform +, default , engage , inputs , jq @@ -7,7 +8,6 @@ , markdownlint-cli , mdbook , mkShell -, system , toolchain }: @@ -25,7 +25,7 @@ mkShell { # # This needs to come before `toolchain` in this list, otherwise # `$PATH` will have stable rustfmt instead. - inputs.fenix.packages.${system}.latest.rustfmt + inputs.fenix.packages.${buildPlatform.system}.latest.rustfmt # Keep sorted engage From c355e2ad394a3c925acf24c87854e0898d0e328d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 17 Aug 2024 11:45:51 -0700 Subject: [PATCH 330/617] ensure we don't use aliases again accidentally --- flake.nix | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 3f65fb2d..136e1601 100644 --- a/flake.nix +++ b/flake.nix @@ -60,7 +60,14 @@ in inputs.flake-utils.lib.eachDefaultSystem (system: let - pkgs = inputs.nixpkgs.legacyPackages.${system}; + pkgs = import inputs.nixpkgs { + inherit system; + + # Some users find it useful to set this on their Nixpkgs instance and + # we want to support that use case, so we set it here too to help us + # test/ensure that this works. + config.allowAliases = false; + }; in { packages = { @@ -79,6 +86,11 @@ crossSystem = { config = crossSystem; }; + + # Some users find it useful to set this on their Nixpkgs + # instance and we want to support that use case, so we set + # it here too to help us test/ensure that this works. + config.allowAliases = false; }).pkgsStatic; in [ From 14afa1357e199e542835fde621e99052e7d12f2b Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 24 Aug 2024 18:59:36 +0000 Subject: [PATCH 331/617] tracing: allow configuring service name This is essential when consuming tracing data from multiple servers. --- src/config.rs | 2 ++ src/observability.rs | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/config.rs b/src/config.rs index 90b047c8..a69e2394 100644 --- a/src/config.rs +++ b/src/config.rs @@ -178,6 +178,7 @@ pub(crate) struct OtelTraceConfig { pub(crate) enable: bool, pub(crate) filter: EnvFilterClone, pub(crate) endpoint: Option, + pub(crate) service_name: String, } impl Default for OtelTraceConfig { @@ -186,6 +187,7 @@ impl Default for OtelTraceConfig { enable: false, filter: default_tracing_filter(), endpoint: None, + service_name: env!("CARGO_PKG_NAME").to_owned(), } } } diff --git a/src/observability.rs b/src/observability.rs index 177001aa..8e3c662c 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -171,8 +171,11 @@ pub(crate) fn init( let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_trace_config( - opentelemetry_sdk::trace::config() - .with_resource(standard_resource()), + opentelemetry_sdk::trace::config().with_resource( + standard_resource( + config.observability.traces.service_name.clone(), + ), + ), ) .with_exporter(exporter) .install_batch(opentelemetry_sdk::runtime::Tokio)?; @@ -248,11 +251,9 @@ pub(crate) fn init( } /// Construct the standard [`Resource`] value to use for this service -fn standard_resource() -> Resource { - Resource::default().merge(&Resource::new([KeyValue::new( - "service.name", - env!("CARGO_PKG_NAME"), - )])) +fn standard_resource(service_name: String) -> Resource { + Resource::default() + .merge(&Resource::new([KeyValue::new("service.name", service_name)])) } /// Holds state relating to metrics @@ -306,7 +307,7 @@ impl Metrics { ) .expect("view should be valid"), ) - .with_resource(standard_resource()) + .with_resource(standard_resource(env!("CARGO_PKG_NAME").to_owned())) .build(); let meter = provider.meter(env!("CARGO_PKG_NAME")); From f03b6cde293088db7db6c7a6e148f93358cdf980 Mon Sep 17 00:00:00 2001 From: avdb13 Date: Sun, 4 Aug 2024 14:48:10 +0200 Subject: [PATCH 332/617] feat: report local users getting banned --- book/changelog.md | 2 ++ src/service/rooms/timeline.rs | 43 +++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index deacaca3..7a3e9c1f 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -202,3 +202,5 @@ This will be the first release of Grapevine since it was forked from Conduit the logs. 13. Support building nix packages without IFD ([!73](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/73)) +14. Report local users getting banned in the server logs and admin room. + ([!65](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/65)) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index f1b9364f..92202bb5 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -14,7 +14,8 @@ use ruma::{ push_rules::PushRulesEvent, room::{ create::RoomCreateEventContent, encrypted::Relation, - member::MembershipState, power_levels::RoomPowerLevelsEventContent, + member::MembershipState, message::RoomMessageEventContent, + power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent, }, GlobalAccountDataEventType, StateEventType, TimelineEventType, @@ -448,20 +449,21 @@ impl Service { #[derive(Deserialize)] struct ExtractMembership { membership: MembershipState, + reason: Option, } // if the state_key fails let target_user_id = UserId::parse(state_key.clone()) .expect("This state_key was previously validated"); - let content = serde_json::from_str::( - pdu.content.get(), - ) - .map_err(|_| { - Error::bad_database("Invalid content in pdu.") - })?; + let ExtractMembership { + membership, + reason, + } = serde_json::from_str(pdu.content.get()).map_err( + |_| Error::bad_database("Invalid content in pdu."), + )?; - let invite_state = match content.membership { + let invite_state = match membership { MembershipState::Invite => { let state = services() .rooms @@ -472,13 +474,36 @@ impl Service { _ => None, }; + if membership == MembershipState::Ban { + let (room, user) = (&pdu.room_id, &target_user_id); + + info!( + %user, + %room, + reason, + "User has been banned from room" + ); + + let reason = match reason.filter(|s| !s.is_empty()) { + Some(s) => format!(": {s}"), + None => String::new(), + }; + + services().admin.send_message( + RoomMessageEventContent::notice_plain(format!( + "User {user} has been banned from room \ + {room}{reason}", + )), + ); + } + // Update our membership info, we do this here incase a user // is invited and immediately leaves we // need the DB to record the invite event for auth services().rooms.state_cache.update_membership( &pdu.room_id, &target_user_id, - content.membership, + membership, &pdu.sender, invite_state, true, From 4ad50e2708aa2543f17cd2a3a42e7ec6f0c0d40f Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 30 Aug 2024 22:59:38 -0700 Subject: [PATCH 333/617] only log banned users if they are local This was the intent of f03b6cde293088db7db6c7a6e148f93358cdf980, but it was missing the check that the user is actually local, and so was logging *all* banned users. --- book/changelog.md | 3 ++- src/service/rooms/timeline.rs | 37 ++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 7a3e9c1f..2e1656f8 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -203,4 +203,5 @@ This will be the first release of Grapevine since it was forked from Conduit 13. Support building nix packages without IFD ([!73](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/73)) 14. Report local users getting banned in the server logs and admin room. - ([!65](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/65)) + ([!65](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/65), + [!84](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/84)) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 92202bb5..b3605bfb 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -477,24 +477,29 @@ impl Service { if membership == MembershipState::Ban { let (room, user) = (&pdu.room_id, &target_user_id); - info!( - %user, - %room, - reason, - "User has been banned from room" - ); + if user.server_name() + == services().globals.server_name() + { + info!( + %user, + %room, + reason, + "User has been banned from room" + ); - let reason = match reason.filter(|s| !s.is_empty()) { - Some(s) => format!(": {s}"), - None => String::new(), - }; + let reason = match reason.filter(|s| !s.is_empty()) + { + Some(s) => format!(": {s}"), + None => String::new(), + }; - services().admin.send_message( - RoomMessageEventContent::notice_plain(format!( - "User {user} has been banned from room \ - {room}{reason}", - )), - ); + services().admin.send_message( + RoomMessageEventContent::notice_plain(format!( + "User {user} has been banned from room \ + {room}{reason}", + )), + ); + } } // Update our membership info, we do this here incase a user From 006ea0eb945735f592fd8c3a1fc26ba65c47798d Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 24 Aug 2024 19:28:54 +0000 Subject: [PATCH 334/617] Fix appservice users getting invited over federation Invites are magic sauce, we need to manually send it off to the appservice if it's for an appservice user. --- book/changelog.md | 3 +++ src/api/server_server.rs | 26 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index 2e1656f8..6559aaba 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -158,6 +158,9 @@ This will be the first release of Grapevine since it was forked from Conduit 12. Fix bug where unexpected keys were deleted from `m.direct` account data events when joining an upgraded room. ([!53](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/53)) +13. Fixed appservice users not receiving federated invites if the local server + isn't already resident in the room + ([!80](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/80)) ### Added diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 890d9997..7a13a055 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -11,6 +11,7 @@ use std::{ use axum::{response::IntoResponse, Json}; use axum_extra::headers::{Authorization, HeaderMapExt}; +use base64::Engine as _; use get_profile_information::v1::ProfileField; use ruma::{ api::{ @@ -55,6 +56,7 @@ use ruma::{ }, serde::{Base64, JsonObject, Raw}, server_util::authorization::XMatrix, + state_res::Event, to_device::DeviceIdOrAllDevices, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, @@ -65,6 +67,7 @@ use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; use tracing::{debug, error, field, warn}; +use super::appservice_server; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, observability::{FoundIn, Lookup, METRICS}, @@ -1829,7 +1832,10 @@ pub(crate) async fn create_invite_route( invite_state.push(pdu.to_stripped_state_event()); // If we are active in the room, the remote server will notify us about the - // join via /send + // invite via m.room.member through /send. If we are not in the room, we + // need to manually record the invited state for clients' /sync through + // update_membership(), and send the invite pseudo-PDU to the affected + // appservices. if !services() .rooms .state_cache @@ -1843,6 +1849,24 @@ pub(crate) async fn create_invite_route( Some(invite_state), true, )?; + + for appservice in services().appservice.read().await.values() { + if appservice.is_user_match(&invited_user) { + appservice_server::send_request( + appservice.registration.clone(), + ruma::api::appservice::event::push_events::v1::Request { + events: vec![pdu.to_room_event()], + txn_id: + base64::engine::general_purpose::URL_SAFE_NO_PAD + .encode(utils::calculate_hash(&[pdu + .event_id() + .as_bytes()])) + .into(), + }, + ) + .await?; + } + } } Ok(Ra(create_invite::v2::Response { From 2db3b18ce8b2819102bba45c01a2da639a74e3ae Mon Sep 17 00:00:00 2001 From: Stephen D Date: Sun, 25 Aug 2024 14:55:52 -0300 Subject: [PATCH 335/617] Clean up existing code in get_server_keys_from_cache. It's a little DRYer now --- src/service/rooms/event_handler.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 1970ea3e..0e46d524 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1669,15 +1669,8 @@ impl Service { let contains_all_ids = |keys: &SigningKeys| { signature_ids.iter().all(|id| { - keys.verify_keys - .keys() - .map(ToString::to_string) - .any(|key_id| id == &key_id) - || keys - .old_verify_keys - .keys() - .map(ToString::to_string) - .any(|key_id| id == &key_id) + keys.verify_keys.contains_key(id) + || keys.old_verify_keys.contains_key(id) }) }; From 1b13d7f7ab2fd3aeef8f1a44554a31a3f43136ec Mon Sep 17 00:00:00 2001 From: Stephen D Date: Sun, 25 Aug 2024 15:12:29 -0300 Subject: [PATCH 336/617] Fix bug when retrieving keys for an event. It's possible for a server to have multiple associated public keys. This can happen when a Matrix server is set up on a particular domain, its key is lost, and the server continues running on the domain. Now there will be two keys associated to the domain. The old logic wouldn't fetch the new key if we already had the old key cached. The new logic will fetch any keys we don't have that we need, rather than just fetching one key per server. --- src/service/rooms/event_handler.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 0e46d524..afd2604d 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1682,8 +1682,13 @@ impl Service { ) })?; + // check that we have the server in our list already, or + // all `signature_ids` are in pub_key_map + // if yes, we don't have to do anything if servers.contains_key(origin) - || pub_key_map.contains_key(origin.as_str()) + || pub_key_map + .get(origin.as_str()) + .is_some_and(contains_all_ids) { continue; } From 06fa49ac27422fa97e68a46b1679698daed43a88 Mon Sep 17 00:00:00 2001 From: Stephen D Date: Sun, 25 Aug 2024 15:13:26 -0300 Subject: [PATCH 337/617] Update changelog to reflect key retrieval logic changes --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 6559aaba..f13a45a3 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -161,6 +161,8 @@ This will be the first release of Grapevine since it was forked from Conduit 13. Fixed appservice users not receiving federated invites if the local server isn't already resident in the room ([!80](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/80)) +14. Fix bug where, if a server has multiple public keys, only one would be fetched. + ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) ### Added From 926091223d40192e290af1f7f491369e82e2ca1b Mon Sep 17 00:00:00 2001 From: Stephen D Date: Sun, 25 Aug 2024 17:24:31 -0300 Subject: [PATCH 338/617] fetch keys that are expired --- src/service/rooms/event_handler.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index afd2604d..0f1ac9b7 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -36,6 +36,7 @@ use ruma::{ MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedServerSigningKeyId, RoomId, RoomVersionId, ServerName, }; +use serde::Deserialize; use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; @@ -48,6 +49,11 @@ use crate::{ Error, PduEvent, Result, }; +#[derive(Deserialize)] +struct ExtractOriginServerTs { + origin_server_ts: MilliSecondsSinceUnixEpoch, +} + pub(crate) struct Service; impl Service { @@ -1630,6 +1636,15 @@ impl Service { let event_id = <&EventId>::try_from(event_id.as_str()) .expect("ruma's reference hashes are valid event ids"); + let ExtractOriginServerTs { + origin_server_ts, + } = ExtractOriginServerTs::deserialize(pdu).map_err(|_| { + Error::BadServerResponse( + "Invalid PDU in server response, origin_server_ts field is \ + missing or invalid", + ) + })?; + if let Some((time, tries)) = services().globals.bad_event_ratelimiter.read().await.get(event_id) { @@ -1669,8 +1684,12 @@ impl Service { let contains_all_ids = |keys: &SigningKeys| { signature_ids.iter().all(|id| { - keys.verify_keys.contains_key(id) - || keys.old_verify_keys.contains_key(id) + keys.verify_keys.get(id).is_some_and(|_| { + keys.valid_until_ts >= origin_server_ts + }) || keys + .old_verify_keys + .get(id) + .is_some_and(|v| v.expired_ts >= origin_server_ts) }) }; From 556f2157a2a3dc3d4c8576fac46da613c92454d2 Mon Sep 17 00:00:00 2001 From: Stephen D Date: Sun, 25 Aug 2024 20:22:50 -0300 Subject: [PATCH 339/617] add expired keys fix to changelog --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index f13a45a3..da515bbe 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -163,6 +163,8 @@ This will be the first release of Grapevine since it was forked from Conduit ([!80](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/80)) 14. Fix bug where, if a server has multiple public keys, only one would be fetched. ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) +15. Fix bug where expired keys may not be re-fetched in some scenarios. + ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) ### Added From 5a5bea3217c692d2600560c3f48223000cfd5e40 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 21 Jul 2024 13:22:42 +0000 Subject: [PATCH 340/617] cargo update ruma httparse This adds authenticated media APIs. --- Cargo.lock | 56 ++++++++++++++++++++------------- src/api/client_server/media.rs | 57 ++++++++++++++++++++++------------ src/api/ruma_wrapper/axum.rs | 2 +- src/api/server_server.rs | 5 +-- src/service/media.rs | 5 +-- 5 files changed, 78 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29beb49a..8fda7703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,6 +1045,15 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-auth" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643c9bbf6a4ea8a656d6b4cd53d34f79e3f841ad5203c1a55fb7d761923bc255" +dependencies = [ + "memchr", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1081,9 +1090,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2111,7 +2120,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.10.1" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "assign", "js_int", @@ -2132,7 +2141,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "js_int", "ruma-common", @@ -2144,7 +2153,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "as_variant", "assign", @@ -2167,7 +2176,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "as_variant", "base64 0.22.1", @@ -2197,7 +2206,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "as_variant", "indexmap 2.2.6", @@ -2213,15 +2222,22 @@ dependencies = [ "thiserror", "tracing", "url", + "web-time", "wildmatch", ] [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ + "bytes", + "http 1.1.0", + "httparse", "js_int", + "memchr", + "mime", + "rand", "ruma-common", "ruma-events", "serde", @@ -2231,7 +2247,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "js_int", "thiserror", @@ -2240,7 +2256,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "js_int", "ruma-common", @@ -2250,7 +2266,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "once_cell", "proc-macro-crate", @@ -2265,7 +2281,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "js_int", "ruma-common", @@ -2277,18 +2293,20 @@ dependencies = [ [[package]] name = "ruma-server-util" version = "0.3.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "headers", + "http 1.1.0", + "http-auth", "ruma-common", + "thiserror", "tracing", - "yap", ] [[package]] name = "ruma-signatures" version = "0.15.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2304,7 +2322,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.11.0" -source = "git+https://github.com/ruma/ruma?branch=main#ba9a492fdee6ad89b179e2b3ab689c3114107012" +source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" dependencies = [ "itertools", "js_int", @@ -3720,12 +3738,6 @@ version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" -[[package]] -name = "yap" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe269e7b803a5e8e20cbd97860e136529cd83bf2c9c6d37b142467e7e1f051f" - [[package]] name = "zerocopy" version = "0.7.34" diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 95c31609..c6b13b1c 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -6,12 +6,15 @@ use http::{ HeaderName, HeaderValue, }; use phf::{phf_set, Set}; -use ruma::api::client::{ - error::ErrorKind, - media::{ - create_content, get_content, get_content_as_filename, - get_content_thumbnail, get_media_config, +use ruma::{ + api::client::{ + error::ErrorKind, + media::{ + create_content, get_content, get_content_as_filename, + get_content_thumbnail, get_media_config, + }, }, + http_headers::{ContentDisposition, ContentDispositionType}, }; use tracing::error; @@ -77,16 +80,17 @@ fn content_security_policy() -> HeaderValue { // Doing this correctly is tricky, so I'm skipping it for now. fn content_disposition_for( content_type: Option<&str>, - filename: Option<&str>, -) -> String { - match ( - content_type.is_some_and(|x| INLINE_CONTENT_TYPES.contains(x)), + filename: Option, +) -> ContentDisposition { + let disposition_type = match content_type { + Some(x) if INLINE_CONTENT_TYPES.contains(x) => { + ContentDispositionType::Inline + } + _ => ContentDispositionType::Attachment, + }; + ContentDisposition { + disposition_type, filename, - ) { - (true, None) => "inline".to_owned(), - (true, Some(x)) => format!("inline; filename={x}"), - (false, None) => "attachment".to_owned(), - (false, Some(x)) => format!("attachment; filename={x}"), } } @@ -114,6 +118,7 @@ fn set_header_or_panic( /// # `GET /_matrix/media/r0/config` /// /// Returns max upload size. +#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_media_config_route( _body: Ar, ) -> Result> { @@ -142,9 +147,12 @@ pub(crate) async fn create_content_route( .create( mxc.clone(), body.filename - .as_ref() - .map(|filename| format!("inline; filename={filename}")) - .as_deref(), + .clone() + .map(|filename| ContentDisposition { + disposition_type: ContentDispositionType::Inline, + filename: Some(filename), + }) + .as_ref(), body.content_type.as_deref(), &body.file, ) @@ -156,6 +164,7 @@ pub(crate) async fn create_content_route( })) } +#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_remote_content( mxc: &str, server_name: &ruma::ServerName, @@ -179,7 +188,7 @@ pub(crate) async fn get_remote_content( .media .create( mxc.to_owned(), - content_response.content_disposition.as_deref(), + content_response.content_disposition.as_ref(), content_response.content_type.as_deref(), &content_response.file, ) @@ -198,6 +207,7 @@ pub(crate) async fn get_remote_content( /// Load media from our server or over federation. /// /// - Only allows federation if `allow_remote` is true +#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_route( body: Ar, ) -> Result { @@ -214,6 +224,7 @@ pub(crate) async fn get_content_route( }) } +#[allow(deprecated)] // unauthenticated media async fn get_content_route_ruma( body: Ar, ) -> Result { @@ -259,6 +270,7 @@ async fn get_content_route_ruma( /// Load media from our server or over federation, permitting desired filename. /// /// - Only allows federation if `allow_remote` is true +#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_as_filename_route( body: Ar, ) -> Result { @@ -275,6 +287,7 @@ pub(crate) async fn get_content_as_filename_route( }) } +#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_as_filename_route_ruma( body: Ar, ) -> Result { @@ -290,7 +303,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( file, content_disposition: Some(content_disposition_for( content_type.as_deref(), - Some(body.filename.as_str()), + Some(body.filename.clone()), )), content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), @@ -305,7 +318,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( Ok(get_content_as_filename::v3::Response { content_disposition: Some(content_disposition_for( remote_content_response.content_type.as_deref(), - Some(body.filename.as_str()), + Some(body.filename.clone()), )), content_type: remote_content_response.content_type, file: remote_content_response.file, @@ -321,6 +334,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( /// Load media thumbnail from our server or over federation. /// /// - Only allows federation if `allow_remote` is true +#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_thumbnail_route( body: Ar, ) -> Result { @@ -342,6 +356,7 @@ pub(crate) async fn get_content_thumbnail_route( &mut r, CONTENT_DISPOSITION, content_disposition_for(content_type.as_deref(), None) + .to_string() .try_into() .expect("generated header value should be valid"), ); @@ -350,6 +365,7 @@ pub(crate) async fn get_content_thumbnail_route( }) } +#[allow(deprecated)] // unauthenticated media async fn get_content_thumbnail_route_ruma( body: Ar, ) -> Result { @@ -393,6 +409,7 @@ async fn get_content_thumbnail_route_ruma( media_id: body.media_id.clone(), timeout_ms: Duration::from_secs(20), allow_redirect: false, + animated: Some(false), }, ) .await?; diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 79fba3a5..6315e5e1 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -212,7 +212,7 @@ async fn ar_from_request_inner( let origin_signatures = BTreeMap::from_iter([( x_matrix.key.to_string(), - CanonicalJsonValue::String(x_matrix.sig), + CanonicalJsonValue::String(x_matrix.sig.to_string()), )]); let signatures = BTreeMap::from_iter([( diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 7a13a055..496a4870 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -244,11 +244,12 @@ where .unwrap(); let key_id = OwnedSigningKeyId::try_from(key_id.clone()).unwrap(); - let signature = signature.as_str().unwrap().to_owned(); + let signature = Base64::parse(signature.as_str().unwrap()) + .expect("generated signature should be valid base64"); http_request.headers_mut().typed_insert(Authorization(XMatrix::new( services().globals.server_name().to_owned(), - Some(destination.to_owned()), + destination.to_owned(), key_id, signature, ))); diff --git a/src/service/media.rs b/src/service/media.rs index 052c726c..1f014f8a 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -1,6 +1,7 @@ use std::io::Cursor; use image::imageops::FilterType; +use ruma::http_headers::ContentDisposition; use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt}, @@ -35,7 +36,7 @@ impl Service { pub(crate) async fn create( &self, mxc: String, - content_disposition: Option<&str>, + content_disposition: Option<&ContentDisposition>, content_type: Option<&str>, file: &[u8], ) -> Result<()> { @@ -44,7 +45,7 @@ impl Service { mxc, 0, 0, - content_disposition, + content_disposition.map(ContentDisposition::to_string).as_deref(), content_type, )?; From 64b3c357dd50bdde64033ce854d4c21999ca8107 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 21 Jul 2024 17:52:57 +0000 Subject: [PATCH 341/617] media: put old API behind legacy_media import --- src/api/client_server/media.rs | 49 ++++++++++++++++------------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index c6b13b1c..552fe4bb 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -9,10 +9,7 @@ use phf::{phf_set, Set}; use ruma::{ api::client::{ error::ErrorKind, - media::{ - create_content, get_content, get_content_as_filename, - get_content_thumbnail, get_media_config, - }, + media::{self as legacy_media, create_content}, }, http_headers::{ContentDisposition, ContentDispositionType}, }; @@ -120,9 +117,9 @@ fn set_header_or_panic( /// Returns max upload size. #[allow(deprecated)] // unauthenticated media pub(crate) async fn get_media_config_route( - _body: Ar, -) -> Result> { - Ok(Ra(get_media_config::v3::Response { + _body: Ar, +) -> Result> { + Ok(Ra(legacy_media::get_media_config::v3::Response { upload_size: services().globals.max_request_size().into(), })) } @@ -169,12 +166,12 @@ pub(crate) async fn get_remote_content( mxc: &str, server_name: &ruma::ServerName, media_id: String, -) -> Result { +) -> Result { let content_response = services() .sending .send_federation_request( server_name, - get_content::v3::Request { + legacy_media::get_content::v3::Request { allow_remote: false, server_name: server_name.to_owned(), media_id, @@ -194,7 +191,7 @@ pub(crate) async fn get_remote_content( ) .await?; - Ok(get_content::v3::Response { + Ok(legacy_media::get_content::v3::Response { file: content_response.file, content_disposition: content_response.content_disposition, content_type: content_response.content_type, @@ -209,7 +206,7 @@ pub(crate) async fn get_remote_content( /// - Only allows federation if `allow_remote` is true #[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_route( - body: Ar, + body: Ar, ) -> Result { get_content_route_ruma(body).await.map(|x| { let mut r = Ra(x).into_response(); @@ -226,8 +223,8 @@ pub(crate) async fn get_content_route( #[allow(deprecated)] // unauthenticated media async fn get_content_route_ruma( - body: Ar, -) -> Result { + body: Ar, +) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -236,7 +233,7 @@ async fn get_content_route_ruma( .. }) = services().media.get(mxc.clone()).await? { - Ok(get_content::v3::Response { + Ok(legacy_media::get_content::v3::Response { file, content_disposition: Some(content_disposition_for( content_type.as_deref(), @@ -251,7 +248,7 @@ async fn get_content_route_ruma( let remote_content_response = get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(get_content::v3::Response { + Ok(legacy_media::get_content::v3::Response { file: remote_content_response.file, content_disposition: Some(content_disposition_for( remote_content_response.content_type.as_deref(), @@ -272,7 +269,7 @@ async fn get_content_route_ruma( /// - Only allows federation if `allow_remote` is true #[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_as_filename_route( - body: Ar, + body: Ar, ) -> Result { get_content_as_filename_route_ruma(body).await.map(|x| { let mut r = Ra(x).into_response(); @@ -289,8 +286,8 @@ pub(crate) async fn get_content_as_filename_route( #[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_as_filename_route_ruma( - body: Ar, -) -> Result { + body: Ar, +) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -299,7 +296,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( .. }) = services().media.get(mxc.clone()).await? { - Ok(get_content_as_filename::v3::Response { + Ok(legacy_media::get_content_as_filename::v3::Response { file, content_disposition: Some(content_disposition_for( content_type.as_deref(), @@ -315,7 +312,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( get_remote_content(&mxc, &body.server_name, body.media_id.clone()) .await?; - Ok(get_content_as_filename::v3::Response { + Ok(legacy_media::get_content_as_filename::v3::Response { content_disposition: Some(content_disposition_for( remote_content_response.content_type.as_deref(), Some(body.filename.clone()), @@ -336,7 +333,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( /// - Only allows federation if `allow_remote` is true #[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_thumbnail_route( - body: Ar, + body: Ar, ) -> Result { get_content_thumbnail_route_ruma(body).await.map(|x| { let mut r = Ra(x).into_response(); @@ -367,8 +364,8 @@ pub(crate) async fn get_content_thumbnail_route( #[allow(deprecated)] // unauthenticated media async fn get_content_thumbnail_route_ruma( - body: Ar, -) -> Result { + body: Ar, +) -> Result { let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); if let Some(FileMeta { @@ -388,7 +385,7 @@ async fn get_content_thumbnail_route_ruma( ) .await? { - Ok(get_content_thumbnail::v3::Response { + Ok(legacy_media::get_content_thumbnail::v3::Response { file, content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), @@ -400,7 +397,7 @@ async fn get_content_thumbnail_route_ruma( .sending .send_federation_request( &body.server_name, - get_content_thumbnail::v3::Request { + legacy_media::get_content_thumbnail::v3::Request { allow_remote: false, height: body.height, width: body.width, @@ -426,7 +423,7 @@ async fn get_content_thumbnail_route_ruma( ) .await?; - Ok(get_content_thumbnail::v3::Response { + Ok(legacy_media::get_content_thumbnail::v3::Response { file: get_thumbnail_response.file, content_type: get_thumbnail_response.content_type, cross_origin_resource_policy: Some("cross-origin".to_owned()), From 94204415ee25785a78046c340d1616aa0e0cfafb Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 21 Jul 2024 17:56:49 +0000 Subject: [PATCH 342/617] Add MxcData helper --- src/api/client_server/media.rs | 50 ++++++++++++++---------------- src/utils.rs | 56 +++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 552fe4bb..79f44481 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -15,7 +15,12 @@ use ruma::{ }; use tracing::error; -use crate::{service::media::FileMeta, services, utils, Ar, Error, Ra, Result}; +use crate::{ + service::media::FileMeta, + services, + utils::{self, MxcData}, + Ar, Error, Ra, Result, +}; const MXC_LENGTH: usize = 32; @@ -133,16 +138,13 @@ pub(crate) async fn get_media_config_route( pub(crate) async fn create_content_route( body: Ar, ) -> Result> { - let mxc = format!( - "mxc://{}/{}", - services().globals.server_name(), - utils::random_string(MXC_LENGTH) - ); + let media_id = utils::random_string(MXC_LENGTH); + let mxc = MxcData::new(services().globals.server_name(), &media_id)?; services() .media .create( - mxc.clone(), + mxc.to_string(), body.filename .clone() .map(|filename| ContentDisposition { @@ -163,18 +165,16 @@ pub(crate) async fn create_content_route( #[allow(deprecated)] // unauthenticated media pub(crate) async fn get_remote_content( - mxc: &str, - server_name: &ruma::ServerName, - media_id: String, + mxc: &MxcData<'_>, ) -> Result { let content_response = services() .sending .send_federation_request( - server_name, + mxc.server_name, legacy_media::get_content::v3::Request { allow_remote: false, - server_name: server_name.to_owned(), - media_id, + server_name: mxc.server_name.to_owned(), + media_id: mxc.media_id.to_owned(), timeout_ms: Duration::from_secs(20), allow_redirect: false, }, @@ -184,7 +184,7 @@ pub(crate) async fn get_remote_content( services() .media .create( - mxc.to_owned(), + mxc.to_string(), content_response.content_disposition.as_ref(), content_response.content_type.as_deref(), &content_response.file, @@ -225,13 +225,13 @@ pub(crate) async fn get_content_route( async fn get_content_route_ruma( body: Ar, ) -> Result { - let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); + let mxc = MxcData::new(&body.server_name, &body.media_id)?; if let Some(FileMeta { content_type, file, .. - }) = services().media.get(mxc.clone()).await? + }) = services().media.get(mxc.to_string()).await? { Ok(legacy_media::get_content::v3::Response { file, @@ -245,9 +245,7 @@ async fn get_content_route_ruma( } else if &*body.server_name != services().globals.server_name() && body.allow_remote { - let remote_content_response = - get_remote_content(&mxc, &body.server_name, body.media_id.clone()) - .await?; + let remote_content_response = get_remote_content(&mxc).await?; Ok(legacy_media::get_content::v3::Response { file: remote_content_response.file, content_disposition: Some(content_disposition_for( @@ -288,13 +286,13 @@ pub(crate) async fn get_content_as_filename_route( pub(crate) async fn get_content_as_filename_route_ruma( body: Ar, ) -> Result { - let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); + let mxc = MxcData::new(&body.server_name, &body.media_id)?; if let Some(FileMeta { content_type, file, .. - }) = services().media.get(mxc.clone()).await? + }) = services().media.get(mxc.to_string()).await? { Ok(legacy_media::get_content_as_filename::v3::Response { file, @@ -308,9 +306,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( } else if &*body.server_name != services().globals.server_name() && body.allow_remote { - let remote_content_response = - get_remote_content(&mxc, &body.server_name, body.media_id.clone()) - .await?; + let remote_content_response = get_remote_content(&mxc).await?; Ok(legacy_media::get_content_as_filename::v3::Response { content_disposition: Some(content_disposition_for( @@ -366,7 +362,7 @@ pub(crate) async fn get_content_thumbnail_route( async fn get_content_thumbnail_route_ruma( body: Ar, ) -> Result { - let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); + let mxc = MxcData::new(&body.server_name, &body.media_id)?; if let Some(FileMeta { content_type, @@ -375,7 +371,7 @@ async fn get_content_thumbnail_route_ruma( }) = services() .media .get_thumbnail( - mxc.clone(), + mxc.to_string(), body.width.try_into().map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") })?, @@ -414,7 +410,7 @@ async fn get_content_thumbnail_route_ruma( services() .media .upload_thumbnail( - mxc, + mxc.to_string(), None, get_thumbnail_response.content_type.as_deref(), body.width.try_into().expect("all UInts are valid u32s"), diff --git a/src/utils.rs b/src/utils.rs index 52956bf6..c1e2d6a6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -13,9 +13,12 @@ use cmp::Ordering; use rand::{prelude::*, rngs::OsRng}; use ring::digest; use ruma::{ - canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, + api::client::error::ErrorKind, canonical_json::try_from_json_map, + CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri, }; +use crate::{Error, Result}; + // Hopefully we have a better chat protocol in 530 years #[allow(clippy::as_conversions, clippy::cast_possible_truncation)] pub(crate) fn millis_since_unix_epoch() -> u64 { @@ -243,6 +246,57 @@ pub(crate) fn dbg_truncate_str(s: &str, mut max_len: usize) -> Cow<'_, str> { } } +/// Data that makes up an `mxc://` URL. +#[derive(Debug, Clone)] +pub(crate) struct MxcData<'a> { + pub(crate) server_name: &'a ruma::ServerName, + pub(crate) media_id: &'a str, +} + +impl<'a> MxcData<'a> { + pub(crate) fn new( + server_name: &'a ruma::ServerName, + media_id: &'a str, + ) -> Result { + if !media_id.bytes().all(|b| { + matches!(b, + b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'-' | b'_' + ) + }) { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid MXC media id", + )); + } + + Ok(Self { + server_name, + media_id, + }) + } +} + +impl fmt::Display for MxcData<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "mxc://{}/{}", self.server_name, self.media_id) + } +} + +impl From> for OwnedMxcUri { + fn from(value: MxcData<'_>) -> Self { + value.to_string().into() + } +} + +impl<'a> TryFrom<&'a MxcUri> for MxcData<'a> { + type Error = MxcUriError; + + fn try_from(value: &'a MxcUri) -> Result { + Ok(Self::new(value.server_name()?, value.media_id()?) + .expect("validated MxcUri should always be valid MxcData")) + } +} + #[cfg(test)] mod tests { use crate::utils::dbg_truncate_str; From edfaa83405e25edd1b97888885025578def1ea0c Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 4 Aug 2024 20:33:09 +0000 Subject: [PATCH 343/617] server_server: make outbound requests using spec v1.11 This is required for authenticated media requests, which are otherwise performed using the unstable endpoint. --- src/api/server_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 496a4870..7844bd47 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -181,7 +181,7 @@ where .try_into_http_request::>( &actual_destination_str, SendAccessToken::IfRequired(""), - &[MatrixVersion::V1_4], + &[MatrixVersion::V1_11], ) .map_err(|error| { warn!( From 7f6ab637527e4143ac38e1c0508fa14483ab3fa7 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 25 Aug 2024 17:25:51 +0000 Subject: [PATCH 344/617] client_server: factor out width/height conversion --- src/api/client_server/media.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 79f44481..53cac95e 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -363,23 +363,19 @@ async fn get_content_thumbnail_route_ruma( body: Ar, ) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; + let width = body.width.try_into().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") + })?; + let height = body.height.try_into().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid.") + })?; if let Some(FileMeta { content_type, file, .. - }) = services() - .media - .get_thumbnail( - mxc.to_string(), - body.width.try_into().map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") - })?, - body.height.try_into().map_err(|_| { - Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") - })?, - ) - .await? + }) = + services().media.get_thumbnail(mxc.to_string(), width, height).await? { Ok(legacy_media::get_content_thumbnail::v3::Response { file, @@ -413,8 +409,8 @@ async fn get_content_thumbnail_route_ruma( mxc.to_string(), None, get_thumbnail_response.content_type.as_deref(), - body.width.try_into().expect("all UInts are valid u32s"), - body.height.try_into().expect("all UInts are valid u32s"), + width, + height, &get_thumbnail_response.file, ) .await?; From 79053ad0522c413f67fc3d46ef46520b6401dc19 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 21 Jul 2024 19:24:29 +0000 Subject: [PATCH 345/617] client_server: use and provide authenticated media API --- src/api/client_server/media.rs | 620 +++++++++++++++++++++++---- src/api/client_server/unversioned.rs | 8 +- src/api/ruma_wrapper.rs | 25 ++ src/main.rs | 16 +- 4 files changed, 568 insertions(+), 101 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 53cac95e..a4c63444 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -3,17 +3,21 @@ use std::time::Duration; use axum::response::IntoResponse; use http::{ header::{CONTENT_DISPOSITION, CONTENT_SECURITY_POLICY, CONTENT_TYPE}, - HeaderName, HeaderValue, + HeaderName, HeaderValue, Method, }; use phf::{phf_set, Set}; use ruma::{ - api::client::{ - error::ErrorKind, - media::{self as legacy_media, create_content}, + api::{ + client::{ + authenticated_media as authenticated_media_client, + error::ErrorKind, + media::{self as legacy_media, create_content}, + }, + federation::authenticated_media as authenticated_media_fed, }, http_headers::{ContentDisposition, ContentDispositionType}, }; -use tracing::error; +use tracing::{debug, error, info, warn}; use crate::{ service::media::FileMeta, @@ -121,7 +125,7 @@ fn set_header_or_panic( /// /// Returns max upload size. #[allow(deprecated)] // unauthenticated media -pub(crate) async fn get_media_config_route( +pub(crate) async fn get_media_config_legacy_route( _body: Ar, ) -> Result> { Ok(Ra(legacy_media::get_media_config::v3::Response { @@ -129,6 +133,17 @@ pub(crate) async fn get_media_config_route( })) } +/// # `GET /_matrix/client/v1/media/config` +/// +/// Returns max upload size. +pub(crate) async fn get_media_config_route( + _body: Ar, +) -> Result> { + Ok(Ra(authenticated_media_client::get_media_config::v1::Response { + upload_size: services().globals.max_request_size().into(), + })) +} + /// # `POST /_matrix/media/r0/upload` /// /// Permanently save media in the server. @@ -163,10 +178,106 @@ pub(crate) async fn create_content_route( })) } -#[allow(deprecated)] // unauthenticated media -pub(crate) async fn get_remote_content( +struct RemoteResponse { + #[allow(unused)] + metadata: authenticated_media_fed::ContentMetadata, + content: authenticated_media_fed::Content, +} + +/// Fetches remote media content from a URL specified in a +/// `/_matrix/federation/v1/media/*/{mediaId}` `Location` header +#[tracing::instrument] +async fn get_redirected_content( + location: String, +) -> Result { + let location = location.parse().map_err(|error| { + warn!(location, %error, "Invalid redirect location"); + Error::BadServerResponse("Invalid redirect location") + })?; + let response = services() + .globals + .federation_client() + .execute(reqwest::Request::new(Method::GET, location)) + .await?; + + let content_type = response + .headers() + .get(CONTENT_TYPE) + .map(|value| { + value.to_str().map_err(|error| { + error!( + ?value, + %error, + "Invalid Content-Type header" + ); + Error::BadServerResponse("Invalid Content-Type header") + }) + }) + .transpose()? + .map(str::to_owned); + + let content_disposition = response + .headers() + .get(CONTENT_DISPOSITION) + .map(|value| { + ContentDisposition::try_from(value.as_bytes()).map_err(|error| { + error!( + ?value, + %error, + "Invalid Content-Disposition header" + ); + Error::BadServerResponse("Invalid Content-Disposition header") + }) + }) + .transpose()?; + + Ok(authenticated_media_fed::Content { + file: response.bytes().await?.to_vec(), + content_type, + content_disposition, + }) +} + +#[tracing::instrument(skip_all)] +async fn get_remote_content_via_federation_api( mxc: &MxcData<'_>, -) -> Result { +) -> Result { + let authenticated_media_fed::get_content::v1::Response { + metadata, + content, + } = services() + .sending + .send_federation_request( + mxc.server_name, + authenticated_media_fed::get_content::v1::Request { + media_id: mxc.media_id.to_owned(), + timeout_ms: Duration::from_secs(20), + }, + ) + .await?; + + let content = match content { + authenticated_media_fed::FileOrLocation::File(content) => { + debug!("Got media from remote server"); + content + } + authenticated_media_fed::FileOrLocation::Location(location) => { + debug!(location, "Following redirect"); + get_redirected_content(location).await? + } + }; + + Ok(RemoteResponse { + metadata, + content, + }) +} + +#[allow(deprecated)] // unauthenticated media +#[tracing::instrument(skip_all)] +async fn get_remote_content_via_legacy_api( + mxc: &MxcData<'_>, +) -> Result { let content_response = services() .sending .send_federation_request( @@ -181,22 +292,53 @@ pub(crate) async fn get_remote_content( ) .await?; + Ok(RemoteResponse { + metadata: authenticated_media_fed::ContentMetadata {}, + content: authenticated_media_fed::Content { + file: content_response.file, + content_disposition: content_response.content_disposition, + content_type: content_response.content_type, + }, + }) +} + +#[tracing::instrument] +pub(crate) async fn get_remote_content( + mxc: &MxcData<'_>, +) -> Result { + let fed_result = get_remote_content_via_federation_api(mxc).await; + + let response = match fed_result { + Ok(response) => { + debug!("Got remote content via authenticated media API"); + response + } + Err(Error::Federation(_, error)) + if error.error_kind() == Some(&ErrorKind::Unrecognized) => + { + info!( + "Remote server does not support authenticated media, falling \ + back to deprecated API" + ); + + get_remote_content_via_legacy_api(mxc).await? + } + Err(e) => { + return Err(e); + } + }; + services() .media .create( mxc.to_string(), - content_response.content_disposition.as_ref(), - content_response.content_type.as_deref(), - &content_response.file, + response.content.content_disposition.as_ref(), + response.content.content_type.as_deref(), + &response.content.file, ) .await?; - Ok(legacy_media::get_content::v3::Response { - file: content_response.file, - content_disposition: content_response.content_disposition, - content_type: content_response.content_type, - cross_origin_resource_policy: Some("cross-origin".to_owned()), - }) + Ok(response) } /// # `GET /_matrix/media/r0/download/{serverName}/{mediaId}` @@ -205,10 +347,71 @@ pub(crate) async fn get_remote_content( /// /// - Only allows federation if `allow_remote` is true #[allow(deprecated)] // unauthenticated media -pub(crate) async fn get_content_route( +pub(crate) async fn get_content_legacy_route( body: Ar, ) -> Result { - get_content_route_ruma(body).await.map(|x| { + use authenticated_media_client::get_content::v1::{ + Request as AmRequest, Response as AmResponse, + }; + use legacy_media::get_content::v3::{ + Request as LegacyRequest, Response as LegacyResponse, + }; + + fn convert_request( + LegacyRequest { + server_name, + media_id, + timeout_ms, + .. + }: LegacyRequest, + ) -> AmRequest { + AmRequest { + server_name, + media_id, + timeout_ms, + } + } + + fn convert_response( + AmResponse { + file, + content_type, + content_disposition, + }: AmResponse, + ) -> LegacyResponse { + LegacyResponse { + file, + content_type, + content_disposition, + cross_origin_resource_policy: Some("cross-origin".to_owned()), + } + } + + let allow_remote = body.allow_remote; + + get_content_route_ruma(body.map_body(convert_request), allow_remote) + .await + .map(|response| { + let response = convert_response(response); + let mut r = Ra(response).into_response(); + + set_header_or_panic( + &mut r, + CONTENT_SECURITY_POLICY, + content_security_policy(), + ); + + r + }) +} + +/// # `GET /_matrix/client/v1/media/download/{serverName}/{mediaId}` +/// +/// Load media from our server or over federation. +pub(crate) async fn get_content_route( + body: Ar, +) -> Result { + get_content_route_ruma(body, true).await.map(|x| { let mut r = Ra(x).into_response(); set_header_or_panic( @@ -221,10 +424,10 @@ pub(crate) async fn get_content_route( }) } -#[allow(deprecated)] // unauthenticated media async fn get_content_route_ruma( - body: Ar, -) -> Result { + body: Ar, + allow_remote: bool, +) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; if let Some(FileMeta { @@ -233,27 +436,25 @@ async fn get_content_route_ruma( .. }) = services().media.get(mxc.to_string()).await? { - Ok(legacy_media::get_content::v3::Response { + Ok(authenticated_media_client::get_content::v1::Response { file, content_disposition: Some(content_disposition_for( content_type.as_deref(), None, )), content_type, - cross_origin_resource_policy: Some("cross-origin".to_owned()), }) } else if &*body.server_name != services().globals.server_name() - && body.allow_remote + && allow_remote { - let remote_content_response = get_remote_content(&mxc).await?; - Ok(legacy_media::get_content::v3::Response { - file: remote_content_response.file, + let remote_response = get_remote_content(&mxc).await?; + Ok(authenticated_media_client::get_content::v1::Response { + file: remote_response.content.file, content_disposition: Some(content_disposition_for( - remote_content_response.content_type.as_deref(), + remote_response.content.content_type.as_deref(), None, )), - content_type: remote_content_response.content_type, - cross_origin_resource_policy: Some("cross-origin".to_owned()), + content_type: remote_response.content.content_type, }) } else { Err(Error::BadRequest(ErrorKind::NotYetUploaded, "Media not found.")) @@ -266,10 +467,75 @@ async fn get_content_route_ruma( /// /// - Only allows federation if `allow_remote` is true #[allow(deprecated)] // unauthenticated media -pub(crate) async fn get_content_as_filename_route( +pub(crate) async fn get_content_as_filename_legacy_route( body: Ar, ) -> Result { - get_content_as_filename_route_ruma(body).await.map(|x| { + use authenticated_media_client::get_content_as_filename::v1::{ + Request as AmRequest, Response as AmResponse, + }; + use legacy_media::get_content_as_filename::v3::{ + Request as LegacyRequest, Response as LegacyResponse, + }; + + fn convert_request( + LegacyRequest { + server_name, + media_id, + filename, + timeout_ms, + .. + }: LegacyRequest, + ) -> AmRequest { + AmRequest { + server_name, + media_id, + filename, + timeout_ms, + } + } + + fn convert_response( + AmResponse { + file, + content_type, + content_disposition, + }: AmResponse, + ) -> LegacyResponse { + LegacyResponse { + file, + content_type, + content_disposition, + cross_origin_resource_policy: Some("cross-origin".to_owned()), + } + } + + let allow_remote = body.allow_remote; + get_content_as_filename_route_ruma( + body.map_body(convert_request), + allow_remote, + ) + .await + .map(|response| { + let response = convert_response(response); + let mut r = Ra(response).into_response(); + + set_header_or_panic( + &mut r, + CONTENT_SECURITY_POLICY, + content_security_policy(), + ); + + r + }) +} + +/// # `GET /_matrix/client/v1/media/download/{serverName}/{mediaId}/{fileName}` +/// +/// Load media from our server or over federation, permitting desired filename. +pub(crate) async fn get_content_as_filename_route( + body: Ar, +) -> Result { + get_content_as_filename_route_ruma(body, true).await.map(|x| { let mut r = Ra(x).into_response(); set_header_or_panic( @@ -282,10 +548,10 @@ pub(crate) async fn get_content_as_filename_route( }) } -#[allow(deprecated)] // unauthenticated media pub(crate) async fn get_content_as_filename_route_ruma( - body: Ar, -) -> Result { + body: Ar, + allow_remote: bool, +) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; if let Some(FileMeta { @@ -294,74 +560,242 @@ pub(crate) async fn get_content_as_filename_route_ruma( .. }) = services().media.get(mxc.to_string()).await? { - Ok(legacy_media::get_content_as_filename::v3::Response { + Ok(authenticated_media_client::get_content_as_filename::v1::Response { file, content_disposition: Some(content_disposition_for( content_type.as_deref(), Some(body.filename.clone()), )), content_type, - cross_origin_resource_policy: Some("cross-origin".to_owned()), }) } else if &*body.server_name != services().globals.server_name() - && body.allow_remote + && allow_remote { - let remote_content_response = get_remote_content(&mxc).await?; + let remote_response = get_remote_content(&mxc).await?; - Ok(legacy_media::get_content_as_filename::v3::Response { + Ok(authenticated_media_client::get_content_as_filename::v1::Response { content_disposition: Some(content_disposition_for( - remote_content_response.content_type.as_deref(), + remote_response.content.content_type.as_deref(), Some(body.filename.clone()), )), - content_type: remote_content_response.content_type, - file: remote_content_response.file, - cross_origin_resource_policy: Some("cross-origin".to_owned()), + content_type: remote_response.content.content_type, + file: remote_response.content.file, }) } else { Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) } } +fn fix_thumbnail_headers(r: &mut axum::response::Response) { + let content_type = r + .headers() + .get(CONTENT_TYPE) + .and_then(|x| std::str::from_utf8(x.as_ref()).ok()) + .map(ToOwned::to_owned); + + set_header_or_panic(r, CONTENT_SECURITY_POLICY, content_security_policy()); + set_header_or_panic( + r, + CONTENT_DISPOSITION, + content_disposition_for(content_type.as_deref(), None) + .to_string() + .try_into() + .expect("generated header value should be valid"), + ); +} + /// # `GET /_matrix/media/r0/thumbnail/{serverName}/{mediaId}` /// /// Load media thumbnail from our server or over federation. /// /// - Only allows federation if `allow_remote` is true #[allow(deprecated)] // unauthenticated media -pub(crate) async fn get_content_thumbnail_route( +pub(crate) async fn get_content_thumbnail_legacy_route( body: Ar, ) -> Result { - get_content_thumbnail_route_ruma(body).await.map(|x| { - let mut r = Ra(x).into_response(); + use authenticated_media_client::get_content_thumbnail::v1::{ + Request as AmRequest, Response as AmResponse, + }; + use legacy_media::get_content_thumbnail::v3::{ + Request as LegacyRequest, Response as LegacyResponse, + }; - let content_type = r - .headers() - .get(CONTENT_TYPE) - .and_then(|x| std::str::from_utf8(x.as_ref()).ok()) - .map(ToOwned::to_owned); + fn convert_request( + LegacyRequest { + server_name, + media_id, + method, + width, + height, + timeout_ms, + animated, + .. + }: LegacyRequest, + ) -> AmRequest { + AmRequest { + server_name, + media_id, + method, + width, + height, + timeout_ms, + animated, + } + } - set_header_or_panic( - &mut r, - CONTENT_SECURITY_POLICY, - content_security_policy(), - ); - set_header_or_panic( - &mut r, - CONTENT_DISPOSITION, - content_disposition_for(content_type.as_deref(), None) - .to_string() - .try_into() - .expect("generated header value should be valid"), - ); + fn convert_response( + AmResponse { + file, + content_type, + }: AmResponse, + ) -> LegacyResponse { + LegacyResponse { + file, + content_type, + cross_origin_resource_policy: Some("cross-origin".to_owned()), + } + } + + let allow_remote = body.allow_remote; + + get_content_thumbnail_route_ruma( + body.map_body(convert_request), + allow_remote, + ) + .await + .map(|response| { + let response = convert_response(response); + let mut r = Ra(response).into_response(); + + fix_thumbnail_headers(&mut r); r }) } +/// # `GET /_matrix/client/v1/media/thumbnail/{serverName}/{mediaId}` +/// +/// Load media thumbnail from our server or over federation. +pub(crate) async fn get_content_thumbnail_route( + body: Ar, +) -> Result { + get_content_thumbnail_route_ruma(body, true).await.map(|x| { + let mut r = Ra(x).into_response(); + + fix_thumbnail_headers(&mut r); + + r + }) +} + +#[tracing::instrument(skip_all)] +async fn get_remote_thumbnail_via_federation_api( + server_name: &ruma::ServerName, + request: authenticated_media_fed::get_content_thumbnail::v1::Request, +) -> Result { + let authenticated_media_fed::get_content_thumbnail::v1::Response { + metadata, + content, + } = services() + .sending + .send_federation_request(server_name, request) + .await?; + + let content = match content { + authenticated_media_fed::FileOrLocation::File(content) => { + debug!("Got thumbnail from remote server"); + content + } + authenticated_media_fed::FileOrLocation::Location(location) => { + debug!(location, "Following redirect"); + get_redirected_content(location).await? + } + }; + + Ok(RemoteResponse { + metadata, + content, + }) +} + #[allow(deprecated)] // unauthenticated media +#[tracing::instrument(skip_all)] +async fn get_remote_thumbnail_via_legacy_api( + server_name: &ruma::ServerName, + authenticated_media_fed::get_content_thumbnail::v1::Request { + media_id, + method, + width, + height, + timeout_ms, + animated, + }: authenticated_media_fed::get_content_thumbnail::v1::Request, +) -> Result { + let content_response = services() + .sending + .send_federation_request( + server_name, + legacy_media::get_content_thumbnail::v3::Request { + server_name: server_name.to_owned(), + allow_remote: false, + allow_redirect: false, + media_id, + method, + width, + height, + timeout_ms, + animated, + }, + ) + .await?; + + Ok(RemoteResponse { + metadata: authenticated_media_fed::ContentMetadata {}, + content: authenticated_media_fed::Content { + file: content_response.file, + content_disposition: None, + content_type: content_response.content_type, + }, + }) +} + +#[tracing::instrument] +pub(crate) async fn get_remote_thumbnail( + server_name: &ruma::ServerName, + request: authenticated_media_fed::get_content_thumbnail::v1::Request, +) -> Result { + let fed_result = + get_remote_thumbnail_via_federation_api(server_name, request.clone()) + .await; + + let response = match fed_result { + Ok(response) => { + debug!("Got remote content via authenticated media API"); + response + } + Err(Error::Federation(_, error)) + if error.error_kind() == Some(&ErrorKind::Unrecognized) => + { + info!( + "Remote server does not support authenticated media, falling \ + back to deprecated API" + ); + + get_remote_thumbnail_via_legacy_api(server_name, request.clone()) + .await? + } + Err(e) => { + return Err(e); + } + }; + + Ok(response) +} + async fn get_content_thumbnail_route_ruma( - body: Ar, -) -> Result { + body: Ar, + allow_remote: bool, +) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; let width = body.width.try_into().map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") @@ -377,48 +811,44 @@ async fn get_content_thumbnail_route_ruma( }) = services().media.get_thumbnail(mxc.to_string(), width, height).await? { - Ok(legacy_media::get_content_thumbnail::v3::Response { + Ok(authenticated_media_client::get_content_thumbnail::v1::Response { file, content_type, - cross_origin_resource_policy: Some("cross-origin".to_owned()), }) } else if &*body.server_name != services().globals.server_name() - && body.allow_remote + && allow_remote { - let get_thumbnail_response = services() - .sending - .send_federation_request( - &body.server_name, - legacy_media::get_content_thumbnail::v3::Request { - allow_remote: false, - height: body.height, - width: body.width, - method: body.method.clone(), - server_name: body.server_name.clone(), - media_id: body.media_id.clone(), - timeout_ms: Duration::from_secs(20), - allow_redirect: false, - animated: Some(false), - }, - ) - .await?; + let get_thumbnail_response = get_remote_thumbnail( + &body.server_name, + authenticated_media_fed::get_content_thumbnail::v1::Request { + height: body.height, + width: body.width, + method: body.method.clone(), + media_id: body.media_id.clone(), + timeout_ms: Duration::from_secs(20), + // we don't support animated thumbnails, so don't try requesting + // one - we're allowed to ignore the client's request for an + // animated thumbnail + animated: Some(false), + }, + ) + .await?; services() .media .upload_thumbnail( mxc.to_string(), None, - get_thumbnail_response.content_type.as_deref(), + get_thumbnail_response.content.content_type.as_deref(), width, height, - &get_thumbnail_response.file, + &get_thumbnail_response.content.file, ) .await?; - Ok(legacy_media::get_content_thumbnail::v3::Response { - file: get_thumbnail_response.file, - content_type: get_thumbnail_response.content_type, - cross_origin_resource_policy: Some("cross-origin".to_owned()), + Ok(authenticated_media_client::get_content_thumbnail::v1::Response { + file: get_thumbnail_response.content.file, + content_type: get_thumbnail_response.content.content_type, }) } else { Err(Error::BadRequest(ErrorKind::NotYetUploaded, "Media not found.")) diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 8ea8136c..92ea88ed 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -29,10 +29,10 @@ pub(crate) async fn get_supported_versions_route( "v1.4".to_owned(), "v1.5".to_owned(), ], - unstable_features: BTreeMap::from_iter([( - "org.matrix.e2e_cross_signing".to_owned(), - true, - )]), + unstable_features: BTreeMap::from_iter([ + ("org.matrix.e2e_cross_signing".to_owned(), true), + ("org.matrix.msc3916.stable".to_owned(), true), + ]), }; Ok(Ra(resp)) diff --git a/src/api/ruma_wrapper.rs b/src/api/ruma_wrapper.rs index f64e29f8..91ea8d35 100644 --- a/src/api/ruma_wrapper.rs +++ b/src/api/ruma_wrapper.rs @@ -24,6 +24,31 @@ pub(crate) struct Ar { pub(crate) appservice_info: Option, } +impl Ar { + pub(crate) fn map_body(self, f: F) -> Ar + where + F: FnOnce(T) -> U, + { + let Ar { + body, + sender_user, + sender_device, + sender_servername, + json_body, + appservice_info, + } = self; + + Ar { + body: f(body), + sender_user, + sender_device, + sender_servername, + json_body, + appservice_info, + } + } +} + impl Deref for Ar { type Target = T; diff --git a/src/main.rs b/src/main.rs index 69ba7023..cfd29cef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -390,12 +390,24 @@ fn routes(config: &Config) -> Router { .ruma_route(c2s::get_message_events_route) .ruma_route(c2s::search_events_route) .ruma_route(c2s::turn_server_route) - .ruma_route(c2s::send_event_to_device_route) + .ruma_route(c2s::send_event_to_device_route); + + // unauthenticated (legacy) media + let router = router + .ruma_route(c2s::get_media_config_legacy_route) + .ruma_route(c2s::get_content_legacy_route) + .ruma_route(c2s::get_content_as_filename_legacy_route) + .ruma_route(c2s::get_content_thumbnail_legacy_route); + + // authenticated media + let router = router .ruma_route(c2s::get_media_config_route) .ruma_route(c2s::create_content_route) .ruma_route(c2s::get_content_route) .ruma_route(c2s::get_content_as_filename_route) - .ruma_route(c2s::get_content_thumbnail_route) + .ruma_route(c2s::get_content_thumbnail_route); + + let router = router .ruma_route(c2s::get_devices_route) .ruma_route(c2s::get_device_route) .ruma_route(c2s::update_device_route) From d3b67188128ffec44c6269372b7e159ca65c7922 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 21 Jul 2024 19:58:05 +0000 Subject: [PATCH 346/617] server_server: implement authenticated media endpoints --- src/api/server_server.rs | 81 +++++++++++++++++++++++++++++++++++++++- src/main.rs | 2 + 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 7844bd47..64faa9b3 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -17,6 +17,7 @@ use ruma::{ api::{ client::error::{Error as RumaError, ErrorKind}, federation::{ + authenticated_media, authorization::get_event_authorization, backfill::get_backfill, device::get_devices::{self, v1::UserDevice}, @@ -72,8 +73,8 @@ use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, observability::{FoundIn, Lookup, METRICS}, service::pdu::{gen_event_id_canonical_json, PduBuilder}, - services, utils, - utils::dbg_truncate_str, + services, + utils::{self, dbg_truncate_str, MxcData}, Ar, Error, PduEvent, Ra, Result, }; @@ -2038,6 +2039,82 @@ pub(crate) async fn claim_keys_route( })) } +/// # `GET /_matrix/federation/v1/media/download/{mediaId}` +/// +/// Downloads media owned by a remote homeserver. +pub(crate) async fn media_download_route( + body: Ar, +) -> Result> { + let mxc = MxcData::new(services().globals.server_name(), &body.media_id)?; + let Some(crate::service::media::FileMeta { + content_disposition, + content_type, + file, + }) = services().media.get(mxc.to_string()).await? + else { + return Err(Error::BadRequest( + ErrorKind::NotYetUploaded, + "Media not found", + )); + }; + + let content_disposition = content_disposition.and_then(|s| { + s.parse().inspect_err( + |error| warn!(%error, "Invalid Content-Disposition in database"), + ) + .ok() + }); + + Ok(Ra(authenticated_media::get_content::v1::Response { + metadata: authenticated_media::ContentMetadata {}, + content: authenticated_media::FileOrLocation::File( + authenticated_media::Content { + file, + content_type, + content_disposition, + }, + ), + })) +} + +/// # `GET /_matrix/federation/v1/media/thumbnail/{mediaId}` +/// +/// Downloads a thumbnail from a remote homeserver. +pub(crate) async fn media_thumbnail_route( + body: Ar, +) -> Result> { + let mxc = MxcData::new(services().globals.server_name(), &body.media_id)?; + let width = body.width.try_into().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid.") + })?; + let height = body.height.try_into().map_err(|_| { + Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid.") + })?; + + let Some(crate::service::media::FileMeta { + content_type, + file, + .. + }) = services().media.get_thumbnail(mxc.to_string(), width, height).await? + else { + return Err(Error::BadRequest( + ErrorKind::NotYetUploaded, + "Media not found", + )); + }; + + Ok(Ra(authenticated_media::get_content_thumbnail::v1::Response { + metadata: authenticated_media::ContentMetadata {}, + content: authenticated_media::FileOrLocation::File( + authenticated_media::Content { + file, + content_type, + content_disposition: None, + }, + ), + })) +} + #[cfg(test)] mod tests { use super::{add_port_to_hostname, get_ip_with_port, FedDest}; diff --git a/src/main.rs b/src/main.rs index cfd29cef..da1010c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -501,6 +501,8 @@ fn routes(config: &Config) -> Router { .ruma_route(s2s::get_profile_information_route) .ruma_route(s2s::get_keys_route) .ruma_route(s2s::claim_keys_route) + .ruma_route(s2s::media_download_route) + .ruma_route(s2s::media_thumbnail_route) } else { router .route("/_matrix/federation/*path", any(federation_disabled)) From 82aacdc1532e7f5e4395e1148d8dfde176e0dd24 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 25 Aug 2024 18:13:19 +0000 Subject: [PATCH 347/617] Update changelog for Authenticated Media --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index da515bbe..0aba0e20 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -212,3 +212,5 @@ This will be the first release of Grapevine since it was forked from Conduit 14. Report local users getting banned in the server logs and admin room. ([!65](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/65), [!84](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/84)) +15. Added support for Authenticated Media ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)). + ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) From a06c8db9965cccd3148e408dfeb20d4efbdf6135 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 4 Aug 2024 20:35:25 +0000 Subject: [PATCH 348/617] Fetch and thumbnail original media if fetching thumbnail fails E.g. because the remote server's thumbnail endpoint is broken in hilarious ways: https://github.com/element-hq/synapse/issues/17518 --- book/changelog.md | 3 ++ src/api/client_server/media.rs | 77 +++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 0aba0e20..4416731a 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -125,6 +125,9 @@ This will be the first release of Grapevine since it was forked from Conduit 10. **BREAKING:** Reorganize config into sections. ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) * Details on how to migrate can be found in the merge request's description. +11. Try to generate thumbnails for remote media ourselves if the federation + thumbnail request fails. + ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) ### Fixed diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index a4c63444..93c1813d 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -804,6 +804,13 @@ async fn get_content_thumbnail_route_ruma( Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid.") })?; + let make_response = |file, content_type| { + authenticated_media_client::get_content_thumbnail::v1::Response { + file, + content_type, + } + }; + if let Some(FileMeta { content_type, file, @@ -811,13 +818,10 @@ async fn get_content_thumbnail_route_ruma( }) = services().media.get_thumbnail(mxc.to_string(), width, height).await? { - Ok(authenticated_media_client::get_content_thumbnail::v1::Response { - file, - content_type, - }) - } else if &*body.server_name != services().globals.server_name() - && allow_remote - { + return Ok(make_response(file, content_type)); + } + + if &*body.server_name != services().globals.server_name() && allow_remote { let get_thumbnail_response = get_remote_thumbnail( &body.server_name, authenticated_media_fed::get_content_thumbnail::v1::Request { @@ -832,25 +836,50 @@ async fn get_content_thumbnail_route_ruma( animated: Some(false), }, ) - .await?; + .await; - services() + match get_thumbnail_response { + Ok(resp) => { + services() + .media + .upload_thumbnail( + mxc.to_string(), + None, + resp.content.content_type.as_deref(), + width, + height, + &resp.content.file, + ) + .await?; + + return Ok(make_response( + resp.content.file, + resp.content.content_type, + )); + } + Err(error) => warn!( + %error, + "Failed to fetch thumbnail via federation, trying to fetch \ + original media and create thumbnail ourselves" + ), + } + + get_remote_content(&mxc).await?; + + if let Some(FileMeta { + content_type, + file, + .. + }) = services() .media - .upload_thumbnail( - mxc.to_string(), - None, - get_thumbnail_response.content.content_type.as_deref(), - width, - height, - &get_thumbnail_response.content.file, - ) - .await?; + .get_thumbnail(mxc.to_string(), width, height) + .await? + { + return Ok(make_response(file, content_type)); + } - Ok(authenticated_media_client::get_content_thumbnail::v1::Response { - file: get_thumbnail_response.content.file, - content_type: get_thumbnail_response.content.content_type, - }) - } else { - Err(Error::BadRequest(ErrorKind::NotYetUploaded, "Media not found.")) + error!("Source media doesn't exist even after fetching it from remote"); } + + Err(Error::BadRequest(ErrorKind::NotYetUploaded, "Media not found.")) } From 84850a163d26edb11fc2460611455fa6de6b6d04 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 11 Aug 2024 16:59:02 +0000 Subject: [PATCH 349/617] Factor content out of FileMeta That's not what *meta*data means --- src/api/client_server/media.rs | 40 +++++++++++++++---------- src/api/server_server.rs | 20 ++++++++----- src/service/media.rs | 55 ++++++++++++++++++++-------------- 3 files changed, 68 insertions(+), 47 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 93c1813d..cf47ed82 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -430,11 +430,13 @@ async fn get_content_route_ruma( ) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; - if let Some(FileMeta { - content_type, + if let Some(( + FileMeta { + content_type, + .. + }, file, - .. - }) = services().media.get(mxc.to_string()).await? + )) = services().media.get(mxc.to_string()).await? { Ok(authenticated_media_client::get_content::v1::Response { file, @@ -554,11 +556,13 @@ pub(crate) async fn get_content_as_filename_route_ruma( ) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; - if let Some(FileMeta { - content_type, + if let Some(( + FileMeta { + content_type, + .. + }, file, - .. - }) = services().media.get(mxc.to_string()).await? + )) = services().media.get(mxc.to_string()).await? { Ok(authenticated_media_client::get_content_as_filename::v1::Response { file, @@ -811,11 +815,13 @@ async fn get_content_thumbnail_route_ruma( } }; - if let Some(FileMeta { - content_type, + if let Some(( + FileMeta { + content_type, + .. + }, file, - .. - }) = + )) = services().media.get_thumbnail(mxc.to_string(), width, height).await? { return Ok(make_response(file, content_type)); @@ -866,11 +872,13 @@ async fn get_content_thumbnail_route_ruma( get_remote_content(&mxc).await?; - if let Some(FileMeta { - content_type, + if let Some(( + FileMeta { + content_type, + .. + }, file, - .. - }) = services() + )) = services() .media .get_thumbnail(mxc.to_string(), width, height) .await? diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 64faa9b3..21fe7e1c 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -2046,11 +2046,13 @@ pub(crate) async fn media_download_route( body: Ar, ) -> Result> { let mxc = MxcData::new(services().globals.server_name(), &body.media_id)?; - let Some(crate::service::media::FileMeta { - content_disposition, - content_type, + let Some(( + crate::service::media::FileMeta { + content_disposition, + content_type, + }, file, - }) = services().media.get(mxc.to_string()).await? + )) = services().media.get(mxc.to_string()).await? else { return Err(Error::BadRequest( ErrorKind::NotYetUploaded, @@ -2091,11 +2093,13 @@ pub(crate) async fn media_thumbnail_route( Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid.") })?; - let Some(crate::service::media::FileMeta { - content_type, + let Some(( + crate::service::media::FileMeta { + content_type, + .. + }, file, - .. - }) = services().media.get_thumbnail(mxc.to_string(), width, height).await? + )) = services().media.get_thumbnail(mxc.to_string(), width, height).await? else { return Err(Error::BadRequest( ErrorKind::NotYetUploaded, diff --git a/src/service/media.rs b/src/service/media.rs index 1f014f8a..21b178a2 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -21,9 +21,7 @@ pub(crate) struct FileMeta { // only the filename instead of the entire `Content-Disposition` header. #[allow(dead_code)] pub(crate) content_disposition: Option, - pub(crate) content_type: Option, - pub(crate) file: Vec, } pub(crate) struct Service { @@ -84,7 +82,10 @@ impl Service { /// Downloads a file. #[tracing::instrument(skip(self))] - pub(crate) async fn get(&self, mxc: String) -> Result> { + pub(crate) async fn get( + &self, + mxc: String, + ) -> Result)>> { if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc, 0, 0) { @@ -96,11 +97,13 @@ impl Service { file.read_to_end(&mut file_data).await?; - Ok(Some(FileMeta { - content_disposition, - content_type, - file: file_data, - })) + Ok(Some(( + FileMeta { + content_disposition, + content_type, + }, + file_data, + ))) } else { Ok(None) } @@ -224,7 +227,7 @@ impl Service { mxc: String, width: u32, height: u32, - ) -> Result> { + ) -> Result)>> { // 0, 0 because that's the original file let (width, height, crop) = Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); @@ -237,11 +240,13 @@ impl Service { let mut file = Vec::new(); File::open(path).await?.read_to_end(&mut file).await?; - return Ok(Some(FileMeta { - content_disposition, - content_type, - file: file.clone(), - })); + return Ok(Some(( + FileMeta { + content_disposition, + content_type, + }, + file.clone(), + ))); } let Ok((content_disposition, content_type, key)) = @@ -271,11 +276,13 @@ impl Service { let Some(thumbnail_bytes) = thumbnail_result? else { debug!("Returning source image as-is"); - return Ok(Some(FileMeta { - content_disposition, - content_type, + return Ok(Some(( + FileMeta { + content_disposition, + content_type, + }, file, - })); + ))); }; debug!("Saving created thumbnail"); @@ -294,10 +301,12 @@ impl Service { let mut f = File::create(path).await?; f.write_all(&thumbnail_bytes).await?; - Ok(Some(FileMeta { - content_disposition, - content_type, - file: thumbnail_bytes.clone(), - })) + Ok(Some(( + FileMeta { + content_disposition, + content_type, + }, + thumbnail_bytes.clone(), + ))) } } From 1ccb1e572b73ae8da94030f9a218961ce55b12a0 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 11 Aug 2024 17:11:48 +0000 Subject: [PATCH 350/617] media: add MediaFileKey wrapper One more win in the fight against the Vecs --- src/database.rs | 10 ++++++---- src/database/key_value/media.rs | 18 +++++++++++++----- src/service/globals.rs | 5 +++-- src/service/media.rs | 13 +++++++++++++ src/service/media/data.rs | 5 +++-- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/database.rs b/src/database.rs index 13f989a2..445c8a6a 100644 --- a/src/database.rs +++ b/src/database.rs @@ -25,9 +25,10 @@ use ruma::{ use tracing::{debug, error, info, info_span, warn, Instrument}; use crate::{ - config::DatabaseBackend, observability::FilterReloadHandles, - service::rooms::timeline::PduCount, services, utils, Config, Error, - PduEvent, Result, Services, SERVICES, + config::DatabaseBackend, + observability::FilterReloadHandles, + service::{media::MediaFileKey, rooms::timeline::PduCount}, + services, utils, Config, Error, PduEvent, Result, Services, SERVICES, }; pub(crate) struct KeyValueDatabase { @@ -606,6 +607,7 @@ impl KeyValueDatabase { if services().globals.database_version()? < 3 { // Move media to filesystem for (key, content) in db.mediaid_file.iter() { + let key = MediaFileKey::new(key); if content.is_empty() { continue; } @@ -613,7 +615,7 @@ impl KeyValueDatabase { let path = services().globals.get_media_file(&key); let mut file = fs::File::create(path)?; file.write_all(&content)?; - db.mediaid_file.insert(&key, &[])?; + db.mediaid_file.insert(key.as_bytes(), &[])?; } services().globals.bump_database_version(3)?; diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 57154a51..868842ba 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -1,6 +1,10 @@ use ruma::api::client::error::ErrorKind; -use crate::{database::KeyValueDatabase, service, utils, Error, Result}; +use crate::{ + database::KeyValueDatabase, + service::{self, media::MediaFileKey}, + utils, Error, Result, +}; impl service::media::Data for KeyValueDatabase { fn create_file_metadata( @@ -10,7 +14,7 @@ impl service::media::Data for KeyValueDatabase { height: u32, content_disposition: Option<&str>, content_type: Option<&str>, - ) -> Result> { + ) -> Result { let mut key = mxc.as_bytes().to_vec(); key.push(0xFF); key.extend_from_slice(&width.to_be_bytes()); @@ -27,7 +31,9 @@ impl service::media::Data for KeyValueDatabase { content_type.as_ref().map(|c| c.as_bytes()).unwrap_or_default(), ); - self.mediaid_file.insert(&key, &[])?; + let key = MediaFileKey::new(key); + + self.mediaid_file.insert(key.as_bytes(), &[])?; Ok(key) } @@ -37,7 +43,7 @@ impl service::media::Data for KeyValueDatabase { mxc: String, width: u32, height: u32, - ) -> Result<(Option, Option, Vec)> { + ) -> Result<(Option, Option, MediaFileKey)> { let mut prefix = mxc.as_bytes().to_vec(); prefix.push(0xFF); prefix.extend_from_slice(&width.to_be_bytes()); @@ -49,7 +55,9 @@ impl service::media::Data for KeyValueDatabase { Error::BadRequest(ErrorKind::NotFound, "Media not found"), )?; - let mut parts = key.rsplit(|&b| b == 0xFF); + let key = MediaFileKey::new(key); + + let mut parts = key.as_bytes().rsplit(|&b| b == 0xFF); let content_type = parts .next() diff --git a/src/service/globals.rs b/src/service/globals.rs index 2bdad38c..75b545fa 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -35,6 +35,7 @@ use trust_dns_resolver::TokioAsyncResolver; use crate::{ api::server_server::FedDest, observability::FilterReloadHandles, + service::media::MediaFileKey, services, utils::on_demand_hashmap::{OnDemandHashMap, TokenSet}, Config, Error, Result, @@ -506,11 +507,11 @@ impl Service { r } - pub(crate) fn get_media_file(&self, key: &[u8]) -> PathBuf { + pub(crate) fn get_media_file(&self, key: &MediaFileKey) -> PathBuf { let mut r = PathBuf::new(); r.push(self.config.database.path.clone()); r.push("media"); - r.push(general_purpose::URL_SAFE_NO_PAD.encode(key)); + r.push(general_purpose::URL_SAFE_NO_PAD.encode(key.as_bytes())); r } diff --git a/src/service/media.rs b/src/service/media.rs index 21b178a2..12cb2538 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -24,6 +24,19 @@ pub(crate) struct FileMeta { pub(crate) content_type: Option, } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub(crate) struct MediaFileKey(Vec); + +impl MediaFileKey { + pub(crate) fn new(key: Vec) -> Self { + Self(key) + } + + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 0aea3f9d..0f337342 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -1,3 +1,4 @@ +use super::MediaFileKey; use crate::Result; pub(crate) trait Data: Send + Sync { @@ -8,7 +9,7 @@ pub(crate) trait Data: Send + Sync { height: u32, content_disposition: Option<&str>, content_type: Option<&str>, - ) -> Result>; + ) -> Result; /// Returns `content_disposition`, `content_type` and the `metadata` key. fn search_file_metadata( @@ -16,5 +17,5 @@ pub(crate) trait Data: Send + Sync { mxc: String, width: u32, height: u32, - ) -> Result<(Option, Option, Vec)>; + ) -> Result<(Option, Option, MediaFileKey)>; } From a4b7df1b3a4adddb04c5dcf159fdb24d0d4ea90d Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 11 Aug 2024 17:19:12 +0000 Subject: [PATCH 351/617] media: use FileMeta instead of tuples --- src/api/client_server/media.rs | 6 +-- src/database/key_value/media.rs | 27 +++++++---- src/service/media.rs | 85 ++++++++++----------------------- src/service/media/data.rs | 8 ++-- 4 files changed, 50 insertions(+), 76 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index cf47ed82..91c0fdfc 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -167,7 +167,7 @@ pub(crate) async fn create_content_route( filename: Some(filename), }) .as_ref(), - body.content_type.as_deref(), + body.content_type.clone(), &body.file, ) .await?; @@ -333,7 +333,7 @@ pub(crate) async fn get_remote_content( .create( mxc.to_string(), response.content.content_disposition.as_ref(), - response.content.content_type.as_deref(), + response.content.content_type.clone(), &response.content.file, ) .await?; @@ -851,7 +851,7 @@ async fn get_content_thumbnail_route_ruma( .upload_thumbnail( mxc.to_string(), None, - resp.content.content_type.as_deref(), + resp.content.content_type.clone(), width, height, &resp.content.file, diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 868842ba..314cd0a9 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -2,7 +2,10 @@ use ruma::api::client::error::ErrorKind; use crate::{ database::KeyValueDatabase, - service::{self, media::MediaFileKey}, + service::{ + self, + media::{FileMeta, MediaFileKey}, + }, utils, Error, Result, }; @@ -12,8 +15,7 @@ impl service::media::Data for KeyValueDatabase { mxc: String, width: u32, height: u32, - content_disposition: Option<&str>, - content_type: Option<&str>, + meta: &FileMeta, ) -> Result { let mut key = mxc.as_bytes().to_vec(); key.push(0xFF); @@ -21,14 +23,17 @@ impl service::media::Data for KeyValueDatabase { key.extend_from_slice(&height.to_be_bytes()); key.push(0xFF); key.extend_from_slice( - content_disposition + meta.content_disposition .as_ref() - .map(|f| f.as_bytes()) + .map(String::as_bytes) .unwrap_or_default(), ); key.push(0xFF); key.extend_from_slice( - content_type.as_ref().map(|c| c.as_bytes()).unwrap_or_default(), + meta.content_type + .as_ref() + .map(String::as_bytes) + .unwrap_or_default(), ); let key = MediaFileKey::new(key); @@ -43,7 +48,7 @@ impl service::media::Data for KeyValueDatabase { mxc: String, width: u32, height: u32, - ) -> Result<(Option, Option, MediaFileKey)> { + ) -> Result<(FileMeta, MediaFileKey)> { let mut prefix = mxc.as_bytes().to_vec(); prefix.push(0xFF); prefix.extend_from_slice(&width.to_be_bytes()); @@ -86,6 +91,12 @@ impl service::media::Data for KeyValueDatabase { }, )?) }; - Ok((content_disposition, content_type, key)) + Ok(( + FileMeta { + content_disposition, + content_type, + }, + key, + )) } } diff --git a/src/service/media.rs b/src/service/media.rs index 12cb2538..94df18e5 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -48,22 +48,21 @@ impl Service { &self, mxc: String, content_disposition: Option<&ContentDisposition>, - content_type: Option<&str>, + content_type: Option, file: &[u8], - ) -> Result<()> { - // Width, Height = 0 if it's not a thumbnail - let key = self.db.create_file_metadata( - mxc, - 0, - 0, - content_disposition.map(ContentDisposition::to_string).as_deref(), + ) -> Result { + let meta = FileMeta { + content_disposition: content_disposition + .map(ContentDisposition::to_string), content_type, - )?; + }; + // Width, Height = 0 if it's not a thumbnail + let key = self.db.create_file_metadata(mxc, 0, 0, &meta)?; let path = services().globals.get_media_file(&key); let mut f = File::create(path).await?; f.write_all(file).await?; - Ok(()) + Ok(meta) } /// Uploads or replaces a file thumbnail. @@ -72,25 +71,23 @@ impl Service { pub(crate) async fn upload_thumbnail( &self, mxc: String, - content_disposition: Option<&str>, - content_type: Option<&str>, + content_disposition: Option, + content_type: Option, width: u32, height: u32, file: &[u8], - ) -> Result<()> { - let key = self.db.create_file_metadata( - mxc, - width, - height, + ) -> Result { + let meta = FileMeta { content_disposition, content_type, - )?; + }; + let key = self.db.create_file_metadata(mxc, width, height, &meta)?; let path = services().globals.get_media_file(&key); let mut f = File::create(path).await?; f.write_all(file).await?; - Ok(()) + Ok(meta) } /// Downloads a file. @@ -99,9 +96,7 @@ impl Service { &self, mxc: String, ) -> Result)>> { - if let Ok((content_disposition, content_type, key)) = - self.db.search_file_metadata(mxc, 0, 0) - { + if let Ok((meta, key)) = self.db.search_file_metadata(mxc, 0, 0) { let path = services().globals.get_media_file(&key); let mut file_data = Vec::new(); let Ok(mut file) = File::open(path).await else { @@ -110,13 +105,7 @@ impl Service { file.read_to_end(&mut file_data).await?; - Ok(Some(( - FileMeta { - content_disposition, - content_type, - }, - file_data, - ))) + Ok(Some((meta, file_data))) } else { Ok(None) } @@ -245,7 +234,7 @@ impl Service { let (width, height, crop) = Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); - if let Ok((content_disposition, content_type, key)) = + if let Ok((meta, key)) = self.db.search_file_metadata(mxc.clone(), width, height) { debug!("Using saved thumbnail"); @@ -253,17 +242,10 @@ impl Service { let mut file = Vec::new(); File::open(path).await?.read_to_end(&mut file).await?; - return Ok(Some(( - FileMeta { - content_disposition, - content_type, - }, - file.clone(), - ))); + return Ok(Some((meta, file.clone()))); } - let Ok((content_disposition, content_type, key)) = - self.db.search_file_metadata(mxc.clone(), 0, 0) + let Ok((meta, key)) = self.db.search_file_metadata(mxc.clone(), 0, 0) else { debug!("Original image not found, can't generate thumbnail"); return Ok(None); @@ -289,37 +271,20 @@ impl Service { let Some(thumbnail_bytes) = thumbnail_result? else { debug!("Returning source image as-is"); - return Ok(Some(( - FileMeta { - content_disposition, - content_type, - }, - file, - ))); + return Ok(Some((meta, file))); }; debug!("Saving created thumbnail"); // Save thumbnail in database so we don't have to generate it // again next time - let thumbnail_key = self.db.create_file_metadata( - mxc, - width, - height, - content_disposition.as_deref(), - content_type.as_deref(), - )?; + let thumbnail_key = + self.db.create_file_metadata(mxc, width, height, &meta)?; let path = services().globals.get_media_file(&thumbnail_key); let mut f = File::create(path).await?; f.write_all(&thumbnail_bytes).await?; - Ok(Some(( - FileMeta { - content_disposition, - content_type, - }, - thumbnail_bytes.clone(), - ))) + Ok(Some((meta, thumbnail_bytes.clone()))) } } diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 0f337342..64fbdbbf 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -1,4 +1,4 @@ -use super::MediaFileKey; +use super::{FileMeta, MediaFileKey}; use crate::Result; pub(crate) trait Data: Send + Sync { @@ -7,15 +7,13 @@ pub(crate) trait Data: Send + Sync { mxc: String, width: u32, height: u32, - content_disposition: Option<&str>, - content_type: Option<&str>, + meta: &FileMeta, ) -> Result; - /// Returns `content_disposition`, `content_type` and the `metadata` key. fn search_file_metadata( &self, mxc: String, width: u32, height: u32, - ) -> Result<(Option, Option, MediaFileKey)>; + ) -> Result<(FileMeta, MediaFileKey)>; } From 3fe0110649a339a143a9c9c5842b34e63b5b6065 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 11 Aug 2024 20:40:01 +0000 Subject: [PATCH 352/617] media: convert allow_remote to enum --- src/api/client_server/media.rs | 45 ++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 91c0fdfc..9b653944 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -178,6 +178,23 @@ pub(crate) async fn create_content_route( })) } +/// Whether or not to allow remote content to be loaded +#[derive(Clone, Copy, PartialEq, Eq)] +enum AllowRemote { + Yes, + No, +} + +impl From for AllowRemote { + fn from(allow: bool) -> Self { + if allow { + Self::Yes + } else { + Self::No + } + } +} + struct RemoteResponse { #[allow(unused)] metadata: authenticated_media_fed::ContentMetadata, @@ -387,7 +404,7 @@ pub(crate) async fn get_content_legacy_route( } } - let allow_remote = body.allow_remote; + let allow_remote = body.allow_remote.into(); get_content_route_ruma(body.map_body(convert_request), allow_remote) .await @@ -411,7 +428,7 @@ pub(crate) async fn get_content_legacy_route( pub(crate) async fn get_content_route( body: Ar, ) -> Result { - get_content_route_ruma(body, true).await.map(|x| { + get_content_route_ruma(body, AllowRemote::Yes).await.map(|x| { let mut r = Ra(x).into_response(); set_header_or_panic( @@ -426,7 +443,7 @@ pub(crate) async fn get_content_route( async fn get_content_route_ruma( body: Ar, - allow_remote: bool, + allow_remote: AllowRemote, ) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; @@ -447,7 +464,7 @@ async fn get_content_route_ruma( content_type, }) } else if &*body.server_name != services().globals.server_name() - && allow_remote + && allow_remote == AllowRemote::Yes { let remote_response = get_remote_content(&mxc).await?; Ok(authenticated_media_client::get_content::v1::Response { @@ -511,7 +528,7 @@ pub(crate) async fn get_content_as_filename_legacy_route( } } - let allow_remote = body.allow_remote; + let allow_remote = body.allow_remote.into(); get_content_as_filename_route_ruma( body.map_body(convert_request), allow_remote, @@ -537,7 +554,7 @@ pub(crate) async fn get_content_as_filename_legacy_route( pub(crate) async fn get_content_as_filename_route( body: Ar, ) -> Result { - get_content_as_filename_route_ruma(body, true).await.map(|x| { + get_content_as_filename_route_ruma(body, AllowRemote::Yes).await.map(|x| { let mut r = Ra(x).into_response(); set_header_or_panic( @@ -550,9 +567,9 @@ pub(crate) async fn get_content_as_filename_route( }) } -pub(crate) async fn get_content_as_filename_route_ruma( +async fn get_content_as_filename_route_ruma( body: Ar, - allow_remote: bool, + allow_remote: AllowRemote, ) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; @@ -573,7 +590,7 @@ pub(crate) async fn get_content_as_filename_route_ruma( content_type, }) } else if &*body.server_name != services().globals.server_name() - && allow_remote + && allow_remote == AllowRemote::Yes { let remote_response = get_remote_content(&mxc).await?; @@ -660,7 +677,7 @@ pub(crate) async fn get_content_thumbnail_legacy_route( } } - let allow_remote = body.allow_remote; + let allow_remote = body.allow_remote.into(); get_content_thumbnail_route_ruma( body.map_body(convert_request), @@ -683,7 +700,7 @@ pub(crate) async fn get_content_thumbnail_legacy_route( pub(crate) async fn get_content_thumbnail_route( body: Ar, ) -> Result { - get_content_thumbnail_route_ruma(body, true).await.map(|x| { + get_content_thumbnail_route_ruma(body, AllowRemote::Yes).await.map(|x| { let mut r = Ra(x).into_response(); fix_thumbnail_headers(&mut r); @@ -798,7 +815,7 @@ pub(crate) async fn get_remote_thumbnail( async fn get_content_thumbnail_route_ruma( body: Ar, - allow_remote: bool, + allow_remote: AllowRemote, ) -> Result { let mxc = MxcData::new(&body.server_name, &body.media_id)?; let width = body.width.try_into().map_err(|_| { @@ -827,7 +844,9 @@ async fn get_content_thumbnail_route_ruma( return Ok(make_response(file, content_type)); } - if &*body.server_name != services().globals.server_name() && allow_remote { + if &*body.server_name != services().globals.server_name() + && allow_remote == AllowRemote::Yes + { let get_thumbnail_response = get_remote_thumbnail( &body.server_name, authenticated_media_fed::get_content_thumbnail::v1::Request { From b4fecbc51719a33d09be1e76d55ae0eec11fb71a Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 28 Aug 2024 17:59:45 +0000 Subject: [PATCH 353/617] client/media: work around matrix-media-repo bug It rejects Authenticated Media requests if X-Matrix values aren't unnecessarily quoted: https://github.com/t2bot/matrix-media-repo/issues/609 --- src/api/client_server/media.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 9b653944..cd117232 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -331,7 +331,9 @@ pub(crate) async fn get_remote_content( response } Err(Error::Federation(_, error)) - if error.error_kind() == Some(&ErrorKind::Unrecognized) => + if error.error_kind() == Some(&ErrorKind::Unrecognized) + // https://github.com/t2bot/matrix-media-repo/issues/609 + || error.error_kind() == Some(&ErrorKind::Unauthorized) => { info!( "Remote server does not support authenticated media, falling \ @@ -795,7 +797,9 @@ pub(crate) async fn get_remote_thumbnail( response } Err(Error::Federation(_, error)) - if error.error_kind() == Some(&ErrorKind::Unrecognized) => + if error.error_kind() == Some(&ErrorKind::Unrecognized) + // https://github.com/t2bot/matrix-media-repo/issues/609 + || error.error_kind() == Some(&ErrorKind::Unauthorized) => { info!( "Remote server does not support authenticated media, falling \ From be14f5bddcf7ac7b2ab97f353efd61ae56704ab2 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sun, 1 Sep 2024 01:16:41 -0700 Subject: [PATCH 354/617] fetch signing keys on join even when no cached keys for origin Silly mistake --- book/changelog.md | 3 +++ src/service/rooms/event_handler.rs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 4416731a..1e3d3ff2 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -168,6 +168,9 @@ This will be the first release of Grapevine since it was forked from Conduit ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) 15. Fix bug where expired keys may not be re-fetched in some scenarios. ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) +16. Fix bug where signing keys would not be fetched when joining a room if we + hadn't previously seen any signing keys from that server. + ([!87](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/87)) ### Added diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 0f1ac9b7..00b11b26 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1714,15 +1714,15 @@ impl Service { trace!(server = %origin, "Loading signing keys for other server"); - if let Some(result) = services().globals.signing_keys_for(origin)? { - if !contains_all_ids(&result) { - trace!( - server = %origin, - "Signing key not loaded for server", - ); - servers.insert(origin.to_owned(), BTreeMap::new()); - } - + let result = services().globals.signing_keys_for(origin)?; + if !result.as_ref().is_some_and(contains_all_ids) { + trace!( + server = %origin, + "Signing key not loaded for server", + ); + servers.insert(origin.to_owned(), BTreeMap::new()); + } + if let Some(result) = result { pub_key_map.insert(origin.to_string(), result); } } From 542e097cdf0016ff9becc4ff4953a266c953fa6a Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 1 Sep 2024 11:32:45 +0000 Subject: [PATCH 355/617] server_server: use non-deprecated create_join_event types Instead of working with v1 types and converting them to v2 as required, do it the other way around. Allows us to remove a very broad #[allow(deprecated)]. --- src/api/server_server.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 21fe7e1c..52b58f90 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1,5 +1,3 @@ -#![allow(deprecated)] - use std::{ collections::BTreeMap, fmt::Debug, @@ -1543,7 +1541,7 @@ async fn create_join_event( sender_servername: &ServerName, room_id: &RoomId, pdu: &RawJsonValue, -) -> Result { +) -> Result { if !services().rooms.metadata.exists(room_id)? { return Err(Error::BadRequest( ErrorKind::NotFound, @@ -1659,7 +1657,7 @@ async fn create_join_event( services().sending.send_pdu(servers, &pdu_id)?; - Ok(create_join_event::v1::RoomState { + Ok(create_join_event::v2::RoomState { auth_chain: auth_chain_ids .filter_map(|id| { services().rooms.timeline.get_pdu_json(&id).ok().flatten() @@ -1675,20 +1673,32 @@ async fn create_join_event( .collect(), // TODO: handle restricted joins event: None, + members_omitted: false, + servers_in_room: None, }) } /// # `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}` /// /// Submits a signed join event. +#[allow(deprecated)] pub(crate) async fn create_join_event_v1_route( body: Ar, ) -> Result> { let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); - let room_state = - create_join_event(sender_servername, &body.room_id, &body.pdu).await?; + let create_join_event::v2::RoomState { + auth_chain, + state, + event, + .. + } = create_join_event(sender_servername, &body.room_id, &body.pdu).await?; + let room_state = create_join_event::v1::RoomState { + auth_chain, + state, + event, + }; Ok(Ra(create_join_event::v1::Response { room_state, @@ -1704,18 +1714,8 @@ pub(crate) async fn create_join_event_v2_route( let sender_servername = body.sender_servername.as_ref().expect("server is authenticated"); - let create_join_event::v1::RoomState { - auth_chain, - state, - event, - } = create_join_event(sender_servername, &body.room_id, &body.pdu).await?; - let room_state = create_join_event::v2::RoomState { - members_omitted: false, - auth_chain, - state, - event, - servers_in_room: None, - }; + let room_state = + create_join_event(sender_servername, &body.room_id, &body.pdu).await?; Ok(Ra(create_join_event::v2::Response { room_state, From 22ce624a81a1abd94b51e7fc888b4e9fd163e928 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 1 Sep 2024 11:34:14 +0000 Subject: [PATCH 356/617] event_handler: remove AsyncRecursiveType alias, simplify signatures --- src/service/rooms/event_handler.rs | 34 ++++++++---------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 00b11b26..49b9820b 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1,14 +1,10 @@ -/// An async function that can recursively call itself. -type AsyncRecursiveType<'a, T> = Pin + 'a + Send>>; - use std::{ collections::{hash_map, BTreeMap, HashMap, HashSet}, - pin::Pin, sync::Arc, time::{Duration, Instant, SystemTime}, }; -use futures_util::{stream::FuturesUnordered, Future, StreamExt}; +use futures_util::{future::BoxFuture, stream::FuturesUnordered, StreamExt}; use ruma::{ api::{ client::error::ErrorKind, @@ -84,15 +80,13 @@ impl Service { /// 13. Use state resolution to find new room state /// 14. Check if the event passes auth based on the "current state" of the /// room, if not soft fail it - // We use some AsyncRecursiveType hacks here so we can call this async - // funtion recursively #[tracing::instrument(skip(self, value, is_timeline_event, pub_key_map))] pub(crate) async fn handle_incoming_pdu<'a>( &self, origin: &'a ServerName, event_id: &'a EventId, room_id: &'a RoomId, - value: BTreeMap, + value: CanonicalJsonObject, is_timeline_event: bool, pub_key_map: &'a RwLock>, ) -> Result>> { @@ -317,7 +311,7 @@ impl Service { r } - #[allow(clippy::type_complexity, clippy::too_many_arguments)] + #[allow(clippy::too_many_arguments)] #[tracing::instrument(skip(self, origin, room_id, value, pub_key_map))] fn handle_outlier_pdu<'a>( &'a self, @@ -325,13 +319,10 @@ impl Service { create_event: &'a PduEvent, event_id: &'a EventId, room_id: &'a RoomId, - mut value: BTreeMap, + mut value: CanonicalJsonObject, auth_events_known: bool, pub_key_map: &'a RwLock>, - ) -> AsyncRecursiveType< - 'a, - Result<(Arc, BTreeMap)>, - > { + ) -> BoxFuture<'a, Result<(Arc, CanonicalJsonObject)>> { Box::pin(async move { // 1.1. Remove unsigned field value.remove("unsigned"); @@ -571,7 +562,7 @@ impl Service { pub(crate) async fn upgrade_outlier_to_timeline_pdu( &self, incoming_pdu: Arc, - val: BTreeMap, + val: CanonicalJsonObject, create_event: &PduEvent, origin: &ServerName, room_id: &RoomId, @@ -1237,7 +1228,6 @@ impl Service { /// b. Look at outlier pdu tree /// c. Ask origin server over federation /// d. TODO: Ask other servers over federation? - #[allow(clippy::type_complexity)] #[tracing::instrument(skip_all)] pub(crate) fn fetch_and_handle_outliers<'a>( &'a self, @@ -1247,10 +1237,7 @@ impl Service { room_id: &'a RoomId, room_version_id: &'a RoomVersionId, pub_key_map: &'a RwLock>, - ) -> AsyncRecursiveType< - 'a, - Vec<(Arc, Option>)>, - > { + ) -> BoxFuture<'a, Vec<(Arc, Option)>> { Box::pin(async move { let back_off = |id| async move { match services() @@ -1462,10 +1449,7 @@ impl Service { initial_set: Vec>, ) -> Result<( Vec>, - HashMap< - Arc, - (Arc, BTreeMap), - >, + HashMap, (Arc, CanonicalJsonObject)>, )> { let mut graph: HashMap, _> = HashMap::new(); let mut eventid_info = HashMap::new(); @@ -1560,7 +1544,7 @@ impl Service { #[tracing::instrument(skip_all)] pub(crate) async fn fetch_required_signing_keys( &self, - event: &BTreeMap, + event: &CanonicalJsonObject, pub_key_map: &RwLock>, ) -> Result<()> { let signatures = event From f52cf53931287e51dec87f48cf3472798d12168a Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 1 Sep 2024 11:34:45 +0000 Subject: [PATCH 357/617] Remove obsolete clippy #[allow]s --- src/service/media.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 94df18e5..6185ad6e 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -66,7 +66,6 @@ impl Service { } /// Uploads or replaces a file thumbnail. - #[allow(clippy::too_many_arguments)] #[tracing::instrument(skip(self, file))] pub(crate) async fn upload_thumbnail( &self, @@ -222,7 +221,6 @@ impl Service { /// /// For width,height <= 96 the server uses another thumbnailing algorithm /// which crops the image afterwards. - #[allow(clippy::too_many_lines)] #[tracing::instrument(skip(self))] pub(crate) async fn get_thumbnail( &self, From 341f4213d0b592337a0561c1054187e7eb28fba4 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 1 Sep 2024 11:35:02 +0000 Subject: [PATCH 358/617] Use self instead of going through services() --- src/service/admin.rs | 5 ++--- src/service/appservice.rs | 14 ++++---------- src/service/globals.rs | 3 +-- src/service/rooms/auth_chain.rs | 19 ++++++------------- src/service/rooms/event_handler.rs | 6 ++---- src/service/rooms/pdu_metadata.rs | 15 +++------------ src/service/rooms/state.rs | 3 +-- src/service/rooms/timeline.rs | 21 +++++++-------------- 8 files changed, 26 insertions(+), 60 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 43c9753d..b3f098a5 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -236,8 +236,7 @@ impl Service { tokio::spawn(async move { let mut receiver = self2.receiver.lock().await; - let Ok(Some(grapevine_room)) = services().admin.get_admin_room() - else { + let Ok(Some(grapevine_room)) = self2.get_admin_room() else { return; }; @@ -1470,7 +1469,7 @@ impl Service { user_id: &UserId, displayname: String, ) -> Result<()> { - if let Some(room_id) = services().admin.get_admin_room()? { + if let Some(room_id) = self.get_admin_room()? { let room_token = services() .globals .roomid_mutex_state diff --git a/src/service/appservice.rs b/src/service/appservice.rs index ff0b9785..b4046f1b 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -11,7 +11,7 @@ use ruma::{ }; use tokio::sync::RwLock; -use crate::{services, Result}; +use crate::Result; /// Compiled regular expressions for a namespace. #[derive(Clone, Debug)] @@ -160,15 +160,9 @@ impl Service { &self, service_name: &str, ) -> Result<()> { - services() - .appservice - .registration_info - .write() - .await - .remove(service_name) - .ok_or_else(|| { - crate::Error::AdminCommand("Appservice not found") - })?; + self.registration_info.write().await.remove(service_name).ok_or_else( + || crate::Error::AdminCommand("Appservice not found"), + )?; self.db.unregister_appservice(service_name) } diff --git a/src/service/globals.rs b/src/service/globals.rs index 75b545fa..dad08d32 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -36,7 +36,6 @@ use crate::{ api::server_server::FedDest, observability::FilterReloadHandles, service::media::MediaFileKey, - services, utils::on_demand_hashmap::{OnDemandHashMap, TokenSet}, Config, Error, Result, }; @@ -517,7 +516,7 @@ impl Service { pub(crate) fn shutdown(&self) { self.shutdown.store(true, atomic::Ordering::Relaxed); - services().globals.rotate.fire(); + self.rotate.fire(); } } diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 98d19662..b45fab8e 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -70,10 +70,8 @@ impl Service { let chunk_key: Vec = chunk.iter().map(|(short, _)| short).copied().collect(); - if let Some(cached) = services() - .rooms - .auth_chain - .get_cached_eventid_authchain(&chunk_key)? + if let Some(cached) = + self.get_cached_eventid_authchain(&chunk_key)? { hits += 1; full_auth_chain.extend(cached.iter().copied()); @@ -86,10 +84,8 @@ impl Service { let mut misses2 = 0; let mut i = 0; for (sevent_id, event_id) in chunk { - if let Some(cached) = services() - .rooms - .auth_chain - .get_cached_eventid_authchain(&[sevent_id])? + if let Some(cached) = + self.get_cached_eventid_authchain(&[sevent_id])? { hits2 += 1; chunk_cache.extend(cached.iter().copied()); @@ -98,7 +94,7 @@ impl Service { let auth_chain = Arc::new( self.get_auth_chain_inner(room_id, &event_id)?, ); - services().rooms.auth_chain.cache_auth_chain( + self.cache_auth_chain( vec![sevent_id], Arc::clone(&auth_chain), )?; @@ -122,10 +118,7 @@ impl Service { "Chunk missed", ); let chunk_cache = Arc::new(chunk_cache); - services() - .rooms - .auth_chain - .cache_auth_chain(chunk_key, Arc::clone(&chunk_cache))?; + self.cache_auth_chain(chunk_key, Arc::clone(&chunk_cache))?; full_auth_chain.extend(chunk_cache.iter()); } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 49b9820b..62c6470b 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -105,7 +105,7 @@ impl Service { )); } - services().rooms.event_handler.acl_check(origin, room_id)?; + self.acl_check(origin, room_id)?; // 1. Skip the PDU if we already have it as a timeline event if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? { @@ -289,9 +289,7 @@ impl Service { .write() .await .insert(room_id.to_owned(), (event_id.to_owned(), start_time)); - let r = services() - .rooms - .event_handler + let r = self .upgrade_outlier_to_timeline_pdu( incoming_pdu, val, diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 319930bd..767ada39 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -45,12 +45,7 @@ impl Service { } } - #[allow( - clippy::too_many_arguments, - clippy::too_many_lines, - // Allowed because this function uses `services()` - clippy::unused_self, - )] + #[allow(clippy::too_many_arguments, clippy::too_many_lines)] #[tracing::instrument(skip(self))] pub(crate) fn paginate_relations_with_filter( &self, @@ -69,9 +64,7 @@ impl Service { match ruma::api::Direction::Backward { ruma::api::Direction::Forward => { // TODO: should be relations_after - let events_after: Vec<_> = services() - .rooms - .pdu_metadata + let events_after: Vec<_> = self .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { @@ -126,9 +119,7 @@ impl Service { }) } ruma::api::Direction::Backward => { - let events_before: Vec<_> = services() - .rooms - .pdu_metadata + let events_before: Vec<_> = self .relations_until(sender_user, room_id, target, from)? .filter(|r| { r.as_ref().map_or(true, |(_, pdu)| { diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index f2ab7b36..7c6a25e4 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -397,8 +397,7 @@ impl Service { state_key: Option<&str>, content: &serde_json::value::RawValue, ) -> Result>> { - let Some(shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? + let Some(shortstatehash) = self.get_room_shortstatehash(room_id)? else { return Ok(HashMap::new()); }; diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index b3605bfb..bb268255 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -575,10 +575,8 @@ impl Service { if let Ok(content) = serde_json::from_str::(pdu.content.get()) { - if let Some(related_pducount) = services() - .rooms - .timeline - .get_pdu_count(&content.relates_to.event_id)? + if let Some(related_pducount) = + self.get_pdu_count(&content.relates_to.event_id)? { services() .rooms @@ -596,10 +594,8 @@ impl Service { } => { // We need to do it again here, because replies don't have // event_id as a top level field - if let Some(related_pducount) = services() - .rooms - .timeline - .get_pdu_count(&in_reply_to.event_id)? + if let Some(related_pducount) = + self.get_pdu_count(&in_reply_to.event_id)? { services().rooms.pdu_metadata.add_relation( PduCount::Normal(count2), @@ -1134,11 +1130,8 @@ impl Service { return Ok(None); } - let pdu_id = services() - .rooms - .timeline - .append_pdu(pdu, pdu_json, new_room_leaves, room_id) - .await?; + let pdu_id = + self.append_pdu(pdu, pdu_json, new_room_leaves, room_id).await?; Ok(Some(pdu_id)) } @@ -1314,7 +1307,7 @@ impl Service { .await; // Skip the PDU if we already have it as a timeline event - if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(&event_id)? { + if let Some(pdu_id) = self.get_pdu_id(&event_id)? { info!(%event_id, ?pdu_id, "We already know this event"); return Ok(()); } From 26322d5a955dbf0650c7a3017708722c7684d906 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 26 Aug 2024 16:47:50 +0000 Subject: [PATCH 359/617] Add PduId wrapper struct Death to Vec --- src/api/client_server/membership.rs | 2 +- src/api/client_server/search.rs | 4 ++- src/api/server_server.rs | 2 +- src/database/key_value/rooms/pdu_metadata.rs | 6 +++- src/database/key_value/rooms/search.rs | 22 ++++++++----- src/database/key_value/rooms/threads.rs | 16 +++++---- src/database/key_value/rooms/timeline.rs | 34 +++++++++++--------- src/database/key_value/sending.rs | 5 +-- src/service/rooms/event_handler.rs | 6 ++-- src/service/rooms/search/data.rs | 8 ++--- src/service/rooms/threads/data.rs | 6 ++-- src/service/rooms/timeline.rs | 31 ++++++++++++++---- src/service/rooms/timeline/data.rs | 14 ++++---- src/service/sending.rs | 17 +++++----- src/utils.rs | 8 ++--- 15 files changed, 110 insertions(+), 71 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 993aedd1..bfd338a6 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1372,7 +1372,7 @@ pub(crate) async fn invite_helper( ) })?; - let pdu_id: Vec = services() + let pdu_id = services() .rooms .event_handler .handle_incoming_pdu( diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 458d4ae2..cd36a13b 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -84,7 +84,9 @@ pub(crate) async fn search_events_route( if let Some(s) = searches .iter_mut() .map(|s| (s.peek().cloned(), s)) - .max_by_key(|(peek, _)| peek.clone()) + .max_by_key(|(peek, _)| { + peek.as_ref().map(|id| id.as_bytes().to_vec()) + }) .and_then(|(_, i)| i.next()) { results.push(s); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 52b58f90..74f7a1b0 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1622,7 +1622,7 @@ async fn create_join_event( .roomid_mutex_federation .lock_key(room_id.to_owned()) .await; - let pdu_id: Vec = services() + let pdu_id = services() .rooms .event_handler .handle_incoming_pdu( diff --git a/src/database/key_value/rooms/pdu_metadata.rs b/src/database/key_value/rooms/pdu_metadata.rs index b890985f..32689bd4 100644 --- a/src/database/key_value/rooms/pdu_metadata.rs +++ b/src/database/key_value/rooms/pdu_metadata.rs @@ -4,7 +4,10 @@ use ruma::{EventId, RoomId, UserId}; use crate::{ database::KeyValueDatabase, - service::{self, rooms::timeline::PduCount}, + service::{ + self, + rooms::timeline::{PduCount, PduId}, + }, services, utils, Error, PduEvent, Result, }; @@ -50,6 +53,7 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { let mut pduid = shortroomid.to_be_bytes().to_vec(); pduid.extend_from_slice(&from.to_be_bytes()); + let pduid = PduId::new(pduid); let mut pdu = services() .rooms diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index 5fb90462..fa48965d 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -1,6 +1,10 @@ use ruma::RoomId; -use crate::{database::KeyValueDatabase, service, services, utils, Result}; +use crate::{ + database::KeyValueDatabase, + service::{self, rooms::timeline::PduId}, + services, utils, Result, +}; /// Splits a string into tokens used as keys in the search inverted index /// @@ -18,7 +22,7 @@ impl service::rooms::search::Data for KeyValueDatabase { fn index_pdu( &self, shortroomid: u64, - pdu_id: &[u8], + pdu_id: &PduId, message_body: &str, ) -> Result<()> { let mut batch = tokenize(message_body).map(|word| { @@ -26,7 +30,7 @@ impl service::rooms::search::Data for KeyValueDatabase { key.extend_from_slice(word.as_bytes()); key.push(0xFF); // TODO: currently we save the room id a second time here - key.extend_from_slice(pdu_id); + key.extend_from_slice(pdu_id.as_bytes()); (key, Vec::new()) }); @@ -37,7 +41,7 @@ impl service::rooms::search::Data for KeyValueDatabase { fn deindex_pdu( &self, shortroomid: u64, - pdu_id: &[u8], + pdu_id: &PduId, message_body: &str, ) -> Result<()> { let batch = tokenize(message_body).map(|word| { @@ -45,7 +49,7 @@ impl service::rooms::search::Data for KeyValueDatabase { key.extend_from_slice(word.as_bytes()); key.push(0xFF); // TODO: currently we save the room id a second time here - key.extend_from_slice(pdu_id); + key.extend_from_slice(pdu_id.as_bytes()); key }); @@ -62,7 +66,7 @@ impl service::rooms::search::Data for KeyValueDatabase { &'a self, room_id: &RoomId, search_string: &str, - ) -> Result> + 'a>, Vec)>> + ) -> Result + 'a>, Vec)>> { let prefix = services() .rooms @@ -87,12 +91,14 @@ impl service::rooms::search::Data for KeyValueDatabase { // Newest pdus first .iter_from(&last_possible_id, true) .take_while(move |(k, _)| k.starts_with(&prefix2)) - .map(move |(key, _)| key[prefix3.len()..].to_vec()) + .map(move |(key, _)| PduId::new(key[prefix3.len()..].to_vec())) }); // We compare b with a because we reversed the iterator earlier let Some(common_elements) = - utils::common_elements(iterators, |a, b| b.cmp(a)) + utils::common_elements(iterators, |a, b| { + b.as_bytes().cmp(a.as_bytes()) + }) else { return Ok(None); }; diff --git a/src/database/key_value/rooms/threads.rs b/src/database/key_value/rooms/threads.rs index 9915198e..bb9a1712 100644 --- a/src/database/key_value/rooms/threads.rs +++ b/src/database/key_value/rooms/threads.rs @@ -6,8 +6,9 @@ use ruma::{ }; use crate::{ - database::KeyValueDatabase, service, services, utils, Error, PduEvent, - Result, + database::KeyValueDatabase, + service::{self, rooms::timeline::PduId}, + services, utils, Error, PduEvent, Result, }; impl service::rooms::threads::Data for KeyValueDatabase { @@ -42,6 +43,9 @@ impl service::rooms::threads::Data for KeyValueDatabase { "Invalid pduid in threadid_userids.", ) })?; + + let pduid = PduId::new(pduid); + let mut pdu = services() .rooms .timeline @@ -61,7 +65,7 @@ impl service::rooms::threads::Data for KeyValueDatabase { fn update_participants( &self, - root_id: &[u8], + root_id: &PduId, participants: &[OwnedUserId], ) -> Result<()> { let users = participants @@ -70,16 +74,16 @@ impl service::rooms::threads::Data for KeyValueDatabase { .collect::>() .join(&[0xFF][..]); - self.threadid_userids.insert(root_id, &users)?; + self.threadid_userids.insert(root_id.as_bytes(), &users)?; Ok(()) } fn get_participants( &self, - root_id: &[u8], + root_id: &PduId, ) -> Result>> { - if let Some(users) = self.threadid_userids.get(root_id)? { + if let Some(users) = self.threadid_userids.get(root_id.as_bytes())? { Ok(Some( users .split(|b| *b == 0xFF) diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 0633e1d1..951975e5 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -10,7 +10,8 @@ use tracing::error; use crate::{ database::KeyValueDatabase, observability::{FoundIn, Lookup, METRICS}, - service, services, utils, Error, PduEvent, Result, + service::{self, rooms::timeline::PduId}, + services, utils, Error, PduEvent, Result, }; impl service::rooms::timeline::Data for KeyValueDatabase { @@ -102,8 +103,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { } /// Returns the pdu's id. - fn get_pdu_id(&self, event_id: &EventId) -> Result>> { - self.eventid_pduid.get(event_id.as_bytes()) + fn get_pdu_id(&self, event_id: &EventId) -> Result> { + self.eventid_pduid.get(event_id.as_bytes()).map(|x| x.map(PduId::new)) } /// Returns the pdu. @@ -170,8 +171,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Returns the pdu. /// /// This does __NOT__ check the outliers `Tree`. - fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result> { - self.pduid_pdu.get(pdu_id)?.map_or(Ok(None), |pdu| { + fn get_pdu_from_id(&self, pdu_id: &PduId) -> Result> { + self.pduid_pdu.get(pdu_id.as_bytes())?.map_or(Ok(None), |pdu| { Ok(Some( serde_json::from_slice(&pdu) .map_err(|_| Error::bad_database("Invalid PDU in db."))?, @@ -182,9 +183,9 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Returns the pdu as a `BTreeMap`. fn get_pdu_json_from_id( &self, - pdu_id: &[u8], + pdu_id: &PduId, ) -> Result> { - self.pduid_pdu.get(pdu_id)?.map_or(Ok(None), |pdu| { + self.pduid_pdu.get(pdu_id.as_bytes())?.map_or(Ok(None), |pdu| { Ok(Some( serde_json::from_slice(&pdu) .map_err(|_| Error::bad_database("Invalid PDU in db."))?, @@ -194,13 +195,13 @@ impl service::rooms::timeline::Data for KeyValueDatabase { fn append_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, pdu: &PduEvent, json: &CanonicalJsonObject, count: u64, ) -> Result<()> { self.pduid_pdu.insert( - pdu_id, + pdu_id.as_bytes(), &serde_json::to_vec(json) .expect("CanonicalJsonObject is always a valid"), )?; @@ -210,7 +211,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase { .unwrap() .insert(pdu.room_id.clone(), PduCount::Normal(count)); - self.eventid_pduid.insert(pdu.event_id.as_bytes(), pdu_id)?; + self.eventid_pduid + .insert(pdu.event_id.as_bytes(), pdu_id.as_bytes())?; self.eventid_outlierpdu.remove(pdu.event_id.as_bytes())?; Ok(()) @@ -218,17 +220,17 @@ impl service::rooms::timeline::Data for KeyValueDatabase { fn prepend_backfill_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, event_id: &EventId, json: &CanonicalJsonObject, ) -> Result<()> { self.pduid_pdu.insert( - pdu_id, + pdu_id.as_bytes(), &serde_json::to_vec(json) .expect("CanonicalJsonObject is always a valid"), )?; - self.eventid_pduid.insert(event_id.as_bytes(), pdu_id)?; + self.eventid_pduid.insert(event_id.as_bytes(), pdu_id.as_bytes())?; self.eventid_outlierpdu.remove(event_id.as_bytes())?; Ok(()) @@ -237,13 +239,13 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Removes a pdu and creates a new one with the same id. fn replace_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, pdu_json: &CanonicalJsonObject, pdu: &PduEvent, ) -> Result<()> { - if self.pduid_pdu.get(pdu_id)?.is_some() { + if self.pduid_pdu.get(pdu_id.as_bytes())?.is_some() { self.pduid_pdu.insert( - pdu_id, + pdu_id.as_bytes(), &serde_json::to_vec(pdu_json) .expect("CanonicalJsonObject is always a valid"), )?; diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 89c8c7ca..c620b40f 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -4,6 +4,7 @@ use crate::{ database::KeyValueDatabase, service::{ self, + rooms::timeline::PduId, sending::{Destination, RequestKey, SendingEventType}, }, services, utils, Error, Result, @@ -61,7 +62,7 @@ impl service::sending::Data for KeyValueDatabase { for (destination, event) in requests { let mut key = destination.get_prefix(); if let SendingEventType::Pdu(value) = &event { - key.extend_from_slice(value); + key.extend_from_slice(value.as_bytes()); } else { key.extend_from_slice( &services().globals.next_count()?.to_be_bytes(), @@ -202,7 +203,7 @@ fn parse_servercurrentevent( Ok(( destination, if value.is_empty() { - SendingEventType::Pdu(event.to_vec()) + SendingEventType::Pdu(PduId::new(event.to_vec())) } else { SendingEventType::Edu(value) }, diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 62c6470b..6bf29e8e 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -37,7 +37,7 @@ use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; -use super::state_compressor::CompressedStateEvent; +use super::{state_compressor::CompressedStateEvent, timeline::PduId}; use crate::{ service::{globals::SigningKeys, pdu}, services, @@ -89,7 +89,7 @@ impl Service { value: CanonicalJsonObject, is_timeline_event: bool, pub_key_map: &'a RwLock>, - ) -> Result>> { + ) -> Result> { // 0. Check the server is in the room if !services().rooms.metadata.exists(room_id)? { return Err(Error::BadRequest( @@ -565,7 +565,7 @@ impl Service { origin: &ServerName, room_id: &RoomId, pub_key_map: &RwLock>, - ) -> Result>> { + ) -> Result> { // Skip the PDU if we already have it as a timeline event if let Ok(Some(pduid)) = services().rooms.timeline.get_pdu_id(&incoming_pdu.event_id) diff --git a/src/service/rooms/search/data.rs b/src/service/rooms/search/data.rs index efc503fe..515b7d04 100644 --- a/src/service/rooms/search/data.rs +++ b/src/service/rooms/search/data.rs @@ -1,19 +1,19 @@ use ruma::RoomId; -use crate::Result; +use crate::{service::rooms::timeline::PduId, Result}; pub(crate) trait Data: Send + Sync { fn index_pdu( &self, shortroomid: u64, - pdu_id: &[u8], + pdu_id: &PduId, message_body: &str, ) -> Result<()>; fn deindex_pdu( &self, shortroomid: u64, - pdu_id: &[u8], + pdu_id: &PduId, message_body: &str, ) -> Result<()>; @@ -22,5 +22,5 @@ pub(crate) trait Data: Send + Sync { &'a self, room_id: &RoomId, search_string: &str, - ) -> Result> + 'a>, Vec)>>; + ) -> Result + 'a>, Vec)>>; } diff --git a/src/service/rooms/threads/data.rs b/src/service/rooms/threads/data.rs index 8a1607db..384c23c8 100644 --- a/src/service/rooms/threads/data.rs +++ b/src/service/rooms/threads/data.rs @@ -3,7 +3,7 @@ use ruma::{ UserId, }; -use crate::{PduEvent, Result}; +use crate::{service::rooms::timeline::PduId, PduEvent, Result}; pub(crate) trait Data: Send + Sync { #[allow(clippy::type_complexity)] @@ -17,11 +17,11 @@ pub(crate) trait Data: Send + Sync { fn update_participants( &self, - root_id: &[u8], + root_id: &PduId, participants: &[OwnedUserId], ) -> Result<()>; fn get_participants( &self, - root_id: &[u8], + root_id: &PduId, ) -> Result>>; } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index bb268255..8df9c410 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -44,6 +44,23 @@ use crate::{ Error, PduEvent, Result, }; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct PduId { + inner: Vec, +} + +impl PduId { + pub(crate) fn new(inner: Vec) -> Self { + Self { + inner, + } + } + + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.inner + } +} + #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] pub(crate) enum PduCount { Backfilled(u64), @@ -146,7 +163,7 @@ impl Service { pub(crate) fn get_pdu_id( &self, event_id: &EventId, - ) -> Result>> { + ) -> Result> { self.db.get_pdu_id(event_id) } @@ -165,7 +182,7 @@ impl Service { /// This does __NOT__ check the outliers `Tree`. pub(crate) fn get_pdu_from_id( &self, - pdu_id: &[u8], + pdu_id: &PduId, ) -> Result> { self.db.get_pdu_from_id(pdu_id) } @@ -173,7 +190,7 @@ impl Service { /// Returns the pdu as a `BTreeMap`. pub(crate) fn get_pdu_json_from_id( &self, - pdu_id: &[u8], + pdu_id: &PduId, ) -> Result> { self.db.get_pdu_json_from_id(pdu_id) } @@ -182,7 +199,7 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) fn replace_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, pdu_json: &CanonicalJsonObject, pdu: &PduEvent, ) -> Result<()> { @@ -202,7 +219,7 @@ impl Service { mut pdu_json: CanonicalJsonObject, leaves: Vec, room_id: &KeyToken, - ) -> Result> { + ) -> Result { assert_eq!(*pdu.room_id, **room_id, "Token for incorrect room passed"); let shortroomid = services() @@ -282,6 +299,7 @@ impl Service { let count2 = services().globals.next_count()?; let mut pdu_id = shortroomid.to_be_bytes().to_vec(); pdu_id.extend_from_slice(&count2.to_be_bytes()); + let pdu_id = PduId::new(pdu_id); // Insert pdu self.db.append_pdu(&pdu_id, pdu, &pdu_json, count2)?; @@ -1106,7 +1124,7 @@ impl Service { state_ids_compressed: Arc>, soft_fail: bool, room_id: &KeyToken, - ) -> Result>> { + ) -> Result> { assert_eq!(*pdu.room_id, **room_id, "Token for incorrect room passed"); // We append to state before appending the pdu, so we don't have a @@ -1344,6 +1362,7 @@ impl Service { let mut pdu_id = shortroomid.to_be_bytes().to_vec(); pdu_id.extend_from_slice(&0_u64.to_be_bytes()); pdu_id.extend_from_slice(&(u64::MAX - count).to_be_bytes()); + let pdu_id = PduId::new(pdu_id); // Insert pdu self.db.prepend_backfill_pdu(&pdu_id, &event_id, &value)?; diff --git a/src/service/rooms/timeline/data.rs b/src/service/rooms/timeline/data.rs index 5ea5ae03..acd0b33c 100644 --- a/src/service/rooms/timeline/data.rs +++ b/src/service/rooms/timeline/data.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use ruma::{CanonicalJsonObject, EventId, OwnedUserId, RoomId, UserId}; use super::PduCount; -use crate::{PduEvent, Result}; +use crate::{service::rooms::timeline::PduId, PduEvent, Result}; pub(crate) trait Data: Send + Sync { fn last_timeline_count( @@ -28,7 +28,7 @@ pub(crate) trait Data: Send + Sync { ) -> Result>; /// Returns the pdu's id. - fn get_pdu_id(&self, event_id: &EventId) -> Result>>; + fn get_pdu_id(&self, event_id: &EventId) -> Result>; /// Returns the pdu. /// @@ -46,18 +46,18 @@ pub(crate) trait Data: Send + Sync { /// Returns the pdu. /// /// This does __NOT__ check the outliers `Tree`. - fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result>; + fn get_pdu_from_id(&self, pdu_id: &PduId) -> Result>; /// Returns the pdu as a `BTreeMap`. fn get_pdu_json_from_id( &self, - pdu_id: &[u8], + pdu_id: &PduId, ) -> Result>; /// Adds a new pdu to the timeline fn append_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, pdu: &PduEvent, json: &CanonicalJsonObject, count: u64, @@ -66,7 +66,7 @@ pub(crate) trait Data: Send + Sync { // Adds a new pdu to the backfilled timeline fn prepend_backfill_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, event_id: &EventId, json: &CanonicalJsonObject, ) -> Result<()>; @@ -74,7 +74,7 @@ pub(crate) trait Data: Send + Sync { /// Removes a pdu and creates a new one with the same id. fn replace_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, pdu_json: &CanonicalJsonObject, pdu: &PduEvent, ) -> Result<()>; diff --git a/src/service/sending.rs b/src/service/sending.rs index 832380c9..9aa1d684 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -37,6 +37,7 @@ use tokio::{ }; use tracing::{debug, error, warn, Span}; +use super::rooms::timeline::PduId; use crate::{ api::{appservice_server, server_server}, services, @@ -83,7 +84,7 @@ impl Destination { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum SendingEventType { // pduid - Pdu(Vec), + Pdu(PduId), // pdu json Edu(Vec), } @@ -565,7 +566,7 @@ impl Service { #[tracing::instrument(skip(self, pdu_id, user, pushkey))] pub(crate) fn send_push_pdu( &self, - pdu_id: &[u8], + pdu_id: &PduId, user: &UserId, pushkey: String, ) -> Result<()> { @@ -589,7 +590,7 @@ impl Service { pub(crate) fn send_pdu>( &self, servers: I, - pdu_id: &[u8], + pdu_id: &PduId, ) -> Result<()> { let requests = servers .into_iter() @@ -644,7 +645,7 @@ impl Service { pub(crate) fn send_pdu_appservice( &self, appservice_id: String, - pdu_id: Vec, + pdu_id: PduId, ) -> Result<()> { let destination = Destination::Appservice(appservice_id); let event_type = SendingEventType::Pdu(pdu_id); @@ -758,8 +759,8 @@ async fn handle_appservice_event( &events .iter() .map(|e| match e { - SendingEventType::Edu(b) - | SendingEventType::Pdu(b) => &**b, + SendingEventType::Edu(b) => &**b, + SendingEventType::Pdu(b) => b.as_bytes(), }) .collect::>(), )) @@ -905,8 +906,8 @@ async fn handle_federation_event( &events .iter() .map(|e| match e { - SendingEventType::Edu(b) - | SendingEventType::Pdu(b) => &**b, + SendingEventType::Edu(b) => &**b, + SendingEventType::Pdu(b) => b.as_bytes(), }) .collect::>(), )) diff --git a/src/utils.rs b/src/utils.rs index c1e2d6a6..42c15f88 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -113,14 +113,14 @@ pub(crate) fn calculate_hash(keys: &[&[u8]]) -> Vec { hash.as_ref().to_owned() } -pub(crate) fn common_elements( +pub(crate) fn common_elements( mut iterators: I, check_order: F, -) -> Option>> +) -> Option> where I: Iterator, - I::Item: Iterator>, - F: Fn(&[u8], &[u8]) -> Ordering, + I::Item: Iterator, + F: Fn(&T, &T) -> Ordering, { let first_iterator = iterators.next()?; let mut other_iterators = From cce83beedbeddf6c6edce2a47fd7fdd810dfe159 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 26 Aug 2024 17:10:43 +0000 Subject: [PATCH 360/617] Properly type stored EDUs --- src/api/client_server/to_device.rs | 3 ++- src/database/key_value/sending.rs | 14 +++++++++---- src/service/sending.rs | 32 ++++++++++++++++-------------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/api/client_server/to_device.rs b/src/api/client_server/to_device.rs index 65fb198c..663908b4 100644 --- a/src/api/client_server/to_device.rs +++ b/src/api/client_server/to_device.rs @@ -5,6 +5,7 @@ use ruma::{ client::{error::ErrorKind, to_device::send_event_to_device}, federation::{self, transactions::edu::DirectDeviceContent}, }, + serde::Raw, to_device::DeviceIdOrAllDevices, }; @@ -40,7 +41,7 @@ pub(crate) async fn send_event_to_device_route( services().sending.send_reliable_edu( target_user_id.server_name(), - serde_json::to_vec( + Raw::new( &federation::transactions::edu::Edu::DirectToDevice( DirectDeviceContent { sender: sender_user.clone(), diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index c620b40f..10d437dd 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -1,4 +1,4 @@ -use ruma::{ServerName, UserId}; +use ruma::{serde::Raw, ServerName, UserId}; use crate::{ database::KeyValueDatabase, @@ -69,7 +69,7 @@ impl service::sending::Data for KeyValueDatabase { ); } let value = if let SendingEventType::Edu(value) = &event { - &**value + value.json().get().as_bytes() } else { &[] }; @@ -100,7 +100,7 @@ impl service::sending::Data for KeyValueDatabase { ) -> Result<()> { for (e, key) in events { let value = if let SendingEventType::Edu(value) = &e { - &**value + value.json().get().as_bytes() } else { &[] }; @@ -205,7 +205,13 @@ fn parse_servercurrentevent( if value.is_empty() { SendingEventType::Pdu(PduId::new(event.to_vec())) } else { - SendingEventType::Edu(value) + SendingEventType::Edu( + Raw::from_json_string( + String::from_utf8(value) + .expect("EDU content in database should be a string"), + ) + .expect("EDU content in database should be valid JSON"), + ) }, )) } diff --git a/src/service/sending.rs b/src/service/sending.rs index 9aa1d684..bc4c33d6 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -28,8 +28,10 @@ use ruma::{ push_rules::PushRulesEvent, receipt::ReceiptType, AnySyncEphemeralRoomEvent, GlobalAccountDataEventType, }, - push, uint, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedUserId, - ServerName, UInt, UserId, + push, + serde::Raw, + uint, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedUserId, ServerName, + UInt, UserId, }; use tokio::{ select, @@ -81,12 +83,12 @@ impl Destination { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub(crate) enum SendingEventType { // pduid Pdu(PduId), // pdu json - Edu(Vec), + Edu(Raw), } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -444,7 +446,7 @@ impl Service { pub(crate) fn select_edus( &self, server_name: &ServerName, - ) -> Result<(Vec>, u64)> { + ) -> Result<(Vec>, u64)> { // u64: count of last edu let since = self.db.get_latest_educount(server_name)?; let mut events = Vec::new(); @@ -532,7 +534,7 @@ impl Service { }; events.push( - serde_json::to_vec(&federation_event) + Raw::new(&federation_event) .expect("json can be serialized"), ); @@ -555,9 +557,7 @@ impl Service { keys: None, }); - events.push( - serde_json::to_vec(&edu).expect("json can be serialized"), - ); + events.push(Raw::new(&edu).expect("json can be serialized")); } Ok((events, max_edu_count)) @@ -622,7 +622,7 @@ impl Service { pub(crate) fn send_reliable_edu( &self, server: &ServerName, - serialized: Vec, + serialized: Raw, id: u64, ) -> Result<()> { let destination = Destination::Normal(server.to_owned()); @@ -759,7 +759,9 @@ async fn handle_appservice_event( &events .iter() .map(|e| match e { - SendingEventType::Edu(b) => &**b, + SendingEventType::Edu(b) => { + b.json().get().as_bytes() + } SendingEventType::Pdu(b) => b.as_bytes(), }) .collect::>(), @@ -885,9 +887,7 @@ async fn handle_federation_event( )); } SendingEventType::Edu(edu) => { - if let Ok(raw) = serde_json::from_slice(edu) { - edu_jsons.push(raw); - } + edu_jsons.push(edu.clone()); } } } @@ -906,7 +906,9 @@ async fn handle_federation_event( &events .iter() .map(|e| match e { - SendingEventType::Edu(b) => &**b, + SendingEventType::Edu(b) => { + b.json().get().as_bytes() + } SendingEventType::Pdu(b) => b.as_bytes(), }) .collect::>(), From f1642c92d1f6e7166bc7ddb6254cb29c8cfc3f52 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 26 Aug 2024 19:16:51 +0000 Subject: [PATCH 361/617] Take iterator in calculate_hash() Avoids unnecessary allocations. --- src/api/server_server.rs | 2 +- src/service/rooms/state.rs | 5 ++--- src/service/rooms/state_compressor.rs | 5 +---- src/service/sending.rs | 30 +++++++-------------------- src/utils.rs | 15 +++++++++++--- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 74f7a1b0..8a8a77d8 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1860,7 +1860,7 @@ pub(crate) async fn create_invite_route( events: vec![pdu.to_room_event()], txn_id: base64::engine::general_purpose::URL_SAFE_NO_PAD - .encode(utils::calculate_hash(&[pdu + .encode(utils::calculate_hash([pdu .event_id() .as_bytes()])) .into(), diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 7c6a25e4..485d2b8c 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -133,9 +133,8 @@ impl Service { let previous_shortstatehash = self.db.get_room_shortstatehash(room_id)?; - let state_hash = calculate_hash( - &state_ids_compressed.iter().map(|s| &s[..]).collect::>(), - ); + let state_hash = + calculate_hash(state_ids_compressed.iter().map(|s| &s[..])); let (shortstatehash, already_existed) = services().rooms.short.get_or_create_shortstatehash(&state_hash)?; diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index f6a7d407..a545cf5e 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -283,10 +283,7 @@ impl Service { services().rooms.state.get_room_shortstatehash(room_id)?; let state_hash = utils::calculate_hash( - &new_state_ids_compressed - .iter() - .map(|bytes| &bytes[..]) - .collect::>(), + new_state_ids_compressed.iter().map(|bytes| &bytes[..]), ); let (new_shortstatehash, already_existed) = diff --git a/src/service/sending.rs b/src/service/sending.rs index bc4c33d6..c486701e 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -755,17 +755,10 @@ async fn handle_appservice_event( appservice::event::push_events::v1::Request { events: pdu_jsons, txn_id: general_purpose::URL_SAFE_NO_PAD - .encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) => { - b.json().get().as_bytes() - } - SendingEventType::Pdu(b) => b.as_bytes(), - }) - .collect::>(), - )) + .encode(calculate_hash(events.iter().map(|e| match e { + SendingEventType::Edu(b) => b.json().get().as_bytes(), + SendingEventType::Pdu(b) => b.as_bytes(), + }))) .into(), }, ) @@ -902,17 +895,10 @@ async fn handle_federation_event( edus: edu_jsons, origin_server_ts: MilliSecondsSinceUnixEpoch::now(), transaction_id: general_purpose::URL_SAFE_NO_PAD - .encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEventType::Edu(b) => { - b.json().get().as_bytes() - } - SendingEventType::Pdu(b) => b.as_bytes(), - }) - .collect::>(), - )) + .encode(calculate_hash(events.iter().map(|e| match e { + SendingEventType::Edu(b) => b.json().get().as_bytes(), + SendingEventType::Pdu(b) => b.as_bytes(), + }))) .into(), }, false, diff --git a/src/utils.rs b/src/utils.rs index 42c15f88..30be1bad 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -106,9 +106,18 @@ where } #[tracing::instrument(skip(keys))] -pub(crate) fn calculate_hash(keys: &[&[u8]]) -> Vec { - // We only hash the pdu's event ids, not the whole pdu - let bytes = keys.join(&0xFF); +pub(crate) fn calculate_hash<'a, I, T>(keys: I) -> Vec +where + I: IntoIterator, + T: AsRef<[u8]>, +{ + let mut bytes = Vec::new(); + for (i, key) in keys.into_iter().enumerate() { + if i != 0 { + bytes.push(0xFF); + } + bytes.extend_from_slice(key.as_ref()); + } let hash = digest::digest(&digest::SHA256, &bytes); hash.as_ref().to_owned() } From b0f33207fef2fc89e03861d98b2983eebfc59a89 Mon Sep 17 00:00:00 2001 From: Lambda Date: Tue, 27 Aug 2024 14:27:12 +0000 Subject: [PATCH 362/617] Add wrapper types for short IDs --- src/database.rs | 48 +++++--- src/database/key_value/globals.rs | 1 + src/database/key_value/rooms/auth_chain.rs | 23 ++-- src/database/key_value/rooms/metadata.rs | 2 +- src/database/key_value/rooms/pdu_metadata.rs | 9 +- src/database/key_value/rooms/search.rs | 14 ++- src/database/key_value/rooms/short.rs | 116 ++++++++++-------- src/database/key_value/rooms/state.rs | 39 +++--- .../key_value/rooms/state_accessor.rs | 39 +++--- .../key_value/rooms/state_compressor.rs | 37 ++++-- src/database/key_value/rooms/threads.rs | 1 + src/database/key_value/rooms/timeline.rs | 1 + src/database/key_value/rooms/user.rs | 28 +++-- src/service/rooms/auth_chain.rs | 15 +-- src/service/rooms/auth_chain/data.rs | 10 +- src/service/rooms/event_handler.rs | 7 +- src/service/rooms/pdu_metadata/data.rs | 7 +- src/service/rooms/search/data.rs | 9 +- src/service/rooms/short.rs | 23 ++++ src/service/rooms/short/data.rs | 27 ++-- src/service/rooms/state.rs | 29 +++-- src/service/rooms/state/data.rs | 18 ++- src/service/rooms/state_accessor.rs | 32 +++-- src/service/rooms/state_accessor/data.rs | 20 +-- src/service/rooms/state_compressor.rs | 79 ++++++++---- src/service/rooms/state_compressor/data.rs | 11 +- src/service/rooms/timeline.rs | 8 +- src/service/rooms/user/data.rs | 6 +- 28 files changed, 427 insertions(+), 232 deletions(-) diff --git a/src/database.rs b/src/database.rs index 445c8a6a..6156c7e0 100644 --- a/src/database.rs +++ b/src/database.rs @@ -27,7 +27,14 @@ use tracing::{debug, error, info, info_span, warn, Instrument}; use crate::{ config::DatabaseBackend, observability::FilterReloadHandles, - service::{media::MediaFileKey, rooms::timeline::PduCount}, + service::{ + media::MediaFileKey, + rooms::{ + short::{ShortEventId, ShortStateHash, ShortStateKey}, + state_compressor::CompressedStateEvent, + timeline::PduCount, + }, + }, services, utils, Config, Error, PduEvent, Result, Services, SERVICES, }; @@ -236,13 +243,14 @@ pub(crate) struct KeyValueDatabase { // Uncategorized trees pub(super) pdu_cache: Mutex>>, - pub(super) shorteventid_cache: Mutex>>, - pub(super) auth_chain_cache: Mutex, Arc>>>, - pub(super) eventidshort_cache: Mutex>, + pub(super) shorteventid_cache: Mutex>>, + pub(super) auth_chain_cache: + Mutex, Arc>>>, + pub(super) eventidshort_cache: Mutex>, pub(super) statekeyshort_cache: - Mutex>, + Mutex>, pub(super) shortstatekey_cache: - Mutex>, + Mutex>, pub(super) our_real_users_cache: RwLock>>>, pub(super) appservice_in_room_cache: @@ -695,15 +703,15 @@ impl KeyValueDatabase { if services().globals.database_version()? < 7 { // Upgrade state store - let mut last_roomstates: HashMap = + let mut last_roomstates: HashMap = HashMap::new(); - let mut current_sstatehash: Option = None; + let mut current_sstatehash: Option = None; let mut current_room = None; let mut current_state = HashSet::new(); let mut counter = 0; let mut handle_state = - |current_sstatehash: u64, + |current_sstatehash: ShortStateHash, current_room: &RoomId, current_state: HashSet<_>, last_roomstates: &mut HashMap<_, _>| { @@ -762,10 +770,14 @@ impl KeyValueDatabase { for (k, seventid) in db.db.open_tree("stateid_shorteventid")?.iter() { - let sstatehash = + let sstatehash = ShortStateHash::new( utils::u64_from_bytes(&k[0..size_of::()]) - .expect("number of bytes is correct"); - let sstatekey = k[size_of::()..].to_vec(); + .expect("number of bytes is correct"), + ); + let sstatekey = ShortStateKey::new( + utils::u64_from_bytes(&k[size_of::()..]) + .expect("number of bytes is correct"), + ); if Some(sstatehash) != current_sstatehash { if let Some(current_sstatehash) = current_sstatehash { handle_state( @@ -803,10 +815,14 @@ impl KeyValueDatabase { } } - let mut val = sstatekey; - val.extend_from_slice(&seventid); - current_state - .insert(val.try_into().expect("size is correct")); + let seventid = ShortEventId::new( + utils::u64_from_bytes(&seventid) + .expect("number of bytes is correct"), + ); + current_state.insert(CompressedStateEvent { + state: sstatekey, + event: seventid, + }); } if let Some(current_sstatehash) = current_sstatehash { diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index b2594226..819ef98c 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -74,6 +74,7 @@ impl service::globals::Data for KeyValueDatabase { .ok() .flatten() .expect("room exists") + .get() .to_be_bytes() .to_vec(); diff --git a/src/database/key_value/rooms/auth_chain.rs b/src/database/key_value/rooms/auth_chain.rs index 896a0f40..b29149f8 100644 --- a/src/database/key_value/rooms/auth_chain.rs +++ b/src/database/key_value/rooms/auth_chain.rs @@ -3,15 +3,16 @@ use std::{collections::HashSet, mem::size_of, sync::Arc}; use crate::{ database::KeyValueDatabase, observability::{FoundIn, Lookup, METRICS}, - service, utils, Result, + service::{self, rooms::short::ShortEventId}, + utils, Result, }; impl service::rooms::auth_chain::Data for KeyValueDatabase { #[tracing::instrument(skip(self, key))] fn get_cached_eventid_authchain( &self, - key: &[u64], - ) -> Result>>> { + key: &[ShortEventId], + ) -> Result>>> { let lookup = Lookup::AuthChain; // Check RAM cache @@ -26,13 +27,15 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { // Check DB cache let chain = self .shorteventid_authchain - .get(&key[0].to_be_bytes())? + .get(&key[0].get().to_be_bytes())? .map(|chain| { chain .chunks_exact(size_of::()) .map(|chunk| { - utils::u64_from_bytes(chunk) - .expect("byte length is correct") + ShortEventId::new( + utils::u64_from_bytes(chunk) + .expect("byte length is correct"), + ) }) .collect() }); @@ -57,16 +60,16 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { fn cache_auth_chain( &self, - key: Vec, - auth_chain: Arc>, + key: Vec, + auth_chain: Arc>, ) -> Result<()> { // Only persist single events in db if key.len() == 1 { self.shorteventid_authchain.insert( - &key[0].to_be_bytes(), + &key[0].get().to_be_bytes(), &auth_chain .iter() - .flat_map(|s| s.to_be_bytes().to_vec()) + .flat_map(|s| s.get().to_be_bytes().to_vec()) .collect::>(), )?; } diff --git a/src/database/key_value/rooms/metadata.rs b/src/database/key_value/rooms/metadata.rs index ab7a5cb8..dc8c7a19 100644 --- a/src/database/key_value/rooms/metadata.rs +++ b/src/database/key_value/rooms/metadata.rs @@ -8,7 +8,7 @@ impl service::rooms::metadata::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn exists(&self, room_id: &RoomId) -> Result { let prefix = match services().rooms.short.get_shortroomid(room_id)? { - Some(b) => b.to_be_bytes().to_vec(), + Some(b) => b.get().to_be_bytes().to_vec(), None => return Ok(false), }; diff --git a/src/database/key_value/rooms/pdu_metadata.rs b/src/database/key_value/rooms/pdu_metadata.rs index 32689bd4..5fc6a897 100644 --- a/src/database/key_value/rooms/pdu_metadata.rs +++ b/src/database/key_value/rooms/pdu_metadata.rs @@ -6,7 +6,10 @@ use crate::{ database::KeyValueDatabase, service::{ self, - rooms::timeline::{PduCount, PduId}, + rooms::{ + short::ShortRoomId, + timeline::{PduCount, PduId}, + }, }, services, utils, Error, PduEvent, Result, }; @@ -22,7 +25,7 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { fn relations_until<'a>( &'a self, user_id: &'a UserId, - shortroomid: u64, + shortroomid: ShortRoomId, target: u64, until: PduCount, ) -> Result> + 'a>> @@ -51,7 +54,7 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { Error::bad_database("Invalid count in tofrom_relation.") })?; - let mut pduid = shortroomid.to_be_bytes().to_vec(); + let mut pduid = shortroomid.get().to_be_bytes().to_vec(); pduid.extend_from_slice(&from.to_be_bytes()); let pduid = PduId::new(pduid); diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index fa48965d..510feca5 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -2,7 +2,10 @@ use ruma::RoomId; use crate::{ database::KeyValueDatabase, - service::{self, rooms::timeline::PduId}, + service::{ + self, + rooms::{short::ShortRoomId, timeline::PduId}, + }, services, utils, Result, }; @@ -21,12 +24,12 @@ impl service::rooms::search::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn index_pdu( &self, - shortroomid: u64, + shortroomid: ShortRoomId, pdu_id: &PduId, message_body: &str, ) -> Result<()> { let mut batch = tokenize(message_body).map(|word| { - let mut key = shortroomid.to_be_bytes().to_vec(); + let mut key = shortroomid.get().to_be_bytes().to_vec(); key.extend_from_slice(word.as_bytes()); key.push(0xFF); // TODO: currently we save the room id a second time here @@ -40,12 +43,12 @@ impl service::rooms::search::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn deindex_pdu( &self, - shortroomid: u64, + shortroomid: ShortRoomId, pdu_id: &PduId, message_body: &str, ) -> Result<()> { let batch = tokenize(message_body).map(|word| { - let mut key = shortroomid.to_be_bytes().to_vec(); + let mut key = shortroomid.get().to_be_bytes().to_vec(); key.extend_from_slice(word.as_bytes()); key.push(0xFF); // TODO: currently we save the room id a second time here @@ -73,6 +76,7 @@ impl service::rooms::search::Data for KeyValueDatabase { .short .get_shortroomid(room_id)? .expect("room exists") + .get() .to_be_bytes() .to_vec(); diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index c665c092..ca499ed2 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -5,12 +5,21 @@ use ruma::{events::StateEventType, EventId, RoomId}; use crate::{ database::KeyValueDatabase, observability::{FoundIn, Lookup, METRICS}, - service, services, utils, Error, Result, + service::{ + self, + rooms::short::{ + ShortEventId, ShortRoomId, ShortStateHash, ShortStateKey, + }, + }, + services, utils, Error, Result, }; impl service::rooms::short::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] - fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result { + fn get_or_create_shorteventid( + &self, + event_id: &EventId, + ) -> Result { let lookup = Lookup::CreateEventIdToShort; if let Some(short) = @@ -39,6 +48,8 @@ impl service::rooms::short::Data for KeyValueDatabase { shorteventid }; + let short = ShortEventId::new(short); + self.eventidshort_cache .lock() .unwrap() @@ -52,7 +63,7 @@ impl service::rooms::short::Data for KeyValueDatabase { &self, event_type: &StateEventType, state_key: &str, - ) -> Result> { + ) -> Result> { let lookup = Lookup::StateKeyToShort; if let Some(short) = self @@ -73,9 +84,11 @@ impl service::rooms::short::Data for KeyValueDatabase { .statekey_shortstatekey .get(&db_key)? .map(|shortstatekey| { - utils::u64_from_bytes(&shortstatekey).map_err(|_| { - Error::bad_database("Invalid shortstatekey in db.") - }) + utils::u64_from_bytes(&shortstatekey) + .map_err(|_| { + Error::bad_database("Invalid shortstatekey in db.") + }) + .map(ShortStateKey::new) }) .transpose()?; @@ -98,7 +111,7 @@ impl service::rooms::short::Data for KeyValueDatabase { &self, event_type: &StateEventType, state_key: &str, - ) -> Result { + ) -> Result { let lookup = Lookup::CreateStateKeyToShort; if let Some(short) = self @@ -134,6 +147,8 @@ impl service::rooms::short::Data for KeyValueDatabase { shortstatekey }; + let short = ShortStateKey::new(short); + self.statekeyshort_cache .lock() .unwrap() @@ -145,7 +160,7 @@ impl service::rooms::short::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn get_eventid_from_short( &self, - shorteventid: u64, + shorteventid: ShortEventId, ) -> Result> { let lookup = Lookup::ShortToEventId; @@ -158,7 +173,7 @@ impl service::rooms::short::Data for KeyValueDatabase { let bytes = self .shorteventid_eventid - .get(&shorteventid.to_be_bytes())? + .get(&shorteventid.get().to_be_bytes())? .ok_or_else(|| { Error::bad_database("Shorteventid does not exist") })?; @@ -187,7 +202,7 @@ impl service::rooms::short::Data for KeyValueDatabase { #[tracing::instrument(skip(self))] fn get_statekey_from_short( &self, - shortstatekey: u64, + shortstatekey: ShortStateKey, ) -> Result<(StateEventType, String)> { let lookup = Lookup::ShortToStateKey; @@ -200,7 +215,7 @@ impl service::rooms::short::Data for KeyValueDatabase { let bytes = self .shortstatekey_statekey - .get(&shortstatekey.to_be_bytes())? + .get(&shortstatekey.get().to_be_bytes())? .ok_or_else(|| { Error::bad_database("Shortstatekey does not exist") })?; @@ -244,51 +259,56 @@ impl service::rooms::short::Data for KeyValueDatabase { fn get_or_create_shortstatehash( &self, state_hash: &[u8], - ) -> Result<(u64, bool)> { - Ok( - if let Some(shortstatehash) = - self.statehash_shortstatehash.get(state_hash)? - { - ( - utils::u64_from_bytes(&shortstatehash).map_err(|_| { - Error::bad_database("Invalid shortstatehash in db.") - })?, - true, - ) - } else { - let shortstatehash = services().globals.next_count()?; - self.statehash_shortstatehash - .insert(state_hash, &shortstatehash.to_be_bytes())?; - (shortstatehash, false) - }, - ) + ) -> Result<(ShortStateHash, bool)> { + let (short, existed) = if let Some(shortstatehash) = + self.statehash_shortstatehash.get(state_hash)? + { + ( + utils::u64_from_bytes(&shortstatehash).map_err(|_| { + Error::bad_database("Invalid shortstatehash in db.") + })?, + true, + ) + } else { + let shortstatehash = services().globals.next_count()?; + self.statehash_shortstatehash + .insert(state_hash, &shortstatehash.to_be_bytes())?; + (shortstatehash, false) + }; + + Ok((ShortStateHash::new(short), existed)) } - fn get_shortroomid(&self, room_id: &RoomId) -> Result> { + fn get_shortroomid(&self, room_id: &RoomId) -> Result> { self.roomid_shortroomid .get(room_id.as_bytes())? .map(|bytes| { - utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database("Invalid shortroomid in db.") - }) + utils::u64_from_bytes(&bytes) + .map_err(|_| { + Error::bad_database("Invalid shortroomid in db.") + }) + .map(ShortRoomId::new) }) .transpose() } - fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { - Ok( - if let Some(short) = - self.roomid_shortroomid.get(room_id.as_bytes())? - { - utils::u64_from_bytes(&short).map_err(|_| { - Error::bad_database("Invalid shortroomid in db.") - })? - } else { - let short = services().globals.next_count()?; - self.roomid_shortroomid - .insert(room_id.as_bytes(), &short.to_be_bytes())?; - short - }, - ) + fn get_or_create_shortroomid( + &self, + room_id: &RoomId, + ) -> Result { + let short = if let Some(short) = + self.roomid_shortroomid.get(room_id.as_bytes())? + { + utils::u64_from_bytes(&short).map_err(|_| { + Error::bad_database("Invalid shortroomid in db.") + })? + } else { + let short = services().globals.next_count()?; + self.roomid_shortroomid + .insert(room_id.as_bytes(), &short.to_be_bytes())?; + short + }; + + Ok(ShortRoomId::new(short)) } } diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index a3246878..b7cbcb39 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -4,21 +4,30 @@ use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId}; use crate::{ database::KeyValueDatabase, - service::{self, globals::marker}, + service::{ + self, + globals::marker, + rooms::short::{ShortEventId, ShortStateHash}, + }, utils::{self, on_demand_hashmap::KeyToken}, Error, Result, }; impl service::rooms::state::Data for KeyValueDatabase { - fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result> { + fn get_room_shortstatehash( + &self, + room_id: &RoomId, + ) -> Result> { self.roomid_shortstatehash.get(room_id.as_bytes())?.map_or( Ok(None), |bytes| { - Ok(Some(utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database( - "Invalid shortstatehash in roomid_shortstatehash", - ) - })?)) + Ok(Some(ShortStateHash::new( + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database( + "Invalid shortstatehash in roomid_shortstatehash", + ) + })?, + ))) }, ) } @@ -26,21 +35,23 @@ impl service::rooms::state::Data for KeyValueDatabase { fn set_room_state( &self, room_id: &KeyToken, - new_shortstatehash: u64, + new_shortstatehash: ShortStateHash, ) -> Result<()> { - self.roomid_shortstatehash - .insert(room_id.as_bytes(), &new_shortstatehash.to_be_bytes())?; + self.roomid_shortstatehash.insert( + room_id.as_bytes(), + &new_shortstatehash.get().to_be_bytes(), + )?; Ok(()) } fn set_event_state( &self, - shorteventid: u64, - shortstatehash: u64, + shorteventid: ShortEventId, + shortstatehash: ShortStateHash, ) -> Result<()> { self.shorteventid_shortstatehash.insert( - &shorteventid.to_be_bytes(), - &shortstatehash.to_be_bytes(), + &shorteventid.get().to_be_bytes(), + &shortstatehash.get().to_be_bytes(), )?; Ok(()) } diff --git a/src/database/key_value/rooms/state_accessor.rs b/src/database/key_value/rooms/state_accessor.rs index 39b7d1d6..b2265ba2 100644 --- a/src/database/key_value/rooms/state_accessor.rs +++ b/src/database/key_value/rooms/state_accessor.rs @@ -4,16 +4,20 @@ use async_trait::async_trait; use ruma::{events::StateEventType, EventId, RoomId}; use crate::{ - database::KeyValueDatabase, service, services, utils, Error, PduEvent, - Result, + database::KeyValueDatabase, + service::{ + self, + rooms::short::{ShortStateHash, ShortStateKey}, + }, + services, utils, Error, PduEvent, Result, }; #[async_trait] impl service::rooms::state_accessor::Data for KeyValueDatabase { async fn state_full_ids( &self, - shortstatehash: u64, - ) -> Result>> { + shortstatehash: ShortStateHash, + ) -> Result>> { let full_state = services() .rooms .state_compressor @@ -40,7 +44,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { async fn state_full( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result>> { let full_state = services() .rooms @@ -87,7 +91,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { /// `state_key`). fn state_get_id( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, event_type: &StateEventType, state_key: &str, ) -> Result>> { @@ -105,7 +109,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { .full_state; Ok(full_state .iter() - .find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes())) + .find(|compressed| compressed.state == shortstatekey) .and_then(|compressed| { services() .rooms @@ -120,7 +124,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { /// `state_key`). fn state_get( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, event_type: &StateEventType, state_key: &str, ) -> Result>> { @@ -131,19 +135,24 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { } /// Returns the state hash for this pdu. - fn pdu_shortstatehash(&self, event_id: &EventId) -> Result> { + fn pdu_shortstatehash( + &self, + event_id: &EventId, + ) -> Result> { self.eventid_shorteventid.get(event_id.as_bytes())?.map_or( Ok(None), |shorteventid| { self.shorteventid_shortstatehash .get(&shorteventid)? .map(|bytes| { - utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database( - "Invalid shortstatehash bytes in \ - shorteventid_shortstatehash", - ) - }) + utils::u64_from_bytes(&bytes) + .map_err(|_| { + Error::bad_database( + "Invalid shortstatehash bytes in \ + shorteventid_shortstatehash", + ) + }) + .map(ShortStateHash::new) }) .transpose() }, diff --git a/src/database/key_value/rooms/state_compressor.rs b/src/database/key_value/rooms/state_compressor.rs index 7fb24698..4f487ce2 100644 --- a/src/database/key_value/rooms/state_compressor.rs +++ b/src/database/key_value/rooms/state_compressor.rs @@ -2,19 +2,28 @@ use std::{collections::HashSet, mem::size_of, sync::Arc}; use crate::{ database::KeyValueDatabase, - service::{self, rooms::state_compressor::data::StateDiff}, + service::{ + self, + rooms::{ + short::ShortStateHash, + state_compressor::{data::StateDiff, CompressedStateEvent}, + }, + }, utils, Error, Result, }; impl service::rooms::state_compressor::Data for KeyValueDatabase { - fn get_statediff(&self, shortstatehash: u64) -> Result { + fn get_statediff( + &self, + shortstatehash: ShortStateHash, + ) -> Result { let value = self .shortstatehash_statediff - .get(&shortstatehash.to_be_bytes())? + .get(&shortstatehash.get().to_be_bytes())? .ok_or_else(|| Error::bad_database("State hash does not exist"))?; let parent = utils::u64_from_bytes(&value[0..size_of::()]) .expect("bytes have right length"); - let parent = (parent != 0).then_some(parent); + let parent = (parent != 0).then_some(ShortStateHash::new(parent)); let mut add_mode = true; let mut added = HashSet::new(); @@ -28,10 +37,13 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase { continue; } if add_mode { - added.insert(v.try_into().expect("we checked the size above")); + added.insert(CompressedStateEvent::from_bytes( + v.try_into().expect("we checked the size above"), + )); } else { - removed - .insert(v.try_into().expect("we checked the size above")); + removed.insert(CompressedStateEvent::from_bytes( + v.try_into().expect("we checked the size above"), + )); } i += 2 * size_of::(); } @@ -45,22 +57,23 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase { fn save_statediff( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, diff: StateDiff, ) -> Result<()> { - let mut value = diff.parent.unwrap_or(0).to_be_bytes().to_vec(); + let mut value = + diff.parent.map_or(0, |h| h.get()).to_be_bytes().to_vec(); for new in diff.added.iter() { - value.extend_from_slice(&new[..]); + value.extend_from_slice(&new.as_bytes()); } if !diff.removed.is_empty() { value.extend_from_slice(&0_u64.to_be_bytes()); for removed in diff.removed.iter() { - value.extend_from_slice(&removed[..]); + value.extend_from_slice(&removed.as_bytes()); } } self.shortstatehash_statediff - .insert(&shortstatehash.to_be_bytes(), &value) + .insert(&shortstatehash.get().to_be_bytes(), &value) } } diff --git a/src/database/key_value/rooms/threads.rs b/src/database/key_value/rooms/threads.rs index bb9a1712..fe762fa0 100644 --- a/src/database/key_value/rooms/threads.rs +++ b/src/database/key_value/rooms/threads.rs @@ -24,6 +24,7 @@ impl service::rooms::threads::Data for KeyValueDatabase { .short .get_shortroomid(room_id)? .expect("room exists") + .get() .to_be_bytes() .to_vec(); diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 951975e5..656598db 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -383,6 +383,7 @@ fn count_to_id( .ok_or_else(|| { Error::bad_database("Looked for bad shortroomid in timeline") })? + .get() .to_be_bytes() .to_vec(); let mut pdu_id = prefix.clone(); diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index 53461d27..b474def3 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -1,7 +1,9 @@ use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; use crate::{ - database::KeyValueDatabase, service, services, utils, Error, Result, + database::KeyValueDatabase, + service::{self, rooms::short::ShortStateHash}, + services, utils, Error, Result, }; impl service::rooms::user::Data for KeyValueDatabase { @@ -95,7 +97,7 @@ impl service::rooms::user::Data for KeyValueDatabase { &self, room_id: &RoomId, token: u64, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result<()> { let shortroomid = services() .rooms @@ -103,36 +105,38 @@ impl service::rooms::user::Data for KeyValueDatabase { .get_shortroomid(room_id)? .expect("room exists"); - let mut key = shortroomid.to_be_bytes().to_vec(); + let mut key = shortroomid.get().to_be_bytes().to_vec(); key.extend_from_slice(&token.to_be_bytes()); self.roomsynctoken_shortstatehash - .insert(&key, &shortstatehash.to_be_bytes()) + .insert(&key, &shortstatehash.get().to_be_bytes()) } fn get_token_shortstatehash( &self, room_id: &RoomId, token: u64, - ) -> Result> { + ) -> Result> { let shortroomid = services() .rooms .short .get_shortroomid(room_id)? .expect("room exists"); - let mut key = shortroomid.to_be_bytes().to_vec(); + let mut key = shortroomid.get().to_be_bytes().to_vec(); key.extend_from_slice(&token.to_be_bytes()); self.roomsynctoken_shortstatehash .get(&key)? .map(|bytes| { - utils::u64_from_bytes(&bytes).map_err(|_| { - Error::bad_database( - "Invalid shortstatehash in \ - roomsynctoken_shortstatehash", - ) - }) + utils::u64_from_bytes(&bytes) + .map_err(|_| { + Error::bad_database( + "Invalid shortstatehash in \ + roomsynctoken_shortstatehash", + ) + }) + .map(ShortStateHash::new) }) .transpose() } diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index b45fab8e..5dcf9275 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -8,6 +8,7 @@ pub(crate) use data::Data; use ruma::{api::client::error::ErrorKind, EventId, RoomId}; use tracing::{debug, error, warn}; +use super::short::ShortEventId; use crate::{services, utils::debug_slice_truncated, Error, Result}; pub(crate) struct Service { @@ -17,16 +18,16 @@ pub(crate) struct Service { impl Service { pub(crate) fn get_cached_eventid_authchain( &self, - key: &[u64], - ) -> Result>>> { + key: &[ShortEventId], + ) -> Result>>> { self.db.get_cached_eventid_authchain(key) } #[tracing::instrument(skip(self))] pub(crate) fn cache_auth_chain( &self, - key: Vec, - auth_chain: Arc>, + key: Vec, + auth_chain: Arc>, ) -> Result<()> { self.db.cache_auth_chain(key, auth_chain) } @@ -51,7 +52,7 @@ impl Service { // I'm afraid to change this in case there is accidental reliance on // the truncation #[allow(clippy::as_conversions, clippy::cast_possible_truncation)] - let bucket_id = (short % NUM_BUCKETS as u64) as usize; + let bucket_id = (short.get() % NUM_BUCKETS as u64) as usize; buckets[bucket_id].insert((short, id.clone())); i += 1; if i % 100 == 0 { @@ -68,7 +69,7 @@ impl Service { continue; } - let chunk_key: Vec = + let chunk_key: Vec<_> = chunk.iter().map(|(short, _)| short).copied().collect(); if let Some(cached) = self.get_cached_eventid_authchain(&chunk_key)? @@ -139,7 +140,7 @@ impl Service { &self, room_id: &RoomId, event_id: &EventId, - ) -> Result> { + ) -> Result> { let mut todo = vec![Arc::from(event_id)]; let mut found = HashSet::new(); diff --git a/src/service/rooms/auth_chain/data.rs b/src/service/rooms/auth_chain/data.rs index 5ecaee34..5d01b1e2 100644 --- a/src/service/rooms/auth_chain/data.rs +++ b/src/service/rooms/auth_chain/data.rs @@ -1,15 +1,15 @@ use std::{collections::HashSet, sync::Arc}; -use crate::Result; +use crate::{service::rooms::short::ShortEventId, Result}; pub(crate) trait Data: Send + Sync { fn get_cached_eventid_authchain( &self, - shorteventid: &[u64], - ) -> Result>>>; + shorteventid: &[ShortEventId], + ) -> Result>>>; fn cache_auth_chain( &self, - shorteventid: Vec, - auth_chain: Arc>, + shorteventid: Vec, + auth_chain: Arc>, ) -> Result<()>; } diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 6bf29e8e..cd9ca375 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -37,7 +37,10 @@ use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; -use super::{state_compressor::CompressedStateEvent, timeline::PduId}; +use super::{ + short::ShortStateKey, state_compressor::CompressedStateEvent, + timeline::PduId, +}; use crate::{ service::{globals::SigningKeys, pdu}, services, @@ -1120,7 +1123,7 @@ impl Service { &self, room_id: &RoomId, room_version_id: &RoomVersionId, - incoming_state: HashMap>, + incoming_state: HashMap>, ) -> Result>> { debug!("Loading current room state ids"); let current_sstatehash = services() diff --git a/src/service/rooms/pdu_metadata/data.rs b/src/service/rooms/pdu_metadata/data.rs index 9aace01f..baab44a4 100644 --- a/src/service/rooms/pdu_metadata/data.rs +++ b/src/service/rooms/pdu_metadata/data.rs @@ -2,7 +2,10 @@ use std::sync::Arc; use ruma::{EventId, RoomId, UserId}; -use crate::{service::rooms::timeline::PduCount, PduEvent, Result}; +use crate::{ + service::rooms::{short::ShortRoomId, timeline::PduCount}, + PduEvent, Result, +}; pub(crate) trait Data: Send + Sync { fn add_relation(&self, from: u64, to: u64) -> Result<()>; @@ -10,7 +13,7 @@ pub(crate) trait Data: Send + Sync { fn relations_until<'a>( &'a self, user_id: &'a UserId, - room_id: u64, + room_id: ShortRoomId, target: u64, until: PduCount, ) -> Result> + 'a>>; diff --git a/src/service/rooms/search/data.rs b/src/service/rooms/search/data.rs index 515b7d04..2372a9aa 100644 --- a/src/service/rooms/search/data.rs +++ b/src/service/rooms/search/data.rs @@ -1,18 +1,21 @@ use ruma::RoomId; -use crate::{service::rooms::timeline::PduId, Result}; +use crate::{ + service::rooms::{short::ShortRoomId, timeline::PduId}, + Result, +}; pub(crate) trait Data: Send + Sync { fn index_pdu( &self, - shortroomid: u64, + shortroomid: ShortRoomId, pdu_id: &PduId, message_body: &str, ) -> Result<()>; fn deindex_pdu( &self, - shortroomid: u64, + shortroomid: ShortRoomId, pdu_id: &PduId, message_body: &str, ) -> Result<()>; diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 411199f4..97a7edc8 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,4 +1,27 @@ mod data; +macro_rules! short_id_type { + ($name:ident) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(transparent)] + pub(crate) struct $name(u64); + + impl $name { + pub(crate) fn new(id: u64) -> Self { + Self(id) + } + + pub(crate) fn get(&self) -> u64 { + self.0 + } + } + }; +} + +short_id_type!(ShortRoomId); +short_id_type!(ShortEventId); +short_id_type!(ShortStateHash); +short_id_type!(ShortStateKey); + pub(crate) use data::Data; pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index dcde51c3..3650a0b1 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -2,38 +2,47 @@ use std::sync::Arc; use ruma::{events::StateEventType, EventId, RoomId}; +use super::{ShortEventId, ShortRoomId, ShortStateHash, ShortStateKey}; use crate::Result; pub(crate) trait Data: Send + Sync { - fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result; + fn get_or_create_shorteventid( + &self, + event_id: &EventId, + ) -> Result; fn get_shortstatekey( &self, event_type: &StateEventType, state_key: &str, - ) -> Result>; + ) -> Result>; fn get_or_create_shortstatekey( &self, event_type: &StateEventType, state_key: &str, - ) -> Result; + ) -> Result; - fn get_eventid_from_short(&self, shorteventid: u64) - -> Result>; + fn get_eventid_from_short( + &self, + shorteventid: ShortEventId, + ) -> Result>; fn get_statekey_from_short( &self, - shortstatekey: u64, + shortstatekey: ShortStateKey, ) -> Result<(StateEventType, String)>; /// Returns `(shortstatehash, already_existed)` fn get_or_create_shortstatehash( &self, state_hash: &[u8], - ) -> Result<(u64, bool)>; + ) -> Result<(ShortStateHash, bool)>; - fn get_shortroomid(&self, room_id: &RoomId) -> Result>; + fn get_shortroomid(&self, room_id: &RoomId) -> Result>; - fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result; + fn get_or_create_shortroomid( + &self, + room_id: &RoomId, + ) -> Result; } diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 485d2b8c..2ff970bb 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -18,7 +18,7 @@ use ruma::{ use serde::Deserialize; use tracing::warn; -use super::state_compressor::CompressedStateEvent; +use super::{short::ShortStateHash, state_compressor::CompressedStateEvent}; use crate::{ service::globals::marker, services, @@ -38,7 +38,7 @@ impl Service { pub(crate) async fn force_state( &self, room_id: &KeyToken, - shortstatehash: u64, + shortstatehash: ShortStateHash, statediffnew: Arc>, _statediffremoved: Arc>, ) -> Result<()> { @@ -126,15 +126,16 @@ impl Service { event_id: &EventId, room_id: &RoomId, state_ids_compressed: Arc>, - ) -> Result { + ) -> Result { let shorteventid = services().rooms.short.get_or_create_shorteventid(event_id)?; let previous_shortstatehash = self.db.get_room_shortstatehash(room_id)?; - let state_hash = - calculate_hash(state_ids_compressed.iter().map(|s| &s[..])); + let state_hash = calculate_hash( + state_ids_compressed.iter().map(CompressedStateEvent::as_bytes), + ); let (shortstatehash, already_existed) = services().rooms.short.get_or_create_shortstatehash(&state_hash)?; @@ -187,7 +188,10 @@ impl Service { /// This adds all current state events (not including the incoming event) /// to `stateid_pduid` and adds the incoming event to `eventid_statehash`. #[tracing::instrument(skip(self, new_pdu))] - pub(crate) fn append_to_state(&self, new_pdu: &PduEvent) -> Result { + pub(crate) fn append_to_state( + &self, + new_pdu: &PduEvent, + ) -> Result { let shorteventid = services() .rooms .short @@ -225,9 +229,9 @@ impl Service { let replaces = states_parents .last() .map(|info| { - info.full_state.iter().find(|bytes| { - bytes.starts_with(&shortstatekey.to_be_bytes()) - }) + info.full_state + .iter() + .find(|compressed| compressed.state == shortstatekey) }) .unwrap_or_default(); @@ -236,7 +240,8 @@ impl Service { } // TODO: statehash with deterministic inputs - let shortstatehash = services().globals.next_count()?; + let shortstatehash = + ShortStateHash::new(services().globals.next_count()?); let mut statediffnew = HashSet::new(); statediffnew.insert(new); @@ -320,7 +325,7 @@ impl Service { pub(crate) fn set_room_state( &self, room_id: &KeyToken, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result<()> { self.db.set_room_state(room_id, shortstatehash) } @@ -362,7 +367,7 @@ impl Service { pub(crate) fn get_room_shortstatehash( &self, room_id: &RoomId, - ) -> Result> { + ) -> Result> { self.db.get_room_shortstatehash(room_id) } diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index 6f15b004..452c3b1b 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -3,25 +3,33 @@ use std::{collections::HashSet, sync::Arc}; use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId}; use crate::{ - service::globals::marker, utils::on_demand_hashmap::KeyToken, Result, + service::{ + globals::marker, + rooms::short::{ShortEventId, ShortStateHash}, + }, + utils::on_demand_hashmap::KeyToken, + Result, }; pub(crate) trait Data: Send + Sync { /// Returns the last state hash key added to the db for the given room. - fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result>; + fn get_room_shortstatehash( + &self, + room_id: &RoomId, + ) -> Result>; /// Set the state hash to a new version, but does not update `state_cache`. fn set_room_state( &self, room_id: &KeyToken, - new_shortstatehash: u64, + new_shortstatehash: ShortStateHash, ) -> Result<()>; /// Associates a state with an event. fn set_event_state( &self, - shorteventid: u64, - shortstatehash: u64, + shorteventid: ShortEventId, + shortstatehash: ShortStateHash, ) -> Result<()>; /// Returns all events we would send as the `prev_events` of the next event. diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index b9e9f702..f27a69ee 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -26,6 +26,7 @@ use ruma::{ use serde_json::value::to_raw_value; use tracing::{error, warn}; +use super::short::{ShortStateHash, ShortStateKey}; use crate::{ observability::{FoundIn, Lookup, METRICS}, service::{globals::marker, pdu::PduBuilder}, @@ -37,8 +38,9 @@ use crate::{ pub(crate) struct Service { pub(crate) db: &'static dyn Data, pub(crate) server_visibility_cache: - Mutex>, - pub(crate) user_visibility_cache: Mutex>, + Mutex>, + pub(crate) user_visibility_cache: + Mutex>, } impl Service { @@ -47,15 +49,15 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) async fn state_full_ids( &self, - shortstatehash: u64, - ) -> Result>> { + shortstatehash: ShortStateHash, + ) -> Result>> { self.db.state_full_ids(shortstatehash).await } #[tracing::instrument(skip(self))] pub(crate) async fn state_full( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result>> { self.db.state_full(shortstatehash).await } @@ -65,7 +67,7 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) fn state_get_id( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, event_type: &StateEventType, state_key: &str, ) -> Result>> { @@ -77,7 +79,7 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) fn state_get( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, event_type: &StateEventType, state_key: &str, ) -> Result>> { @@ -88,7 +90,7 @@ impl Service { #[tracing::instrument(skip(self))] fn user_membership( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, user_id: &UserId, ) -> Result { self.state_get( @@ -109,7 +111,11 @@ impl Service { /// The user was a joined member at this state (potentially in the past) #[tracing::instrument(skip(self), ret(level = "trace"))] - fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool { + fn user_was_joined( + &self, + shortstatehash: ShortStateHash, + user_id: &UserId, + ) -> bool { self.user_membership(shortstatehash, user_id) .is_ok_and(|s| s == MembershipState::Join) } @@ -117,7 +123,11 @@ impl Service { /// The user was an invited or joined room member at this state (potentially /// in the past) #[tracing::instrument(skip(self), ret(level = "trace"))] - fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool { + fn user_was_invited( + &self, + shortstatehash: ShortStateHash, + user_id: &UserId, + ) -> bool { self.user_membership(shortstatehash, user_id).is_ok_and(|s| { s == MembershipState::Join || s == MembershipState::Invite }) @@ -315,7 +325,7 @@ impl Service { pub(crate) fn pdu_shortstatehash( &self, event_id: &EventId, - ) -> Result> { + ) -> Result> { self.db.pdu_shortstatehash(event_id) } diff --git a/src/service/rooms/state_accessor/data.rs b/src/service/rooms/state_accessor/data.rs index 68214f1d..4fd1b2af 100644 --- a/src/service/rooms/state_accessor/data.rs +++ b/src/service/rooms/state_accessor/data.rs @@ -3,7 +3,10 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use ruma::{events::StateEventType, EventId, RoomId}; -use crate::{PduEvent, Result}; +use crate::{ + service::rooms::short::{ShortStateHash, ShortStateKey}, + PduEvent, Result, +}; #[async_trait] pub(crate) trait Data: Send + Sync { @@ -11,19 +14,19 @@ pub(crate) trait Data: Send + Sync { /// with state_hash, this gives the full state for the given state_hash. async fn state_full_ids( &self, - shortstatehash: u64, - ) -> Result>>; + shortstatehash: ShortStateHash, + ) -> Result>>; async fn state_full( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result>>; /// Returns a single PDU from `room_id` with key (`event_type`, /// `state_key`). fn state_get_id( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, event_type: &StateEventType, state_key: &str, ) -> Result>>; @@ -32,13 +35,16 @@ pub(crate) trait Data: Send + Sync { /// `state_key`). fn state_get( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, event_type: &StateEventType, state_key: &str, ) -> Result>>; /// Returns the state hash for this pdu. - fn pdu_shortstatehash(&self, event_id: &EventId) -> Result>; + fn pdu_shortstatehash( + &self, + event_id: &EventId, + ) -> Result>; /// Returns the full room state. async fn room_state_full( diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index a545cf5e..42dc8878 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -1,4 +1,5 @@ use std::{ + array, collections::HashSet, mem::size_of, sync::{Arc, Mutex}, @@ -17,9 +18,11 @@ pub(crate) mod data; pub(crate) use data::Data; use data::StateDiff; +use super::short::{ShortEventId, ShortStateHash, ShortStateKey}; + #[derive(Clone)] pub(crate) struct CompressedStateLayer { - pub(crate) shortstatehash: u64, + pub(crate) shortstatehash: ShortStateHash, pub(crate) full_state: Arc>, pub(crate) added: Arc>, pub(crate) removed: Arc>, @@ -29,10 +32,45 @@ pub(crate) struct Service { pub(crate) db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub(crate) stateinfo_cache: Mutex>>, + pub(crate) stateinfo_cache: + Mutex>>, } -pub(crate) type CompressedStateEvent = [u8; 2 * size_of::()]; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct CompressedStateEvent { + pub(crate) state: ShortStateKey, + pub(crate) event: ShortEventId, +} + +impl CompressedStateEvent { + pub(crate) fn as_bytes( + &self, + ) -> [u8; size_of::() + size_of::()] { + let mut bytes = self + .state + .get() + .to_be_bytes() + .into_iter() + .chain(self.event.get().to_be_bytes()); + array::from_fn(|_| bytes.next().unwrap()) + } + + pub(crate) fn from_bytes( + bytes: [u8; size_of::() + size_of::()], + ) -> Self { + let state = ShortStateKey::new(u64::from_be_bytes( + bytes[0..8].try_into().unwrap(), + )); + let event = ShortEventId::new(u64::from_be_bytes( + bytes[8..16].try_into().unwrap(), + )); + + Self { + state, + event, + } + } +} impl Service { /// Returns a stack with info on shortstatehash, full state, added diff and @@ -41,7 +79,7 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) fn load_shortstatehash_info( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result> { let lookup = Lookup::StateInfo; @@ -96,18 +134,16 @@ impl Service { #[allow(clippy::unused_self)] pub(crate) fn compress_state_event( &self, - shortstatekey: u64, + shortstatekey: ShortStateKey, event_id: &EventId, ) -> Result { - let mut v = shortstatekey.to_be_bytes().to_vec(); - v.extend_from_slice( - &services() + Ok(CompressedStateEvent { + state: shortstatekey, + event: services() .rooms .short - .get_or_create_shorteventid(event_id)? - .to_be_bytes(), - ); - Ok(v.try_into().expect("we checked the size above")) + .get_or_create_shorteventid(event_id)?, + }) } /// Returns shortstatekey, event id @@ -116,14 +152,13 @@ impl Service { pub(crate) fn parse_compressed_state_event( &self, compressed_event: &CompressedStateEvent, - ) -> Result<(u64, Arc)> { + ) -> Result<(ShortStateKey, Arc)> { Ok(( - utils::u64_from_bytes(&compressed_event[0..size_of::()]) - .expect("bytes have right length"), - services().rooms.short.get_eventid_from_short( - utils::u64_from_bytes(&compressed_event[size_of::()..]) - .expect("bytes have right length"), - )?, + compressed_event.state, + services() + .rooms + .short + .get_eventid_from_short(compressed_event.event)?, )) } @@ -155,7 +190,7 @@ impl Service { ))] pub(crate) fn save_state_from_diff( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, statediffnew: Arc>, statediffremoved: Arc>, diff_to_sibling: usize, @@ -275,7 +310,7 @@ impl Service { room_id: &RoomId, new_state_ids_compressed: Arc>, ) -> Result<( - u64, + ShortStateHash, Arc>, Arc>, )> { @@ -283,7 +318,7 @@ impl Service { services().rooms.state.get_room_shortstatehash(room_id)?; let state_hash = utils::calculate_hash( - new_state_ids_compressed.iter().map(|bytes| &bytes[..]), + new_state_ids_compressed.iter().map(CompressedStateEvent::as_bytes), ); let (new_shortstatehash, already_existed) = diff --git a/src/service/rooms/state_compressor/data.rs b/src/service/rooms/state_compressor/data.rs index 3d9ffc19..aafaf009 100644 --- a/src/service/rooms/state_compressor/data.rs +++ b/src/service/rooms/state_compressor/data.rs @@ -1,19 +1,22 @@ use std::{collections::HashSet, sync::Arc}; use super::CompressedStateEvent; -use crate::Result; +use crate::{service::rooms::short::ShortStateHash, Result}; pub(crate) struct StateDiff { - pub(crate) parent: Option, + pub(crate) parent: Option, pub(crate) added: Arc>, pub(crate) removed: Arc>, } pub(crate) trait Data: Send + Sync { - fn get_statediff(&self, shortstatehash: u64) -> Result; + fn get_statediff( + &self, + shortstatehash: ShortStateHash, + ) -> Result; fn save_statediff( &self, - shortstatehash: u64, + shortstatehash: ShortStateHash, diff: StateDiff, ) -> Result<()>; } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 8df9c410..257b2fac 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -31,7 +31,7 @@ use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; use tracing::{error, info, warn}; -use super::state_compressor::CompressedStateEvent; +use super::{short::ShortRoomId, state_compressor::CompressedStateEvent}; use crate::{ api::server_server, service::{ @@ -297,7 +297,7 @@ impl Service { .reset_notification_counts(&pdu.sender, &pdu.room_id)?; let count2 = services().globals.next_count()?; - let mut pdu_id = shortroomid.to_be_bytes().to_vec(); + let mut pdu_id = shortroomid.get().to_be_bytes().to_vec(); pdu_id.extend_from_slice(&count2.to_be_bytes()); let pdu_id = PduId::new(pdu_id); @@ -1194,7 +1194,7 @@ impl Service { &self, event_id: &EventId, reason: &PduEvent, - shortroomid: u64, + shortroomid: ShortRoomId, ) -> Result<()> { // TODO: Don't reserialize, keep original json if let Some(pdu_id) = self.get_pdu_id(event_id)? { @@ -1359,7 +1359,7 @@ impl Service { .await; let count = services().globals.next_count()?; - let mut pdu_id = shortroomid.to_be_bytes().to_vec(); + let mut pdu_id = shortroomid.get().to_be_bytes().to_vec(); pdu_id.extend_from_slice(&0_u64.to_be_bytes()); pdu_id.extend_from_slice(&(u64::MAX - count).to_be_bytes()); let pdu_id = PduId::new(pdu_id); diff --git a/src/service/rooms/user/data.rs b/src/service/rooms/user/data.rs index bfcb8f70..d948e3e0 100644 --- a/src/service/rooms/user/data.rs +++ b/src/service/rooms/user/data.rs @@ -1,6 +1,6 @@ use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId}; -use crate::Result; +use crate::{service::rooms::short::ShortStateHash, Result}; pub(crate) trait Data: Send + Sync { fn reset_notification_counts( @@ -32,14 +32,14 @@ pub(crate) trait Data: Send + Sync { &self, room_id: &RoomId, token: u64, - shortstatehash: u64, + shortstatehash: ShortStateHash, ) -> Result<()>; fn get_token_shortstatehash( &self, room_id: &RoomId, token: u64, - ) -> Result>; + ) -> Result>; fn get_shared_rooms<'a>( &'a self, From 5c4062742f0b118712cfb4cc7b74d0193aaffaa4 Mon Sep 17 00:00:00 2001 From: Lambda Date: Tue, 3 Sep 2024 20:35:09 +0000 Subject: [PATCH 363/617] Log curl command line for all requests at trace --- src/api/server_server.rs | 10 ++++++- src/main.rs | 52 ++++++++++++++++++++++------------ src/utils.rs | 60 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 19 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 8a8a77d8..0e58a087 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -64,7 +64,7 @@ use ruma::{ }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; -use tracing::{debug, error, field, warn}; +use tracing::{debug, error, field, trace, trace_span, warn}; use super::appservice_server; use crate::{ @@ -253,6 +253,14 @@ where signature, ))); + // can be enabled selectively using `filter = + // grapevine[outgoing_request_curl]=trace` in config + trace_span!("outgoing_request_curl").in_scope(|| { + trace!( + cmd = utils::curlify(&http_request), + "curl command line for outgoing request" + ); + }); let reqwest_request = reqwest::Request::try_from(http_request)?; let url = reqwest_request.url().clone(); diff --git a/src/main.rs b/src/main.rs index da1010c7..8af1825c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -147,26 +147,42 @@ async fn run_server() -> Result<(), error::Serve> { let middlewares = ServiceBuilder::new() .sensitive_headers([header::AUTHORIZATION]) .layer(axum::middleware::from_fn(spawn_task)) - .layer(TraceLayer::new_for_http().make_span_with( - |request: &http::Request<_>| { - let endpoint = if let Some(endpoint) = - request.extensions().get::() - { - endpoint.as_str() - } else { - request.uri().path() - }; + .layer( + TraceLayer::new_for_http() + .make_span_with(|request: &http::Request<_>| { + let endpoint = if let Some(endpoint) = + request.extensions().get::() + { + endpoint.as_str() + } else { + request.uri().path() + }; - let method = request.method(); + let method = request.method(); - tracing::info_span!( - "http_request", - otel.name = format!("{method} {endpoint}"), - %method, - %endpoint, - ) - }, - )) + tracing::info_span!( + "http_request", + otel.name = format!("{method} {endpoint}"), + %method, + %endpoint, + ) + }) + .on_request( + |request: &http::Request<_>, _span: &tracing::Span| { + // can be enabled selectively using `filter = + // grapevine[incoming_request_curl]=trace` in config + tracing::trace_span!("incoming_request_curl").in_scope( + || { + tracing::trace!( + cmd = utils::curlify(request), + "curl command line for incoming request \ + (guessed hostname)" + ); + }, + ); + }, + ), + ) .layer(axum::middleware::from_fn(unrecognized_method)) .layer( CorsLayer::new() diff --git a/src/utils.rs b/src/utils.rs index 30be1bad..504d21b1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -306,6 +306,66 @@ impl<'a> TryFrom<&'a MxcUri> for MxcData<'a> { } } +fn curlify_args(req: &http::Request) -> Option> { + let mut args = + vec!["curl".to_owned(), "-X".to_owned(), req.method().to_string()]; + + for (name, val) in req.headers() { + args.extend([ + "-H".to_owned(), + format!("{name}: {}", val.to_str().ok()?), + ]); + } + + let fix_uri = || { + if req.uri().scheme().is_some() { + return None; + } + if req.uri().authority().is_some() { + return None; + } + let mut parts = req.uri().clone().into_parts(); + + parts.scheme = Some(http::uri::Scheme::HTTPS); + + let host = + req.headers().get(http::header::HOST)?.to_str().ok()?.to_owned(); + parts.authority = + Some(http::uri::Authority::from_maybe_shared(host).ok()?); + + http::uri::Uri::from_parts(parts).ok() + }; + + let uri = if let Some(new_uri) = fix_uri() { + Cow::Owned(new_uri) + } else { + Cow::Borrowed(req.uri()) + }; + + args.push(uri.to_string()); + + Some(args) +} + +pub(crate) fn curlify(req: &http::Request) -> Option { + let args = curlify_args(req)?; + + Some( + args.into_iter() + .map(|arg| { + if arg.chars().all(|c| { + c.is_alphanumeric() || ['-', '_', ':', '/'].contains(&c) + }) { + arg + } else { + format!("'{}'", arg.replace('\'', "\\'")) + } + }) + .collect::>() + .join(" "), + ) +} + #[cfg(test)] mod tests { use crate::utils::dbg_truncate_str; From 74589043f72add79a048fa8ed96230817cdcde5b Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 5 Sep 2024 18:50:44 +0000 Subject: [PATCH 364/617] Fix weird type gymnastics --- src/service/media.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 6185ad6e..a175f035 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -171,8 +171,8 @@ impl Service { / u64::from(original_height) }; if use_width { - if intermediate <= u64::from(::std::u32::MAX) { - (width, intermediate.try_into().unwrap_or(u32::MAX)) + if let Ok(intermediate) = u32::try_from(intermediate) { + (width, intermediate) } else { ( (u64::from(width) * u64::from(::std::u32::MAX) @@ -182,8 +182,8 @@ impl Service { ::std::u32::MAX, ) } - } else if intermediate <= u64::from(::std::u32::MAX) { - (intermediate.try_into().unwrap_or(u32::MAX), height) + } else if let Ok(intermediate) = u32::try_from(intermediate) { + (intermediate, height) } else { ( ::std::u32::MAX, From 3a556846237f9c86fd225ae3f6038c342ae6c9de Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 5 Sep 2024 18:35:24 +0000 Subject: [PATCH 365/617] Update MSRV to 1.81.0 Plus a "__CARGO_FIX_YOLO=1 cargo clippy --fix" --- Cargo.toml | 5 ++--- flake.lock | 6 +++--- flake.nix | 2 +- rust-toolchain.toml | 2 +- src/api/client_server/account.rs | 3 +-- src/api/client_server/context.rs | 3 +-- src/api/client_server/message.rs | 3 +-- src/api/client_server/sync.rs | 9 +++------ src/api/client_server/user_directory.rs | 3 +-- src/api/server_server.rs | 8 +++----- .../key_value/rooms/edus/read_receipt.rs | 6 ++---- src/database/key_value/rooms/pdu_metadata.rs | 15 ++++++++------- src/database/key_value/rooms/threads.rs | 17 +++++++---------- src/service/media.rs | 8 ++++---- 14 files changed, 38 insertions(+), 52 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d7682651..8d38d852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ explicit_outlives_requirements = "warn" macro_use_extern_crate = "warn" missing_abi = "warn" noop_method_call = "warn" -pointer_structural_match = "warn" single_use_lifetimes = "warn" unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" @@ -17,7 +16,7 @@ unused_qualifications = "warn" [workspace.lints.clippy] # Groups. Keep alphabetically sorted -pedantic = "warn" +pedantic = { level = "warn", priority = -1 } # Lints. Keep alphabetically sorted as_conversions = "warn" @@ -80,7 +79,7 @@ version = "0.1.0" edition = "2021" # See also `rust-toolchain.toml` -rust-version = "1.78.0" +rust-version = "1.81.0" [lints] workspace = true diff --git a/flake.lock b/flake.lock index 3a58f74d..c63aebc0 100644 --- a/flake.lock +++ b/flake.lock @@ -250,13 +250,13 @@ "rust-manifest": { "flake": false, "locked": { - "narHash": "sha256-aZFye4UrtlcvLHrISldx4g9uGt3thDbVlLMK5keBSj0=", + "narHash": "sha256-tB9BZB6nRHDk5ELIVlGYlIjViLKBjQl52nC1avhcCwA=", "type": "file", - "url": "https://static.rust-lang.org/dist/channel-rust-1.78.0.toml" + "url": "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml" }, "original": { "type": "file", - "url": "https://static.rust-lang.org/dist/channel-rust-1.78.0.toml" + "url": "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml" } }, "systems": { diff --git a/flake.nix b/flake.nix index 136e1601..50fe2135 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ rust-manifest = { # Keep version in sync with rust-toolchain.toml - url = "https://static.rust-lang.org/dist/channel-rust-1.78.0.toml"; + url = "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml"; flake = false; }; }; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 19939854..42c0fad3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,7 +9,7 @@ # If you're having trouble making the relevant changes, bug a maintainer. [toolchain] -channel = "1.78.0" +channel = "1.81.0" components = [ # For rust-analyzer "rust-src", diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 25202750..d660c6ba 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -316,8 +316,7 @@ pub(crate) async fn register_route( /// - Requires UIAA to verify user password /// - Changes the password of the sender user /// - The password hash is calculated using argon2 with 32 character salt, the -/// plain password is -/// not saved +/// plain password is not saved /// /// If `logout_devices` is true it does the following for each device except the /// sender device: diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 76f3d2f0..ba803454 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -16,8 +16,7 @@ use crate::{services, Ar, Error, Ra, Result}; /// Allows loading room history around an event. /// /// - Only works if the user is joined (TODO: always allow, but only show events -/// if the user was -/// joined, depending on `history_visibility`) +/// if the user was joined, depending on `history_visibility`) #[allow(clippy::too_many_lines)] pub(crate) async fn get_context_route( body: Ar, diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 6f29205f..df723d07 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -117,8 +117,7 @@ pub(crate) async fn send_message_event_route( /// Allows paginating through room history. /// /// - Only works if the user is joined (TODO: always allow, but only show events -/// where the user was -/// joined, depending on `history_visibility`) +/// where the user was joined, depending on `history_visibility`) #[allow(clippy::too_many_lines)] pub(crate) async fn get_message_events_route( body: Ar, diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index f2985301..252036a5 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -36,8 +36,7 @@ use crate::{ /// Synchronize the client's state with the latest state on the server. /// /// - This endpoint takes a `since` parameter which should be the `next_batch` -/// value from a -/// previous request for incremental syncs. +/// value from a previous request for incremental syncs. /// /// Calling this endpoint without a `since` parameter returns: /// - Some of the most recent events of each timeline @@ -50,11 +49,9 @@ use crate::{ /// - Some of the most recent events of each timeline that happened after /// `since` /// - If user joined the room after `since`: All state events (unless lazy -/// loading is activated) and -/// all device list updates in that room +/// loading is activated) and all device list updates in that room /// - If the user was already in the room: A list of all events that are in the -/// state now, but were -/// not in the state at `since` +/// state now, but were not in the state at `since` /// - If the state we send contains a member event: Joined and invited member /// counts, heroes /// - Device list updates that happened after `since` diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 93fbd6cd..5b866dd8 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -13,8 +13,7 @@ use crate::{services, Ar, Ra, Result}; /// Searches all known users for a match. /// /// - Hides any local users that aren't in any public rooms (i.e. those that -/// have the join rule set to public) -/// and don't share a room with the sender +/// have the join rule set to public) and don't share a room with the sender pub(crate) async fn search_users_route( body: Ar, ) -> Result> { diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 0e58a087..74eeac31 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -540,7 +540,7 @@ async fn request_well_known(destination: &str) -> Option { let response = services() .globals .default_client() - .get(&format!("https://{destination}/.well-known/matrix/server")) + .get(format!("https://{destination}/.well-known/matrix/server")) .send() .await; debug!("Got well known response"); @@ -573,8 +573,7 @@ pub(crate) async fn get_server_version_route( /// Gets the public signing keys of this server. /// /// - Matrix does not support invalidating public keys, so the key returned by -/// this will be valid -/// forever. +/// this will be valid forever. // Response type for this endpoint is Json because we need to calculate a // signature for the response pub(crate) async fn get_server_keys_route() -> Result { @@ -625,8 +624,7 @@ pub(crate) async fn get_server_keys_route() -> Result { /// Gets the public signing keys of this server. /// /// - Matrix does not support invalidating public keys, so the key returned by -/// this will be valid -/// forever. +/// this will be valid forever. pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { get_server_keys_route().await } diff --git a/src/database/key_value/rooms/edus/read_receipt.rs b/src/database/key_value/rooms/edus/read_receipt.rs index a897ca6d..f8e1470f 100644 --- a/src/database/key_value/rooms/edus/read_receipt.rs +++ b/src/database/key_value/rooms/edus/read_receipt.rs @@ -1,5 +1,3 @@ -use std::mem; - use ruma::{ events::receipt::ReceiptEvent, serde::Raw, CanonicalJsonObject, OwnedUserId, RoomId, UserId, @@ -83,7 +81,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { .take_while(move |(k, _)| k.starts_with(&prefix2)) .map(move |(k, v)| { let count = utils::u64_from_bytes( - &k[prefix.len()..prefix.len() + mem::size_of::()], + &k[prefix.len()..prefix.len() + size_of::()], ) .map_err(|_| { Error::bad_database( @@ -92,7 +90,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase { })?; let user_id = UserId::parse( utils::string_from_bytes( - &k[prefix.len() + mem::size_of::() + 1..], + &k[prefix.len() + size_of::() + 1..], ) .map_err(|_| { Error::bad_database( diff --git a/src/database/key_value/rooms/pdu_metadata.rs b/src/database/key_value/rooms/pdu_metadata.rs index 5fc6a897..2376e334 100644 --- a/src/database/key_value/rooms/pdu_metadata.rs +++ b/src/database/key_value/rooms/pdu_metadata.rs @@ -1,4 +1,4 @@ -use std::{mem, sync::Arc}; +use std::sync::Arc; use ruma::{EventId, RoomId, UserId}; @@ -47,12 +47,13 @@ impl service::rooms::pdu_metadata::Data for KeyValueDatabase { .iter_from(¤t, true) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(tofrom, _data)| { - let from = utils::u64_from_bytes( - &tofrom[(mem::size_of::())..], - ) - .map_err(|_| { - Error::bad_database("Invalid count in tofrom_relation.") - })?; + let from = + utils::u64_from_bytes(&tofrom[(size_of::())..]) + .map_err(|_| { + Error::bad_database( + "Invalid count in tofrom_relation.", + ) + })?; let mut pduid = shortroomid.get().to_be_bytes().to_vec(); pduid.extend_from_slice(&from.to_be_bytes()); diff --git a/src/database/key_value/rooms/threads.rs b/src/database/key_value/rooms/threads.rs index fe762fa0..8f331d4f 100644 --- a/src/database/key_value/rooms/threads.rs +++ b/src/database/key_value/rooms/threads.rs @@ -1,5 +1,3 @@ -use std::mem; - use ruma::{ api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId, @@ -36,14 +34,13 @@ impl service::rooms::threads::Data for KeyValueDatabase { .iter_from(¤t, true) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(pduid, _users)| { - let count = utils::u64_from_bytes( - &pduid[(mem::size_of::())..], - ) - .map_err(|_| { - Error::bad_database( - "Invalid pduid in threadid_userids.", - ) - })?; + let count = + utils::u64_from_bytes(&pduid[(size_of::())..]) + .map_err(|_| { + Error::bad_database( + "Invalid pduid in threadid_userids.", + ) + })?; let pduid = PduId::new(pduid); diff --git a/src/service/media.rs b/src/service/media.rs index a175f035..c03fd92f 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -175,19 +175,19 @@ impl Service { (width, intermediate) } else { ( - (u64::from(width) * u64::from(::std::u32::MAX) + (u64::from(width) * u64::from(u32::MAX) / intermediate) .try_into() .unwrap_or(u32::MAX), - ::std::u32::MAX, + u32::MAX, ) } } else if let Ok(intermediate) = u32::try_from(intermediate) { (intermediate, height) } else { ( - ::std::u32::MAX, - (u64::from(height) * u64::from(::std::u32::MAX) + u32::MAX, + (u64::from(height) * u64::from(u32::MAX) / intermediate) .try_into() .unwrap_or(u32::MAX), From 806cc0cb281d184760eadd971757a0ab70020625 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 5 Sep 2024 10:47:02 -0700 Subject: [PATCH 366/617] serve well-known client and server config This way users can have a simpler time configuring this stuff and we can worry about the spec compliance parts and specifying the same thing over and over parts. --- src/api.rs | 1 + src/api/well_known.rs | 58 +++++++++++++++++++++++++++++++++++++++++++ src/config.rs | 35 ++++++++++++++++++++++++++ src/main.rs | 6 ++++- 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/api/well_known.rs diff --git a/src/api.rs b/src/api.rs index 442d2f2b..b19322b8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -2,3 +2,4 @@ pub(crate) mod appservice_server; pub(crate) mod client_server; pub(crate) mod ruma_wrapper; pub(crate) mod server_server; +pub(crate) mod well_known; diff --git a/src/api/well_known.rs b/src/api/well_known.rs new file mode 100644 index 00000000..e32d45cb --- /dev/null +++ b/src/api/well_known.rs @@ -0,0 +1,58 @@ +#![warn(missing_docs, clippy::missing_docs_in_private_items)] + +//! Handle requests for `/.well-known/matrix/...` files + +use http::StatusCode; +use ruma::api::{ + client::discovery::discover_homeserver as client, + federation::discovery::discover_homeserver as server, +}; + +use crate::{services, Ar, Ra}; + +/// Handler for `/.well-known/matrix/server` +pub(crate) async fn server( + _: Ar, +) -> Result, StatusCode> { + let Some(authority) = + services().globals.config.server_discovery.server.authority.clone() + else { + return Err(StatusCode::NOT_FOUND); + }; + + if authority == services().globals.config.server_name { + // Delegation isn't needed in this case + return Err(StatusCode::NOT_FOUND); + } + + Ok(Ra(server::Response::new(authority))) +} + +/// Handler for `/.well-known/matrix/client` +pub(crate) async fn client(_: Ar) -> Ra { + let authority = services() + .globals + .config + .server_discovery + .client + .authority + .clone() + .unwrap_or_else(|| services().globals.config.server_name.clone()); + + let scheme = if services().globals.config.server_discovery.client.insecure { + "http" + } else { + "https" + }; + + let base_url = format!("{scheme}://{authority}"); + + // I wish ruma used an actual URL type instead of `String` + Ra(client::Response { + homeserver: client::HomeserverInfo::new(base_url.clone()), + identity_server: None, + sliding_sync_proxy: Some(client::SlidingSyncProxyInfo { + url: base_url, + }), + }) +} diff --git a/src/config.rs b/src/config.rs index a69e2394..e331364f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -30,7 +30,13 @@ pub(crate) struct Config { pub(crate) listen: Vec, pub(crate) tls: Option, + /// The name of this homeserver + /// + /// This is the value that will appear e.g. in user IDs and room aliases. pub(crate) server_name: OwnedServerName, + + #[serde(default)] + pub(crate) server_discovery: ServerDiscovery, pub(crate) database: DatabaseConfig, #[serde(default)] pub(crate) federation: FederationConfig, @@ -63,6 +69,35 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, } +#[derive(Debug, Default, Deserialize)] +pub(crate) struct ServerDiscovery { + /// Server-server discovery configuration + #[serde(default)] + pub(crate) server: ServerServerDiscovery, + + /// Client-server discovery configuration + #[serde(default)] + pub(crate) client: ClientServerDiscovery, +} + +/// Server-server discovery configuration +#[derive(Debug, Default, Deserialize)] +pub(crate) struct ServerServerDiscovery { + /// The alternative authority to make server-server API requests to + pub(crate) authority: Option, +} + +/// Client-server discovery configuration +#[derive(Debug, Default, Deserialize)] +pub(crate) struct ClientServerDiscovery { + /// The alternative authority to make client-server API requests to + pub(crate) authority: Option, + + /// Controls whether HTTPS is used + #[serde(default)] + pub(crate) insecure: bool, +} + #[derive(Debug, Deserialize)] pub(crate) struct TlsConfig { pub(crate) certs: String, diff --git a/src/main.rs b/src/main.rs index 8af1825c..4a683b49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,7 @@ mod service; mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; -use api::{client_server, server_server}; +use api::{client_server, server_server, well_known}; pub(crate) use config::{Config, ListenConfig}; pub(crate) use database::KeyValueDatabase; pub(crate) use service::{pdu::PduEvent, Services}; @@ -491,6 +491,10 @@ fn routes(config: &Config) -> Router { .route("/", get(it_works)) .fallback(not_found); + let router = router + .route("/.well-known/matrix/client", get(well_known::client)) + .route("/.well-known/matrix/server", get(well_known::server)); + if config.federation.enable { router .ruma_route(s2s::get_server_version_route) From 449c27642ceeb02bfbd6811528b03cdbc71dab49 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 8 Sep 2024 14:07:16 -0700 Subject: [PATCH 367/617] hide sliding sync behind explicit option We want to make sure users know this sliding sync impl is pretty buggy before they attempt to use it. --- src/api/well_known.rs | 12 +++++++++--- src/config.rs | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/api/well_known.rs b/src/api/well_known.rs index e32d45cb..f0c0df07 100644 --- a/src/api/well_known.rs +++ b/src/api/well_known.rs @@ -51,8 +51,14 @@ pub(crate) async fn client(_: Ar) -> Ra { Ra(client::Response { homeserver: client::HomeserverInfo::new(base_url.clone()), identity_server: None, - sliding_sync_proxy: Some(client::SlidingSyncProxyInfo { - url: base_url, - }), + sliding_sync_proxy: services() + .globals + .config + .server_discovery + .client + .advertise_sliding_sync + .then_some(client::SlidingSyncProxyInfo { + url: base_url, + }), }) } diff --git a/src/config.rs b/src/config.rs index e331364f..51761f85 100644 --- a/src/config.rs +++ b/src/config.rs @@ -96,6 +96,9 @@ pub(crate) struct ClientServerDiscovery { /// Controls whether HTTPS is used #[serde(default)] pub(crate) insecure: bool, + + #[serde(default, rename = "advertise_buggy_sliding_sync")] + pub(crate) advertise_sliding_sync: bool, } #[derive(Debug, Deserialize)] From 9e6a5e6604966f1b64a26371e8d20f8976489bbc Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 5 Sep 2024 10:57:39 -0700 Subject: [PATCH 368/617] update changelog --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 1e3d3ff2..747d4d6c 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -220,3 +220,5 @@ This will be the first release of Grapevine since it was forked from Conduit [!84](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/84)) 15. Added support for Authenticated Media ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)). ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) +16. Added support for configuring and serving `/.well-known/matrix/...` data. + ([!90](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/90)) From 5691cf08683688101bd41076e71623db6ab8fe0c Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 13 Sep 2024 13:31:04 +0000 Subject: [PATCH 369/617] Better debugging for signing key fetching --- src/service/rooms/event_handler.rs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index cd9ca375..23b08bf7 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1909,17 +1909,24 @@ impl Service { query_via_trusted_servers: bool, ) -> Result { let contains_all_ids = |keys: &SigningKeys| { - signature_ids.iter().all(|id| { - keys.verify_keys + for id in &signature_ids { + let in_verify_keys = keys + .verify_keys .keys() .map(ToString::to_string) - .any(|key_id| id == &key_id) - || keys - .old_verify_keys - .keys() - .map(ToString::to_string) - .any(|key_id| id == &key_id) - }) + .any(|key_id| id == &key_id); + let in_old_verify_keys = keys + .old_verify_keys + .keys() + .map(ToString::to_string) + .any(|key_id| id == &key_id); + + if !in_verify_keys && !in_old_verify_keys { + trace!(id, "signature key not yet in known set"); + return false; + } + } + true }; let permit = services() @@ -1982,6 +1989,9 @@ impl Service { debug!( server = %origin, + key_ids = ?result.verify_keys.keys().collect::>(), + old_key_ids = + ?result.old_verify_keys.keys().collect::>(), ts_threshold = %ts_threshold.get(), ts_valid_until = %result.valid_until_ts.get(), "Loaded signing keys for server", @@ -2000,6 +2010,7 @@ impl Service { } expires_soon_or_has_expired = true; + trace!("Found all keys, but they will expire too soon"); } } From 458a7458dc27b5a977608fba0ed90697c46cf0c7 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 13 Sep 2024 14:46:27 +0000 Subject: [PATCH 370/617] Support specifying old_verify_keys in config --- book/changelog.md | 2 ++ src/config.rs | 8 +++++++- src/service/globals/data.rs | 14 +++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 747d4d6c..3b0f49b3 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -222,3 +222,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) 16. Added support for configuring and serving `/.well-known/matrix/...` data. ([!90](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/90)) +17. Added support for configuring old verify/signing keys in config (`federation.old_verify_keys`) + ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) diff --git a/src/config.rs b/src/config.rs index 51761f85..01e65bc1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,12 +1,16 @@ use std::{ borrow::Cow, + collections::BTreeMap, fmt::{self, Display}, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, }; use once_cell::sync::Lazy; -use ruma::{OwnedServerName, RoomVersionId}; +use ruma::{ + api::federation::discovery::OldVerifyKey, OwnedServerName, + OwnedServerSigningKeyId, RoomVersionId, +}; use serde::Deserialize; use crate::error; @@ -288,6 +292,7 @@ pub(crate) struct FederationConfig { pub(crate) trusted_servers: Vec, pub(crate) max_fetch_prev_events: u16, pub(crate) max_concurrent_requests: u16, + pub(crate) old_verify_keys: BTreeMap, } impl Default for FederationConfig { @@ -299,6 +304,7 @@ impl Default for FederationConfig { ], max_fetch_prev_events: 100, max_concurrent_requests: 100, + old_verify_keys: BTreeMap::new(), } } } diff --git a/src/service/globals/data.rs b/src/service/globals/data.rs index 6971e2e2..28e7e512 100644 --- a/src/service/globals/data.rs +++ b/src/service/globals/data.rs @@ -18,17 +18,29 @@ use crate::{services, Result}; /// don't require post-validation #[derive(Deserialize, Debug, Clone)] pub(crate) struct SigningKeys { + // FIXME: Use [`OwnedServerSigningKeyId`] as key + // Not yet feasibly because they get passed to `verify_event`, see https://github.com/ruma/ruma/pull/1808 pub(crate) verify_keys: BTreeMap, pub(crate) old_verify_keys: BTreeMap, + pub(crate) valid_until_ts: MilliSecondsSinceUnixEpoch, } impl SigningKeys { /// Creates the `SigningKeys` struct, using the keys of the current server pub(crate) fn load_own_keys() -> Self { + let old_verify_keys = services() + .globals + .config + .federation + .old_verify_keys + .iter() + .map(|(id, key)| (id.to_string(), key.clone())) + .collect(); + let mut keys = Self { verify_keys: BTreeMap::new(), - old_verify_keys: BTreeMap::new(), + old_verify_keys, valid_until_ts: MilliSecondsSinceUnixEpoch::from_system_time( SystemTime::now() + Duration::from_secs(7 * 86400), ) From 296824fef4bd83d89e1983fe8c31a44cee65c995 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 13 Sep 2024 14:49:41 +0000 Subject: [PATCH 371/617] Always use local keypair instead of trying to find our own keys in cache --- book/changelog.md | 5 +++++ src/service/globals.rs | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 3b0f49b3..aadaf9d1 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -171,6 +171,11 @@ This will be the first release of Grapevine since it was forked from Conduit 16. Fix bug where signing keys would not be fetched when joining a room if we hadn't previously seen any signing keys from that server. ([!87](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/87)) +17. Fixed bug + ([#48](https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/48)) + that caused us to attempt to fetch our own signing keys from ourselves over + federation, and fail ("Won't send federation request to ourselves"). + ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) ### Added diff --git a/src/service/globals.rs b/src/service/globals.rs index dad08d32..b4ccba9d 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -435,9 +435,11 @@ impl Service { &self, origin: &ServerName, ) -> Result> { - Ok(self.db.signing_keys_for(origin)?.or_else(|| { - (origin == self.server_name()).then(SigningKeys::load_own_keys) - })) + if origin == self.server_name() { + Ok(Some(SigningKeys::load_own_keys())) + } else { + self.db.signing_keys_for(origin) + } } /// Filters the key map of multiple servers down to keys that should be From 3bb4a25c1db6ed2e7d80343c01f7e05819c3fb70 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 13 Sep 2024 16:50:11 +0000 Subject: [PATCH 372/617] Include old verify keys in _matrix/key/v2/server response --- src/api/server_server.rs | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 74eeac31..dbfa6615 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -4,7 +4,7 @@ use std::{ mem, net::{IpAddr, SocketAddr}, sync::Arc, - time::{Duration, Instant, SystemTime}, + time::Instant, }; use axum::{response::IntoResponse, Json}; @@ -22,7 +22,6 @@ use ruma::{ directory::{get_public_rooms, get_public_rooms_filtered}, discovery::{ get_server_keys, get_server_version, ServerSigningKeys, - VerifyKey, }, event::{ get_event, get_missing_events, get_room_state, @@ -70,7 +69,10 @@ use super::appservice_server; use crate::{ api::client_server::{self, claim_keys_helper, get_keys_helper}, observability::{FoundIn, Lookup, METRICS}, - service::pdu::{gen_event_id_canonical_json, PduBuilder}, + service::{ + globals::SigningKeys, + pdu::{gen_event_id_canonical_json, PduBuilder}, + }, services, utils::{self, dbg_truncate_str, MxcData}, Ar, Error, PduEvent, Ra, Result, @@ -577,29 +579,31 @@ pub(crate) async fn get_server_version_route( // Response type for this endpoint is Json because we need to calculate a // signature for the response pub(crate) async fn get_server_keys_route() -> Result { - let mut verify_keys: BTreeMap = - BTreeMap::new(); - verify_keys.insert( - format!("ed25519:{}", services().globals.keypair().version()) - .try_into() - .expect("found invalid server signing keys in DB"), - VerifyKey { - key: Base64::new( - services().globals.keypair().public_key().to_vec(), - ), - }, - ); + fn convert_key_ids( + keys: BTreeMap, + ) -> BTreeMap { + keys.into_iter() + .map(|(id, key)| { + let id = id + .try_into() + .expect("found invalid server signing keys in DB"); + (id, key) + }) + .collect() + } + + let keys = SigningKeys::load_own_keys(); + let verify_keys = convert_key_ids(keys.verify_keys); + let old_verify_keys = convert_key_ids(keys.old_verify_keys); + let mut response = serde_json::from_slice( get_server_keys::v2::Response { server_key: Raw::new(&ServerSigningKeys { server_name: services().globals.server_name().to_owned(), verify_keys, - old_verify_keys: BTreeMap::new(), + old_verify_keys, signatures: BTreeMap::new(), - valid_until_ts: MilliSecondsSinceUnixEpoch::from_system_time( - SystemTime::now() + Duration::from_secs(86400 * 7), - ) - .expect("time is valid"), + valid_until_ts: keys.valid_until_ts, }) .expect("static conversion, no errors"), } From e2cba15ed2ec1681cc455d66a7bb2053cc204f23 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 14 Sep 2024 20:11:29 -0700 Subject: [PATCH 373/617] factor out helper for parsing media keys Leaving this private in `database::key_value::media` because the way the metadata is encoded in media keys is a mess. I want to fix that in the future, and want to limit the number of things that rely on it for now. --- src/database/key_value/media.rs | 115 ++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 37 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 314cd0a9..1748b360 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -9,6 +9,82 @@ use crate::{ utils, Error, Result, }; +struct MediaFileKeyParts { + mxc: OwnedMxcUri, + width: u32, + height: u32, + meta: FileMeta, +} + +impl TryFrom<&MediaFileKey> for MediaFileKeyParts { + type Error = Error; + + fn try_from(key: &MediaFileKey) -> Result { + let mut parts = key.as_bytes().split(|&b| b == 0xFF); + + let mxc_bytes = parts + .next() + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + let mxc = utils::string_from_bytes(mxc_bytes) + .map_err(|_| { + Error::bad_database("Media MXC URI in db is invalid unicode.") + })? + .into(); + + let thumbnail_size_bytes = parts + .next() + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + let thumbnail_size_bytes: &[u8; 8] = + thumbnail_size_bytes.try_into().map_err(|_| { + Error::bad_database("Media ID thumbnail size in db is invalid") + })?; + let width = u32::from_be_bytes( + thumbnail_size_bytes[..4].try_into().expect("should be 4 bytes"), + ); + let height = u32::from_be_bytes( + thumbnail_size_bytes[4..].try_into().expect("should be 4 bytes"), + ); + + let content_type = parts + .next() + .map(|bytes| { + utils::string_from_bytes(bytes).map_err(|_| { + Error::bad_database( + "Content type in mediaid_file is invalid unicode.", + ) + }) + }) + .transpose()?; + + let content_disposition_bytes = parts + .next() + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + + let content_disposition = if content_disposition_bytes.is_empty() { + None + } else { + Some(utils::string_from_bytes(content_disposition_bytes).map_err( + |_| { + Error::bad_database( + "Content Disposition in mediaid_file is invalid \ + unicode.", + ) + }, + )?) + }; + + Ok(MediaFileKeyParts { + mxc, + width, + height, + meta: FileMeta { + content_disposition, + content_type, + }, + }) + } +} + impl service::media::Data for KeyValueDatabase { fn create_file_metadata( &self, @@ -61,42 +137,7 @@ impl service::media::Data for KeyValueDatabase { )?; let key = MediaFileKey::new(key); - - let mut parts = key.as_bytes().rsplit(|&b| b == 0xFF); - - let content_type = parts - .next() - .map(|bytes| { - utils::string_from_bytes(bytes).map_err(|_| { - Error::bad_database( - "Content type in mediaid_file is invalid unicode.", - ) - }) - }) - .transpose()?; - - let content_disposition_bytes = parts - .next() - .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; - - let content_disposition = if content_disposition_bytes.is_empty() { - None - } else { - Some(utils::string_from_bytes(content_disposition_bytes).map_err( - |_| { - Error::bad_database( - "Content Disposition in mediaid_file is invalid \ - unicode.", - ) - }, - )?) - }; - Ok(( - FileMeta { - content_disposition, - content_type, - }, - key, - )) + let parts = MediaFileKeyParts::try_from(&key)?; + Ok((parts.meta, key)) } } From 7672cc8473ad02746599953130c985a34b9f4b2f Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 14 Sep 2024 20:43:56 -0700 Subject: [PATCH 374/617] use OwnedMxcUri in media service Not using `MxcData` because it borrows it's fields, and so we wouldn't be able to return an owned `MxcData` from functions that read the db. --- src/api/client_server/media.rs | 22 +++++++++++----------- src/api/server_server.rs | 4 ++-- src/database/key_value/media.rs | 6 +++--- src/service/media.rs | 10 +++++----- src/service/media/data.rs | 6 ++++-- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index cd117232..ab11894c 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -159,7 +159,7 @@ pub(crate) async fn create_content_route( services() .media .create( - mxc.to_string(), + mxc.clone().into(), body.filename .clone() .map(|filename| ContentDisposition { @@ -350,7 +350,7 @@ pub(crate) async fn get_remote_content( services() .media .create( - mxc.to_string(), + mxc.clone().into(), response.content.content_disposition.as_ref(), response.content.content_type.clone(), &response.content.file, @@ -455,7 +455,7 @@ async fn get_content_route_ruma( .. }, file, - )) = services().media.get(mxc.to_string()).await? + )) = services().media.get(mxc.clone().into()).await? { Ok(authenticated_media_client::get_content::v1::Response { file, @@ -581,7 +581,7 @@ async fn get_content_as_filename_route_ruma( .. }, file, - )) = services().media.get(mxc.to_string()).await? + )) = services().media.get(mxc.clone().into()).await? { Ok(authenticated_media_client::get_content_as_filename::v1::Response { file, @@ -842,8 +842,10 @@ async fn get_content_thumbnail_route_ruma( .. }, file, - )) = - services().media.get_thumbnail(mxc.to_string(), width, height).await? + )) = services() + .media + .get_thumbnail(mxc.clone().into(), width, height) + .await? { return Ok(make_response(file, content_type)); } @@ -872,7 +874,7 @@ async fn get_content_thumbnail_route_ruma( services() .media .upload_thumbnail( - mxc.to_string(), + mxc.clone().into(), None, resp.content.content_type.clone(), width, @@ -901,10 +903,8 @@ async fn get_content_thumbnail_route_ruma( .. }, file, - )) = services() - .media - .get_thumbnail(mxc.to_string(), width, height) - .await? + )) = + services().media.get_thumbnail(mxc.into(), width, height).await? { return Ok(make_response(file, content_type)); } diff --git a/src/api/server_server.rs b/src/api/server_server.rs index dbfa6615..8065ac70 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -2062,7 +2062,7 @@ pub(crate) async fn media_download_route( content_type, }, file, - )) = services().media.get(mxc.to_string()).await? + )) = services().media.get(mxc.into()).await? else { return Err(Error::BadRequest( ErrorKind::NotYetUploaded, @@ -2109,7 +2109,7 @@ pub(crate) async fn media_thumbnail_route( .. }, file, - )) = services().media.get_thumbnail(mxc.to_string(), width, height).await? + )) = services().media.get_thumbnail(mxc.into(), width, height).await? else { return Err(Error::BadRequest( ErrorKind::NotYetUploaded, diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 1748b360..5ecb0388 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -1,4 +1,4 @@ -use ruma::api::client::error::ErrorKind; +use ruma::{api::client::error::ErrorKind, OwnedMxcUri}; use crate::{ database::KeyValueDatabase, @@ -88,7 +88,7 @@ impl TryFrom<&MediaFileKey> for MediaFileKeyParts { impl service::media::Data for KeyValueDatabase { fn create_file_metadata( &self, - mxc: String, + mxc: OwnedMxcUri, width: u32, height: u32, meta: &FileMeta, @@ -121,7 +121,7 @@ impl service::media::Data for KeyValueDatabase { fn search_file_metadata( &self, - mxc: String, + mxc: OwnedMxcUri, width: u32, height: u32, ) -> Result<(FileMeta, MediaFileKey)> { diff --git a/src/service/media.rs b/src/service/media.rs index c03fd92f..fa626d45 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -1,7 +1,7 @@ use std::io::Cursor; use image::imageops::FilterType; -use ruma::http_headers::ContentDisposition; +use ruma::{http_headers::ContentDisposition, OwnedMxcUri}; use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt}, @@ -46,7 +46,7 @@ impl Service { #[tracing::instrument(skip(self, file))] pub(crate) async fn create( &self, - mxc: String, + mxc: OwnedMxcUri, content_disposition: Option<&ContentDisposition>, content_type: Option, file: &[u8], @@ -69,7 +69,7 @@ impl Service { #[tracing::instrument(skip(self, file))] pub(crate) async fn upload_thumbnail( &self, - mxc: String, + mxc: OwnedMxcUri, content_disposition: Option, content_type: Option, width: u32, @@ -93,7 +93,7 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) async fn get( &self, - mxc: String, + mxc: OwnedMxcUri, ) -> Result)>> { if let Ok((meta, key)) = self.db.search_file_metadata(mxc, 0, 0) { let path = services().globals.get_media_file(&key); @@ -224,7 +224,7 @@ impl Service { #[tracing::instrument(skip(self))] pub(crate) async fn get_thumbnail( &self, - mxc: String, + mxc: OwnedMxcUri, width: u32, height: u32, ) -> Result)>> { diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 64fbdbbf..e5fe4767 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -1,10 +1,12 @@ +use ruma::OwnedMxcUri; + use super::{FileMeta, MediaFileKey}; use crate::Result; pub(crate) trait Data: Send + Sync { fn create_file_metadata( &self, - mxc: String, + mxc: OwnedMxcUri, width: u32, height: u32, meta: &FileMeta, @@ -12,7 +14,7 @@ pub(crate) trait Data: Send + Sync { fn search_file_metadata( &self, - mxc: String, + mxc: OwnedMxcUri, width: u32, height: u32, ) -> Result<(FileMeta, MediaFileKey)>; From d7087c66bb53f789fa32312bdc9705daa10c3fd2 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 14 Sep 2024 20:20:02 -0700 Subject: [PATCH 375/617] add admin command to delete individual media files --- src/database/key_value/media.rs | 21 +++++++++++++++++++++ src/service/admin.rs | 16 ++++++++++++++-- src/service/media.rs | 33 ++++++++++++++++++++++++++++++++- src/service/media/data.rs | 11 +++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 5ecb0388..10a55554 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -140,4 +140,25 @@ impl service::media::Data for KeyValueDatabase { let parts = MediaFileKeyParts::try_from(&key)?; Ok((parts.meta, key)) } + + fn delete_file_metadata(&self, key: MediaFileKey) -> Result<()> { + self.mediaid_file.remove(key.as_bytes()) + } + + fn search_thumbnails_metadata( + &self, + mxc: OwnedMxcUri, + ) -> Result> { + let mut prefix = mxc.as_bytes().to_vec(); + prefix.push(0xFF); + + self.mediaid_file + .scan_prefix(prefix) + .map(|(key, _)| { + let key = MediaFileKey::new(key); + let parts = MediaFileKeyParts::try_from(&key)?; + Ok((parts.meta, key)) + }) + .collect() + } } diff --git a/src/service/admin.rs b/src/service/admin.rs index b3f098a5..5bb56c4f 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -23,8 +23,8 @@ use ruma::{ TimelineEventType, }, signatures::verify_json, - EventId, MilliSecondsSinceUnixEpoch, OwnedRoomId, RoomId, RoomVersionId, - ServerName, UserId, + EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, RoomId, + RoomVersionId, ServerName, UserId, }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; @@ -179,6 +179,12 @@ enum AdminCommand { room_id: Box, }, + /// Delete media and all associated thumbnails. + DeleteMedia { + /// mxc:// URI of the media to delete + mxc: OwnedMxcUri, + }, + /// Verify json signatures /// [commandbody]() /// # ``` @@ -791,6 +797,12 @@ impl Service { services().rooms.metadata.disable_room(&room_id, false)?; RoomMessageEventContent::text_plain("Room enabled.") } + AdminCommand::DeleteMedia { + mxc, + } => { + services().media.delete(mxc).await?; + RoomMessageEventContent::text_plain("Media deleted.") + } AdminCommand::DeactivateUser { leave_rooms, user_id, diff --git a/src/service/media.rs b/src/service/media.rs index fa626d45..830a43e4 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use image::imageops::FilterType; use ruma::{http_headers::ContentDisposition, OwnedMxcUri}; use tokio::{ - fs::File, + fs::{self, File}, io::{AsyncReadExt, AsyncWriteExt}, }; use tracing::{debug, warn}; @@ -110,6 +110,37 @@ impl Service { } } + /// Deletes a media object and all associated thumbnails. + #[tracing::instrument(skip(self))] + pub(crate) async fn delete(&self, mxc: OwnedMxcUri) -> Result<()> { + let (_, key) = self.db.search_file_metadata(mxc.clone(), 0, 0)?; + + let thumbnails = self.db.search_thumbnails_metadata(mxc)?; + for (_, thumbnail_key) in thumbnails { + self.delete_by_key(thumbnail_key).await?; + } + + self.delete_by_key(key).await?; + + Ok(()) + } + + /// Deletes a specific media key, which may or may not be a thumbnail. + /// + /// When deleting a non-thumbnail key with this method, the associated + /// thumbnails are not deleted. + async fn delete_by_key(&self, key: MediaFileKey) -> Result<()> { + let path = services().globals.get_media_file(&key); + match fs::remove_file(path).await { + Ok(()) => (), + // The file in the fs may already have been deleted by hand + Err(e) if e.kind() == std::io::ErrorKind::NotFound => (), + other_error => other_error?, + } + self.db.delete_file_metadata(key)?; + Ok(()) + } + /// Returns width, height of the thumbnail and whether it should be cropped. /// Returns None when the server should send the original file. fn thumbnail_properties( diff --git a/src/service/media/data.rs b/src/service/media/data.rs index e5fe4767..d7734845 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -18,4 +18,15 @@ pub(crate) trait Data: Send + Sync { width: u32, height: u32, ) -> Result<(FileMeta, MediaFileKey)>; + + fn delete_file_metadata(&self, key: MediaFileKey) -> Result<()>; + + /// Return all thumbnail keys/metadata associated with a MXC. + /// + /// The original file is not returned. To fetch the key/metadata of the + /// original file, use [`Data::search_file_metadata`]. + fn search_thumbnails_metadata( + &self, + mxc: OwnedMxcUri, + ) -> Result>; } From 9d14c5d461c4d7481e7c18e3d4625cee64634ce1 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sun, 15 Sep 2024 00:34:20 -0700 Subject: [PATCH 376/617] add admin command to delete all remote media files --- src/database/key_value/media.rs | 23 +++++++++++++ src/service/admin.rs | 59 +++++++++++++++++++++++++++++++-- src/service/media.rs | 9 +++++ src/service/media/data.rs | 9 +++++ 4 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 10a55554..5b097f51 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -161,4 +161,27 @@ impl service::media::Data for KeyValueDatabase { }) .collect() } + + fn all_file_metadata( + &self, + ) -> Box< + dyn Iterator> + '_, + > { + Box::new( + self.mediaid_file + .iter() + .map(|(key, _)| { + let key = MediaFileKey::new(key); + + let parts = MediaFileKeyParts::try_from(&key)?; + if parts.width != 0 && parts.height != 0 { + // Skip thumbnails + return Ok(None); + }; + + Ok(Some((parts.mxc, parts.meta, key))) + }) + .filter_map(Result::transpose), + ) + } } diff --git a/src/service/admin.rs b/src/service/admin.rs index 5bb56c4f..a1b81ebb 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -23,8 +23,8 @@ use ruma::{ TimelineEventType, }, signatures::verify_json, - EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, RoomId, - RoomVersionId, ServerName, UserId, + EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, + OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; @@ -185,6 +185,17 @@ enum AdminCommand { mxc: OwnedMxcUri, }, + /// Delete cached remote media from the database. + /// + /// This media may still be fetched and cached again in the future. + DeleteRemoteMedia { + /// If specified, only delete remote media from this origin. + /// + /// If not specified, all remote media will be deleted. + #[clap(long)] + origin: Option, + }, + /// Verify json signatures /// [commandbody]() /// # ``` @@ -803,6 +814,50 @@ impl Service { services().media.delete(mxc).await?; RoomMessageEventContent::text_plain("Media deleted.") } + AdminCommand::DeleteRemoteMedia { + origin, + } => { + if origin.as_deref() == Some(services().globals.server_name()) { + return Ok(RoomMessageEventContent::text_plain( + "Specified origin is this server. Will not delete \ + anything.", + )); + } + + let mut count = 0; + + // The `media.iter_all()` iterator is not `Send`, so spawn it in + // a separate thread and send the results over a channel. + let (tx, mut rx) = mpsc::channel(1); + tokio::task::spawn_blocking(move || { + for mxc in services().media.iter_all() { + if tx.blocking_send(mxc).is_err() { + break; + } + } + }); + + while let Some(mxc) = rx.recv().await { + let mxc = mxc?; + let server_name = mxc.server_name(); + + if server_name == Ok(services().globals.server_name()) { + continue; + } + if let Some(origin) = &origin { + if server_name != Ok(origin) { + continue; + } + } + + count += 1; + services().media.delete(mxc).await?; + } + + RoomMessageEventContent::text_plain(format!( + "{count} media objects deleted." + )) + } AdminCommand::DeactivateUser { leave_rooms, user_id, diff --git a/src/service/media.rs b/src/service/media.rs index 830a43e4..0b4903aa 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -141,6 +141,15 @@ impl Service { Ok(()) } + /// List all media stored in the database. + /// + /// Each MXC is list once. Thumbnails are not included separately from the + /// original media. + #[tracing::instrument(skip(self))] + pub(crate) fn iter_all(&self) -> impl Iterator> { + self.db.all_file_metadata().map(|media| media.map(|(mxc, ..)| mxc)) + } + /// Returns width, height of the thumbnail and whether it should be cropped. /// Returns None when the server should send the original file. fn thumbnail_properties( diff --git a/src/service/media/data.rs b/src/service/media/data.rs index d7734845..139032dc 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -29,4 +29,13 @@ pub(crate) trait Data: Send + Sync { &self, mxc: OwnedMxcUri, ) -> Result>; + + /// Returns an iterator over metadata for all media. + /// + /// Thumbnails are not included. + fn all_file_metadata( + &self, + ) -> Box< + dyn Iterator> + '_, + >; } From ba7b224c38f45e4ef5ee03bd0eab9aa1337b5d8a Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sun, 15 Sep 2024 01:04:39 -0700 Subject: [PATCH 377/617] add dry-run mode to delete-remote-media-files admin command --- src/service/admin.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index a1b81ebb..81a5c017 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -189,6 +189,11 @@ enum AdminCommand { /// /// This media may still be fetched and cached again in the future. DeleteRemoteMedia { + /// Output the number of media objects that would be deleted, but do + /// not actually delete anything. + #[clap(short, long)] + dry_run: bool, + /// If specified, only delete remote media from this origin. /// /// If not specified, all remote media will be deleted. @@ -815,6 +820,7 @@ impl Service { RoomMessageEventContent::text_plain("Media deleted.") } AdminCommand::DeleteRemoteMedia { + dry_run, origin, } => { if origin.as_deref() == Some(services().globals.server_name()) { @@ -851,12 +857,17 @@ impl Service { } count += 1; - services().media.delete(mxc).await?; + if !dry_run { + services().media.delete(mxc).await?; + } } - RoomMessageEventContent::text_plain(format!( - "{count} media objects deleted." - )) + let message = if dry_run { + format!("{count} media objects would be deleted.") + } else { + format!("{count} media objects deleted.") + }; + RoomMessageEventContent::text_plain(message) } AdminCommand::DeactivateUser { leave_rooms, From 48850605b03c662cdc3fdc52eee91c033922d137 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sun, 15 Sep 2024 12:57:15 -0700 Subject: [PATCH 378/617] changelog entry for media deletion admin commands --- book/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index aadaf9d1..b397fa27 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -229,3 +229,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!90](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/90)) 17. Added support for configuring old verify/signing keys in config (`federation.old_verify_keys`) ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) +18. Added admin commands to delete media + ([!99](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/99)) From b9ee89892069deeef865f77b0e62326f2270c11e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 12 Sep 2024 17:12:49 -0700 Subject: [PATCH 379/617] require client base_url, rename from authority The previous code used `server_name` as a fallback but in reality there is no real relationship between `server_name` and the location clients are supposed to make requests to. Additionally, the `insecure` option is gone, because we now allow users to control the entire URL, so they're free to choose the scheme. --- book/changelog.md | 7 +++++-- src/api/well_known.rs | 20 +++----------------- src/config.rs | 15 +++++---------- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index b397fa27..ddd97572 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -225,8 +225,11 @@ This will be the first release of Grapevine since it was forked from Conduit [!84](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/84)) 15. Added support for Authenticated Media ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)). ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) -16. Added support for configuring and serving `/.well-known/matrix/...` data. - ([!90](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/90)) +16. **BREAKING:** Added support for configuring and serving + `/.well-known/matrix/...` data. + ([!90](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/90), + [!94](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/94)) + * The `server_discovery.client.base_url` option is now required. 17. Added support for configuring old verify/signing keys in config (`federation.old_verify_keys`) ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) 18. Added admin commands to delete media diff --git a/src/api/well_known.rs b/src/api/well_known.rs index f0c0df07..44210e55 100644 --- a/src/api/well_known.rs +++ b/src/api/well_known.rs @@ -30,24 +30,10 @@ pub(crate) async fn server( /// Handler for `/.well-known/matrix/client` pub(crate) async fn client(_: Ar) -> Ra { - let authority = services() - .globals - .config - .server_discovery - .client - .authority - .clone() - .unwrap_or_else(|| services().globals.config.server_name.clone()); - - let scheme = if services().globals.config.server_discovery.client.insecure { - "http" - } else { - "https" - }; - - let base_url = format!("{scheme}://{authority}"); - // I wish ruma used an actual URL type instead of `String` + let base_url = + services().globals.config.server_discovery.client.base_url.to_string(); + Ra(client::Response { homeserver: client::HomeserverInfo::new(base_url.clone()), identity_server: None, diff --git a/src/config.rs b/src/config.rs index 01e65bc1..e2c2b0ff 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,6 +7,7 @@ use std::{ }; use once_cell::sync::Lazy; +use reqwest::Url; use ruma::{ api::federation::discovery::OldVerifyKey, OwnedServerName, OwnedServerSigningKeyId, RoomVersionId, @@ -39,7 +40,6 @@ pub(crate) struct Config { /// This is the value that will appear e.g. in user IDs and room aliases. pub(crate) server_name: OwnedServerName, - #[serde(default)] pub(crate) server_discovery: ServerDiscovery, pub(crate) database: DatabaseConfig, #[serde(default)] @@ -73,14 +73,13 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, } -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Deserialize)] pub(crate) struct ServerDiscovery { /// Server-server discovery configuration #[serde(default)] pub(crate) server: ServerServerDiscovery, /// Client-server discovery configuration - #[serde(default)] pub(crate) client: ClientServerDiscovery, } @@ -92,14 +91,10 @@ pub(crate) struct ServerServerDiscovery { } /// Client-server discovery configuration -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Deserialize)] pub(crate) struct ClientServerDiscovery { - /// The alternative authority to make client-server API requests to - pub(crate) authority: Option, - - /// Controls whether HTTPS is used - #[serde(default)] - pub(crate) insecure: bool, + /// The base URL to make client-server API requests to + pub(crate) base_url: Url, #[serde(default, rename = "advertise_buggy_sliding_sync")] pub(crate) advertise_sliding_sync: bool, From 0d6a7eb96838537597d7aaf7a04826549ac9c6ad Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 18 Sep 2024 19:59:51 +0000 Subject: [PATCH 380/617] Disable unauthenticated media access --- book/changelog.md | 3 +++ src/config.rs | 2 ++ src/main.rs | 31 +++++++++++++++++++++++++------ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index ddd97572..684b1cf6 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -128,6 +128,9 @@ This will be the first release of Grapevine since it was forked from Conduit 11. Try to generate thumbnails for remote media ourselves if the federation thumbnail request fails. ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) +12. **BREAKING:** Disable unauthenticated access to media by default, set the + `serve_media_unauthenticated` config option to `true` to enable it. + ([!103](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/103)) ### Fixed diff --git a/src/config.rs b/src/config.rs index e2c2b0ff..197be925 100644 --- a/src/config.rs +++ b/src/config.rs @@ -60,6 +60,8 @@ pub(crate) struct Config { pub(crate) allow_encryption: bool, #[serde(default = "true_fn")] pub(crate) allow_room_creation: bool, + #[serde(default = "false_fn")] + pub(crate) serve_media_unauthenticated: bool, #[serde(default = "default_default_room_version")] pub(crate) default_room_version: RoomVersionId, #[serde(default)] diff --git a/src/main.rs b/src/main.rs index 4a683b49..049fd10c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -408,12 +408,24 @@ fn routes(config: &Config) -> Router { .ruma_route(c2s::turn_server_route) .ruma_route(c2s::send_event_to_device_route); - // unauthenticated (legacy) media - let router = router - .ruma_route(c2s::get_media_config_legacy_route) - .ruma_route(c2s::get_content_legacy_route) - .ruma_route(c2s::get_content_as_filename_legacy_route) - .ruma_route(c2s::get_content_thumbnail_legacy_route); + // deprecated, but unproblematic + let router = router.ruma_route(c2s::get_media_config_legacy_route); + let router = if config.serve_media_unauthenticated { + router + .ruma_route(c2s::get_content_legacy_route) + .ruma_route(c2s::get_content_as_filename_legacy_route) + .ruma_route(c2s::get_content_thumbnail_legacy_route) + } else { + router + .route( + "/_matrix/media/v3/download/*path", + any(unauthenticated_media_disabled), + ) + .route( + "/_matrix/media/v3/thumbnail/*path", + any(unauthenticated_media_disabled), + ) + }; // authenticated media let router = router @@ -570,6 +582,13 @@ async fn federation_disabled(_: Uri) -> impl IntoResponse { Error::bad_config("Federation is disabled.") } +async fn unauthenticated_media_disabled(_: Uri) -> impl IntoResponse { + Error::BadRequest( + ErrorKind::NotFound, + "Unauthenticated media access is disabled", + ) +} + async fn not_found(method: Method, uri: Uri) -> impl IntoResponse { debug!(%method, %uri, "Unknown route"); Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request") From ca6bc74074b10880cdafa6ab049ce2c9d2b83f8f Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 18 Sep 2024 21:47:19 +0000 Subject: [PATCH 381/617] Fix X-Matrix signature validation for incoming requests For HTTP/1 requests, an inbound Request's URI contains only the path and query parameters, since there's no way to synthesize the authority part. This is exactly what we need for the X-Matrix "uri" field. HTTP/2 requests however can contain the :authority pseudo-header, which is used to populate the Request's URI. Using a URL that includes an authority breaks the signature check. Largely inspired by conduit MR !631 (https://gitlab.com/famedly/conduit/-/merge_requests/631). Co-authored-by: strawberry --- book/changelog.md | 2 ++ src/api/ruma_wrapper/axum.rs | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index 684b1cf6..031a34ae 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -179,6 +179,8 @@ This will be the first release of Grapevine since it was forked from Conduit that caused us to attempt to fetch our own signing keys from ourselves over federation, and fail ("Won't send federation request to ourselves"). ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) +18. Fixed incoming HTTP/2 requests failing federation signature check. + ([!104](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/104)) ### Added diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 6315e5e1..712386d9 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -220,6 +220,16 @@ async fn ar_from_request_inner( CanonicalJsonValue::Object(origin_signatures), )]); + let x_matrix_uri = parts + .uri + .path_and_query() + .ok_or_else(|| { + Error::BadRequest( + ErrorKind::InvalidParam, + "No HTTP path/query", + ) + })? + .to_string(); let mut request_map = BTreeMap::from_iter([ ( "method".to_owned(), @@ -227,7 +237,7 @@ async fn ar_from_request_inner( ), ( "uri".to_owned(), - CanonicalJsonValue::String(parts.uri.to_string()), + CanonicalJsonValue::String(x_matrix_uri), ), ( "origin".to_owned(), From d848e787d3a689bdb5cfa30f913934ac2eebd83f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 17 Sep 2024 20:58:30 -0700 Subject: [PATCH 382/617] ignore files that were probably never created File data is inserted into the database before being created on disk, which means that it's possible for data to exist in the database that doesn't exist on disk. In this case, the media deletion functions should simply ignore this error. --- src/service/media.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/service/media.rs b/src/service/media.rs index 0b4903aa..03109004 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -135,6 +135,10 @@ impl Service { Ok(()) => (), // The file in the fs may already have been deleted by hand Err(e) if e.kind() == std::io::ErrorKind::NotFound => (), + // The file may have never existed in the fs because the name was + // too long + #[cfg(unix)] + Err(e) if e.raw_os_error() == Some(nix::libc::ENAMETOOLONG) => (), other_error => other_error?, } self.db.delete_file_metadata(key)?; From cb3e0c620ab478728507d32183483b5845a66a04 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 17 Sep 2024 21:16:22 -0700 Subject: [PATCH 383/617] improve media key decoding logs On my HS I observed 5 instances of keys with the following format: * MXC bytes. * A 0xFF byte. * 4 bytes where the width and height are supposed to be, which are supposed to be 8 bytes in length. * 3 consecutive 0xFF bytes. This means that the `content-type` and `content-disposition` sections both parse as the empty string, and there's an extra separator at the end too. * Extra bytes, all of which were `image/png`. The 4 bytes where the width and height are supposed to be were one of: * 003ED000 * 003EE000 * 003EF001 Which seems to have some kind of pattern to it... After much digging, we have absolutely no idea what could've caused this. Cursed. --- src/database/key_value/media.rs | 18 +++++++++++------- src/utils.rs | 12 ++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 5b097f51..e3f771d3 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -19,24 +19,28 @@ struct MediaFileKeyParts { impl TryFrom<&MediaFileKey> for MediaFileKeyParts { type Error = Error; + #[tracing::instrument( + err, + fields(key = utils::u8_slice_to_hex(key.as_bytes())), + )] fn try_from(key: &MediaFileKey) -> Result { let mut parts = key.as_bytes().split(|&b| b == 0xFF); let mxc_bytes = parts .next() - .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + .ok_or_else(|| Error::BadDatabase("Media ID in db is invalid."))?; let mxc = utils::string_from_bytes(mxc_bytes) .map_err(|_| { - Error::bad_database("Media MXC URI in db is invalid unicode.") + Error::BadDatabase("Media MXC URI in db is invalid unicode.") })? .into(); let thumbnail_size_bytes = parts .next() - .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + .ok_or_else(|| Error::BadDatabase("Media ID in db is invalid."))?; let thumbnail_size_bytes: &[u8; 8] = thumbnail_size_bytes.try_into().map_err(|_| { - Error::bad_database("Media ID thumbnail size in db is invalid") + Error::BadDatabase("Media ID thumbnail size in db is invalid") })?; let width = u32::from_be_bytes( thumbnail_size_bytes[..4].try_into().expect("should be 4 bytes"), @@ -49,7 +53,7 @@ impl TryFrom<&MediaFileKey> for MediaFileKeyParts { .next() .map(|bytes| { utils::string_from_bytes(bytes).map_err(|_| { - Error::bad_database( + Error::BadDatabase( "Content type in mediaid_file is invalid unicode.", ) }) @@ -58,14 +62,14 @@ impl TryFrom<&MediaFileKey> for MediaFileKeyParts { let content_disposition_bytes = parts .next() - .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + .ok_or_else(|| Error::BadDatabase("Media ID in db is invalid."))?; let content_disposition = if content_disposition_bytes.is_empty() { None } else { Some(utils::string_from_bytes(content_disposition_bytes).map_err( |_| { - Error::bad_database( + Error::BadDatabase( "Content Disposition in mediaid_file is invalid \ unicode.", ) diff --git a/src/utils.rs b/src/utils.rs index 504d21b1..e0d62063 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,6 +4,7 @@ pub(crate) mod on_demand_hashmap; use std::{ borrow::Cow, cmp, fmt, + fmt::Write, str::FromStr, time::{SystemTime, UNIX_EPOCH}, }; @@ -366,6 +367,17 @@ pub(crate) fn curlify(req: &http::Request) -> Option { ) } +/// Format a u8 slice as an uppercase hex string +/// +/// The output does not contain a leading `0x` nor any non-hex characters (e.g. +/// whitespace or commas do not appear in the output). +pub(crate) fn u8_slice_to_hex(slice: &[u8]) -> String { + slice.iter().fold(String::new(), |mut acc, x| { + write!(acc, "{x:X}").expect("in-memory write should succeed"); + acc + }) +} + #[cfg(test)] mod tests { use crate::utils::dbg_truncate_str; From b34d78a030c664d6c17028710f92e2a3918bef2e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 19 Sep 2024 12:37:22 -0700 Subject: [PATCH 384/617] skip over broken keys instead of aborting Errors will show up in the logs in this case with detailed information about what broke. In the future we should add some kind of database integrity check functionality and also functionality to repair/delete broken data, but for now this at least makes it work 99.99% of the time. --- src/service/admin.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 81a5c017..cf8ab5db 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -844,7 +844,10 @@ impl Service { }); while let Some(mxc) = rx.recv().await { - let mxc = mxc?; + let Ok(mxc) = mxc else { + continue; + }; + let server_name = mxc.server_name(); if server_name == Ok(services().globals.server_name()) { From 88b009a8d4c8627fd07d2976b065263128dfab20 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 17 Sep 2024 21:09:25 -0700 Subject: [PATCH 385/617] update changelog --- book/changelog.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 031a34ae..9687498b 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -106,7 +106,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52), [!54](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/54), [!56](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/56), - [!69](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/69)) + [!69](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/69), + [!102](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/102)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. @@ -238,4 +239,5 @@ This will be the first release of Grapevine since it was forked from Conduit 17. Added support for configuring old verify/signing keys in config (`federation.old_verify_keys`) ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) 18. Added admin commands to delete media - ([!99](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/99)) + ([!99](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/99), + [!102](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/102)) From d3889946576c92d9e4241325c610f11e582728fe Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 19 Sep 2024 13:38:41 -0700 Subject: [PATCH 386/617] rewrite media key parser Fixes a regression in e2cba15ed2ec1681cc455d66a7bb2053cc204f23 where the Content-Type and Content-Disposition parts are extracted in the wrong order. Fixes a long-standing issue in b6d721374f970cca912477a3972bb857758d983d where the Content-Type part was allowed to be completely missing rather than present and 0 bytes long. Improves the error messages for various parsing failures to be unique and more obvious. --- src/database/key_value/media.rs | 78 +++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index e3f771d3..9fc15e54 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -26,57 +26,67 @@ impl TryFrom<&MediaFileKey> for MediaFileKeyParts { fn try_from(key: &MediaFileKey) -> Result { let mut parts = key.as_bytes().split(|&b| b == 0xFF); + // Extract parts + let mxc_bytes = parts .next() - .ok_or_else(|| Error::BadDatabase("Media ID in db is invalid."))?; + .ok_or_else(|| Error::BadDatabase("Missing MXC URI bytes"))?; + + let thumbnail_size_bytes = parts.next().ok_or_else(|| { + Error::BadDatabase("Missing thumbnail size bytes") + })?; + + let content_disposition_bytes = parts.next().ok_or_else(|| { + Error::BadDatabase("Missing Content-Disposition bytes") + })?; + + let content_type_bytes = parts + .next() + .ok_or_else(|| Error::BadDatabase("Missing Content-Type bytes"))?; + + if parts.next().is_some() { + return Err(Error::BadDatabase("Too many parts")); + } + + // Parse parts + let mxc = utils::string_from_bytes(mxc_bytes) - .map_err(|_| { - Error::BadDatabase("Media MXC URI in db is invalid unicode.") - })? + .map_err(|_| Error::BadDatabase("Invalid unicode in MXC URI"))? .into(); - let thumbnail_size_bytes = parts - .next() - .ok_or_else(|| Error::BadDatabase("Media ID in db is invalid."))?; - let thumbnail_size_bytes: &[u8; 8] = - thumbnail_size_bytes.try_into().map_err(|_| { - Error::BadDatabase("Media ID thumbnail size in db is invalid") - })?; - let width = u32::from_be_bytes( - thumbnail_size_bytes[..4].try_into().expect("should be 4 bytes"), - ); - let height = u32::from_be_bytes( - thumbnail_size_bytes[4..].try_into().expect("should be 4 bytes"), - ); + let (width, height) = <&[u8; 8]>::try_from(thumbnail_size_bytes) + .map(|eight_bytes| { + let width = u32::from_be_bytes( + eight_bytes[..4].try_into().expect("should be 4 bytes"), + ); + let height = u32::from_be_bytes( + eight_bytes[4..].try_into().expect("should be 4 bytes"), + ); - let content_type = parts - .next() - .map(|bytes| { - utils::string_from_bytes(bytes).map_err(|_| { - Error::BadDatabase( - "Content type in mediaid_file is invalid unicode.", - ) - }) + (width, height) }) - .transpose()?; - - let content_disposition_bytes = parts - .next() - .ok_or_else(|| Error::BadDatabase("Media ID in db is invalid."))?; + .map_err(|_| { + Error::BadDatabase("Wrong number of thumbnail size bytes") + })?; let content_disposition = if content_disposition_bytes.is_empty() { None } else { Some(utils::string_from_bytes(content_disposition_bytes).map_err( |_| { - Error::BadDatabase( - "Content Disposition in mediaid_file is invalid \ - unicode.", - ) + Error::BadDatabase("Invalid unicode in Content-Disposition") }, )?) }; + let content_type = if content_type_bytes.is_empty() { + None + } else { + Some(utils::string_from_bytes(content_type_bytes).map_err( + |_| Error::BadDatabase("Invalid unicode in Content-Type"), + )?) + }; + Ok(MediaFileKeyParts { mxc, width, From 1ee3bbb316cb7823ce36097d8d33c779e7ff80eb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 20 Sep 2024 16:52:05 -0700 Subject: [PATCH 387/617] oops, i dropped my fork The maintainers had a discussion internally and decided it's unlikely that we'll have the capacity to try to do a rewrite, which was the original reason for the suffix's presence. So, now can get rid of it. --- README.md | 2 +- book.toml | 2 +- book/changelog.md | 164 +++++++++++++++++++++---------------------- book/introduction.md | 4 +- 4 files changed, 86 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 3f4c2c9a..f4de02de 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ A Matrix homeserver. [Click here to read the latest version.][0] -[0]: https://matrix.pages.gitlab.computer.surgery/grapevine-fork/ +[0]: https://matrix.pages.gitlab.computer.surgery/grapevine/ diff --git a/book.toml b/book.toml index f71ac6e0..038e5202 100644 --- a/book.toml +++ b/book.toml @@ -9,4 +9,4 @@ build-dir = "public" [output.html] git-repository-icon = "fa-git-square" -git-repository-url = "https://gitlab.computer.surgery/matrix/grapevine-fork" +git-repository-url = "https://gitlab.computer.surgery/matrix/grapevine" diff --git a/book/changelog.md b/book/changelog.md index 9687498b..b34d3412 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -35,187 +35,187 @@ This will be the first release of Grapevine since it was forked from Conduit ### Security 1. Prevent XSS via user-uploaded media. - ([!8](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/8)) + ([!8](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/8)) 2. Switch from incorrect, hand-rolled `X-Matrix` `Authorization` parser to the much better implementation provided by Ruma. - ([!31](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/31)) + ([!31](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/31)) * This is not practically exploitable to our knowledge, but this change does reduce risk. 3. Switch to a more trustworthy password hashing library. - ([!29](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/29)) + ([!29](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/29)) * This is not practically exploitable to our knowledge, but this change does reduce risk. 4. Don't return redacted events from the search endpoint. - ([!41 (f74043d)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=f74043df9aa59b406b5086c2e9fa2791a31aa41b), - [!41 (83cdc9c)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=83cdc9c708cd7b50fe1ab40ea6a68dcf252c190b)) + ([!41 (f74043d)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/41/diffs?commit_id=f74043df9aa59b406b5086c2e9fa2791a31aa41b), + [!41 (83cdc9c)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/41/diffs?commit_id=83cdc9c708cd7b50fe1ab40ea6a68dcf252c190b)) 5. Prevent impersonation in EDUs. - ([!41 (da99b07)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=da99b0706e683a2d347768efe5b50676abdf7b44)) + ([!41 (da99b07)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/41/diffs?commit_id=da99b0706e683a2d347768efe5b50676abdf7b44)) * `m.signing_key_update` was not affected by this bug. 6. Verify PDUs and transactions against the temporally-correct signing keys. - ([!41 (9087da9)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/41/diffs?commit_id=9087da91db8585f34d026a48ba8fdf64865ba14d)) + ([!41 (9087da9)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/41/diffs?commit_id=9087da91db8585f34d026a48ba8fdf64865ba14d)) 7. Only allow the admin bot to change the room ID that the admin room alias points to. - ([!42](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/42)) + ([!42](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/42)) ### Removed 1. Remove update checker. - ([17a0b34](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/17a0b3430934fbb8370066ee9dc3506102c5b3f6)) + ([17a0b34](https://gitlab.computer.surgery/matrix/grapevine/-/commit/17a0b3430934fbb8370066ee9dc3506102c5b3f6)) 2. Remove optional automatic display name emoji for newly registered users. - ([cddf699](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/cddf6991f280008b5af5acfab6a9719bb0cfb7f1)) + ([cddf699](https://gitlab.computer.surgery/matrix/grapevine/-/commit/cddf6991f280008b5af5acfab6a9719bb0cfb7f1)) 3. Remove admin room welcome message on first startup. - ([c9945f6](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/c9945f6bbac6e22af6cf955cfa99826d4b04fe8c)) + ([c9945f6](https://gitlab.computer.surgery/matrix/grapevine/-/commit/c9945f6bbac6e22af6cf955cfa99826d4b04fe8c)) 4. Remove incomplete presence implementation. - ([f27941d](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/f27941d5108acda250921c6a58499a46568fd030)) + ([f27941d](https://gitlab.computer.surgery/matrix/grapevine/-/commit/f27941d5108acda250921c6a58499a46568fd030)) 5. Remove Debian packaging. - ([d41f0fb](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/d41f0fbf72dae6562358173f425d23bb0e174ca2)) + ([d41f0fb](https://gitlab.computer.surgery/matrix/grapevine/-/commit/d41f0fbf72dae6562358173f425d23bb0e174ca2)) 6. Remove Docker packaging. - ([!48](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/48)) + ([!48](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/48)) 7. **BREAKING:** Remove unstable room versions. - ([!59](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/59)) + ([!59](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/59)) ### Changed 1. **BREAKING:** Rename `conduit_cache_capacity_modifier` configuration option to `cache_capacity_modifier`. - ([5619d7e](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/5619d7e3180661731800e253b558b88b407d2ae7)) + ([5619d7e](https://gitlab.computer.surgery/matrix/grapevine/-/commit/5619d7e3180661731800e253b558b88b407d2ae7)) * If you are explicitly setting this configuration option, make sure to change its name before updating. 2. **BREAKING:** Rename Conduit to Grapevine. - ([360e020](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/360e020b644bd012ed438708b661a25fbd124f68)) + ([360e020](https://gitlab.computer.surgery/matrix/grapevine/-/commit/360e020b644bd012ed438708b661a25fbd124f68)) * The `CONDUIT_VERSION_EXTRA` build-time environment variable has been renamed to `GRAPEVINE_VERSION_EXTRA`. This change only affects distribution packagers or non-Nix users who are building from source. If you fall into one of those categories *and* were explicitly setting this environment variable, make sure to change its name before building Grapevine. 3. **BREAKING:** Change the default port from 8000 to 6167. - ([f205280](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/f2052805201f0685d850592b1c96f4861c58fb22)) + ([f205280](https://gitlab.computer.surgery/matrix/grapevine/-/commit/f2052805201f0685d850592b1c96f4861c58fb22)) * If you relied on the default port being 8000, either update your other configuration to use the new port, or explicitly configure Grapevine's port to 8000. 4. Improve tracing spans and events. - ([!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) - (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), - [!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) - (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), - [!11 (f556fce)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/11/diffs?commit_id=f556fce73eb7beec2ed7b1781df0acdf47920d9c) - (merged as [ac42e0b](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/ac42e0bfff6af8677636a3dc1a56701a3255071d)), - [!18](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/18), - [!26](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/26), - [!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50), - [!52](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/52), - [!54](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/54), - [!56](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/56), - [!69](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/69), - [!102](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/102)) + ([!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) + (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), + [!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) + (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), + [!11 (f556fce)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/11/diffs?commit_id=f556fce73eb7beec2ed7b1781df0acdf47920d9c) + (merged as [ac42e0b](https://gitlab.computer.surgery/matrix/grapevine/-/commit/ac42e0bfff6af8677636a3dc1a56701a3255071d)), + [!18](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/18), + [!26](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/26), + [!50](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/50), + [!52](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/52), + [!54](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/54), + [!56](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/56), + [!69](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/69), + [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. - ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) + ([!12](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. - ([!24](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/24)) + ([!24](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/24)) * If you relied on federation being disabled by default, make sure to explicitly disable it before upgrading. 7. **BREAKING:** Remove the `[global]` section from the configuration file. - ([!38](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/38)) + ([!38](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/38)) * Details on how to migrate can be found in the merge request's description. 8. **BREAKING:** Allow specifying multiple transport listeners in the configuration file. - ([!39](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/39)) + ([!39](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/39)) * Details on how to migrate can be found in the merge request's description. 9. Increase default log level so that span information is included. - ([!50](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/50)) + ([!50](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/50)) 10. **BREAKING:** Reorganize config into sections. - ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) + ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) * Details on how to migrate can be found in the merge request's description. 11. Try to generate thumbnails for remote media ourselves if the federation thumbnail request fails. - ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) + ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58)) 12. **BREAKING:** Disable unauthenticated access to media by default, set the `serve_media_unauthenticated` config option to `true` to enable it. - ([!103](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/103)) + ([!103](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/103)) ### Fixed 1. Fix questionable numeric conversions. - ([71c48f6](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/71c48f66c4922813c2dc30b7b875200e06ce4b75)) + ([71c48f6](https://gitlab.computer.surgery/matrix/grapevine/-/commit/71c48f66c4922813c2dc30b7b875200e06ce4b75)) 2. Stop sending no-longer-valid cached responses from the `/_matrix/client/{r0,v3}/sync` endpoints. - ([!7](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/7)) + ([!7](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/7)) 3. Stop returning extra E2EE device updates from `/_matrix/client/{r0,v3}/sync` as that violates the specification. - ([!12](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/12)) + ([!12](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/12)) 4. Make certain membership state transitions work correctly again. - ([!16](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/16)) + ([!16](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/16)) * For example, it was previously impossible to unban users from rooms. 5. Ensure that `tracing-flame` flushes all its data before the process exits. - ([!20 (263edcc)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/20/diffs?commit_id=263edcc8a127ad2a541a3bb6ad35a8a459ea5616)) + ([!20 (263edcc)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/20/diffs?commit_id=263edcc8a127ad2a541a3bb6ad35a8a459ea5616)) 6. Reduce the likelihood of locking up the async runtime. - ([!19](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/19)) + ([!19](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/19)) 7. Fix dynamically linked jemalloc builds. - ([!23](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/23)) + ([!23](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/23)) 8. Fix search results not including subsequent pages in certain situations. - ([!35 (0cdf032)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/35/diffs?commit_id=0cdf03288ab8fa363c313bd929c8b5183d14ab77)) + ([!35 (0cdf032)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/35/diffs?commit_id=0cdf03288ab8fa363c313bd929c8b5183d14ab77)) 9. Fix search results missing events in subsequent pages in certain situations. - ([!35 (3551a6e)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/35/diffs?commit_id=3551a6ef7a29219b9b30f50a7e8c92b92debcdcf)) + ([!35 (3551a6e)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/35/diffs?commit_id=3551a6ef7a29219b9b30f50a7e8c92b92debcdcf)) 10. Only process admin commands if the admin bot is in the admin room. - ([!43](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/43)) + ([!43](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/43)) 11. Fix bug where invalid account data from a client could prevent a user from joining any upgraded rooms and brick rooms that affected users attempted to upgrade. - ([!53](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/53)) + ([!53](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/53)) 12. Fix bug where unexpected keys were deleted from `m.direct` account data events when joining an upgraded room. - ([!53](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/53)) + ([!53](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/53)) 13. Fixed appservice users not receiving federated invites if the local server isn't already resident in the room - ([!80](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/80)) + ([!80](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/80)) 14. Fix bug where, if a server has multiple public keys, only one would be fetched. - ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) + ([!78](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/78)) 15. Fix bug where expired keys may not be re-fetched in some scenarios. - ([!78](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/78)) + ([!78](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/78)) 16. Fix bug where signing keys would not be fetched when joining a room if we hadn't previously seen any signing keys from that server. - ([!87](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/87)) + ([!87](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/87)) 17. Fixed bug - ([#48](https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/48)) + ([#48](https://gitlab.computer.surgery/matrix/grapevine/-/issues/48)) that caused us to attempt to fetch our own signing keys from ourselves over federation, and fail ("Won't send federation request to ourselves"). - ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) + ([!96](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/96)) 18. Fixed incoming HTTP/2 requests failing federation signature check. - ([!104](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/104)) + ([!104](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/104)) ### Added 1. Add various conveniences for users of the Nix package. - ([51f9650](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/51f9650ca7bc9378690d331192c85fea3c151b58), - [bbb1a6f](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/bbb1a6fea45b16e8d4f94c1afbf7fa22c9281f37)) + ([51f9650](https://gitlab.computer.surgery/matrix/grapevine/-/commit/51f9650ca7bc9378690d331192c85fea3c151b58), + [bbb1a6f](https://gitlab.computer.surgery/matrix/grapevine/-/commit/bbb1a6fea45b16e8d4f94c1afbf7fa22c9281f37)) 2. Add a NixOS module. - ([33e7a46](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/33e7a46b5385ea9035c9d13c6775d63e5626a4c7)) + ([33e7a46](https://gitlab.computer.surgery/matrix/grapevine/-/commit/33e7a46b5385ea9035c9d13c6775d63e5626a4c7)) 3. Add a Conduit compat mode. - ([a25f2ec](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/a25f2ec95045c5620c98eead88197a0bf13e6bb3)) + ([a25f2ec](https://gitlab.computer.surgery/matrix/grapevine/-/commit/a25f2ec95045c5620c98eead88197a0bf13e6bb3)) * **BREAKING:** If you're migrating from Conduit, this option must be enabled or else your homeserver will refuse to start. 4. Include `GRAPEVINE_VERSION_EXTRA` information in the `/_matrix/federation/v1/version` endpoint. - ([509b70b](https://gitlab.computer.surgery/matrix/grapevine-fork/-/commit/509b70bd827fec23b88e223b57e0df3b42cede34)) + ([509b70b](https://gitlab.computer.surgery/matrix/grapevine/-/commit/509b70bd827fec23b88e223b57e0df3b42cede34)) 5. Allow multiple tracing subscribers to be active at once. - ([!20 (7a154f74)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/20/diffs?commit_id=7a154f74166c1309ca5752149e02bbe44cd91431)) + ([!20 (7a154f74)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/20/diffs?commit_id=7a154f74166c1309ca5752149e02bbe44cd91431)) 6. Allow configuring the filter for `tracing-flame`. - ([!20 (507de06)](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/20/diffs?commit_id=507de063f53f52e0cf8e2c1a67215a5ad87bb35a)) + ([!20 (507de06)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/20/diffs?commit_id=507de063f53f52e0cf8e2c1a67215a5ad87bb35a)) 7. Collect HTTP response time metrics via OpenTelemetry and optionally expose them as Prometheus metrics. This functionality is disabled by default. - ([!22](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/22)) + ([!22](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/22)) 8. Collect metrics for lookup results (e.g. cache hits/misses). - ([!15](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/15), - [!36](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/36)) + ([!15](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/15), + [!36](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/36)) 9. Add configuration options for controlling the log format and colors. - ([!46](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/46)) + ([!46](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/46)) 10. Recognize the `!admin` prefix to invoke admin commands. - ([!45](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/45)) + ([!45](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/45)) 11. Add the `set-tracing-filter` admin command to change log/metrics/flame filters dynamically at runtime. - ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) + ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) 12. Add more configuration options. - ([!49](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/49)) + ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) * `observability.traces.filter`: The `tracing` filter to use for OpenTelemetry traces. * `observability.traces.endpoint`: Where OpenTelemetry should send traces. @@ -225,19 +225,19 @@ This will be the first release of Grapevine since it was forked from Conduit * `observability.logs.timestamp`: Whether timestamps should be included in the logs. 13. Support building nix packages without IFD - ([!73](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/73)) + ([!73](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/73)) 14. Report local users getting banned in the server logs and admin room. - ([!65](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/65), - [!84](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/84)) + ([!65](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/65), + [!84](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/84)) 15. Added support for Authenticated Media ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)). - ([!58](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/58)) + ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58)) 16. **BREAKING:** Added support for configuring and serving `/.well-known/matrix/...` data. - ([!90](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/90), - [!94](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/94)) + ([!90](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/90), + [!94](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/94)) * The `server_discovery.client.base_url` option is now required. 17. Added support for configuring old verify/signing keys in config (`federation.old_verify_keys`) - ([!96](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/96)) + ([!96](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/96)) 18. Added admin commands to delete media - ([!99](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/99), - [!102](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/102)) + ([!99](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/99), + [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102)) diff --git a/book/introduction.md b/book/introduction.md index fa739d48..72b8a871 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -46,8 +46,8 @@ our ability to accomplish our goals: want to pull in. [envsubst]: https://github.com/a8m/envsubst -[migration-tool]: https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/38 -[db-compat]: https://gitlab.computer.surgery/matrix/grapevine-fork/-/issues/17 +[migration-tool]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/38 +[db-compat]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/17 ## Project management From be87774a3b24fbac0dbd3c697dbe0fa7e117400c Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 31 Aug 2024 20:51:26 -0700 Subject: [PATCH 388/617] set up structure for multiple cli commands The previous cli is now behind the 'serve' subcommand. --- nix/modules/default/default.nix | 2 +- src/args.rs | 35 ---------------------- src/cli.rs | 51 +++++++++++++++++++++++++++++++++ src/main.rs | 11 +++++-- 4 files changed, 60 insertions(+), 39 deletions(-) delete mode 100644 src/args.rs create mode 100644 src/cli.rs diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index d6d8ac65..3bc86c56 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -79,7 +79,7 @@ in # Keep sorted serviceConfig = { DynamicUser = true; - ExecStart = "${lib.getExe cfg.package} --config ${configFile}"; + ExecStart = "${lib.getExe cfg.package} serve --config ${configFile}"; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; diff --git a/src/args.rs b/src/args.rs deleted file mode 100644 index 33862de2..00000000 --- a/src/args.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Integration with `clap` - -use std::path::PathBuf; - -use clap::{CommandFactory as _, FromArgMatches as _, Parser}; - -/// Command line arguments -#[derive(Parser)] -#[clap(about, version = crate::version())] -pub(crate) struct Args { - /// Path to the configuration file - #[clap(long, short)] - pub(crate) config: Option, -} - -/// Parse command line arguments into structured data -pub(crate) fn parse() -> Args { - let mut command = Args::command().mut_arg("config", |x| { - let help = "Set the path to the configuration file"; - x.help(help).long_help(format!( - "{}\n\nIf this option is specified, the provided value is used \ - as-is.\n\nIf this option is not specified, then the XDG Base \ - Directory Specification is followed, searching for the path `{}` \ - in the configuration directories. - ", - help, - crate::config::DEFAULT_PATH.display(), - )) - }); - - match Args::from_arg_matches(&command.get_matches_mut()) { - Ok(x) => x, - Err(e) => e.format(&mut command).exit(), - } -} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..cb6f91bb --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,51 @@ +//! Integration with `clap` + +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +/// Command line arguments +#[derive(Parser)] +#[clap( + about, + version = crate::version(), +)] +pub(crate) struct Args { + #[clap(subcommand)] + pub(crate) command: Command, +} + +#[derive(Subcommand)] +pub(crate) enum Command { + /// Run the server. + Serve(ServeArgs), +} + +/// Wrapper for the `--config` arg. +/// +/// This exists to centralize the `mut_arg` code that sets the help value based +/// on runtime information. +#[derive(clap::Args)] +#[clap(mut_arg("config", |x| { + let help = "Set the path to the configuration file"; + x.help(help).long_help(format!( + "{}\n\nIf this option is specified, the provided value is used \ + as-is.\n\nIf this option is not specified, then the XDG Base \ + Directory Specification is followed, searching for the path `{}` \ + in the configuration directories. + ", + help, + crate::config::DEFAULT_PATH.display(), + )) +}))] +pub(crate) struct ConfigArg { + /// Path to the configuration file + #[clap(long, short)] + pub(crate) config: Option, +} + +#[derive(clap::Args)] +pub(crate) struct ServeArgs { + #[clap(flatten)] + pub(crate) config: ConfigArg, +} diff --git a/src/main.rs b/src/main.rs index 049fd10c..e90e9f7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use axum::{ use axum_server::{ bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, }; +use clap::Parser; use futures_util::FutureExt; use http::{ header::{self, HeaderName}, @@ -41,7 +42,7 @@ use tower_http::{ use tracing::{debug, error, info, info_span, warn, Instrument}; mod api; -mod args; +mod cli; mod config; mod database; mod error; @@ -51,6 +52,7 @@ mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; use api::{client_server, server_server, well_known}; +use cli::{Args, Command}; pub(crate) use config::{Config, ListenConfig}; pub(crate) use database::KeyValueDatabase; pub(crate) use service::{pdu::PduEvent, Services}; @@ -108,9 +110,12 @@ async fn main() -> ExitCode { async fn try_main() -> Result<(), error::Main> { use error::Main as Error; - let args = args::parse(); + let args = Args::parse(); + // This is a placeholder, the logic specific to the 'serve' command will be + // moved to another file in a later commit + let Command::Serve(args) = args.command; - let config = config::load(args.config.as_ref()).await?; + let config = config::load(args.config.config.as_ref()).await?; let (_guard, reload_handles) = observability::init(&config)?; From 86515d53cc09d700092dc5b24760b236e0159cb9 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 5 Sep 2024 21:09:13 -0700 Subject: [PATCH 389/617] move 'serve' command logic into a submodule of 'cli' The changes to 'main.rs' and 'cli/serve.rs' in this commit are almost pure code-motion. --- src/cli.rs | 15 ++ src/cli/serve.rs | 657 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 664 +---------------------------------------------- 3 files changed, 677 insertions(+), 659 deletions(-) create mode 100644 src/cli/serve.rs diff --git a/src/cli.rs b/src/cli.rs index cb6f91bb..cb8f4154 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,9 +1,16 @@ //! Integration with `clap` +//! +//! CLI argument structs are defined in this module. Execution logic for each +//! command goes in a submodule. use std::path::PathBuf; use clap::{Parser, Subcommand}; +use crate::error; + +mod serve; + /// Command line arguments #[derive(Parser)] #[clap( @@ -49,3 +56,11 @@ pub(crate) struct ServeArgs { #[clap(flatten)] pub(crate) config: ConfigArg, } + +impl Args { + pub(crate) async fn run(self) -> Result<(), error::Main> { + match self.command { + Command::Serve(args) => serve::run(args).await, + } + } +} diff --git a/src/cli/serve.rs b/src/cli/serve.rs new file mode 100644 index 00000000..71980e42 --- /dev/null +++ b/src/cli/serve.rs @@ -0,0 +1,657 @@ +use std::{future::Future, net::SocketAddr, sync::atomic, time::Duration}; + +use axum::{ + extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, + response::IntoResponse, + routing::{any, get, on, MethodFilter}, + Router, +}; +use axum_server::{ + bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, +}; +use futures_util::FutureExt; +use http::{ + header::{self, HeaderName}, + Method, StatusCode, Uri, +}; +use ruma::api::{ + client::{ + error::{Error as RumaError, ErrorBody, ErrorKind}, + uiaa::UiaaResponse, + }, + IncomingRequest, +}; +use tokio::{signal, task::JoinSet}; +use tower::ServiceBuilder; +use tower_http::{ + cors::{self, CorsLayer}, + trace::TraceLayer, + ServiceBuilderExt as _, +}; +use tracing::{debug, info, info_span, warn, Instrument}; + +use super::ServeArgs; +use crate::{ + api::{ + client_server, + ruma_wrapper::{Ar, Ra}, + server_server, well_known, + }, + config, + config::{Config, ListenConfig}, + database::KeyValueDatabase, + error, observability, services, utils, + utils::error::{Error, Result}, +}; + +pub(crate) async fn run(args: ServeArgs) -> Result<(), error::Main> { + use error::Main as Error; + + let config = config::load(args.config.config.as_ref()).await?; + + let (_guard, reload_handles) = observability::init(&config)?; + + // This is needed for opening lots of file descriptors, which tends to + // happen more often when using RocksDB and making lots of federation + // connections at startup. The soft limit is usually 1024, and the hard + // limit is usually 512000; I've personally seen it hit >2000. + // + // * https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6 + // * https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741 + #[cfg(unix)] + maximize_fd_limit() + .expect("should be able to increase the soft limit to the hard limit"); + + info!("Loading database"); + KeyValueDatabase::load_or_create(config, reload_handles) + .await + .map_err(Error::DatabaseError)?; + + info!("Starting server"); + run_server().await?; + + Ok(()) +} + +#[allow(clippy::too_many_lines)] +async fn run_server() -> Result<(), error::Serve> { + use error::Serve as Error; + + let config = &services().globals.config; + + let x_requested_with = HeaderName::from_static("x-requested-with"); + + let middlewares = ServiceBuilder::new() + .sensitive_headers([header::AUTHORIZATION]) + .layer(axum::middleware::from_fn(spawn_task)) + .layer( + TraceLayer::new_for_http() + .make_span_with(|request: &http::Request<_>| { + let endpoint = if let Some(endpoint) = + request.extensions().get::() + { + endpoint.as_str() + } else { + request.uri().path() + }; + + let method = request.method(); + + tracing::info_span!( + "http_request", + otel.name = format!("{method} {endpoint}"), + %method, + %endpoint, + ) + }) + .on_request( + |request: &http::Request<_>, _span: &tracing::Span| { + // can be enabled selectively using `filter = + // grapevine[incoming_request_curl]=trace` in config + tracing::trace_span!("incoming_request_curl").in_scope( + || { + tracing::trace!( + cmd = utils::curlify(request), + "curl command line for incoming request \ + (guessed hostname)" + ); + }, + ); + }, + ), + ) + .layer(axum::middleware::from_fn(unrecognized_method)) + .layer( + CorsLayer::new() + .allow_origin(cors::Any) + .allow_methods([ + Method::GET, + Method::POST, + Method::PUT, + Method::DELETE, + Method::OPTIONS, + ]) + .allow_headers([ + header::ORIGIN, + x_requested_with, + header::CONTENT_TYPE, + header::ACCEPT, + header::AUTHORIZATION, + ]) + .max_age(Duration::from_secs(86400)), + ) + .layer(DefaultBodyLimit::max( + config + .max_request_size + .try_into() + .expect("failed to convert max request size"), + )) + .layer(axum::middleware::from_fn(observability::http_metrics_layer)); + + let app = routes(config).layer(middlewares).into_make_service(); + let mut handles = Vec::new(); + let mut servers = JoinSet::new(); + + let tls_config = if let Some(tls) = &config.tls { + Some(RustlsConfig::from_pem_file(&tls.certs, &tls.key).await.map_err( + |err| Error::LoadCerts { + certs: tls.certs.clone(), + key: tls.key.clone(), + err, + }, + )?) + } else { + None + }; + + if config.listen.is_empty() { + return Err(Error::NoListeners); + } + + for listen in &config.listen { + info!(listener = %listen, "Listening for incoming traffic"); + match listen { + ListenConfig::Tcp { + address, + port, + tls, + } => { + let addr = SocketAddr::from((*address, *port)); + let handle = ServerHandle::new(); + handles.push(handle.clone()); + let server = if *tls { + let tls_config = tls_config + .clone() + .ok_or_else(|| Error::NoTlsCerts(listen.clone()))?; + bind_rustls(addr, tls_config) + .handle(handle) + .serve(app.clone()) + .left_future() + } else { + bind(addr).handle(handle).serve(app.clone()).right_future() + }; + servers.spawn( + server.then(|result| async { (listen.clone(), result) }), + ); + } + } + } + + #[cfg(feature = "systemd")] + sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) + .expect("should be able to notify systemd"); + + tokio::spawn(shutdown_signal(handles)); + + while let Some(result) = servers.join_next().await { + let (listen, result) = + result.expect("should be able to join server task"); + result.map_err(|err| Error::Listen(err, listen))?; + } + + Ok(()) +} + +/// Ensures the request runs in a new tokio thread. +/// +/// The axum request handler task gets cancelled if the connection is shut down; +/// by spawning our own task, processing continue after the client disconnects. +async fn spawn_task( + req: axum::extract::Request, + next: axum::middleware::Next, +) -> std::result::Result { + if services().globals.shutdown.load(atomic::Ordering::Relaxed) { + return Err(StatusCode::SERVICE_UNAVAILABLE); + } + tokio::spawn(next.run(req)) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) +} + +async fn unrecognized_method( + req: axum::extract::Request, + next: axum::middleware::Next, +) -> std::result::Result { + let method = req.method().clone(); + let uri = req.uri().clone(); + let inner = next.run(req).await; + if inner.status() == StatusCode::METHOD_NOT_ALLOWED { + warn!(%method, %uri, "Method not allowed"); + return Ok(Ra(UiaaResponse::MatrixError(RumaError { + body: ErrorBody::Standard { + kind: ErrorKind::Unrecognized, + message: "M_UNRECOGNIZED: Unrecognized request".to_owned(), + }, + status_code: StatusCode::METHOD_NOT_ALLOWED, + })) + .into_response()); + } + Ok(inner) +} + +#[allow(clippy::too_many_lines)] +fn routes(config: &Config) -> Router { + use client_server as c2s; + use server_server as s2s; + + let router = Router::new() + .ruma_route(c2s::get_supported_versions_route) + .ruma_route(c2s::get_register_available_route) + .ruma_route(c2s::register_route) + .ruma_route(c2s::get_login_types_route) + .ruma_route(c2s::login_route) + .ruma_route(c2s::whoami_route) + .ruma_route(c2s::logout_route) + .ruma_route(c2s::logout_all_route) + .ruma_route(c2s::change_password_route) + .ruma_route(c2s::deactivate_route) + .ruma_route(c2s::third_party_route) + .ruma_route(c2s::request_3pid_management_token_via_email_route) + .ruma_route(c2s::request_3pid_management_token_via_msisdn_route) + .ruma_route(c2s::get_capabilities_route) + .ruma_route(c2s::get_pushrules_all_route) + .ruma_route(c2s::set_pushrule_route) + .ruma_route(c2s::get_pushrule_route) + .ruma_route(c2s::set_pushrule_enabled_route) + .ruma_route(c2s::get_pushrule_enabled_route) + .ruma_route(c2s::get_pushrule_actions_route) + .ruma_route(c2s::set_pushrule_actions_route) + .ruma_route(c2s::delete_pushrule_route) + .ruma_route(c2s::get_room_event_route) + .ruma_route(c2s::get_room_aliases_route) + .ruma_route(c2s::get_filter_route) + .ruma_route(c2s::create_filter_route) + .ruma_route(c2s::set_global_account_data_route) + .ruma_route(c2s::set_room_account_data_route) + .ruma_route(c2s::get_global_account_data_route) + .ruma_route(c2s::get_room_account_data_route) + .ruma_route(c2s::set_displayname_route) + .ruma_route(c2s::get_displayname_route) + .ruma_route(c2s::set_avatar_url_route) + .ruma_route(c2s::get_avatar_url_route) + .ruma_route(c2s::get_profile_route) + .ruma_route(c2s::upload_keys_route) + .ruma_route(c2s::get_keys_route) + .ruma_route(c2s::claim_keys_route) + .ruma_route(c2s::create_backup_version_route) + .ruma_route(c2s::update_backup_version_route) + .ruma_route(c2s::delete_backup_version_route) + .ruma_route(c2s::get_latest_backup_info_route) + .ruma_route(c2s::get_backup_info_route) + .ruma_route(c2s::add_backup_keys_route) + .ruma_route(c2s::add_backup_keys_for_room_route) + .ruma_route(c2s::add_backup_keys_for_session_route) + .ruma_route(c2s::delete_backup_keys_for_room_route) + .ruma_route(c2s::delete_backup_keys_for_session_route) + .ruma_route(c2s::delete_backup_keys_route) + .ruma_route(c2s::get_backup_keys_for_room_route) + .ruma_route(c2s::get_backup_keys_for_session_route) + .ruma_route(c2s::get_backup_keys_route) + .ruma_route(c2s::set_read_marker_route) + .ruma_route(c2s::create_receipt_route) + .ruma_route(c2s::create_typing_event_route) + .ruma_route(c2s::create_room_route) + .ruma_route(c2s::redact_event_route) + .ruma_route(c2s::report_event_route) + .ruma_route(c2s::create_alias_route) + .ruma_route(c2s::delete_alias_route) + .ruma_route(c2s::get_alias_route) + .ruma_route(c2s::join_room_by_id_route) + .ruma_route(c2s::join_room_by_id_or_alias_route) + .ruma_route(c2s::joined_members_route) + .ruma_route(c2s::leave_room_route) + .ruma_route(c2s::forget_room_route) + .ruma_route(c2s::joined_rooms_route) + .ruma_route(c2s::kick_user_route) + .ruma_route(c2s::ban_user_route) + .ruma_route(c2s::unban_user_route) + .ruma_route(c2s::invite_user_route) + .ruma_route(c2s::set_room_visibility_route) + .ruma_route(c2s::get_room_visibility_route) + .ruma_route(c2s::get_public_rooms_route) + .ruma_route(c2s::get_public_rooms_filtered_route) + .ruma_route(c2s::search_users_route) + .ruma_route(c2s::get_member_events_route) + .ruma_route(c2s::get_protocols_route) + .ruma_route(c2s::send_message_event_route) + .ruma_route(c2s::send_state_event_for_key_route) + .ruma_route(c2s::get_state_events_route) + .ruma_route(c2s::get_state_events_for_key_route) + .ruma_route(c2s::sync_events_route) + .ruma_route(c2s::sync_events_v4_route) + .ruma_route(c2s::get_context_route) + .ruma_route(c2s::get_message_events_route) + .ruma_route(c2s::search_events_route) + .ruma_route(c2s::turn_server_route) + .ruma_route(c2s::send_event_to_device_route); + + // deprecated, but unproblematic + let router = router.ruma_route(c2s::get_media_config_legacy_route); + let router = if config.serve_media_unauthenticated { + router + .ruma_route(c2s::get_content_legacy_route) + .ruma_route(c2s::get_content_as_filename_legacy_route) + .ruma_route(c2s::get_content_thumbnail_legacy_route) + } else { + router + .route( + "/_matrix/media/v3/download/*path", + any(unauthenticated_media_disabled), + ) + .route( + "/_matrix/media/v3/thumbnail/*path", + any(unauthenticated_media_disabled), + ) + }; + + // authenticated media + let router = router + .ruma_route(c2s::get_media_config_route) + .ruma_route(c2s::create_content_route) + .ruma_route(c2s::get_content_route) + .ruma_route(c2s::get_content_as_filename_route) + .ruma_route(c2s::get_content_thumbnail_route); + + let router = router + .ruma_route(c2s::get_devices_route) + .ruma_route(c2s::get_device_route) + .ruma_route(c2s::update_device_route) + .ruma_route(c2s::delete_device_route) + .ruma_route(c2s::delete_devices_route) + .ruma_route(c2s::get_tags_route) + .ruma_route(c2s::update_tag_route) + .ruma_route(c2s::delete_tag_route) + .ruma_route(c2s::upload_signing_keys_route) + .ruma_route(c2s::upload_signatures_route) + .ruma_route(c2s::get_key_changes_route) + .ruma_route(c2s::get_pushers_route) + .ruma_route(c2s::set_pushers_route) + .ruma_route(c2s::upgrade_room_route) + .ruma_route(c2s::get_threads_route) + .ruma_route(c2s::get_relating_events_with_rel_type_and_event_type_route) + .ruma_route(c2s::get_relating_events_with_rel_type_route) + .ruma_route(c2s::get_relating_events_route) + .ruma_route(c2s::get_hierarchy_route); + + // Ruma doesn't have support for multiple paths for a single endpoint yet, + // and these routes share one Ruma request / response type pair with + // {get,send}_state_event_for_key_route. These two endpoints also allow + // trailing slashes. + let router = router + .route( + "/_matrix/client/r0/rooms/:room_id/state/:event_type", + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), + ) + .route( + "/_matrix/client/v3/rooms/:room_id/state/:event_type", + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), + ) + .route( + "/_matrix/client/r0/rooms/:room_id/state/:event_type/", + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), + ) + .route( + "/_matrix/client/v3/rooms/:room_id/state/:event_type/", + get(c2s::get_state_events_for_empty_key_route) + .put(c2s::send_state_event_for_empty_key_route), + ); + + let router = if config.observability.metrics.enable { + router.route( + "/metrics", + get(|| async { observability::METRICS.export() }), + ) + } else { + router + }; + + let router = router + .route( + "/_matrix/client/r0/rooms/:room_id/initialSync", + get(initial_sync), + ) + .route( + "/_matrix/client/v3/rooms/:room_id/initialSync", + get(initial_sync), + ) + .route("/", get(it_works)) + .fallback(not_found); + + let router = router + .route("/.well-known/matrix/client", get(well_known::client)) + .route("/.well-known/matrix/server", get(well_known::server)); + + if config.federation.enable { + router + .ruma_route(s2s::get_server_version_route) + .route("/_matrix/key/v2/server", get(s2s::get_server_keys_route)) + .route( + "/_matrix/key/v2/server/:key_id", + get(s2s::get_server_keys_deprecated_route), + ) + .ruma_route(s2s::get_public_rooms_route) + .ruma_route(s2s::get_public_rooms_filtered_route) + .ruma_route(s2s::send_transaction_message_route) + .ruma_route(s2s::get_event_route) + .ruma_route(s2s::get_backfill_route) + .ruma_route(s2s::get_missing_events_route) + .ruma_route(s2s::get_event_authorization_route) + .ruma_route(s2s::get_room_state_route) + .ruma_route(s2s::get_room_state_ids_route) + .ruma_route(s2s::create_join_event_template_route) + .ruma_route(s2s::create_join_event_v1_route) + .ruma_route(s2s::create_join_event_v2_route) + .ruma_route(s2s::create_invite_route) + .ruma_route(s2s::get_devices_route) + .ruma_route(s2s::get_room_information_route) + .ruma_route(s2s::get_profile_information_route) + .ruma_route(s2s::get_keys_route) + .ruma_route(s2s::claim_keys_route) + .ruma_route(s2s::media_download_route) + .ruma_route(s2s::media_thumbnail_route) + } else { + router + .route("/_matrix/federation/*path", any(federation_disabled)) + .route("/_matrix/key/*path", any(federation_disabled)) + } +} + +async fn shutdown_signal(handles: Vec) { + let ctrl_c = async { + signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + let sig: &str; + + tokio::select! { + () = ctrl_c => { sig = "Ctrl+C"; }, + () = terminate => { sig = "SIGTERM"; }, + } + + warn!(signal = %sig, "Shutting down due to signal"); + + services().globals.shutdown(); + + for handle in handles { + handle.graceful_shutdown(Some(Duration::from_secs(30))); + } + + #[cfg(feature = "systemd")] + sd_notify::notify(true, &[sd_notify::NotifyState::Stopping]) + .expect("should be able to notify systemd"); +} + +async fn federation_disabled(_: Uri) -> impl IntoResponse { + Error::bad_config("Federation is disabled.") +} + +async fn unauthenticated_media_disabled(_: Uri) -> impl IntoResponse { + Error::BadRequest( + ErrorKind::NotFound, + "Unauthenticated media access is disabled", + ) +} + +async fn not_found(method: Method, uri: Uri) -> impl IntoResponse { + debug!(%method, %uri, "Unknown route"); + Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request") +} + +async fn initial_sync(_uri: Uri) -> impl IntoResponse { + Error::BadRequest( + ErrorKind::GuestAccessForbidden, + "Guest access not implemented", + ) +} + +async fn it_works() -> &'static str { + "Hello from Grapevine!" +} + +trait RouterExt { + fn ruma_route(self, handler: H) -> Self + where + H: RumaHandler, + T: 'static; +} + +impl RouterExt for Router { + fn ruma_route(self, handler: H) -> Self + where + H: RumaHandler, + T: 'static, + { + handler.add_to_router(self) + } +} + +pub(crate) trait RumaHandler { + // Can't transform to a handler without boxing or relying on the + // nightly-only impl-trait-in-traits feature. Moving a small amount of + // extra logic into the trait allows bypassing both. + fn add_to_router(self, router: Router) -> Router; +} + +macro_rules! impl_ruma_handler { + ( $($ty:ident),* $(,)? ) => { + #[axum::async_trait] + #[allow(non_snake_case)] + impl + RumaHandler<($($ty,)* Ar,)> for F + where + Req: IncomingRequest + Send + 'static, + Resp: IntoResponse, + F: FnOnce($($ty,)* Ar) -> Fut + Clone + Send + 'static, + Fut: Future> + + Send, + E: IntoResponse, + $( $ty: FromRequestParts<()> + Send + 'static, )* + { + fn add_to_router(self, mut router: Router) -> Router { + let meta = Req::METADATA; + let method_filter = method_to_filter(meta.method); + + for path in meta.history.all_paths() { + let handler = self.clone(); + + router = router.route( + path, + on( + method_filter, + |$( $ty: $ty, )* req: Ar| async move { + let span = info_span!( + "run_ruma_handler", + auth.user = ?req.sender_user, + auth.device = ?req.sender_device, + auth.servername = ?req.sender_servername, + auth.appservice_id = ?req.appservice_info + .as_ref() + .map(|i| &i.registration.id) + ); + handler($($ty,)* req).instrument(span).await + } + ) + ) + } + + router + } + } + }; +} + +impl_ruma_handler!(); +impl_ruma_handler!(T1); +impl_ruma_handler!(T1, T2); +impl_ruma_handler!(T1, T2, T3); +impl_ruma_handler!(T1, T2, T3, T4); +impl_ruma_handler!(T1, T2, T3, T4, T5); +impl_ruma_handler!(T1, T2, T3, T4, T5, T6); +impl_ruma_handler!(T1, T2, T3, T4, T5, T6, T7); +impl_ruma_handler!(T1, T2, T3, T4, T5, T6, T7, T8); + +fn method_to_filter(method: Method) -> MethodFilter { + match method { + Method::DELETE => MethodFilter::DELETE, + Method::GET => MethodFilter::GET, + Method::HEAD => MethodFilter::HEAD, + Method::OPTIONS => MethodFilter::OPTIONS, + Method::PATCH => MethodFilter::PATCH, + Method::POST => MethodFilter::POST, + Method::PUT => MethodFilter::PUT, + Method::TRACE => MethodFilter::TRACE, + m => panic!("Unsupported HTTP method: {m:?}"), + } +} + +#[cfg(unix)] +#[tracing::instrument(err)] +fn maximize_fd_limit() -> Result<(), nix::errno::Errno> { + use nix::sys::resource::{getrlimit, setrlimit, Resource}; + + let res = Resource::RLIMIT_NOFILE; + + let (soft_limit, hard_limit) = getrlimit(res)?; + + debug!(soft_limit, "Current nofile soft limit"); + + setrlimit(res, hard_limit, hard_limit)?; + + debug!(hard_limit, "Increased nofile soft limit to the hard limit"); + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index e90e9f7d..080b9714 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,44 +2,10 @@ // work anyway #![cfg_attr(not(any(feature = "sqlite", feature = "rocksdb")), allow(unused))] -use std::{ - future::Future, - net::SocketAddr, - process::ExitCode, - sync::{atomic, RwLock}, - time::Duration, -}; +use std::{process::ExitCode, sync::RwLock}; -use axum::{ - extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, - response::IntoResponse, - routing::{any, get, on, MethodFilter}, - Router, -}; -use axum_server::{ - bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, -}; use clap::Parser; -use futures_util::FutureExt; -use http::{ - header::{self, HeaderName}, - Method, StatusCode, Uri, -}; -use ruma::api::{ - client::{ - error::{Error as RumaError, ErrorBody, ErrorKind}, - uiaa::UiaaResponse, - }, - IncomingRequest, -}; -use tokio::{signal, task::JoinSet}; -use tower::ServiceBuilder; -use tower_http::{ - cors::{self, CorsLayer}, - trace::TraceLayer, - ServiceBuilderExt as _, -}; -use tracing::{debug, error, info, info_span, warn, Instrument}; +use tracing::error; mod api; mod cli; @@ -51,10 +17,7 @@ mod service; mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; -use api::{client_server, server_server, well_known}; -use cli::{Args, Command}; -pub(crate) use config::{Config, ListenConfig}; -pub(crate) use database::KeyValueDatabase; +pub(crate) use config::Config; pub(crate) use service::{pdu::PduEvent, Services}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] use tikv_jemallocator::Jemalloc; @@ -91,7 +54,8 @@ fn version() -> String { #[tokio::main] async fn main() -> ExitCode { - let Err(e) = try_main().await else { + let args = cli::Args::parse(); + let Err(e) = args.run().await else { return ExitCode::SUCCESS; }; @@ -105,621 +69,3 @@ async fn main() -> ExitCode { ExitCode::FAILURE } - -/// Fallible entrypoint -async fn try_main() -> Result<(), error::Main> { - use error::Main as Error; - - let args = Args::parse(); - // This is a placeholder, the logic specific to the 'serve' command will be - // moved to another file in a later commit - let Command::Serve(args) = args.command; - - let config = config::load(args.config.config.as_ref()).await?; - - let (_guard, reload_handles) = observability::init(&config)?; - - // This is needed for opening lots of file descriptors, which tends to - // happen more often when using RocksDB and making lots of federation - // connections at startup. The soft limit is usually 1024, and the hard - // limit is usually 512000; I've personally seen it hit >2000. - // - // * https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6 - // * https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741 - #[cfg(unix)] - maximize_fd_limit() - .expect("should be able to increase the soft limit to the hard limit"); - - info!("Loading database"); - KeyValueDatabase::load_or_create(config, reload_handles) - .await - .map_err(Error::DatabaseError)?; - - info!("Starting server"); - run_server().await?; - - Ok(()) -} - -#[allow(clippy::too_many_lines)] -async fn run_server() -> Result<(), error::Serve> { - use error::Serve as Error; - - let config = &services().globals.config; - - let x_requested_with = HeaderName::from_static("x-requested-with"); - - let middlewares = ServiceBuilder::new() - .sensitive_headers([header::AUTHORIZATION]) - .layer(axum::middleware::from_fn(spawn_task)) - .layer( - TraceLayer::new_for_http() - .make_span_with(|request: &http::Request<_>| { - let endpoint = if let Some(endpoint) = - request.extensions().get::() - { - endpoint.as_str() - } else { - request.uri().path() - }; - - let method = request.method(); - - tracing::info_span!( - "http_request", - otel.name = format!("{method} {endpoint}"), - %method, - %endpoint, - ) - }) - .on_request( - |request: &http::Request<_>, _span: &tracing::Span| { - // can be enabled selectively using `filter = - // grapevine[incoming_request_curl]=trace` in config - tracing::trace_span!("incoming_request_curl").in_scope( - || { - tracing::trace!( - cmd = utils::curlify(request), - "curl command line for incoming request \ - (guessed hostname)" - ); - }, - ); - }, - ), - ) - .layer(axum::middleware::from_fn(unrecognized_method)) - .layer( - CorsLayer::new() - .allow_origin(cors::Any) - .allow_methods([ - Method::GET, - Method::POST, - Method::PUT, - Method::DELETE, - Method::OPTIONS, - ]) - .allow_headers([ - header::ORIGIN, - x_requested_with, - header::CONTENT_TYPE, - header::ACCEPT, - header::AUTHORIZATION, - ]) - .max_age(Duration::from_secs(86400)), - ) - .layer(DefaultBodyLimit::max( - config - .max_request_size - .try_into() - .expect("failed to convert max request size"), - )) - .layer(axum::middleware::from_fn(observability::http_metrics_layer)); - - let app = routes(config).layer(middlewares).into_make_service(); - let mut handles = Vec::new(); - let mut servers = JoinSet::new(); - - let tls_config = if let Some(tls) = &config.tls { - Some(RustlsConfig::from_pem_file(&tls.certs, &tls.key).await.map_err( - |err| Error::LoadCerts { - certs: tls.certs.clone(), - key: tls.key.clone(), - err, - }, - )?) - } else { - None - }; - - if config.listen.is_empty() { - return Err(Error::NoListeners); - } - - for listen in &config.listen { - info!(listener = %listen, "Listening for incoming traffic"); - match listen { - ListenConfig::Tcp { - address, - port, - tls, - } => { - let addr = SocketAddr::from((*address, *port)); - let handle = ServerHandle::new(); - handles.push(handle.clone()); - let server = if *tls { - let tls_config = tls_config - .clone() - .ok_or_else(|| Error::NoTlsCerts(listen.clone()))?; - bind_rustls(addr, tls_config) - .handle(handle) - .serve(app.clone()) - .left_future() - } else { - bind(addr).handle(handle).serve(app.clone()).right_future() - }; - servers.spawn( - server.then(|result| async { (listen.clone(), result) }), - ); - } - } - } - - #[cfg(feature = "systemd")] - sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) - .expect("should be able to notify systemd"); - - tokio::spawn(shutdown_signal(handles)); - - while let Some(result) = servers.join_next().await { - let (listen, result) = - result.expect("should be able to join server task"); - result.map_err(|err| Error::Listen(err, listen))?; - } - - Ok(()) -} - -/// Ensures the request runs in a new tokio thread. -/// -/// The axum request handler task gets cancelled if the connection is shut down; -/// by spawning our own task, processing continue after the client disconnects. -async fn spawn_task( - req: axum::extract::Request, - next: axum::middleware::Next, -) -> std::result::Result { - if services().globals.shutdown.load(atomic::Ordering::Relaxed) { - return Err(StatusCode::SERVICE_UNAVAILABLE); - } - tokio::spawn(next.run(req)) - .await - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) -} - -async fn unrecognized_method( - req: axum::extract::Request, - next: axum::middleware::Next, -) -> std::result::Result { - let method = req.method().clone(); - let uri = req.uri().clone(); - let inner = next.run(req).await; - if inner.status() == StatusCode::METHOD_NOT_ALLOWED { - warn!(%method, %uri, "Method not allowed"); - return Ok(Ra(UiaaResponse::MatrixError(RumaError { - body: ErrorBody::Standard { - kind: ErrorKind::Unrecognized, - message: "M_UNRECOGNIZED: Unrecognized request".to_owned(), - }, - status_code: StatusCode::METHOD_NOT_ALLOWED, - })) - .into_response()); - } - Ok(inner) -} - -#[allow(clippy::too_many_lines)] -fn routes(config: &Config) -> Router { - use client_server as c2s; - use server_server as s2s; - - let router = Router::new() - .ruma_route(c2s::get_supported_versions_route) - .ruma_route(c2s::get_register_available_route) - .ruma_route(c2s::register_route) - .ruma_route(c2s::get_login_types_route) - .ruma_route(c2s::login_route) - .ruma_route(c2s::whoami_route) - .ruma_route(c2s::logout_route) - .ruma_route(c2s::logout_all_route) - .ruma_route(c2s::change_password_route) - .ruma_route(c2s::deactivate_route) - .ruma_route(c2s::third_party_route) - .ruma_route(c2s::request_3pid_management_token_via_email_route) - .ruma_route(c2s::request_3pid_management_token_via_msisdn_route) - .ruma_route(c2s::get_capabilities_route) - .ruma_route(c2s::get_pushrules_all_route) - .ruma_route(c2s::set_pushrule_route) - .ruma_route(c2s::get_pushrule_route) - .ruma_route(c2s::set_pushrule_enabled_route) - .ruma_route(c2s::get_pushrule_enabled_route) - .ruma_route(c2s::get_pushrule_actions_route) - .ruma_route(c2s::set_pushrule_actions_route) - .ruma_route(c2s::delete_pushrule_route) - .ruma_route(c2s::get_room_event_route) - .ruma_route(c2s::get_room_aliases_route) - .ruma_route(c2s::get_filter_route) - .ruma_route(c2s::create_filter_route) - .ruma_route(c2s::set_global_account_data_route) - .ruma_route(c2s::set_room_account_data_route) - .ruma_route(c2s::get_global_account_data_route) - .ruma_route(c2s::get_room_account_data_route) - .ruma_route(c2s::set_displayname_route) - .ruma_route(c2s::get_displayname_route) - .ruma_route(c2s::set_avatar_url_route) - .ruma_route(c2s::get_avatar_url_route) - .ruma_route(c2s::get_profile_route) - .ruma_route(c2s::upload_keys_route) - .ruma_route(c2s::get_keys_route) - .ruma_route(c2s::claim_keys_route) - .ruma_route(c2s::create_backup_version_route) - .ruma_route(c2s::update_backup_version_route) - .ruma_route(c2s::delete_backup_version_route) - .ruma_route(c2s::get_latest_backup_info_route) - .ruma_route(c2s::get_backup_info_route) - .ruma_route(c2s::add_backup_keys_route) - .ruma_route(c2s::add_backup_keys_for_room_route) - .ruma_route(c2s::add_backup_keys_for_session_route) - .ruma_route(c2s::delete_backup_keys_for_room_route) - .ruma_route(c2s::delete_backup_keys_for_session_route) - .ruma_route(c2s::delete_backup_keys_route) - .ruma_route(c2s::get_backup_keys_for_room_route) - .ruma_route(c2s::get_backup_keys_for_session_route) - .ruma_route(c2s::get_backup_keys_route) - .ruma_route(c2s::set_read_marker_route) - .ruma_route(c2s::create_receipt_route) - .ruma_route(c2s::create_typing_event_route) - .ruma_route(c2s::create_room_route) - .ruma_route(c2s::redact_event_route) - .ruma_route(c2s::report_event_route) - .ruma_route(c2s::create_alias_route) - .ruma_route(c2s::delete_alias_route) - .ruma_route(c2s::get_alias_route) - .ruma_route(c2s::join_room_by_id_route) - .ruma_route(c2s::join_room_by_id_or_alias_route) - .ruma_route(c2s::joined_members_route) - .ruma_route(c2s::leave_room_route) - .ruma_route(c2s::forget_room_route) - .ruma_route(c2s::joined_rooms_route) - .ruma_route(c2s::kick_user_route) - .ruma_route(c2s::ban_user_route) - .ruma_route(c2s::unban_user_route) - .ruma_route(c2s::invite_user_route) - .ruma_route(c2s::set_room_visibility_route) - .ruma_route(c2s::get_room_visibility_route) - .ruma_route(c2s::get_public_rooms_route) - .ruma_route(c2s::get_public_rooms_filtered_route) - .ruma_route(c2s::search_users_route) - .ruma_route(c2s::get_member_events_route) - .ruma_route(c2s::get_protocols_route) - .ruma_route(c2s::send_message_event_route) - .ruma_route(c2s::send_state_event_for_key_route) - .ruma_route(c2s::get_state_events_route) - .ruma_route(c2s::get_state_events_for_key_route) - .ruma_route(c2s::sync_events_route) - .ruma_route(c2s::sync_events_v4_route) - .ruma_route(c2s::get_context_route) - .ruma_route(c2s::get_message_events_route) - .ruma_route(c2s::search_events_route) - .ruma_route(c2s::turn_server_route) - .ruma_route(c2s::send_event_to_device_route); - - // deprecated, but unproblematic - let router = router.ruma_route(c2s::get_media_config_legacy_route); - let router = if config.serve_media_unauthenticated { - router - .ruma_route(c2s::get_content_legacy_route) - .ruma_route(c2s::get_content_as_filename_legacy_route) - .ruma_route(c2s::get_content_thumbnail_legacy_route) - } else { - router - .route( - "/_matrix/media/v3/download/*path", - any(unauthenticated_media_disabled), - ) - .route( - "/_matrix/media/v3/thumbnail/*path", - any(unauthenticated_media_disabled), - ) - }; - - // authenticated media - let router = router - .ruma_route(c2s::get_media_config_route) - .ruma_route(c2s::create_content_route) - .ruma_route(c2s::get_content_route) - .ruma_route(c2s::get_content_as_filename_route) - .ruma_route(c2s::get_content_thumbnail_route); - - let router = router - .ruma_route(c2s::get_devices_route) - .ruma_route(c2s::get_device_route) - .ruma_route(c2s::update_device_route) - .ruma_route(c2s::delete_device_route) - .ruma_route(c2s::delete_devices_route) - .ruma_route(c2s::get_tags_route) - .ruma_route(c2s::update_tag_route) - .ruma_route(c2s::delete_tag_route) - .ruma_route(c2s::upload_signing_keys_route) - .ruma_route(c2s::upload_signatures_route) - .ruma_route(c2s::get_key_changes_route) - .ruma_route(c2s::get_pushers_route) - .ruma_route(c2s::set_pushers_route) - .ruma_route(c2s::upgrade_room_route) - .ruma_route(c2s::get_threads_route) - .ruma_route(c2s::get_relating_events_with_rel_type_and_event_type_route) - .ruma_route(c2s::get_relating_events_with_rel_type_route) - .ruma_route(c2s::get_relating_events_route) - .ruma_route(c2s::get_hierarchy_route); - - // Ruma doesn't have support for multiple paths for a single endpoint yet, - // and these routes share one Ruma request / response type pair with - // {get,send}_state_event_for_key_route. These two endpoints also allow - // trailing slashes. - let router = router - .route( - "/_matrix/client/r0/rooms/:room_id/state/:event_type", - get(c2s::get_state_events_for_empty_key_route) - .put(c2s::send_state_event_for_empty_key_route), - ) - .route( - "/_matrix/client/v3/rooms/:room_id/state/:event_type", - get(c2s::get_state_events_for_empty_key_route) - .put(c2s::send_state_event_for_empty_key_route), - ) - .route( - "/_matrix/client/r0/rooms/:room_id/state/:event_type/", - get(c2s::get_state_events_for_empty_key_route) - .put(c2s::send_state_event_for_empty_key_route), - ) - .route( - "/_matrix/client/v3/rooms/:room_id/state/:event_type/", - get(c2s::get_state_events_for_empty_key_route) - .put(c2s::send_state_event_for_empty_key_route), - ); - - let router = if config.observability.metrics.enable { - router.route( - "/metrics", - get(|| async { observability::METRICS.export() }), - ) - } else { - router - }; - - let router = router - .route( - "/_matrix/client/r0/rooms/:room_id/initialSync", - get(initial_sync), - ) - .route( - "/_matrix/client/v3/rooms/:room_id/initialSync", - get(initial_sync), - ) - .route("/", get(it_works)) - .fallback(not_found); - - let router = router - .route("/.well-known/matrix/client", get(well_known::client)) - .route("/.well-known/matrix/server", get(well_known::server)); - - if config.federation.enable { - router - .ruma_route(s2s::get_server_version_route) - .route("/_matrix/key/v2/server", get(s2s::get_server_keys_route)) - .route( - "/_matrix/key/v2/server/:key_id", - get(s2s::get_server_keys_deprecated_route), - ) - .ruma_route(s2s::get_public_rooms_route) - .ruma_route(s2s::get_public_rooms_filtered_route) - .ruma_route(s2s::send_transaction_message_route) - .ruma_route(s2s::get_event_route) - .ruma_route(s2s::get_backfill_route) - .ruma_route(s2s::get_missing_events_route) - .ruma_route(s2s::get_event_authorization_route) - .ruma_route(s2s::get_room_state_route) - .ruma_route(s2s::get_room_state_ids_route) - .ruma_route(s2s::create_join_event_template_route) - .ruma_route(s2s::create_join_event_v1_route) - .ruma_route(s2s::create_join_event_v2_route) - .ruma_route(s2s::create_invite_route) - .ruma_route(s2s::get_devices_route) - .ruma_route(s2s::get_room_information_route) - .ruma_route(s2s::get_profile_information_route) - .ruma_route(s2s::get_keys_route) - .ruma_route(s2s::claim_keys_route) - .ruma_route(s2s::media_download_route) - .ruma_route(s2s::media_thumbnail_route) - } else { - router - .route("/_matrix/federation/*path", any(federation_disabled)) - .route("/_matrix/key/*path", any(federation_disabled)) - } -} - -async fn shutdown_signal(handles: Vec) { - let ctrl_c = async { - signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); - }; - - #[cfg(unix)] - let terminate = async { - signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to install signal handler") - .recv() - .await; - }; - - #[cfg(not(unix))] - let terminate = std::future::pending::<()>(); - - let sig: &str; - - tokio::select! { - () = ctrl_c => { sig = "Ctrl+C"; }, - () = terminate => { sig = "SIGTERM"; }, - } - - warn!(signal = %sig, "Shutting down due to signal"); - - services().globals.shutdown(); - - for handle in handles { - handle.graceful_shutdown(Some(Duration::from_secs(30))); - } - - #[cfg(feature = "systemd")] - sd_notify::notify(true, &[sd_notify::NotifyState::Stopping]) - .expect("should be able to notify systemd"); -} - -async fn federation_disabled(_: Uri) -> impl IntoResponse { - Error::bad_config("Federation is disabled.") -} - -async fn unauthenticated_media_disabled(_: Uri) -> impl IntoResponse { - Error::BadRequest( - ErrorKind::NotFound, - "Unauthenticated media access is disabled", - ) -} - -async fn not_found(method: Method, uri: Uri) -> impl IntoResponse { - debug!(%method, %uri, "Unknown route"); - Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request") -} - -async fn initial_sync(_uri: Uri) -> impl IntoResponse { - Error::BadRequest( - ErrorKind::GuestAccessForbidden, - "Guest access not implemented", - ) -} - -async fn it_works() -> &'static str { - "Hello from Grapevine!" -} - -trait RouterExt { - fn ruma_route(self, handler: H) -> Self - where - H: RumaHandler, - T: 'static; -} - -impl RouterExt for Router { - fn ruma_route(self, handler: H) -> Self - where - H: RumaHandler, - T: 'static, - { - handler.add_to_router(self) - } -} - -pub(crate) trait RumaHandler { - // Can't transform to a handler without boxing or relying on the - // nightly-only impl-trait-in-traits feature. Moving a small amount of - // extra logic into the trait allows bypassing both. - fn add_to_router(self, router: Router) -> Router; -} - -macro_rules! impl_ruma_handler { - ( $($ty:ident),* $(,)? ) => { - #[axum::async_trait] - #[allow(non_snake_case)] - impl - RumaHandler<($($ty,)* Ar,)> for F - where - Req: IncomingRequest + Send + 'static, - Resp: IntoResponse, - F: FnOnce($($ty,)* Ar) -> Fut + Clone + Send + 'static, - Fut: Future> - + Send, - E: IntoResponse, - $( $ty: FromRequestParts<()> + Send + 'static, )* - { - fn add_to_router(self, mut router: Router) -> Router { - let meta = Req::METADATA; - let method_filter = method_to_filter(meta.method); - - for path in meta.history.all_paths() { - let handler = self.clone(); - - router = router.route( - path, - on( - method_filter, - |$( $ty: $ty, )* req: Ar| async move { - let span = info_span!( - "run_ruma_handler", - auth.user = ?req.sender_user, - auth.device = ?req.sender_device, - auth.servername = ?req.sender_servername, - auth.appservice_id = ?req.appservice_info - .as_ref() - .map(|i| &i.registration.id) - ); - handler($($ty,)* req).instrument(span).await - } - ) - ) - } - - router - } - } - }; -} - -impl_ruma_handler!(); -impl_ruma_handler!(T1); -impl_ruma_handler!(T1, T2); -impl_ruma_handler!(T1, T2, T3); -impl_ruma_handler!(T1, T2, T3, T4); -impl_ruma_handler!(T1, T2, T3, T4, T5); -impl_ruma_handler!(T1, T2, T3, T4, T5, T6); -impl_ruma_handler!(T1, T2, T3, T4, T5, T6, T7); -impl_ruma_handler!(T1, T2, T3, T4, T5, T6, T7, T8); - -fn method_to_filter(method: Method) -> MethodFilter { - match method { - Method::DELETE => MethodFilter::DELETE, - Method::GET => MethodFilter::GET, - Method::HEAD => MethodFilter::HEAD, - Method::OPTIONS => MethodFilter::OPTIONS, - Method::PATCH => MethodFilter::PATCH, - Method::POST => MethodFilter::POST, - Method::PUT => MethodFilter::PUT, - Method::TRACE => MethodFilter::TRACE, - m => panic!("Unsupported HTTP method: {m:?}"), - } -} - -#[cfg(unix)] -#[tracing::instrument(err)] -fn maximize_fd_limit() -> Result<(), nix::errno::Errno> { - use nix::sys::resource::{getrlimit, setrlimit, Resource}; - - let res = Resource::RLIMIT_NOFILE; - - let (soft_limit, hard_limit) = getrlimit(res)?; - - debug!(soft_limit, "Current nofile soft limit"); - - setrlimit(res, hard_limit, hard_limit)?; - - debug!(hard_limit, "Increased nofile soft limit to the hard limit"); - - Ok(()) -} From 5315bac0c5090e64e1a9b79d423f4d9236390c0d Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 20 Sep 2024 22:25:29 -0700 Subject: [PATCH 390/617] split out separate error type for serve command --- src/cli.rs | 3 ++- src/cli/serve.rs | 4 ++-- src/error.rs | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index cb8f4154..c997ad67 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -60,7 +60,8 @@ pub(crate) struct ServeArgs { impl Args { pub(crate) async fn run(self) -> Result<(), error::Main> { match self.command { - Command::Serve(args) => serve::run(args).await, + Command::Serve(args) => serve::run(args).await?, } + Ok(()) } } diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 71980e42..d8345c11 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -44,8 +44,8 @@ use crate::{ utils::error::{Error, Result}, }; -pub(crate) async fn run(args: ServeArgs) -> Result<(), error::Main> { - use error::Main as Error; +pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { + use error::ServeCommand as Error; let config = config::load(args.config.config.as_ref()).await?; diff --git a/src/error.rs b/src/error.rs index 70f0124e..d2fcae24 100644 --- a/src/error.rs +++ b/src/error.rs @@ -40,6 +40,16 @@ impl fmt::Display for DisplayWithSources<'_> { #[allow(missing_docs)] #[derive(Error, Debug)] pub(crate) enum Main { + #[error(transparent)] + ServeCommand(#[from] ServeCommand), +} + +/// Errors returned from the `serve` CLI subcommand. +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum ServeCommand { #[error("failed to load configuration")] Config(#[from] Config), From c1bf4a8ee3344dd69f5fb42b6147b49229d2424b Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sat, 21 Sep 2024 12:50:10 -0700 Subject: [PATCH 391/617] changelog entry for CLI compatibility break --- book/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index b34d3412..5403d6c4 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -132,6 +132,10 @@ This will be the first release of Grapevine since it was forked from Conduit 12. **BREAKING:** Disable unauthenticated access to media by default, set the `serve_media_unauthenticated` config option to `true` to enable it. ([!103](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/103)) +13. **BREAKING:** Split CLI into multiple subcommands. The CLI invocation to run + the server is now behind the `serve` command, so `grapevine --config ...` + becomes `grapevine serve --config ...`. + ([!108](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/108)) ### Fixed From c24f79b79ba0fd71e56e981c218acc70df016ee9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 19 Sep 2024 16:20:13 -0700 Subject: [PATCH 392/617] update rust deps except rocksdb and otel clownery * OTel v0.25.0 requires downgrading Tokio to 1.38 [0] * They have a fix for this but aren't cutting a release just for release schedule reasons [1] * Prometheus support (at least for server-pull) was dropped at OTel v0.23 and isn't planned to be picked up again until OTel v1 [2] * No real reasoning was provided for this decision AFAICT [3] [4] * So many compiler errors * Unhelpful changelogs The last two points are what made me give up on trying to upgrade to OTel v0.24 too. RocksDB isn't updated because we'd need to update our nixpkgs too but that causes other problems, such as an upstream bug in liburing when building for musl. [0]: https://github.com/open-telemetry/opentelemetry-rust/issues/2094 [1]: https://github.com/open-telemetry/opentelemetry-rust/issues/2094#issuecomment-2346834030 [2]: https://docs.rs/opentelemetry-prometheus/0.17.0/opentelemetry_prometheus/index.html [3]: https://github.com/open-telemetry/opentelemetry-rust/pull/1792 [4]: https://github.com/open-telemetry/opentelemetry-rust/pull/1792#issuecomment-2121514344 --- Cargo.lock | 1054 ++++++++++++++++----------- Cargo.toml | 46 +- book/changelog.md | 3 +- src/api/client_server/membership.rs | 2 +- 4 files changed, 635 insertions(+), 470 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8fda7703..60cd76f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "ahash" version = "0.8.11" @@ -40,15 +46,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arc-swap" @@ -104,9 +110,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", @@ -125,6 +131,33 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-lc-rs" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + [[package]] name = "axum" version = "0.6.20" @@ -138,7 +171,7 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.30", "itoa", "matchit", "memchr", @@ -148,25 +181,25 @@ dependencies = [ "rustversion", "serde", "sync_wrapper 0.1.2", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] [[package]] name = "axum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", - "axum-core 0.4.3", + "axum-core 0.4.4", "bytes", "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "itoa", "matchit", @@ -180,7 +213,7 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sync_wrapper 1.0.1", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -205,20 +238,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -226,22 +259,22 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" dependencies = [ - "axum 0.7.5", - "axum-core 0.4.3", + "axum 0.7.6", + "axum-core 0.4.4", "bytes", "futures-util", "headers", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "serde", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -249,40 +282,41 @@ dependencies = [ [[package]] name = "axum-server" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad46c3ec4e12f4a4b6835e173ba21c25e484c9d02b49770bf006ce5367c036" +checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8" dependencies = [ "arc-swap", "bytes", "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "pin-project-lite", - "rustls 0.21.12", + "rustls", "rustls-pemfile", + "rustls-pki-types", "tokio", - "tokio-rustls 0.24.1", - "tower", + "tokio-rustls", + "tower 0.4.13", "tower-service", ] [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.8.0", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -309,18 +343,21 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools", "lazy_static", "lazycell", + "log", + "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn", + "which", ] [[package]] @@ -331,9 +368,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "blake2" @@ -361,9 +398,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -372,10 +409,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "bytes" -version = "1.6.0" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bzip2-sys" @@ -390,13 +433,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -422,9 +465,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -433,9 +476,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -443,9 +486,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstyle", "clap_lex", @@ -454,11 +497,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -466,9 +509,18 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] [[package]] name = "color_quant" @@ -484,9 +536,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" +checksum = "7782af8f90fe69a4bb41e460abe1727d493403d8b2cc43201a3a3e906b24379f" [[package]] name = "core-foundation" @@ -500,15 +552,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -534,16 +586,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -602,6 +653,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ed25519" version = "2.2.3" @@ -629,17 +686,17 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-as-inner" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "syn", @@ -690,12 +747,12 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -713,6 +770,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures-channel" version = "0.3.30" @@ -818,9 +881,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -834,7 +897,7 @@ version = "0.1.0" dependencies = [ "argon2", "async-trait", - "axum 0.7.5", + "axum 0.7.6", "axum-extra", "axum-server", "base64 0.22.1", @@ -845,7 +908,7 @@ dependencies = [ "html-escape", "http 1.1.0", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "image", "jsonwebtoken", @@ -880,7 +943,7 @@ dependencies = [ "tikv-jemallocator", "tokio", "toml", - "tower", + "tower 0.5.1", "tower-http", "tracing", "tracing-flame", @@ -902,7 +965,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.6", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", @@ -911,9 +974,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -921,7 +984,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.2.6", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", @@ -976,12 +1039,6 @@ dependencies = [ "http 1.1.0", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1003,6 +1060,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "hostname" version = "0.3.1" @@ -1047,9 +1113,9 @@ dependencies = [ [[package]] name = "http-auth" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643c9bbf6a4ea8a656d6b4cd53d34f79e3f841ad5203c1a55fb7d761923bc255" +checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" dependencies = [ "memchr", ] @@ -1067,9 +1133,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http 1.1.0", @@ -1077,14 +1143,14 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1102,9 +1168,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -1126,16 +1192,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -1147,18 +1213,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", - "rustls 0.22.4", + "rustls", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls", "tower-service", ] @@ -1168,7 +1235,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.28", + "hyper 0.14.30", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -1176,20 +1243,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http-body 1.0.1", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -1216,12 +1283,12 @@ dependencies = [ [[package]] name = "image" -version = "0.25.1" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" dependencies = [ "bytemuck", - "byteorder", + "byteorder-lite", "color_quant", "gif", "num-traits", @@ -1242,9 +1309,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -1260,14 +1327,14 @@ dependencies = [ "socket2", "widestring", "windows-sys 0.48.0", - "winreg 0.50.0", + "winreg", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "itertools" @@ -1286,18 +1353,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -1357,9 +1424,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -1369,25 +1436,25 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "libsqlite3-sys" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", @@ -1396,9 +1463,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "pkg-config", @@ -1429,9 +1496,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru-cache" @@ -1444,9 +1511,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.4" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +checksum = "fcb44a01837a858d47e5a630d2ccf304c8efcc4b83b8f9f75b7a9ee4fcc6e57d" dependencies = [ "cc", "libc", @@ -1481,9 +1548,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -1499,32 +1566,48 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", ] [[package]] -name = "mio" -version = "0.8.11" +name = "miniz_oxide" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", @@ -1552,9 +1635,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -1596,9 +1679,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -1705,9 +1788,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.2.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" dependencies = [ "num-traits", ] @@ -1738,7 +1821,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1752,6 +1835,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pem" version = "3.0.4" @@ -1858,12 +1947,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "png" version = "0.17.13" @@ -1874,7 +1957,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.7.4", ] [[package]] @@ -1885,24 +1968,37 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn", +] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1958,10 +2054,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] -name = "quote" -version = "1.0.36" +name = "quinn" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.0.0", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash 2.0.0", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1998,23 +2142,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2028,13 +2172,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -2045,25 +2189,25 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", "futures-core", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-rustls", "hyper-util", "ipnet", @@ -2073,23 +2217,24 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", - "rustls-native-certs", + "quinn", + "rustls", + "rustls-native-certs 0.7.3", "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls", "tokio-socks", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.52.0", + "windows-registry", ] [[package]] @@ -2120,7 +2265,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.10.1" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "assign", "js_int", @@ -2141,7 +2286,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "js_int", "ruma-common", @@ -2153,7 +2298,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", "assign", @@ -2176,14 +2321,14 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", "base64 0.22.1", "bytes", "form_urlencoded", "http 1.1.0", - "indexmap 2.2.6", + "indexmap 2.5.0", "js_int", "konst", "percent-encoding", @@ -2206,10 +2351,10 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", - "indexmap 2.2.6", + "indexmap 2.5.0", "js_int", "js_option", "percent-encoding", @@ -2229,7 +2374,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "bytes", "http 1.1.0", @@ -2247,7 +2392,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "js_int", "thiserror", @@ -2256,7 +2401,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "js_int", "ruma-common", @@ -2266,8 +2411,9 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ + "cfg-if", "once_cell", "proc-macro-crate", "proc-macro2", @@ -2281,7 +2427,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "js_int", "ruma-common", @@ -2293,7 +2439,7 @@ dependencies = [ [[package]] name = "ruma-server-util" version = "0.3.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "headers", "http 1.1.0", @@ -2306,7 +2452,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.15.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2322,7 +2468,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.11.0" -source = "git+https://github.com/ruma/ruma?branch=main#14d7415f0d80aadf425c2384c0f348d1c03527c8" +source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "itertools", "js_int", @@ -2336,11 +2482,11 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2387,21 +2533,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustc-hash" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2410,35 +2562,37 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", + "aws-lc-rs", + "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -2449,9 +2603,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -2459,26 +2613,17 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -2498,11 +2643,11 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2511,29 +2656,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sd-notify" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "621e3680f3e07db4c9c2c3fb07c6223ab2fab2e54bd3c04c3ae037990f428c32" +checksum = "4646d6f919800cd25c50edb49438a1381e2cd4833c027e75e8897981c50b8b5e" [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -2542,9 +2677,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -2558,18 +2693,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -2583,7 +2718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" dependencies = [ "form_urlencoded", - "indexmap 2.2.6", + "indexmap 2.5.0", "itoa", "ryu", "serde", @@ -2591,11 +2726,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2612,9 +2748,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -2637,7 +2773,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "itoa", "ryu", "serde", @@ -2777,20 +2913,20 @@ dependencies = [ [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -2808,15 +2944,15 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -2834,6 +2970,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "terminal_size" @@ -2847,18 +2986,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -2877,9 +3016,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.4+5.3.0-patched" +version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" dependencies = [ "cc", "libc", @@ -2887,9 +3026,9 @@ dependencies = [ [[package]] name = "tikv-jemallocator" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca" +checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" dependencies = [ "libc", "tikv-jemalloc-sys", @@ -2928,9 +3067,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2943,20 +3082,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2971,9 +3109,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -2982,30 +3120,20 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.21.12", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.4", + "rustls", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-socks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", @@ -3015,9 +3143,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", @@ -3026,9 +3154,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -3039,47 +3167,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.14", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" -dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow", ] [[package]] @@ -3096,14 +3213,14 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.30", "hyper-timeout", "percent-encoding", "pin-project", "prost", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -3130,18 +3247,31 @@ dependencies = [ ] [[package]] -name = "tower-http" -version = "0.5.2" +name = "tower" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ - "bitflags 2.5.0", + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41515cc9e193536d93fd0dbbea0c73819c08eca76e0b30909a325c3ec90985bb" +dependencies = [ + "bitflags 2.6.0", "bytes", "http 1.1.0", - "http-body 1.0.0", - "http-body-util", + "http-body 1.0.1", "pin-project-lite", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -3149,15 +3279,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -3344,15 +3474,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -3371,9 +3501,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -3389,9 +3519,9 @@ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", ] @@ -3410,9 +3540,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -3431,19 +3561,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -3456,9 +3587,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -3468,9 +3599,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3478,9 +3609,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -3491,15 +3622,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -3521,6 +3652,18 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "widestring" version = "1.1.0" @@ -3555,6 +3698,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3570,7 +3743,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -3590,18 +3772,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -3612,9 +3794,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -3624,9 +3806,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -3636,15 +3818,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -3654,9 +3836,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -3666,9 +3848,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -3678,9 +3860,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -3690,24 +3872,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -3722,16 +3895,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "xdg" version = "2.5.2" @@ -3740,18 +3903,19 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", @@ -3760,15 +3924,15 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", @@ -3782,9 +3946,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-jpeg" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 8d38d852..192aa6d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,21 +87,21 @@ workspace = true # Keep sorted [dependencies] argon2 = "0.5.3" -async-trait = "0.1.80" -axum = { version = "0.7.5", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } -axum-extra = { version = "0.9.3", features = ["typed-header"] } -axum-server = { version = "0.6.0", features = ["tls-rustls"] } +async-trait = "0.1.82" +axum = { version = "0.7.6", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } +axum-extra = { version = "0.9.4", features = ["typed-header"] } +axum-server = { version = "0.7.1", features = ["tls-rustls"] } base64 = "0.22.1" -bytes = "1.6.0" -clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } +bytes = "1.7.2" +clap = { version = "4.5.18", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } futures-util = { version = "0.3.30", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" http = "1.1.0" -http-body-util = "0.1.1" -hyper = "1.3.1" -hyper-util = { version = "0.1.4", features = ["client", "client-legacy", "service"] } -image = { version = "0.25.1", default-features = false, features = ["jpeg", "png", "gif"] } +http-body-util = "0.1.2" +hyper = "1.4.1" +hyper-util = { version = "0.1.8", features = ["client", "client-legacy", "service"] } +image = { version = "0.25.2", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.3.0" lru-cache = "0.1.2" num_cpus = "1.16.0" @@ -115,26 +115,26 @@ parking_lot = { version = "0.12.3", optional = true } phf = { version = "0.11.2", features = ["macros"] } prometheus = "0.13.4" rand = "0.8.5" -regex = "1.10.4" -reqwest = { version = "0.12.4", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } +regex = "1.10.6" +reqwest = { version = "0.12.7", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } -rusqlite = { version = "0.31.0", optional = true, features = ["bundled"] } -sd-notify = { version = "0.4.1", optional = true } -serde = { version = "1.0.202", features = ["rc"] } +rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } +sd-notify = { version = "0.4.2", optional = true } +serde = { version = "1.0.210", features = ["rc"] } serde_html_form = "0.2.6" -serde_json = { version = "1.0.117", features = ["raw_value"] } +serde_json = { version = "1.0.128", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" -strum = { version = "0.26.2", features = ["derive"] } -thiserror = "1.0.61" +strum = { version = "0.26.3", features = ["derive"] } +thiserror = "1.0.64" thread_local = "1.1.8" -tikv-jemallocator = { version = "0.5.4", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -tokio = { version = "1.37.0", features = ["fs", "macros", "signal", "sync"] } -toml = "0.8.14" -tower = { version = "0.4.13", features = ["util"] } -tower-http = { version = "0.5.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } +tikv-jemallocator = { version = "0.6.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } +tokio = { version = "1.40.0", features = ["fs", "macros", "signal", "sync"] } +toml = "0.8.19" +tower = { version = "0.5.1", features = ["util"] } +tower-http = { version = "0.6.0", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.40", features = [] } tracing-flame = "0.2.0" tracing-opentelemetry = "0.24.0" diff --git a/book/changelog.md b/book/changelog.md index 5403d6c4..b06790d9 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -234,7 +234,8 @@ This will be the first release of Grapevine since it was forked from Conduit ([!65](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/65), [!84](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/84)) 15. Added support for Authenticated Media ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)). - ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58)) + ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58), + [!111](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/111)) 16. **BREAKING:** Added support for configuring and serving `/.well-known/matrix/...` data. ([!90](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/90), diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index bfd338a6..5496299c 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -107,7 +107,7 @@ pub(crate) async fn join_room_by_id_or_alias_route( let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) { Ok(room_id) => { - let mut servers = body.server_name.clone(); + let mut servers = body.via.clone(); servers.extend( services() .rooms From b0d1cc1b63057f234efe2a988fc96641494818a2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 22 Sep 2024 20:04:01 -0700 Subject: [PATCH 393/617] bump otel to v0.24.0 Someone contributed opentelemetry-prometheus support for v0.24 and this version also doesn't put stupid requirements on the tokio version. This version of the OTel ecosystem also fixes an apparent bug with some hacks I plan on doing in the future... --- Cargo.lock | 293 +++++++++++++------------------------------ Cargo.toml | 12 +- src/observability.rs | 23 ++-- 3 files changed, 110 insertions(+), 218 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60cd76f4..4cefcc55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,34 +158,6 @@ dependencies = [ "paste", ] -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper 0.1.2", - "tower 0.4.13", - "tower-layer", - "tower-service", -] - [[package]] name = "axum" version = "0.7.6" @@ -193,13 +165,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", - "axum-core 0.4.4", + "axum-core", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "itoa", "matchit", @@ -219,23 +191,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.4.4" @@ -245,8 +200,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -263,13 +218,13 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" dependencies = [ - "axum 0.7.6", - "axum-core 0.4.4", + "axum", + "axum-core", "bytes", "futures-util", "headers", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -289,10 +244,10 @@ dependencies = [ "arc-swap", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "pin-project-lite", "rustls", @@ -346,7 +301,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -897,7 +852,7 @@ version = "0.1.0" dependencies = [ "argon2", "async-trait", - "axum 0.7.6", + "axum", "axum-extra", "axum-server", "base64 0.22.1", @@ -906,9 +861,9 @@ dependencies = [ "futures-util", "hmac", "html-escape", - "http 1.1.0", + "http", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "image", "jsonwebtoken", @@ -953,25 +908,6 @@ dependencies = [ "xdg", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.5.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.6" @@ -983,7 +919,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http", "indexmap 2.5.0", "slab", "tokio", @@ -1024,7 +960,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http 1.1.0", + "http", "httpdate", "mime", "sha1", @@ -1036,7 +972,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 1.1.0", + "http", ] [[package]] @@ -1089,17 +1025,6 @@ dependencies = [ "utf8-width", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.1.0" @@ -1120,17 +1045,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1138,7 +1052,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] @@ -1149,8 +1063,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -1166,30 +1080,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.4.1" @@ -1199,9 +1089,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -1218,8 +1108,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http", + "hyper", "hyper-util", "rustls", "rustls-native-certs 0.8.0", @@ -1231,14 +1121,15 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.30", + "hyper", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] @@ -1250,9 +1141,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -1345,6 +1236,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1700,9 +1600,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "opentelemetry" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b69a91d4893e713e06f724597ad630f1fa76057a5e1026c0ca67054a9032a76" +checksum = "4c365a63eec4f55b7efeceb724f1336f26a9cf3427b70e59e2cd2a5b947fba96" dependencies = [ "futures-core", "futures-sink", @@ -1714,22 +1614,22 @@ dependencies = [ [[package]] name = "opentelemetry-jaeger-propagator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c190755e0aeec909343896f94670446ac686dd1eaf5e2beb4149a7148cfe1d6c" +checksum = "fc0a68a13b92fc708d875ad659b08b35d08b8ef2403e01944b39ca21e5b08b17" dependencies = [ "opentelemetry", ] [[package]] name = "opentelemetry-otlp" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94c69209c05319cdf7460c6d4c055ed102be242a0a6245835d7bc42c6ec7f54" +checksum = "6b925a602ffb916fb7421276b86756027b37ee708f9dce2dbdcc51739f07e727" dependencies = [ "async-trait", "futures-core", - "http 0.2.12", + "http", "opentelemetry", "opentelemetry-proto", "opentelemetry_sdk", @@ -1741,9 +1641,9 @@ dependencies = [ [[package]] name = "opentelemetry-prometheus" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1a24eafe47b693cb938f8505f240dc26c71db60df9aca376b4f857e9653ec7" +checksum = "cc4191ce34aa274621861a7a9d68dbcf618d5b6c66b10081631b61fd81fbc015" dependencies = [ "once_cell", "opentelemetry", @@ -1754,9 +1654,9 @@ dependencies = [ [[package]] name = "opentelemetry-proto" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984806e6cf27f2b49282e2a05e288f30594f3dbc74eb7a6e99422bc48ed78162" +checksum = "30ee9f20bff9c984511a02f082dc8ede839e4a9bf15cc2487c8d6fea5ad850d9" dependencies = [ "opentelemetry", "opentelemetry_sdk", @@ -1766,35 +1666,25 @@ dependencies = [ [[package]] name = "opentelemetry_sdk" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae312d58eaa90a82d2e627fd86e075cf5230b3f11794e2ed74199ebbe572d4fd" +checksum = "692eac490ec80f24a17828d49b40b60f5aeaccdfe6a503f939713afd22bc28df" dependencies = [ "async-trait", "futures-channel", "futures-executor", "futures-util", "glob", - "lazy_static", "once_cell", "opentelemetry", - "ordered-float", "percent-encoding", "rand", + "serde_json", "thiserror", "tokio", "tokio-stream", ] -[[package]] -name = "ordered-float" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" -dependencies = [ - "num-traits", -] - [[package]] name = "overload" version = "0.1.1" @@ -2020,9 +1910,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -2030,12 +1920,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "syn", @@ -2203,11 +2093,11 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-rustls", "hyper-util", "ipnet", @@ -2304,7 +2194,7 @@ dependencies = [ "assign", "bytes", "date_header", - "http 1.1.0", + "http", "js_int", "js_option", "maplit", @@ -2327,7 +2217,7 @@ dependencies = [ "base64 0.22.1", "bytes", "form_urlencoded", - "http 1.1.0", + "http", "indexmap 2.5.0", "js_int", "konst", @@ -2377,7 +2267,7 @@ version = "0.9.0" source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "bytes", - "http 1.1.0", + "http", "httparse", "js_int", "memchr", @@ -2442,7 +2332,7 @@ version = "0.3.0" source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "headers", - "http 1.1.0", + "http", "http-auth", "ruma-common", "thiserror", @@ -2470,7 +2360,7 @@ name = "ruma-state-res" version = "0.11.0" source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ - "itertools", + "itertools 0.12.1", "js_int", "ruma-common", "ruma-events", @@ -3097,16 +2987,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.4.0" @@ -3201,23 +3081,26 @@ dependencies = [ [[package]] name = "tonic" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", - "axum 0.6.20", - "base64 0.21.7", + "axum", + "base64 0.22.1", "bytes", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", "prost", + "socket2", "tokio", "tokio-stream", "tower 0.4.13", @@ -3268,8 +3151,8 @@ checksum = "41515cc9e193536d93fd0dbbea0c73819c08eca76e0b30909a325c3ec90985bb" dependencies = [ "bitflags 2.6.0", "bytes", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", "tower 0.5.1", "tower-layer", @@ -3346,9 +3229,9 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f68803492bf28ab40aeccaecc7021096bd256baf7ca77c3d425d89b35a7be4e4" +checksum = "a9784ed4da7d921bc8df6963f8c80a0e4ce34ba6ba76668acadd3edbd985ff3b" dependencies = [ "js-sys", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 192aa6d4..8bd47de4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,11 +106,11 @@ jsonwebtoken = "9.3.0" lru-cache = "0.1.2" num_cpus = "1.16.0" once_cell = "1.19.0" -opentelemetry = "0.23.0" -opentelemetry-jaeger-propagator = "0.2.0" -opentelemetry-otlp = "0.16.0" -opentelemetry-prometheus = "0.16.0" -opentelemetry_sdk = { version = "0.23.0", features = ["rt-tokio"] } +opentelemetry = "0.24.0" +opentelemetry-jaeger-propagator = "0.3.0" +opentelemetry-otlp = "0.17.0" +opentelemetry-prometheus = "0.17.0" +opentelemetry_sdk = { version = "0.24.0", features = ["rt-tokio"] } parking_lot = { version = "0.12.3", optional = true } phf = { version = "0.11.2", features = ["macros"] } prometheus = "0.13.4" @@ -137,7 +137,7 @@ tower = { version = "0.5.1", features = ["util"] } tower-http = { version = "0.6.0", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.40", features = [] } tracing-flame = "0.2.0" -tracing-opentelemetry = "0.24.0" +tracing-opentelemetry = "0.25.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } trust-dns-resolver = "0.23.2" xdg = "2.5.2" diff --git a/src/observability.rs b/src/observability.rs index 8e3c662c..fb5df965 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -10,10 +10,7 @@ use axum::{ }; use http::Method; use once_cell::sync::Lazy; -use opentelemetry::{ - metrics::{MeterProvider, Unit}, - KeyValue, -}; +use opentelemetry::{metrics::MeterProvider, trace::TracerProvider, KeyValue}; use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::{ metrics::{new_view, Aggregation, Instrument, SdkMeterProvider, Stream}, @@ -168,10 +165,10 @@ pub(crate) fn init( if let Some(endpoint) = &config.observability.traces.endpoint { exporter = exporter.with_endpoint(endpoint); } - let tracer = opentelemetry_otlp::new_pipeline() + let tracer_provider = opentelemetry_otlp::new_pipeline() .tracing() .with_trace_config( - opentelemetry_sdk::trace::config().with_resource( + opentelemetry_sdk::trace::Config::default().with_resource( standard_resource( config.observability.traces.service_name.clone(), ), @@ -179,6 +176,18 @@ pub(crate) fn init( ) .with_exporter(exporter) .install_batch(opentelemetry_sdk::runtime::Tokio)?; + + // The passed value sets the library name, and `""` seems to be + // morally equivalent to passing `None`, which is probably fine + // because what other library is there to use for this anyway? + // + // Prior to opentelemetry v0.24, this value was set for us by the + // opentelemetry-otlp crate. Trying to automate getting the right + // values doesn't seem worth it, as alluded to above. + let tracer = tracer_provider.tracer(""); + + opentelemetry::global::set_tracer_provider(tracer_provider); + Ok((tracing_opentelemetry::layer().with_tracer(tracer), ())) }, )?; @@ -315,7 +324,7 @@ impl Metrics { let http_requests_histogram = meter .f64_histogram(http_requests_histogram_name) - .with_unit(Unit::new("seconds")) + .with_unit("seconds") .with_description("Histogram of HTTP requests") .init(); From d62d0e2f0ec07666e515f1eb2424370a6f94498b Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 21 Sep 2024 13:30:54 +0000 Subject: [PATCH 394/617] Split routes into components --- src/cli/serve.rs | 103 +++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 39 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index d8345c11..95ae9b44 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -249,10 +249,37 @@ async fn unrecognized_method( Ok(inner) } -#[allow(clippy::too_many_lines)] -fn routes(config: &Config) -> Router { +/// Routes for legacy unauthenticated `/_matrix/media/*` APIs (used by both +/// clients and federation) +fn legacy_media_routes(config: &Config) -> Router { + use client_server as c2s; + + let router = Router::new(); + + // deprecated, but unproblematic + let router = router.ruma_route(c2s::get_media_config_legacy_route); + + if config.serve_media_unauthenticated { + router + .ruma_route(c2s::get_content_legacy_route) + .ruma_route(c2s::get_content_as_filename_legacy_route) + .ruma_route(c2s::get_content_thumbnail_legacy_route) + } else { + router + .route( + "/_matrix/media/v3/download/*path", + any(unauthenticated_media_disabled), + ) + .route( + "/_matrix/media/v3/thumbnail/*path", + any(unauthenticated_media_disabled), + ) + } +} + +#[allow(clippy::too_many_lines)] +fn client_routes() -> Router { use client_server as c2s; - use server_server as s2s; let router = Router::new() .ruma_route(c2s::get_supported_versions_route) @@ -345,25 +372,6 @@ fn routes(config: &Config) -> Router { .ruma_route(c2s::turn_server_route) .ruma_route(c2s::send_event_to_device_route); - // deprecated, but unproblematic - let router = router.ruma_route(c2s::get_media_config_legacy_route); - let router = if config.serve_media_unauthenticated { - router - .ruma_route(c2s::get_content_legacy_route) - .ruma_route(c2s::get_content_as_filename_legacy_route) - .ruma_route(c2s::get_content_thumbnail_legacy_route) - } else { - router - .route( - "/_matrix/media/v3/download/*path", - any(unauthenticated_media_disabled), - ) - .route( - "/_matrix/media/v3/thumbnail/*path", - any(unauthenticated_media_disabled), - ) - }; - // authenticated media let router = router .ruma_route(c2s::get_media_config_route) @@ -419,16 +427,7 @@ fn routes(config: &Config) -> Router { .put(c2s::send_state_event_for_empty_key_route), ); - let router = if config.observability.metrics.enable { - router.route( - "/metrics", - get(|| async { observability::METRICS.export() }), - ) - } else { - router - }; - - let router = router + router .route( "/_matrix/client/r0/rooms/:room_id/initialSync", get(initial_sync), @@ -437,15 +436,13 @@ fn routes(config: &Config) -> Router { "/_matrix/client/v3/rooms/:room_id/initialSync", get(initial_sync), ) - .route("/", get(it_works)) - .fallback(not_found); +} - let router = router - .route("/.well-known/matrix/client", get(well_known::client)) - .route("/.well-known/matrix/server", get(well_known::server)); +fn federation_routes(config: &Config) -> Router { + use server_server as s2s; if config.federation.enable { - router + Router::new() .ruma_route(s2s::get_server_version_route) .route("/_matrix/key/v2/server", get(s2s::get_server_keys_route)) .route( @@ -473,12 +470,40 @@ fn routes(config: &Config) -> Router { .ruma_route(s2s::media_download_route) .ruma_route(s2s::media_thumbnail_route) } else { - router + Router::new() .route("/_matrix/federation/*path", any(federation_disabled)) .route("/_matrix/key/*path", any(federation_disabled)) } } +fn metrics_routes(config: &Config) -> Router { + if config.observability.metrics.enable { + Router::new().route( + "/metrics", + get(|| async { observability::METRICS.export() }), + ) + } else { + Router::new() + } +} + +fn well_known_routes() -> Router { + Router::new() + .route("/.well-known/matrix/client", get(well_known::client)) + .route("/.well-known/matrix/server", get(well_known::server)) +} + +fn routes(config: &Config) -> Router { + Router::new() + .merge(client_routes()) + .merge(federation_routes(config)) + .merge(legacy_media_routes(config)) + .merge(well_known_routes()) + .merge(metrics_routes(config)) + .route("/", get(it_works)) + .fallback(not_found) +} + async fn shutdown_signal(handles: Vec) { let ctrl_c = async { signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); From 084d862e5175df9350a8ba2f67f2c28c4de43d75 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 21 Sep 2024 15:55:17 +0000 Subject: [PATCH 395/617] Allow configuring served components per listener --- book/changelog.md | 2 ++ src/cli/serve.rs | 51 ++++++++++++++++++++++++-------------- src/config.rs | 63 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 89 insertions(+), 27 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index b06790d9..26ed1d67 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -246,3 +246,5 @@ This will be the first release of Grapevine since it was forked from Conduit 18. Added admin commands to delete media ([!99](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/99), [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102)) +19. Allow configuring the served API components per listener. + ([!109](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/109)) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 95ae9b44..8c9a2168 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -1,4 +1,7 @@ -use std::{future::Future, net::SocketAddr, sync::atomic, time::Duration}; +use std::{ + collections::HashSet, future::Future, net::SocketAddr, sync::atomic, + time::Duration, +}; use axum::{ extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, @@ -38,7 +41,7 @@ use crate::{ server_server, well_known, }, config, - config::{Config, ListenConfig}, + config::{Config, ListenComponent, ListenTransport}, database::KeyValueDatabase, error, observability, services, utils, utils::error::{Error, Result}, @@ -148,7 +151,6 @@ async fn run_server() -> Result<(), error::Serve> { )) .layer(axum::middleware::from_fn(observability::http_metrics_layer)); - let app = routes(config).layer(middlewares).into_make_service(); let mut handles = Vec::new(); let mut servers = JoinSet::new(); @@ -170,25 +172,29 @@ async fn run_server() -> Result<(), error::Serve> { for listen in &config.listen { info!(listener = %listen, "Listening for incoming traffic"); - match listen { - ListenConfig::Tcp { + let app = routes(config, &listen.components) + .layer(middlewares.clone()) + .into_make_service(); + + match listen.transport { + ListenTransport::Tcp { address, port, tls, } => { - let addr = SocketAddr::from((*address, *port)); + let addr = SocketAddr::from((address, port)); let handle = ServerHandle::new(); handles.push(handle.clone()); - let server = if *tls { + let server = if tls { let tls_config = tls_config .clone() .ok_or_else(|| Error::NoTlsCerts(listen.clone()))?; bind_rustls(addr, tls_config) .handle(handle) - .serve(app.clone()) + .serve(app) .left_future() } else { - bind(addr).handle(handle).serve(app.clone()).right_future() + bind(addr).handle(handle).serve(app).right_future() }; servers.spawn( server.then(|result| async { (listen.clone(), result) }), @@ -493,15 +499,24 @@ fn well_known_routes() -> Router { .route("/.well-known/matrix/server", get(well_known::server)) } -fn routes(config: &Config) -> Router { - Router::new() - .merge(client_routes()) - .merge(federation_routes(config)) - .merge(legacy_media_routes(config)) - .merge(well_known_routes()) - .merge(metrics_routes(config)) - .route("/", get(it_works)) - .fallback(not_found) +fn routes(config: &Config, components: &HashSet) -> Router { + let mut router = Router::new(); + for &component in components { + router = router.merge(match component { + ListenComponent::Client => client_routes(), + ListenComponent::Federation => federation_routes(config), + ListenComponent::Metrics => metrics_routes(config), + ListenComponent::WellKnown => well_known_routes(), + }); + } + + if components.contains(&ListenComponent::Client) + || components.contains(&ListenComponent::Federation) + { + router = router.merge(legacy_media_routes(config)); + } + + router.route("/", get(it_works)).fallback(not_found) } async fn shutdown_signal(handles: Vec) { diff --git a/src/config.rs b/src/config.rs index 197be925..394313e0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use std::{ borrow::Cow, - collections::BTreeMap, + collections::{BTreeMap, HashSet}, fmt::{self, Display}, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, @@ -13,6 +13,7 @@ use ruma::{ OwnedServerSigningKeyId, RoomVersionId, }; use serde::Deserialize; +use strum::{Display, EnumIter, IntoEnumIterator}; use crate::error; @@ -108,9 +109,27 @@ pub(crate) struct TlsConfig { pub(crate) key: String, } +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, EnumIter, Display, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub(crate) enum ListenComponent { + Client, + Federation, + Metrics, + WellKnown, +} + +impl ListenComponent { + fn all_components() -> HashSet { + Self::iter().collect() + } +} + #[derive(Clone, Debug, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] -pub(crate) enum ListenConfig { +pub(crate) enum ListenTransport { Tcp { #[serde(default = "default_address")] address: IpAddr, @@ -121,15 +140,15 @@ pub(crate) enum ListenConfig { }, } -impl Display for ListenConfig { +impl Display for ListenTransport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ListenConfig::Tcp { + ListenTransport::Tcp { address, port, tls: false, } => write!(f, "http://{address}:{port}"), - ListenConfig::Tcp { + ListenTransport::Tcp { address, port, tls: true, @@ -138,6 +157,29 @@ impl Display for ListenConfig { } } +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct ListenConfig { + #[serde(default = "ListenComponent::all_components")] + pub(crate) components: HashSet, + #[serde(flatten)] + pub(crate) transport: ListenTransport, +} + +impl Display for ListenConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} ({})", + self.transport, + self.components + .iter() + .map(ListenComponent::to_string) + .collect::>() + .join(", ") + ) + } +} + #[derive(Copy, Clone, Default, Debug, Deserialize)] #[serde(rename_all = "snake_case")] pub(crate) enum LogFormat { @@ -315,10 +357,13 @@ fn true_fn() -> bool { } fn default_listen() -> Vec { - vec![ListenConfig::Tcp { - address: default_address(), - port: default_port(), - tls: false, + vec![ListenConfig { + components: ListenComponent::all_components(), + transport: ListenTransport::Tcp { + address: default_address(), + port: default_port(), + tls: false, + }, }] } From e2318cad8a2e443a0cd948a3535b35fe067bf4a1 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 23 Sep 2024 23:33:29 -0700 Subject: [PATCH 396/617] fix serving tls by setting rustls default crypto provider The rustls version bump in c24f79b79ba0fd71e56e981c218acc70df016ee9 introduced a panic when serving listeners with 'tls = true': > thread 'main' panicked at /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-vendor-cargo-deps/c19b7c6f923b580ac259164a89f2577984ad5ab09ee9d583b888f934adbbe8d0/rustls-0.23.13/src/crypto/mod.rs:265:14: > no process-level CryptoProvider available -- call CryptoProvider::install_default() before this point This commit fixes this by setting the default provider to ring. I chose ring (the old rustls default) over aws-lc-rs (the new default) for a few reasons: - Judging by github issues, aws-lc-rs seems to have a lot of build problems. We don't need more of that. - The "motivation" section in the aws-lc-rs docs only talks about FIPS, which we do not care about. - My past experience with things that start with "aws-" has been very negative. --- Cargo.lock | 98 +----------------------------------------------- Cargo.toml | 3 +- src/cli/serve.rs | 4 ++ 3 files changed, 8 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4cefcc55..caff42d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,33 +131,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "aws-lc-rs" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" -dependencies = [ - "aws-lc-sys", - "mirai-annotations", - "paste", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", - "libc", - "paste", -] - [[package]] name = "axum" version = "0.7.6" @@ -304,15 +277,12 @@ dependencies = [ "itertools 0.12.1", "lazy_static", "lazycell", - "log", - "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", "syn", - "which", ] [[package]] @@ -468,15 +438,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "cmake" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" -dependencies = [ - "cc", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -608,12 +569,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "ed25519" version = "2.2.3" @@ -725,12 +680,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futures-channel" version = "0.3.30" @@ -886,6 +835,7 @@ dependencies = [ "ruma", "rusqlite", "rust-rocksdb", + "rustls", "sd-notify", "serde", "serde_html_form", @@ -996,15 +946,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "hostname" version = "0.3.1" @@ -1495,12 +1436,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "nix" version = "0.29.0" @@ -1725,12 +1660,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pem" version = "3.0.4" @@ -1865,16 +1794,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -2456,7 +2375,7 @@ version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ - "aws-lc-rs", + "log", "once_cell", "ring", "rustls-pki-types", @@ -2513,7 +2432,6 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -3535,18 +3453,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "widestring" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 8bd47de4..7904770e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ argon2 = "0.5.3" async-trait = "0.1.82" axum = { version = "0.7.6", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } axum-extra = { version = "0.9.4", features = ["typed-header"] } -axum-server = { version = "0.7.1", features = ["tls-rustls"] } +axum-server = { version = "0.7.1", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" bytes = "1.7.2" clap = { version = "4.5.18", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } @@ -121,6 +121,7 @@ ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } +rustls = { version = "0.23.13", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.2", optional = true } serde = { version = "1.0.210", features = ["rc"] } serde_html_form = "0.2.6" diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 8c9a2168..a71ebf1f 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -52,6 +52,10 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { let config = config::load(args.config.config.as_ref()).await?; + rustls::crypto::ring::default_provider() + .install_default() + .expect("rustls default crypto provider should not be already set"); + let (_guard, reload_handles) = observability::init(&config)?; // This is needed for opening lots of file descriptors, which tends to From 059dfe54e34649f86c6352aa21e0e0fcaa747c7d Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 30 Aug 2024 17:47:57 -0700 Subject: [PATCH 397/617] factor out helper function for db migrations --- src/database.rs | 131 ++++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 77 deletions(-) diff --git a/src/database.rs b/src/database.rs index 6156c7e0..5e145acd 100644 --- a/src/database.rs +++ b/src/database.rs @@ -570,7 +570,7 @@ impl KeyValueDatabase { if services().users.count()? > 0 { // MIGRATIONS - if services().globals.database_version()? < 1 { + migration(1, || { for (roomserverid, _) in db.roomserverids.iter() { let mut parts = roomserverid.split(|&b| b == 0xFF); let room_id = @@ -585,13 +585,10 @@ impl KeyValueDatabase { db.serverroomids.insert(&serverroomid, &[])?; } + Ok(()) + })?; - services().globals.bump_database_version(1)?; - - warn!("Migration: 0 -> 1 finished"); - } - - if services().globals.database_version()? < 2 { + migration(2, || { // We accidentally inserted hashed versions of "" into the db // instead of just "" for (userid, password) in db.userid_password.iter() { @@ -606,13 +603,10 @@ impl KeyValueDatabase { db.userid_password.insert(&userid, b"")?; } } + Ok(()) + })?; - services().globals.bump_database_version(2)?; - - warn!("Migration: 1 -> 2 finished"); - } - - if services().globals.database_version()? < 3 { + migration(3, || { // Move media to filesystem for (key, content) in db.mediaid_file.iter() { let key = MediaFileKey::new(key); @@ -625,13 +619,10 @@ impl KeyValueDatabase { file.write_all(&content)?; db.mediaid_file.insert(key.as_bytes(), &[])?; } + Ok(()) + })?; - services().globals.bump_database_version(3)?; - - warn!("Migration: 2 -> 3 finished"); - } - - if services().globals.database_version()? < 4 { + migration(4, || { // Add federated users to services() as deactivated for our_user in services().users.iter() { let our_user = our_user?; @@ -654,13 +645,10 @@ impl KeyValueDatabase { } } } + Ok(()) + })?; - services().globals.bump_database_version(4)?; - - warn!("Migration: 3 -> 4 finished"); - } - - if services().globals.database_version()? < 5 { + migration(5, || { // Upgrade user data store for (roomuserdataid, _) in db.roomuserdataid_accountdata.iter() { @@ -679,13 +667,10 @@ impl KeyValueDatabase { db.roomusertype_roomuserdataid .insert(&key, &roomuserdataid)?; } + Ok(()) + })?; - services().globals.bump_database_version(5)?; - - warn!("Migration: 4 -> 5 finished"); - } - - if services().globals.database_version()? < 6 { + migration(6, || { // Set room member count for (roomid, _) in db.roomid_shortstatehash.iter() { let string = utils::string_from_bytes(&roomid).unwrap(); @@ -695,13 +680,10 @@ impl KeyValueDatabase { .state_cache .update_joined_count(room_id)?; } + Ok(()) + })?; - services().globals.bump_database_version(6)?; - - warn!("Migration: 5 -> 6 finished"); - } - - if services().globals.database_version()? < 7 { + migration(7, || { // Upgrade state store let mut last_roomstates: HashMap = HashMap::new(); @@ -833,13 +815,10 @@ impl KeyValueDatabase { &mut last_roomstates, )?; } + Ok(()) + })?; - services().globals.bump_database_version(7)?; - - warn!("Migration: 6 -> 7 finished"); - } - - if services().globals.database_version()? < 8 { + migration(8, || { // Generate short room ids for all rooms for (room_id, _) in db.roomid_shortstatehash.iter() { let shortroomid = @@ -892,13 +871,10 @@ impl KeyValueDatabase { }); db.eventid_pduid.insert_batch(&mut batch2)?; + Ok(()) + })?; - services().globals.bump_database_version(8)?; - - warn!("Migration: 7 -> 8 finished"); - } - - if services().globals.database_version()? < 9 { + migration(9, || { // Update tokenids db layout let mut iter = db .tokenids @@ -942,13 +918,10 @@ impl KeyValueDatabase { for key in batch2 { db.tokenids.remove(&key)?; } + Ok(()) + })?; - services().globals.bump_database_version(9)?; - - warn!("Migration: 8 -> 9 finished"); - } - - if services().globals.database_version()? < 10 { + migration(10, || { // Add other direction for shortstatekeys for (statekey, shortstatekey) in db.statekey_shortstatekey.iter() @@ -962,20 +935,15 @@ impl KeyValueDatabase { for user_id in services().users.iter().filter_map(Result::ok) { services().users.mark_device_key_update(&user_id)?; } + Ok(()) + })?; - services().globals.bump_database_version(10)?; - - warn!("Migration: 9 -> 10 finished"); - } - - if services().globals.database_version()? < 11 { + migration(11, || { db.db.open_tree("userdevicesessionid_uiaarequest")?.clear()?; - services().globals.bump_database_version(11)?; + Ok(()) + })?; - warn!("Migration: 10 -> 11 finished"); - } - - if services().globals.database_version()? < 12 { + migration(12, || { for username in services().users.list_local_users()? { let user = match UserId::parse_with_server_name( username.clone(), @@ -1072,15 +1040,12 @@ impl KeyValueDatabase { .expect("to json value always works"), )?; } - - services().globals.bump_database_version(12)?; - - warn!("Migration: 11 -> 12 finished"); - } + Ok(()) + })?; // This migration can be reused as-is anytime the server-default // rules are updated. - if services().globals.database_version()? < 13 { + migration(13, || { for username in services().users.list_local_users()? { let user = match UserId::parse_with_server_name( username.clone(), @@ -1131,11 +1096,8 @@ impl KeyValueDatabase { .expect("to json value always works"), )?; } - - services().globals.bump_database_version(13)?; - - warn!("Migration: 12 -> 13 finished"); - } + Ok(()) + })?; assert_eq!( services().globals.database_version().unwrap(), @@ -1277,3 +1239,18 @@ fn set_emergency_access() -> Result { res } + +/// If the current version is older than `new_version`, execute a migration +/// function. +fn migration(new_version: u64, migration: F) -> Result<(), Error> +where + F: FnOnce() -> Result<(), Error>, +{ + let current_version = services().globals.database_version()?; + if current_version < new_version { + migration()?; + services().globals.bump_database_version(new_version)?; + warn!("Migration: {current_version} -> {new_version} finished"); + } + Ok(()) +} From 279c6472c5c32103ea132243e8b7f128f5472717 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Sun, 8 Sep 2024 16:31:51 -0700 Subject: [PATCH 398/617] split some logic out of KeyValueDatabase::load_or_create This method did _a lot_ of things at the same time. In order to use `KeyValueDatabase` for the migrate-db command, we need to be able to open a db without attempting to apply all the migrations and without spawning a bunch of unrelated background tasks. The state after this refactor is still not great, but it's enough to do a migration tool. --- src/cli/serve.rs | 10 ++- src/database.rs | 142 +++++++++++++---------------------------- src/service/globals.rs | 78 ++++++++++++++++++++-- 3 files changed, 127 insertions(+), 103 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index a71ebf1f..2dcdb765 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -70,9 +70,15 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .expect("should be able to increase the soft limit to the hard limit"); info!("Loading database"); - KeyValueDatabase::load_or_create(config, reload_handles) - .await + let db = KeyValueDatabase::load_or_create(config, reload_handles) .map_err(Error::DatabaseError)?; + db.apply_migrations().await.map_err(Error::DatabaseError)?; + + info!("Starting background tasks"); + services().admin.start_handler(); + services().sending.start_handler(); + KeyValueDatabase::start_cleanup_task(); + services().globals.set_emergency_access(); info!("Starting server"); run_server().await?; diff --git a/src/database.rs b/src/database.rs index 5e145acd..bd433dd7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -14,9 +14,7 @@ use abstraction::{KeyValueDatabaseEngine, KvTree}; use lru_cache::LruCache; use ruma::{ events::{ - push_rules::{PushRulesEvent, PushRulesEventContent}, - room::message::RoomMessageEventContent, - GlobalAccountDataEvent, GlobalAccountDataEventType, StateEventType, + push_rules::PushRulesEvent, GlobalAccountDataEventType, StateEventType, }, push::Ruleset, CanonicalJsonValue, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, @@ -314,16 +312,17 @@ impl KeyValueDatabase { Ok(()) } - /// Load an existing database or create a new one. + /// Load an existing database or create a new one, and initialize all + /// services with the loaded database. #[cfg_attr( not(any(feature = "rocksdb", feature = "sqlite")), allow(unreachable_code) )] #[allow(clippy::too_many_lines)] - pub(crate) async fn load_or_create( + pub(crate) fn load_or_create( config: Config, reload_handles: FilterReloadHandles, - ) -> Result<()> { + ) -> Result<&'static KeyValueDatabase> { Self::check_db_setup(&config)?; if !Path::new(&config.database.path).exists() { @@ -565,13 +564,23 @@ impl KeyValueDatabase { } } + Ok(db) + } + + /// Ensure that the database is at the current version, applying migrations + /// if necessary. + /// + /// If it is not possible to migrate the database to the current version, + /// returns an error. + #[allow(clippy::too_many_lines)] + pub(crate) async fn apply_migrations(&self) -> Result<()> { // If the database has any data, perform data migrations before starting let latest_database_version = 13; if services().users.count()? > 0 { // MIGRATIONS migration(1, || { - for (roomserverid, _) in db.roomserverids.iter() { + for (roomserverid, _) in self.roomserverids.iter() { let mut parts = roomserverid.split(|&b| b == 0xFF); let room_id = parts.next().expect("split always returns one element"); @@ -583,7 +592,7 @@ impl KeyValueDatabase { serverroomid.push(0xFF); serverroomid.extend_from_slice(room_id); - db.serverroomids.insert(&serverroomid, &[])?; + self.serverroomids.insert(&serverroomid, &[])?; } Ok(()) })?; @@ -591,7 +600,7 @@ impl KeyValueDatabase { migration(2, || { // We accidentally inserted hashed versions of "" into the db // instead of just "" - for (userid, password) in db.userid_password.iter() { + for (userid, password) in self.userid_password.iter() { let password = utils::string_from_bytes(&password); let empty_hashed_password = password @@ -600,7 +609,7 @@ impl KeyValueDatabase { }); if empty_hashed_password { - db.userid_password.insert(&userid, b"")?; + self.userid_password.insert(&userid, b"")?; } } Ok(()) @@ -608,7 +617,7 @@ impl KeyValueDatabase { migration(3, || { // Move media to filesystem - for (key, content) in db.mediaid_file.iter() { + for (key, content) in self.mediaid_file.iter() { let key = MediaFileKey::new(key); if content.is_empty() { continue; @@ -617,7 +626,7 @@ impl KeyValueDatabase { let path = services().globals.get_media_file(&key); let mut file = fs::File::create(path)?; file.write_all(&content)?; - db.mediaid_file.insert(key.as_bytes(), &[])?; + self.mediaid_file.insert(key.as_bytes(), &[])?; } Ok(()) })?; @@ -650,7 +659,8 @@ impl KeyValueDatabase { migration(5, || { // Upgrade user data store - for (roomuserdataid, _) in db.roomuserdataid_accountdata.iter() + for (roomuserdataid, _) in + self.roomuserdataid_accountdata.iter() { let mut parts = roomuserdataid.split(|&b| b == 0xFF); let room_id = parts.next().unwrap(); @@ -664,7 +674,7 @@ impl KeyValueDatabase { key.push(0xFF); key.extend_from_slice(event_type); - db.roomusertype_roomuserdataid + self.roomusertype_roomuserdataid .insert(&key, &roomuserdataid)?; } Ok(()) @@ -672,7 +682,7 @@ impl KeyValueDatabase { migration(6, || { // Set room member count - for (roomid, _) in db.roomid_shortstatehash.iter() { + for (roomid, _) in self.roomid_shortstatehash.iter() { let string = utils::string_from_bytes(&roomid).unwrap(); let room_id = <&RoomId>::try_from(string.as_str()).unwrap(); services() @@ -750,7 +760,7 @@ impl KeyValueDatabase { }; for (k, seventid) in - db.db.open_tree("stateid_shorteventid")?.iter() + self.db.open_tree("stateid_shorteventid")?.iter() { let sstatehash = ShortStateHash::new( utils::u64_from_bytes(&k[0..size_of::()]) @@ -776,7 +786,7 @@ impl KeyValueDatabase { current_state = HashSet::new(); current_sstatehash = Some(sstatehash); - let event_id = db + let event_id = self .shorteventid_eventid .get(&seventid) .unwrap() @@ -820,14 +830,14 @@ impl KeyValueDatabase { migration(8, || { // Generate short room ids for all rooms - for (room_id, _) in db.roomid_shortstatehash.iter() { + for (room_id, _) in self.roomid_shortstatehash.iter() { let shortroomid = services().globals.next_count()?.to_be_bytes(); - db.roomid_shortroomid.insert(&room_id, &shortroomid)?; + self.roomid_shortroomid.insert(&room_id, &shortroomid)?; info!("Migration: 8"); } // Update pduids db layout - let mut batch = db.pduid_pdu.iter().filter_map(|(key, v)| { + let mut batch = self.pduid_pdu.iter().filter_map(|(key, v)| { if !key.starts_with(b"!") { return None; } @@ -835,7 +845,7 @@ impl KeyValueDatabase { let room_id = parts.next().unwrap(); let count = parts.next().unwrap(); - let short_room_id = db + let short_room_id = self .roomid_shortroomid .get(room_id) .unwrap() @@ -847,10 +857,10 @@ impl KeyValueDatabase { Some((new_key, v)) }); - db.pduid_pdu.insert_batch(&mut batch)?; + self.pduid_pdu.insert_batch(&mut batch)?; let mut batch2 = - db.eventid_pduid.iter().filter_map(|(k, value)| { + self.eventid_pduid.iter().filter_map(|(k, value)| { if !value.starts_with(b"!") { return None; } @@ -858,7 +868,7 @@ impl KeyValueDatabase { let room_id = parts.next().unwrap(); let count = parts.next().unwrap(); - let short_room_id = db + let short_room_id = self .roomid_shortroomid .get(room_id) .unwrap() @@ -870,13 +880,13 @@ impl KeyValueDatabase { Some((k, new_value)) }); - db.eventid_pduid.insert_batch(&mut batch2)?; + self.eventid_pduid.insert_batch(&mut batch2)?; Ok(()) })?; migration(9, || { // Update tokenids db layout - let mut iter = db + let mut iter = self .tokenids .iter() .filter_map(|(key, _)| { @@ -889,7 +899,7 @@ impl KeyValueDatabase { let _pdu_id_room = parts.next().unwrap(); let pdu_id_count = parts.next().unwrap(); - let short_room_id = db + let short_room_id = self .roomid_shortroomid .get(room_id) .unwrap() @@ -903,20 +913,21 @@ impl KeyValueDatabase { .peekable(); while iter.peek().is_some() { - db.tokenids.insert_batch(&mut iter.by_ref().take(1000))?; + self.tokenids + .insert_batch(&mut iter.by_ref().take(1000))?; debug!("Inserted smaller batch"); } info!("Deleting starts"); - let batch2: Vec<_> = db + let batch2: Vec<_> = self .tokenids .iter() .filter_map(|(key, _)| key.starts_with(b"!").then_some(key)) .collect(); for key in batch2 { - db.tokenids.remove(&key)?; + self.tokenids.remove(&key)?; } Ok(()) })?; @@ -924,9 +935,9 @@ impl KeyValueDatabase { migration(10, || { // Add other direction for shortstatekeys for (statekey, shortstatekey) in - db.statekey_shortstatekey.iter() + self.statekey_shortstatekey.iter() { - db.shortstatekey_statekey + self.shortstatekey_statekey .insert(&shortstatekey, &statekey)?; } @@ -939,7 +950,9 @@ impl KeyValueDatabase { })?; migration(11, || { - db.db.open_tree("userdevicesessionid_uiaarequest")?.clear()?; + self.db + .open_tree("userdevicesessionid_uiaarequest")? + .clear()?; Ok(()) })?; @@ -1125,39 +1138,6 @@ impl KeyValueDatabase { ); } - services().admin.start_handler(); - - // Set emergency access for the grapevine user - match set_emergency_access() { - Ok(pwd_set) => { - if pwd_set { - warn!( - "The Grapevine account emergency password is set! \ - Please unset it as soon as you finish admin account \ - recovery!" - ); - services().admin.send_message( - RoomMessageEventContent::text_plain( - "The Grapevine account emergency password is set! \ - Please unset it as soon as you finish admin \ - account recovery!", - ), - ); - } - } - Err(error) => { - error!( - %error, - "Could not set the configured emergency password for the \ - Grapevine user", - ); - } - }; - - services().sending.start_handler(); - - Self::start_cleanup_task(); - Ok(()) } @@ -1210,36 +1190,6 @@ impl KeyValueDatabase { } } -/// Sets the emergency password and push rules for the @grapevine account in -/// case emergency password is set -fn set_emergency_access() -> Result { - let admin_bot = services().globals.admin_bot_user_id.as_ref(); - - services().users.set_password( - admin_bot, - services().globals.emergency_password().as_deref(), - )?; - - let (ruleset, res) = match services().globals.emergency_password() { - Some(_) => (Ruleset::server_default(admin_bot), Ok(true)), - None => (Ruleset::new(), Ok(false)), - }; - - services().account_data.update( - None, - admin_bot, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(&GlobalAccountDataEvent { - content: PushRulesEventContent { - global: ruleset, - }, - }) - .expect("to json value always works"), - )?; - - res -} - /// If the current version is older than `new_version`, execute a migration /// function. fn migration(new_version: u64, migration: F) -> Result<(), Error> diff --git a/src/service/globals.rs b/src/service/globals.rs index b4ccba9d..9a280a23 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -23,19 +23,27 @@ use hyper_util::{ }; use reqwest::dns::{Addrs, Name, Resolve, Resolving}; use ruma::{ - api::federation::discovery::ServerSigningKeys, serde::Base64, DeviceId, - MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomAliasId, OwnedRoomId, - OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId, ServerName, - UserId, + api::federation::discovery::ServerSigningKeys, + events::{ + push_rules::PushRulesEventContent, + room::message::RoomMessageEventContent, GlobalAccountDataEvent, + GlobalAccountDataEventType, + }, + push::Ruleset, + serde::Base64, + DeviceId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomAliasId, + OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId, + ServerName, UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; -use tracing::{error, Instrument}; +use tracing::{error, warn, Instrument}; use trust_dns_resolver::TokioAsyncResolver; use crate::{ api::server_server::FedDest, observability::FilterReloadHandles, service::media::MediaFileKey, + services, utils::on_demand_hashmap::{OnDemandHashMap, TokenSet}, Config, Error, Result, }; @@ -406,6 +414,66 @@ impl Service { &self.config.emergency_password } + /// If the emergency password option is set, attempts to set the emergency + /// password and push rules for the @grapevine account. + /// + /// If an error occurs, it is logged. + pub(crate) fn set_emergency_access(&self) { + let inner = || -> Result { + let admin_bot = self.admin_bot_user_id.as_ref(); + + services().users.set_password( + admin_bot, + self.emergency_password().as_deref(), + )?; + + let (ruleset, res) = match self.emergency_password() { + Some(_) => (Ruleset::server_default(admin_bot), Ok(true)), + None => (Ruleset::new(), Ok(false)), + }; + + services().account_data.update( + None, + admin_bot, + GlobalAccountDataEventType::PushRules.to_string().into(), + &serde_json::to_value(&GlobalAccountDataEvent { + content: PushRulesEventContent { + global: ruleset, + }, + }) + .expect("to json value always works"), + )?; + + res + }; + + match inner() { + Ok(pwd_set) => { + if pwd_set { + warn!( + "The Grapevine account emergency password is set! \ + Please unset it as soon as you finish admin account \ + recovery!" + ); + services().admin.send_message( + RoomMessageEventContent::text_plain( + "The Grapevine account emergency password is set! \ + Please unset it as soon as you finish admin \ + account recovery!", + ), + ); + } + } + Err(error) => { + error!( + %error, + "Could not set the configured emergency password for the \ + Grapevine user", + ); + } + }; + } + pub(crate) fn supported_room_versions(&self) -> Vec { self.stable_room_versions.clone() } From 75ef57e0cec210a9fdf3650abd7993890870d26d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 24 Sep 2024 17:19:20 -0700 Subject: [PATCH 399/617] remove config check * Database load function is the wrong place for this * There's no good lower bound to check for this * Surely people setting this to a small value would realize what they're in for --- src/database.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/database.rs b/src/database.rs index bd433dd7..2ab3bc6a 100644 --- a/src/database.rs +++ b/src/database.rs @@ -357,13 +357,6 @@ impl KeyValueDatabase { return Err(Error::bad_config("Registration token is empty")); } - if config.max_request_size < 1024 { - error!( - ?config.max_request_size, - "Max request size is less than 1KB. Please increase it.", - ); - } - let db_raw = Box::new(Self { db: builder.clone(), userid_password: builder.open_tree("userid_password")?, From e9caf228b3581e0ce458147bafa59deb590eb2f4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 24 Sep 2024 17:18:22 -0700 Subject: [PATCH 400/617] move config check into config load function --- src/config.rs | 10 ++++++++-- src/database.rs | 4 ---- src/error.rs | 3 +++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 394313e0..c1ebfeab 100644 --- a/src/config.rs +++ b/src/config.rs @@ -438,10 +438,16 @@ where let path = path.as_ref(); - toml::from_str( + let config: Config = toml::from_str( &tokio::fs::read_to_string(path) .await .map_err(|e| Error::Read(e, path.to_owned()))?, ) - .map_err(|e| Error::Parse(e, path.to_owned())) + .map_err(|e| Error::Parse(e, path.to_owned()))?; + + if config.registration_token.as_deref() == Some("") { + return Err(Error::RegistrationTokenEmpty); + } + + Ok(config) } diff --git a/src/database.rs b/src/database.rs index 2ab3bc6a..160a3093 100644 --- a/src/database.rs +++ b/src/database.rs @@ -353,10 +353,6 @@ impl KeyValueDatabase { } }; - if config.registration_token == Some(String::new()) { - return Err(Error::bad_config("Registration token is empty")); - } - let db_raw = Box::new(Self { db: builder.clone(), userid_password: builder.open_tree("userid_password")?, diff --git a/src/error.rs b/src/error.rs index d2fcae24..180fc6a9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -95,6 +95,9 @@ pub(crate) enum Config { #[error("failed to parse configuration file {1:?}")] Parse(#[source] toml::de::Error, PathBuf), + + #[error("registration token must not be empty")] + RegistrationTokenEmpty, } /// Errors that can occur while searching for a config file From c2c6083277e740064fa124c64806560220bbe492 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 24 Sep 2024 17:44:33 -0700 Subject: [PATCH 401/617] make load_or_create *only* load_or_create Extracted the other logic to its current singular callsite for now. The load_or_create function finally does nothing other than load or create the database (and do some related checks, which is fine). This paves the way for more/better database surgery tooling. --- src/cli/serve.rs | 34 +++++++++++++++++++++----- src/database.rs | 62 +++++++++++++----------------------------------- src/error.rs | 12 ++++++++++ 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 2dcdb765..f2152e55 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -40,11 +40,14 @@ use crate::{ ruma_wrapper::{Ar, Ra}, server_server, well_known, }, - config, - config::{Config, ListenComponent, ListenTransport}, + config::{self, Config, ListenComponent, ListenTransport}, database::KeyValueDatabase, - error, observability, services, utils, - utils::error::{Error, Result}, + error, observability, services, + utils::{ + self, + error::{Error, Result}, + }, + Services, SERVICES, }; pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { @@ -70,8 +73,27 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .expect("should be able to increase the soft limit to the hard limit"); info!("Loading database"); - let db = KeyValueDatabase::load_or_create(config, reload_handles) - .map_err(Error::DatabaseError)?; + let db = Box::leak(Box::new( + KeyValueDatabase::load_or_create(&config) + .map_err(Error::DatabaseError)?, + )); + + // This is the first and only time we initialize the SERVICE static + *SERVICES.write().unwrap() = Some(Box::leak(Box::new( + Services::build(db, config, reload_handles) + .map_err(Error::InitializeServices)?, + ))); + + // Matrix resource ownership is based on the server name; changing it + // requires recreating the database from scratch. This check needs to be + // done before background tasks are started to avoid data races. + if services().users.count().map(|x| x > 0).map_err(Error::NonZeroUsers)? { + let admin_bot = services().globals.admin_bot_user_id.as_ref(); + if !services().users.exists(admin_bot).map_err(Error::AdminBotExists)? { + return Err(Error::Renamed); + } + } + db.apply_migrations().await.map_err(Error::DatabaseError)?; info!("Starting background tasks"); diff --git a/src/database.rs b/src/database.rs index 160a3093..abfad85a 100644 --- a/src/database.rs +++ b/src/database.rs @@ -24,7 +24,6 @@ use tracing::{debug, error, info, info_span, warn, Instrument}; use crate::{ config::DatabaseBackend, - observability::FilterReloadHandles, service::{ media::MediaFileKey, rooms::{ @@ -33,7 +32,7 @@ use crate::{ timeline::PduCount, }, }, - services, utils, Config, Error, PduEvent, Result, Services, SERVICES, + services, utils, Config, Error, PduEvent, Result, }; pub(crate) struct KeyValueDatabase { @@ -319,11 +318,8 @@ impl KeyValueDatabase { allow(unreachable_code) )] #[allow(clippy::too_many_lines)] - pub(crate) fn load_or_create( - config: Config, - reload_handles: FilterReloadHandles, - ) -> Result<&'static KeyValueDatabase> { - Self::check_db_setup(&config)?; + pub(crate) fn load_or_create(config: &Config) -> Result { + Self::check_db_setup(config)?; if !Path::new(&config.database.path).exists() { fs::create_dir_all(&config.database.path).map_err(|_| { @@ -339,21 +335,19 @@ impl KeyValueDatabase { not(any(feature = "rocksdb", feature = "sqlite")), allow(unused_variables) )] - let builder: Arc = match config - .database - .backend - { - #[cfg(feature = "sqlite")] - DatabaseBackend::Sqlite => { - Arc::new(Arc::::open(&config)?) - } - #[cfg(feature = "rocksdb")] - DatabaseBackend::Rocksdb => { - Arc::new(Arc::::open(&config)?) - } - }; + let builder: Arc = + match config.database.backend { + #[cfg(feature = "sqlite")] + DatabaseBackend::Sqlite => { + Arc::new(Arc::::open(config)?) + } + #[cfg(feature = "rocksdb")] + DatabaseBackend::Rocksdb => { + Arc::new(Arc::::open(config)?) + } + }; - let db_raw = Box::new(Self { + let db = Self { db: builder.clone(), userid_password: builder.open_tree("userid_password")?, userid_displayname: builder.open_tree("userid_displayname")?, @@ -527,31 +521,7 @@ impl KeyValueDatabase { our_real_users_cache: RwLock::new(HashMap::new()), appservice_in_room_cache: RwLock::new(HashMap::new()), lasttimelinecount_cache: Mutex::new(HashMap::new()), - }); - - let db = Box::leak(db_raw); - - let services_raw = - Box::new(Services::build(db, config, reload_handles)?); - - // This is the first and only time we initialize the SERVICE static - *SERVICES.write().unwrap() = Some(Box::leak(services_raw)); - - // Matrix resource ownership is based on the server name; changing it - // requires recreating the database from scratch. - if services().users.count()? > 0 { - let admin_bot = services().globals.admin_bot_user_id.as_ref(); - if !services().users.exists(admin_bot)? { - error!( - user_id = %admin_bot, - "The admin bot does not exist and the database is not new", - ); - return Err(Error::bad_database( - "Cannot reuse an existing database after changing the \ - server name, please delete the old one first.", - )); - } - } + }; Ok(db) } diff --git a/src/error.rs b/src/error.rs index 180fc6a9..2ad565a3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,6 +61,18 @@ pub(crate) enum ServeCommand { #[error("failed to serve requests")] Serve(#[from] Serve), + + #[error("failed to initialize services")] + InitializeServices(#[source] crate::utils::error::Error), + + #[error("failed to check if there are any users")] + NonZeroUsers(#[source] crate::utils::error::Error), + + #[error("failed to check if the admin bot exists")] + AdminBotExists(#[source] crate::utils::error::Error), + + #[error("`server_name` in the database and config file differ")] + Renamed, } /// Observability initialization errors From 1fd20cdebae6f6c7c8dced3c7cce70c97ed53cd9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 24 Sep 2024 19:46:21 -0700 Subject: [PATCH 402/617] factor server_name change check into a reusable fn --- src/cli/serve.rs | 10 +--------- src/error.rs | 10 ++++++++++ src/service/globals.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index f2152e55..ab65396b 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -84,15 +84,7 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .map_err(Error::InitializeServices)?, ))); - // Matrix resource ownership is based on the server name; changing it - // requires recreating the database from scratch. This check needs to be - // done before background tasks are started to avoid data races. - if services().users.count().map(|x| x > 0).map_err(Error::NonZeroUsers)? { - let admin_bot = services().globals.admin_bot_user_id.as_ref(); - if !services().users.exists(admin_bot).map_err(Error::AdminBotExists)? { - return Err(Error::Renamed); - } - } + services().globals.err_if_server_name_changed()?; db.apply_migrations().await.map_err(Error::DatabaseError)?; diff --git a/src/error.rs b/src/error.rs index 2ad565a3..106484df 100644 --- a/src/error.rs +++ b/src/error.rs @@ -65,6 +65,16 @@ pub(crate) enum ServeCommand { #[error("failed to initialize services")] InitializeServices(#[source] crate::utils::error::Error), + #[error("`server_name` change check failed")] + ServerNameChanged(#[from] ServerNameChanged), +} + +/// Error generated if `server_name` has changed or if checking this failed +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum ServerNameChanged { #[error("failed to check if there are any users")] NonZeroUsers(#[source] crate::utils::error::Error), diff --git a/src/service/globals.rs b/src/service/globals.rs index 9a280a23..a1662c9e 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -305,6 +305,37 @@ impl Service { Ok(s) } + /// Check if `server_name` in the DB and config differ, return error if so + /// + /// Matrix resource ownership is based on the server name; changing it + /// requires recreating the database from scratch. This check needs to be + /// done before background tasks are started to avoid data races. + // Allowed because this function calls `services()` + #[allow(clippy::unused_self)] + pub(crate) fn err_if_server_name_changed( + &self, + ) -> Result<(), crate::error::ServerNameChanged> { + use crate::error::ServerNameChanged as Error; + + if services() + .users + .count() + .map(|x| x > 0) + .map_err(Error::NonZeroUsers)? + { + let admin_bot = self.admin_bot_user_id.as_ref(); + if !services() + .users + .exists(admin_bot) + .map_err(Error::AdminBotExists)? + { + return Err(Error::Renamed); + } + } + + Ok(()) + } + /// Returns this server's keypair. pub(crate) fn keypair(&self) -> &ruma::signatures::Ed25519KeyPair { &self.keypair From 032e1ca3c6c5c3284d8a5bdcfc544a3b2499d148 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 24 Sep 2024 19:48:33 -0700 Subject: [PATCH 403/617] hide global services jank in service module Mainly to make it easier to initialize the SERVICES global correctly in more than one place. Also this stuff really shouldn't live at the crate root anyway. --- src/cli/serve.rs | 10 ++++------ src/main.rs | 15 ++------------- src/service.rs | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index ab65396b..79a55482 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -47,7 +47,7 @@ use crate::{ self, error::{Error, Result}, }, - Services, SERVICES, + Services, }; pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { @@ -78,11 +78,9 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .map_err(Error::DatabaseError)?, )); - // This is the first and only time we initialize the SERVICE static - *SERVICES.write().unwrap() = Some(Box::leak(Box::new( - Services::build(db, config, reload_handles) - .map_err(Error::InitializeServices)?, - ))); + Services::build(db, config, reload_handles) + .map_err(Error::InitializeServices)? + .install(); services().globals.err_if_server_name_changed()?; diff --git a/src/main.rs b/src/main.rs index 080b9714..903906a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ // work anyway #![cfg_attr(not(any(feature = "sqlite", feature = "rocksdb")), allow(unused))] -use std::{process::ExitCode, sync::RwLock}; +use std::process::ExitCode; use clap::Parser; use tracing::error; @@ -18,7 +18,7 @@ mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; pub(crate) use config::Config; -pub(crate) use service::{pdu::PduEvent, Services}; +pub(crate) use service::{pdu::PduEvent, services, Services}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] use tikv_jemallocator::Jemalloc; pub(crate) use utils::error::{Error, Result}; @@ -27,17 +27,6 @@ pub(crate) use utils::error::{Error, Result}; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -pub(crate) static SERVICES: RwLock> = - RwLock::new(None); - -/// Convenient access to the global [`Services`] instance -pub(crate) fn services() -> &'static Services { - SERVICES - .read() - .unwrap() - .expect("SERVICES should be initialized when this is called") -} - /// Returns the current version of the crate with extra info if supplied /// /// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string diff --git a/src/service.rs b/src/service.rs index 61216a61..4bf7c5e2 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,6 +1,6 @@ use std::{ collections::{BTreeMap, HashMap}, - sync::{Arc, Mutex as StdMutex}, + sync::{Arc, Mutex as StdMutex, RwLock as StdRwLock}, }; use lru_cache::LruCache; @@ -22,6 +22,16 @@ pub(crate) mod transaction_ids; pub(crate) mod uiaa; pub(crate) mod users; +static SERVICES: StdRwLock> = StdRwLock::new(None); + +/// Convenient access to the global [`Services`] instance +pub(crate) fn services() -> &'static Services { + SERVICES + .read() + .unwrap() + .expect("SERVICES should be initialized when this is called") +} + pub(crate) struct Services { pub(crate) appservice: appservice::Service, pub(crate) pusher: pusher::Service, @@ -154,6 +164,11 @@ impl Services { }) } + /// Installs `self` to be globally accessed via [`services`] + pub(crate) fn install(self) { + *SERVICES.write().unwrap() = Some(Box::leak(Box::new(self))); + } + async fn memory_usage(&self) -> String { let lazy_load_waiting = self.rooms.lazy_loading.lazy_load_waiting.lock().await.len(); From ad37eae8694af76485e3d598dfa92f60341e4280 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 24 Sep 2024 20:05:18 -0700 Subject: [PATCH 404/617] use OnceLock instead of RwLock for SERVICES It actually has the semantics we need. Until we get rid of SERVICES. --- src/service.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/service.rs b/src/service.rs index 4bf7c5e2..c2dcb0f8 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,6 +1,6 @@ use std::{ collections::{BTreeMap, HashMap}, - sync::{Arc, Mutex as StdMutex, RwLock as StdRwLock}, + sync::{Arc, Mutex as StdMutex, OnceLock}, }; use lru_cache::LruCache; @@ -22,14 +22,11 @@ pub(crate) mod transaction_ids; pub(crate) mod uiaa; pub(crate) mod users; -static SERVICES: StdRwLock> = StdRwLock::new(None); +static SERVICES: OnceLock<&'static Services> = OnceLock::new(); /// Convenient access to the global [`Services`] instance pub(crate) fn services() -> &'static Services { - SERVICES - .read() - .unwrap() - .expect("SERVICES should be initialized when this is called") + SERVICES.get().expect("`Services::install` should have been called first") } pub(crate) struct Services { @@ -166,7 +163,10 @@ impl Services { /// Installs `self` to be globally accessed via [`services`] pub(crate) fn install(self) { - *SERVICES.write().unwrap() = Some(Box::leak(Box::new(self))); + assert!( + SERVICES.set(Box::leak(Box::new(self))).is_ok(), + "Services::install was called more than once" + ); } async fn memory_usage(&self) -> String { From 9add9a1e96c48de4f5bbd39f6233cd8b2933824e Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 24 Sep 2024 21:26:06 -0700 Subject: [PATCH 405/617] fix room version comparisons Fixes a set of bugs introduced by 00b77144c192618c75e6c5c0106e021f18f2bbac, where we replaced explicit `RoomVersionId` matches with `version < V11` comparisons. The `Ord` impl on `RoomVersionId` does not work like that, and is in fact a lexicographic string comparison[1]. The most visible effect of these bugs is that incoming redaction events would sometimes be ignored. Instead of reverting to the explicit matches, which were quite verbose, I implemented a `RoomVersion` struct that has flags for each property that we care about. This is similar to the approach used by ruma[2] and synapse[3]. [1]: https://github.com/ruma/ruma/blob/7cfa3be0c69c9a63658bd6fa56c8c6c4d9fd4c71/crates/ruma-common/src/identifiers/room_version_id.rs#L136 [2]: https://github.com/ruma/ruma/blob/7cfa3be0c69c9a63658bd6fa56c8c6c4d9fd4c71/crates/ruma-state-res/src/room_version.rs [3]: https://github.com/element-hq/synapse/blob/c856ae47247579446bbe1a1adc1564158e5e0643/synapse/api/room_versions.py --- src/api/client_server/membership.rs | 5 +- src/api/client_server/room.rs | 87 +++++++--------- src/service/admin.rs | 25 ++--- src/service/rooms/event_handler.rs | 78 +++++++-------- src/service/rooms/timeline.rs | 147 ++++++++++++---------------- src/utils.rs | 1 + src/utils/error.rs | 6 ++ src/utils/room_version.rs | 87 ++++++++++++++++ 8 files changed, 237 insertions(+), 199 deletions(-) create mode 100644 src/utils/room_version.rs diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 5496299c..95aefcf4 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1032,8 +1032,9 @@ async fn join_room_by_id_helper( info!("Running send_join auth check"); let authenticated = state_res::event_auth::auth_check( - &state_res::RoomVersion::new(&room_version_id) - .expect("room version is supported"), + &state_res::RoomVersion::new(&room_version_id).map_err(|_| { + Error::UnsupportedRoomVersion(room_version_id.clone()) + })?, &parsed_join_pdu, // TODO: third party invite None::, diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 835a57f4..c2488464 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -24,14 +24,14 @@ use ruma::{ }, int, serde::JsonObject, - CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, RoomVersionId, + CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, }; use serde_json::{json, value::to_raw_value}; use tracing::{info, warn}; use crate::{ - api::client_server::invite_helper, service::pdu::PduBuilder, services, Ar, - Error, Ra, Result, + api::client_server::invite_helper, service::pdu::PduBuilder, services, + utils::room_version::RoomVersion, Ar, Error, Ra, Result, }; /// # `POST /_matrix/client/r0/createRoom` @@ -114,7 +114,7 @@ pub(crate) async fn create_room_route( } } - let room_version = match body.room_version.clone() { + let room_version_id = match body.room_version.clone() { Some(room_version) => { if services() .globals @@ -131,6 +131,7 @@ pub(crate) async fn create_room_route( } None => services().globals.default_room_version(), }; + let room_version = RoomVersion::try_from(&room_version_id)?; let content = match &body.creation_content { Some(content) => { @@ -138,30 +139,20 @@ pub(crate) async fn create_room_route( .deserialize_as::() .expect("Invalid creation content"); - match &room_version { - room_version if *room_version < RoomVersionId::V11 => { - content.insert( - "creator".into(), - json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest( - ErrorKind::BadJson, - "Invalid creation content", - ) - })?, - ); - } - // V11 removed the "creator" key - RoomVersionId::V11 => {} - _ => { - return Err(Error::BadServerResponse( - "Unsupported room version.", - )) - } + if room_version.create_event_creator_prop { + content.insert( + "creator".into(), + json!(&sender_user).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) + })?, + ); } - content.insert( "room_version".into(), - json!(room_version.as_str()).try_into().map_err(|_| { + json!(room_version_id.as_str()).try_into().map_err(|_| { Error::BadRequest( ErrorKind::BadJson, "Invalid creation content", @@ -171,16 +162,10 @@ pub(crate) async fn create_room_route( content } None => { - let content = match &room_version { - room_version if *room_version < RoomVersionId::V11 => { - RoomCreateEventContent::new_v1(sender_user.to_owned()) - } - RoomVersionId::V11 => RoomCreateEventContent::new_v11(), - _ => { - return Err(Error::BadServerResponse( - "Unsupported room version.", - )) - } + let content = if room_version.create_event_creator_prop { + RoomCreateEventContent::new_v1(sender_user.to_owned()) + } else { + RoomCreateEventContent::new_v11() }; let mut content = serde_json::from_str::( to_raw_value(&content) @@ -195,7 +180,7 @@ pub(crate) async fn create_room_route( .unwrap(); content.insert( "room_version".into(), - json!(room_version.as_str()).try_into().map_err(|_| { + json!(room_version_id.as_str()).try_into().map_err(|_| { Error::BadRequest( ErrorKind::BadJson, "Invalid creation content", @@ -598,6 +583,7 @@ pub(crate) async fn upgrade_room_route( "This server does not support that room version.", )); } + let new_version = RoomVersion::try_from(&body.new_version)?; // Create a replacement room let replacement_room = RoomId::new(services().globals.server_name()); @@ -661,23 +647,18 @@ pub(crate) async fn upgrade_room_route( // Send a m.room.create event containing a predecessor field and the // applicable room_version - match &body.new_version { - room_version if *room_version < RoomVersionId::V11 => { - create_event_content.insert( - "creator".into(), - json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest( - ErrorKind::BadJson, - "Error forming creation event", - ) - })?, - ); - } - RoomVersionId::V11 => { - // "creator" key no longer exists in V11 rooms - create_event_content.remove("creator"); - } - _ => return Err(Error::BadServerResponse("Unsupported room version.")), + if new_version.create_event_creator_prop { + create_event_content.insert( + "creator".into(), + json!(&sender_user).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Error forming creation event", + ) + })?, + ); + } else { + create_event_content.remove("creator"); } create_event_content.insert( "room_version".into(), diff --git a/src/service/admin.rs b/src/service/admin.rs index cf8ab5db..e46368ed 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -34,7 +34,7 @@ use super::pdu::PduBuilder; use crate::{ api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH}, services, - utils::{self, dbg_truncate_str}, + utils::{self, dbg_truncate_str, room_version::RoomVersion}, Error, PduEvent, Result, }; @@ -1301,23 +1301,18 @@ impl Service { services().users.create(&services().globals.admin_bot_user_id, None)?; - let room_version = services().globals.default_room_version(); - let mut content = match &room_version { - room_version if *room_version < RoomVersionId::V11 => { - RoomCreateEventContent::new_v1( - services().globals.admin_bot_user_id.clone(), - ) - } - RoomVersionId::V11 => RoomCreateEventContent::new_v11(), - _ => { - return Err(Error::BadServerResponse( - "Unsupported room version.", - )) - } + let room_version_id = services().globals.default_room_version(); + let room_version = RoomVersion::try_from(&room_version_id)?; + let mut content = if room_version.create_event_creator_prop { + RoomCreateEventContent::new_v1( + services().globals.admin_bot_user_id.clone(), + ) + } else { + RoomCreateEventContent::new_v11() }; content.federate = true; content.predecessor = None; - content.room_version = room_version; + content.room_version = room_version_id; // 1. The room create event services() diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 23b08bf7..d5e7a6a4 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -27,7 +27,7 @@ use ruma::{ StateEventType, TimelineEventType, }, int, - state_res::{self, RoomVersion, StateMap}, + state_res::{self, StateMap}, uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedServerSigningKeyId, RoomId, RoomVersionId, ServerName, @@ -44,7 +44,7 @@ use super::{ use crate::{ service::{globals::SigningKeys, pdu}, services, - utils::debug_slice_truncated, + utils::{debug_slice_truncated, room_version::RoomVersion}, Error, PduEvent, Result, }; @@ -339,8 +339,10 @@ impl Service { )?; let room_version_id = &create_event_content.room_version; - let room_version = RoomVersion::new(room_version_id) - .expect("room version is supported"); + let ruma_room_version = + state_res::RoomVersion::new(room_version_id).map_err(|_| { + Error::UnsupportedRoomVersion(room_version_id.clone()) + })?; // TODO: For RoomVersion6 we must check that Raw<..> is canonical, // do we anywhere? @@ -527,7 +529,7 @@ impl Service { } if !state_res::event_auth::auth_check( - &room_version, + &ruma_room_version, &incoming_pdu, // TODO: third party invite None::, @@ -601,8 +603,11 @@ impl Service { )?; let room_version_id = &create_event_content.room_version; - let room_version = RoomVersion::new(room_version_id) - .expect("room version is supported"); + let room_version = RoomVersion::try_from(room_version_id)?; + let ruma_room_version = state_res::RoomVersion::new(room_version_id) + .map_err(|_| { + Error::UnsupportedRoomVersion(room_version_id.clone()) + })?; // 10. Fetch missing state and auth chain events by calling /state_ids // at backwards extremities doing all the checks in this list @@ -885,7 +890,7 @@ impl Service { // 11. Check the auth of the event passes based on the state of the // event let check_result = state_res::event_auth::auth_check( - &room_version, + &ruma_room_version, &incoming_pdu, // TODO: third party invite None::, @@ -930,7 +935,7 @@ impl Service { )?; let soft_fail = !state_res::event_auth::auth_check( - &room_version, + &ruma_room_version, &incoming_pdu, None::, |k, s| auth_events.get(&(k.clone(), s.to_owned())), @@ -939,45 +944,34 @@ impl Service { Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") })? || incoming_pdu.kind == TimelineEventType::RoomRedaction - && match room_version_id { - room_version if *room_version < RoomVersionId::V11 => { - if let Some(redact_id) = &incoming_pdu.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - true, - )? - } else { - false - } - } - RoomVersionId::V11 => { - let content = serde_json::from_str::< - RoomRedactionEventContent, - >( - incoming_pdu.content.get() + && if room_version.redaction_event_redacts_in_content { + let content = + serde_json::from_str::( + incoming_pdu.content.get(), ) .map_err(|_| { Error::bad_database("Invalid content in redaction pdu.") })?; - if let Some(redact_id) = &content.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - true, - )? - } else { - false - } - } - _ => { - return Err(Error::BadServerResponse( - "Unsupported room version.", - )) + if let Some(redact_id) = &content.redacts { + !services().rooms.state_accessor.user_can_redact( + redact_id, + &incoming_pdu.sender, + &incoming_pdu.room_id, + true, + )? + } else { + false } + } else if let Some(redact_id) = &incoming_pdu.redacts { + !services().rooms.state_accessor.user_can_redact( + redact_id, + &incoming_pdu.sender, + &incoming_pdu.room_id, + true, + )? + } else { + false }; // 13. Use state resolution to find new room state diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 257b2fac..53802841 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -21,10 +21,9 @@ use ruma::{ GlobalAccountDataEventType, StateEventType, TimelineEventType, }, push::{Action, Ruleset, Tweak}, - state_res::{self, Event, RoomVersion}, + state_res::{self, Event}, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, - OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId, - ServerName, UserId, + OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId, }; use serde::Deserialize; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; @@ -40,7 +39,7 @@ use crate::{ pdu::{EventHash, PduBuilder}, }, services, - utils::{self, on_demand_hashmap::KeyToken}, + utils::{self, on_demand_hashmap::KeyToken, room_version::RoomVersion}, Error, PduEvent, Result, }; @@ -410,44 +409,32 @@ impl Service { TimelineEventType::RoomRedaction => { let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; - match &room_version_id { - room_version if *room_version < RoomVersionId::V11 => { - if let Some(redact_id) = &pdu.redacts { - if services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - self.redact_pdu(redact_id, pdu, shortroomid)?; - } + let room_version = RoomVersion::try_from(&room_version_id)?; + if room_version.redaction_event_redacts_in_content { + let content = serde_json::from_str::< + RoomRedactionEventContent, + >(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid content in redaction pdu.") + })?; + if let Some(redact_id) = &content.redacts { + if services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { + self.redact_pdu(redact_id, pdu, shortroomid)?; } } - RoomVersionId::V11 => { - let content = - serde_json::from_str::( - pdu.content.get(), - ) - .map_err(|_| { - Error::bad_database( - "Invalid content in redaction pdu.", - ) - })?; - if let Some(redact_id) = &content.redacts { - if services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - self.redact_pdu(redact_id, pdu, shortroomid)?; - } - } - } - _ => { - return Err(Error::BadServerResponse( - "Unsupported room version.", - )); + } else if let Some(redact_id) = &pdu.redacts { + if services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { + self.redact_pdu(redact_id, pdu, shortroomid)?; } }; } @@ -737,8 +724,10 @@ impl Service { } })?; - let room_version = RoomVersion::new(&room_version_id) - .expect("room version is supported"); + let ruma_room_version = state_res::RoomVersion::new(&room_version_id) + .map_err(|_| { + Error::UnsupportedRoomVersion(room_version_id.clone()) + })?; let auth_events = services().rooms.state.get_auth_events( room_id, @@ -810,7 +799,7 @@ impl Service { }; let auth_check = state_res::auth_check( - &room_version, + &ruma_room_version, &pdu, // TODO: third_party_invite None::, @@ -1008,57 +997,41 @@ impl Service { // If redaction event is not authorized, do not append it to the // timeline if pdu.kind == TimelineEventType::RoomRedaction { - match services().rooms.state.get_room_version(&pdu.room_id)? { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - if let Some(redact_id) = &pdu.redacts { - if !services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User cannot redact this event.", - )); - } - }; - } - RoomVersionId::V11 => { - let content = serde_json::from_str::< - RoomRedactionEventContent, - >(pdu.content.get()) + let room_version_id = + services().rooms.state.get_room_version(&pdu.room_id)?; + let room_version = RoomVersion::try_from(&room_version_id)?; + if room_version.redaction_event_redacts_in_content { + let content = + serde_json::from_str::( + pdu.content.get(), + ) .map_err(|_| { Error::bad_database("Invalid content in redaction pdu.") })?; - if let Some(redact_id) = &content.redacts { - if !services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User cannot redact this event.", - )); - } + if let Some(redact_id) = &content.redacts { + if !services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "User cannot redact this event.", + )); } } - _ => { + } else if let Some(redact_id) = &pdu.redacts { + if !services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { return Err(Error::BadRequest( - ErrorKind::UnsupportedRoomVersion, - "Unsupported room version", + ErrorKind::forbidden(), + "User cannot redact this event.", )); } } diff --git a/src/utils.rs b/src/utils.rs index e0d62063..c9cafa56 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,6 @@ pub(crate) mod error; pub(crate) mod on_demand_hashmap; +pub(crate) mod room_version; use std::{ borrow::Cow, diff --git a/src/utils/error.rs b/src/utils/error.rs index 7b87b5f5..49b5ab15 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -80,6 +80,8 @@ pub(crate) enum Error { AdminCommand(&'static str), #[error("from {0}: {1}")] Redaction(OwnedServerName, ruma::canonical_json::RedactionError), + #[error("unsupported room version {0}")] + UnsupportedRoomVersion(ruma::RoomVersionId), #[error("{0} in {1}")] InconsistentRoomState(&'static str, ruma::OwnedRoomId), } @@ -146,6 +148,10 @@ impl Error { _ => StatusCode::BAD_REQUEST, }, ), + Self::UnsupportedRoomVersion(_) => ( + ErrorKind::UnsupportedRoomVersion, + StatusCode::INTERNAL_SERVER_ERROR, + ), Self::Conflict(_) => (Unknown, StatusCode::CONFLICT), _ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR), }; diff --git a/src/utils/room_version.rs b/src/utils/room_version.rs new file mode 100644 index 00000000..389dc367 --- /dev/null +++ b/src/utils/room_version.rs @@ -0,0 +1,87 @@ +use ruma::RoomVersionId; + +use crate::Error; + +/// Properties that we care about in grapevine that differ between supported +/// room versions. +/// +/// This is similar to [`ruma::state_res::RoomVersion`], except that it has +/// properties that are relevant to us instead of only properties relevant to +/// ruma state resolution. +/// +/// When branching for different room versions, prefer constructing a +/// `RoomVersion` and branching on it's properties over branching based on the +/// [`RoomVersionId`] directly. If there is a relevant property that is not +/// already included in `RoomVersion`, add it. In particular, comparisons like +/// `room_version_id < RoomVersionId::V11` do not work, because room versions +/// do not have a defined order. Ruma implements `PartialOrd` on `RoomVersionId` +/// as a lexicographic string comparison, which is almost certainly not what you +/// want. +pub(crate) struct RoomVersion { + /// Whether `m.room.create` state events have a `creator` property. + /// + /// The `creator` property is always equivalent to the event `sender`, and + /// was [removed][spec] in v11. + /// + /// [spec]: https://spec.matrix.org/v1.11/rooms/v11/#remove-the-creator-property-of-mroomcreate-events + pub(crate) create_event_creator_prop: bool, + + /// Whether the `redacts` property of `m.room.redaction` events is a + /// property in the event `content` or a top-level property of the event. + /// + /// This property was [moved][spec] from top-level to `content` in v11. + /// + /// [spec]: https://spec.matrix.org/v1.11/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property + pub(crate) redaction_event_redacts_in_content: bool, +} + +// Allowed so that we can use struct-update syntax for incremental changes +// between versions even when all fields change. +#[allow(clippy::needless_update)] +mod known_versions { + use super::RoomVersion; + + pub(super) const V6: RoomVersion = RoomVersion { + create_event_creator_prop: true, + redaction_event_redacts_in_content: false, + }; + + pub(super) const V7: RoomVersion = RoomVersion { + ..V6 + }; + + pub(super) const V8: RoomVersion = RoomVersion { + ..V7 + }; + + pub(super) const V9: RoomVersion = RoomVersion { + ..V8 + }; + + pub(super) const V10: RoomVersion = RoomVersion { + ..V9 + }; + + pub(super) const V11: RoomVersion = RoomVersion { + create_event_creator_prop: false, + redaction_event_redacts_in_content: true, + ..V10 + }; +} + +/// May return an error for unsupported room versions. +impl TryFrom<&RoomVersionId> for RoomVersion { + type Error = Error; + + fn try_from(version: &RoomVersionId) -> Result { + match version { + RoomVersionId::V6 => Ok(known_versions::V6), + RoomVersionId::V7 => Ok(known_versions::V7), + RoomVersionId::V8 => Ok(known_versions::V8), + RoomVersionId::V9 => Ok(known_versions::V9), + RoomVersionId::V10 => Ok(known_versions::V10), + RoomVersionId::V11 => Ok(known_versions::V11), + _ => Err(Error::UnsupportedRoomVersion(version.clone())), + } + } +} From 6ab87f97dd30b1146d18443b8ea66844567399e5 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 22 Sep 2024 11:01:33 -0700 Subject: [PATCH 406/617] include traceresponse header if possible This can help with debugging. --- book/changelog.md | 3 +++ src/cli/serve.rs | 3 ++- src/observability.rs | 52 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 26ed1d67..377e9ce8 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -248,3 +248,6 @@ This will be the first release of Grapevine since it was forked from Conduit [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102)) 19. Allow configuring the served API components per listener. ([!109](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/109)) +20. Include the [`traceresponse` header](https://w3c.github.io/trace-context/#traceresponse-header) + if OpenTelemetry Tracing is in use. + ([!112](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/112)) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 79a55482..0e646dc0 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -171,7 +171,8 @@ async fn run_server() -> Result<(), error::Serve> { .try_into() .expect("failed to convert max request size"), )) - .layer(axum::middleware::from_fn(observability::http_metrics_layer)); + .layer(axum::middleware::from_fn(observability::http_metrics_layer)) + .layer(axum::middleware::from_fn(observability::traceresponse_layer)); let mut handles = Vec::new(); let mut servers = JoinSet::new(); diff --git a/src/observability.rs b/src/observability.rs index fb5df965..059ed436 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -18,9 +18,13 @@ use opentelemetry_sdk::{ }; use strum::{AsRefStr, IntoStaticStr}; use tokio::time::Instant; +use tracing::Span; use tracing_flame::{FlameLayer, FlushGuard}; +use tracing_opentelemetry::OtelData; use tracing_subscriber::{ - layer::SubscriberExt, reload, EnvFilter, Layer, Registry, + layer::SubscriberExt, + registry::{LookupSpan, SpanData}, + reload, EnvFilter, Layer, Registry, }; use crate::{ @@ -138,8 +142,7 @@ fn make_backend( > where L: Layer, - S: tracing::Subscriber - + for<'span> tracing_subscriber::registry::LookupSpan<'span>, + S: tracing::Subscriber + for<'span> LookupSpan<'span>, { if !enable { return Ok((None, None, None)); @@ -423,3 +426,46 @@ pub(crate) async fn http_metrics_layer(req: Request, next: Next) -> Response { } } } + +/// Add `traceresponse` header if possible +/// +/// See also . +pub(crate) async fn traceresponse_layer(req: Request, next: Next) -> Response { + let mut resp = next.run(req).await; + + let ids = tracing::dispatcher::get_default(|dispatch| { + Span::current() + .id() + .and_then(|id| { + dispatch + .downcast_ref::() + .and_then(|x| x.span_data(&id)) + }) + .and_then(|x| { + x.extensions() + .get::() + .and_then(|x| x.builder.trace_id.zip(x.builder.span_id)) + }) + }); + + if let Some((trace_id, span_id)) = ids { + let headers = resp.headers_mut(); + + headers.insert( + "traceresponse", + format!( + "{:02x}-{}-{}-{:02x}", + 0, + trace_id, + span_id, + // Doesn't seem to be possible to get the SpanContext here, but + // this should be a fine default value + 0, + ) + .try_into() + .expect("traceresponse value should be a valid header value"), + ); + } + + resp +} From 39880cc6ace0a737928a2d1bab942839fca41d09 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 26 Sep 2024 16:27:58 -0700 Subject: [PATCH 407/617] Abstract over sd_notify --- src/cli/serve.rs | 12 ++++-------- src/main.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 0e646dc0..38832e6b 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -42,12 +42,12 @@ use crate::{ }, config::{self, Config, ListenComponent, ListenTransport}, database::KeyValueDatabase, - error, observability, services, + error, observability, services, set_application_state, utils::{ self, error::{Error, Result}, }, - Services, + ApplicationState, Services, }; pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { @@ -226,9 +226,7 @@ async fn run_server() -> Result<(), error::Serve> { } } - #[cfg(feature = "systemd")] - sd_notify::notify(true, &[sd_notify::NotifyState::Ready]) - .expect("should be able to notify systemd"); + set_application_state(ApplicationState::Ready); tokio::spawn(shutdown_signal(handles)); @@ -573,9 +571,7 @@ async fn shutdown_signal(handles: Vec) { handle.graceful_shutdown(Some(Duration::from_secs(30))); } - #[cfg(feature = "systemd")] - sd_notify::notify(true, &[sd_notify::NotifyState::Stopping]) - .expect("should be able to notify systemd"); + set_application_state(ApplicationState::Stopping); } async fn federation_disabled(_: Uri) -> impl IntoResponse { diff --git a/src/main.rs b/src/main.rs index 903906a6..e0365fc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use std::process::ExitCode; use clap::Parser; -use tracing::error; +use tracing::{error, info}; mod api; mod cli; @@ -41,6 +41,31 @@ fn version() -> String { } } +#[derive(Debug, Clone, Copy)] +enum ApplicationState { + Ready, + Stopping, +} + +fn set_application_state(state: ApplicationState) { + info!(?state, "Application state changed"); + + #[cfg(feature = "systemd")] + { + use sd_notify::NotifyState; + + fn notify(states: &[NotifyState<'_>]) { + sd_notify::notify(false, states) + .expect("should be able to notify systemd"); + } + + match state { + ApplicationState::Ready => notify(&[NotifyState::Ready]), + ApplicationState::Stopping => notify(&[NotifyState::Stopping]), + }; + } +} + #[tokio::main] async fn main() -> ExitCode { let args = cli::Args::parse(); From 94d523ebcbb67a8ed2ec84d09ba04792d91c3b23 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 26 Sep 2024 16:29:03 -0700 Subject: [PATCH 408/617] Reload TLS config on SIGHUP --- Cargo.toml | 2 +- book/changelog.md | 3 ++ src/cli/serve.rs | 72 ++++++++++++++++++++++++++++++++++++++--------- src/main.rs | 16 +++++++++++ 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7904770e..b406358b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ trust-dns-resolver = "0.23.2" xdg = "2.5.2" [target.'cfg(unix)'.dependencies] -nix = { version = "0.29", features = ["resource"] } +nix = { version = "0.29", features = ["resource", "time"] } [features] default = ["rocksdb", "sqlite", "systemd"] diff --git a/book/changelog.md b/book/changelog.md index 377e9ce8..66506096 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -251,3 +251,6 @@ This will be the first release of Grapevine since it was forked from Conduit 20. Include the [`traceresponse` header](https://w3c.github.io/trace-context/#traceresponse-header) if OpenTelemetry Tracing is in use. ([!112](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/112)) +21. Sending SIGHUP to the grapevine process now reloads TLS certificates from + disk. + ([!97](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/97)) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 38832e6b..bfde80cf 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -228,7 +228,7 @@ async fn run_server() -> Result<(), error::Serve> { set_application_state(ApplicationState::Ready); - tokio::spawn(shutdown_signal(handles)); + tokio::spawn(handle_signals(tls_config, handles)); while let Some(result) = servers.join_next().await { let (listen, result) = @@ -540,28 +540,72 @@ fn routes(config: &Config, components: &HashSet) -> Router { router.route("/", get(it_works)).fallback(not_found) } -async fn shutdown_signal(handles: Vec) { - let ctrl_c = async { - signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); - }; +async fn reload_tls_config( + tls_config: &RustlsConfig, +) -> Result<(), error::Serve> { + let config = services() + .globals + .config + .tls + .as_ref() + .expect("TLS config should exist if TLS listener exists"); + tls_config.reload_from_pem_file(&config.certs, &config.key).await.map_err( + |err| error::Serve::LoadCerts { + certs: config.certs.clone(), + key: config.key.clone(), + err, + }, + )?; + + Ok(()) +} + +async fn handle_signals( + tls_config: Option, + handles: Vec, +) { #[cfg(unix)] - let terminate = async { - signal::unix::signal(signal::unix::SignalKind::terminate()) + async fn wait_signal(sig: signal::unix::SignalKind) { + signal::unix::signal(sig) .expect("failed to install signal handler") .recv() .await; + } + + #[cfg(unix)] + let terminate = || wait_signal(signal::unix::SignalKind::terminate()); + #[cfg(not(unix))] + let terminate = || std::future::pending::<()>(); + + #[cfg(unix)] + let sighup = || wait_signal(signal::unix::SignalKind::hangup()); + #[cfg(not(unix))] + let sighup = || std::future::pending::<()>(); + + let ctrl_c = || async { + signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); }; - #[cfg(not(unix))] - let terminate = std::future::pending::<()>(); + let sig = loop { + tokio::select! { + () = sighup() => { + info!("Received reload request"); - let sig: &str; + set_application_state(ApplicationState::Reloading); - tokio::select! { - () = ctrl_c => { sig = "Ctrl+C"; }, - () = terminate => { sig = "SIGTERM"; }, - } + if let Some(tls_config) = tls_config.as_ref() { + if let Err(error) = reload_tls_config(tls_config).await { + error!(?error, "Failed to reload TLS config"); + } + } + + set_application_state(ApplicationState::Ready); + }, + () = terminate() => { break "SIGTERM"; }, + () = ctrl_c() => { break "Ctrl+C"; }, + } + }; warn!(signal = %sig, "Shutting down due to signal"); diff --git a/src/main.rs b/src/main.rs index e0365fc3..dfef8616 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,7 @@ fn version() -> String { #[derive(Debug, Clone, Copy)] enum ApplicationState { Ready, + Reloading, Stopping, } @@ -61,6 +62,21 @@ fn set_application_state(state: ApplicationState) { match state { ApplicationState::Ready => notify(&[NotifyState::Ready]), + ApplicationState::Reloading => { + let timespec = nix::time::clock_gettime( + nix::time::ClockId::CLOCK_MONOTONIC, + ) + .expect("CLOCK_MONOTONIC should be usable"); + let monotonic_usec = + timespec.tv_sec() * 1_000_000 + timespec.tv_nsec() / 1000; + + notify(&[ + NotifyState::Reloading, + NotifyState::Custom(&format!( + "MONOTONIC_USEC={monotonic_usec}", + )), + ]); + } ApplicationState::Stopping => notify(&[NotifyState::Stopping]), }; } From 6022d560946a3d65b79ebe53411f6077f161729b Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 21 Sep 2024 10:47:55 +0000 Subject: [PATCH 409/617] Use enums for options to send_request(), add allow_loopback --- src/api/server_server.rs | 23 +++++++++++++++++++---- src/service/sending.rs | 15 ++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 8065ac70..9030bf99 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -134,11 +134,24 @@ impl FedDest { } } -#[tracing::instrument(skip(request, log_error), fields(url))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum LogRequestError { + Yes, + No, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum AllowLoopbackRequests { + Yes, + No, +} + +#[tracing::instrument(skip(request, log_error, allow_loopback), fields(url))] pub(crate) async fn send_request( destination: &ServerName, request: T, - log_error: bool, + log_error: LogRequestError, + allow_loopback: AllowLoopbackRequests, ) -> Result where T: OutgoingRequest + Debug, @@ -147,7 +160,9 @@ where return Err(Error::BadConfig("Federation is disabled.")); } - if destination == services().globals.server_name() { + if destination == services().globals.server_name() + && allow_loopback == AllowLoopbackRequests::No + { return Err(Error::bad_config( "Won't send federation request to ourselves", )); @@ -273,7 +288,7 @@ where services().globals.federation_client().execute(reqwest_request).await; let mut response = response.inspect_err(|error| { - if log_error { + if log_error == LogRequestError::Yes { warn!(%error, "Could not send request"); } })?; diff --git a/src/service/sending.rs b/src/service/sending.rs index c486701e..2e3fcc5a 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -41,7 +41,10 @@ use tracing::{debug, error, warn, Span}; use super::rooms::timeline::PduId; use crate::{ - api::{appservice_server, server_server}, + api::{ + appservice_server, + server_server::{self, AllowLoopbackRequests, LogRequestError}, + }, services, utils::{calculate_hash, debug_slice_truncated}, Config, Error, PduEvent, Result, @@ -677,7 +680,12 @@ impl Service { debug!("Got permit"); let response = tokio::time::timeout( Duration::from_secs(2 * 60), - server_server::send_request(destination, request, true), + server_server::send_request( + destination, + request, + LogRequestError::Yes, + AllowLoopbackRequests::No, + ), ) .await .map_err(|_| { @@ -901,7 +909,8 @@ async fn handle_federation_event( }))) .into(), }, - false, + LogRequestError::No, + AllowLoopbackRequests::No, ) .await?; From e14b7f28f2c4d5be9e40ebbfaa8ec5ac169596cb Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 20 Sep 2024 12:05:24 +0000 Subject: [PATCH 410/617] Implement federation self-test --- book/changelog.md | 2 ++ src/cli/serve.rs | 39 ++++++++++++++++++++++++++++++++++++--- src/config.rs | 2 ++ src/error.rs | 6 ++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 66506096..cd3af642 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -254,3 +254,5 @@ This will be the first release of Grapevine since it was forked from Conduit 21. Sending SIGHUP to the grapevine process now reloads TLS certificates from disk. ([!97](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/97)) +22. Added a federation self-test, perfomed automatically on startup. + ([!106](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/106)) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index bfde80cf..0991066b 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -22,6 +22,7 @@ use ruma::api::{ error::{Error as RumaError, ErrorBody, ErrorKind}, uiaa::UiaaResponse, }, + federation::discovery::get_server_version, IncomingRequest, }; use tokio::{signal, task::JoinSet}; @@ -38,7 +39,8 @@ use crate::{ api::{ client_server, ruma_wrapper::{Ar, Ra}, - server_server, well_known, + server_server::{self, AllowLoopbackRequests, LogRequestError}, + well_known, }, config::{self, Config, ListenComponent, ListenTransport}, database::KeyValueDatabase, @@ -98,6 +100,30 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { Ok(()) } +#[tracing::instrument] +async fn federation_self_test() -> Result<()> { + let response = server_server::send_request( + &services().globals.config.server_name, + get_server_version::v1::Request {}, + LogRequestError::Yes, + AllowLoopbackRequests::Yes, + ) + .await?; + + if !response + .server + .as_ref() + .is_some_and(|s| s.name.as_deref() == Some(env!("CARGO_PKG_NAME"))) + { + error!(?response, "unexpected server version"); + return Err(Error::BadConfig( + "Got unexpected version from our own version endpoint", + )); + } + + Ok(()) +} + #[allow(clippy::too_many_lines)] async fn run_server() -> Result<(), error::Serve> { use error::Serve as Error; @@ -226,10 +252,17 @@ async fn run_server() -> Result<(), error::Serve> { } } - set_application_state(ApplicationState::Ready); - tokio::spawn(handle_signals(tls_config, handles)); + if config.federation.enable && config.federation.self_test { + federation_self_test() + .await + .map_err(error::Serve::FederationSelfTestFailed)?; + debug!("Federation self-test completed successfully"); + } + + set_application_state(ApplicationState::Ready); + while let Some(result) = servers.join_next().await { let (listen, result) = result.expect("should be able to join server task"); diff --git a/src/config.rs b/src/config.rs index c1ebfeab..c980c26b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -328,6 +328,7 @@ pub(crate) struct ObservabilityConfig { #[serde(default)] pub(crate) struct FederationConfig { pub(crate) enable: bool, + pub(crate) self_test: bool, pub(crate) trusted_servers: Vec, pub(crate) max_fetch_prev_events: u16, pub(crate) max_concurrent_requests: u16, @@ -338,6 +339,7 @@ impl Default for FederationConfig { fn default() -> Self { Self { enable: true, + self_test: true, trusted_servers: vec![ OwnedServerName::try_from("matrix.org").unwrap() ], diff --git a/src/error.rs b/src/error.rs index 106484df..72bcd337 100644 --- a/src/error.rs +++ b/src/error.rs @@ -160,4 +160,10 @@ pub(crate) enum Serve { #[error("failed to run request listener on {1}")] Listen(#[source] std::io::Error, ListenConfig), + + #[error( + "federation self-test failed (set `federation.self_test = false` in \ + config to disable)" + )] + FederationSelfTestFailed(#[source] crate::Error), } From 287f6b9163674fbf63e0817105abc746d0ee980c Mon Sep 17 00:00:00 2001 From: mikoto Date: Tue, 17 Sep 2024 18:07:58 +0200 Subject: [PATCH 411/617] refactor calculate_invite_state That was terribly named and terribly implemented. Co-authored-by: Charles Hall --- src/api/client_server/membership.rs | 2 +- src/service/rooms/state.rs | 79 ++++++++++++----------------- src/service/rooms/timeline.rs | 2 +- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 95aefcf4..00f1db9e 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1307,7 +1307,7 @@ pub(crate) async fn invite_helper( )?; let invite_room_state = - services().rooms.state.calculate_invite_state(&pdu)?; + services().rooms.state.get_helpful_invite_events(&pdu)?; drop(room_token); diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 2ff970bb..f45effd7 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -1,6 +1,7 @@ mod data; use std::{ collections::{HashMap, HashSet}, + iter, sync::Arc, }; @@ -266,58 +267,42 @@ impl Service { } } + /// Gather events to help the invited user identify the room + /// + /// Also includes the invite event itself. #[tracing::instrument(skip(self, invite_event))] - pub(crate) fn calculate_invite_state( + pub(crate) fn get_helpful_invite_events( &self, invite_event: &PduEvent, ) -> Result>> { - let mut state = Vec::new(); - // Add recommended events - if let Some(e) = services().rooms.state_accessor.room_state_get( - &invite_event.room_id, - &StateEventType::RoomCreate, - "", - )? { - state.push(e.to_stripped_state_event()); - } - if let Some(e) = services().rooms.state_accessor.room_state_get( - &invite_event.room_id, - &StateEventType::RoomJoinRules, - "", - )? { - state.push(e.to_stripped_state_event()); - } - if let Some(e) = services().rooms.state_accessor.room_state_get( - &invite_event.room_id, - &StateEventType::RoomCanonicalAlias, - "", - )? { - state.push(e.to_stripped_state_event()); - } - if let Some(e) = services().rooms.state_accessor.room_state_get( - &invite_event.room_id, - &StateEventType::RoomAvatar, - "", - )? { - state.push(e.to_stripped_state_event()); - } - if let Some(e) = services().rooms.state_accessor.room_state_get( - &invite_event.room_id, - &StateEventType::RoomName, - "", - )? { - state.push(e.to_stripped_state_event()); - } - if let Some(e) = services().rooms.state_accessor.room_state_get( - &invite_event.room_id, - &StateEventType::RoomMember, - invite_event.sender.as_str(), - )? { - state.push(e.to_stripped_state_event()); - } + let helpful_events = [ + (StateEventType::RoomCreate, ""), + (StateEventType::RoomJoinRules, ""), + (StateEventType::RoomCanonicalAlias, ""), + (StateEventType::RoomAvatar, ""), + (StateEventType::RoomName, ""), + (StateEventType::RoomMember, invite_event.sender.as_str()), + ]; - state.push(invite_event.to_stripped_state_event()); - Ok(state) + let helpful_events = + helpful_events.into_iter().filter_map(|(state_type, state_key)| { + let state = services().rooms.state_accessor.room_state_get( + &invite_event.room_id, + &state_type, + state_key, + ); + + match state { + Ok(Some(x)) => Some(Ok(x.to_stripped_state_event())), + Err(x) => Some(Err(x)), + Ok(None) => None, + } + }); + + let actual_event = + iter::once(Ok(invite_event.to_stripped_state_event())); + + helpful_events.chain(actual_event).collect() } /// Set the state hash to a new version, but does not update state_cache. diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 53802841..c70c7033 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -473,7 +473,7 @@ impl Service { let state = services() .rooms .state - .calculate_invite_state(pdu)?; + .get_helpful_invite_events(pdu)?; Some(state) } _ => None, From 5a490a4397f0c6a36dab1cb631dadc67a849deab Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 3 Oct 2024 15:28:24 -0700 Subject: [PATCH 412/617] fix mod/use order Yes, it does actually bother me, thanks for asking. --- src/database.rs | 9 +++++---- src/database/key_value/rooms.rs | 4 ++-- src/database/key_value/rooms/edus.rs | 4 ++-- src/main.rs | 4 ++-- src/service/account_data.rs | 1 + src/service/appservice.rs | 7 ++++--- src/service/globals.rs | 6 ++++-- src/service/key_backups.rs | 1 + src/service/pusher.rs | 6 ++++-- src/service/rooms/auth_chain.rs | 6 ++++-- src/service/rooms/directory.rs | 1 + src/service/rooms/edus/read_receipt.rs | 1 + src/service/rooms/lazy_loading.rs | 6 ++++-- src/service/rooms/metadata.rs | 1 + src/service/rooms/outlier.rs | 1 + src/service/rooms/pdu_metadata.rs | 6 ++++-- src/service/rooms/search.rs | 1 + src/service/rooms/short.rs | 3 ++- src/service/rooms/state.rs | 6 ++++-- src/service/rooms/state_accessor.rs | 6 ++++-- src/service/rooms/state_cache.rs | 6 ++++-- src/service/rooms/state_compressor.rs | 3 +-- src/service/rooms/threads.rs | 7 ++++--- src/service/rooms/timeline.rs | 7 ++++--- src/service/rooms/user.rs | 1 + src/service/sending.rs | 7 ++++--- src/service/transaction_ids.rs | 1 + src/service/uiaa.rs | 7 ++++--- src/service/users.rs | 6 ++++-- src/utils.rs | 8 ++++---- 30 files changed, 83 insertions(+), 50 deletions(-) diff --git a/src/database.rs b/src/database.rs index abfad85a..641f1f7c 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,6 +1,3 @@ -pub(crate) mod abstraction; -pub(crate) mod key_value; - use std::{ collections::{BTreeMap, HashMap, HashSet}, fs, @@ -10,7 +7,6 @@ use std::{ sync::{Arc, Mutex, RwLock}, }; -use abstraction::{KeyValueDatabaseEngine, KvTree}; use lru_cache::LruCache; use ruma::{ events::{ @@ -35,6 +31,11 @@ use crate::{ services, utils, Config, Error, PduEvent, Result, }; +pub(crate) mod abstraction; +pub(crate) mod key_value; + +use abstraction::{KeyValueDatabaseEngine, KvTree}; + pub(crate) struct KeyValueDatabase { db: Arc, diff --git a/src/database/key_value/rooms.rs b/src/database/key_value/rooms.rs index e7b53d30..825dd1de 100644 --- a/src/database/key_value/rooms.rs +++ b/src/database/key_value/rooms.rs @@ -1,3 +1,5 @@ +use crate::{database::KeyValueDatabase, service}; + mod alias; mod auth_chain; mod directory; @@ -16,6 +18,4 @@ mod threads; mod timeline; mod user; -use crate::{database::KeyValueDatabase, service}; - impl service::rooms::Data for KeyValueDatabase {} diff --git a/src/database/key_value/rooms/edus.rs b/src/database/key_value/rooms/edus.rs index 90f2b24a..bf25ee37 100644 --- a/src/database/key_value/rooms/edus.rs +++ b/src/database/key_value/rooms/edus.rs @@ -1,5 +1,5 @@ -mod read_receipt; - use crate::{database::KeyValueDatabase, service}; +mod read_receipt; + impl service::rooms::edus::Data for KeyValueDatabase {} diff --git a/src/main.rs b/src/main.rs index dfef8616..2726b4bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use std::process::ExitCode; use clap::Parser; +#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] +use tikv_jemallocator::Jemalloc; use tracing::{error, info}; mod api; @@ -19,8 +21,6 @@ mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; pub(crate) use config::Config; pub(crate) use service::{pdu::PduEvent, services, Services}; -#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] -use tikv_jemallocator::Jemalloc; pub(crate) use utils::error::{Error, Result}; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] diff --git a/src/service/account_data.rs b/src/service/account_data.rs index 411199f4..4f4b4344 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/appservice.rs b/src/service/appservice.rs index b4046f1b..d0b1626d 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -1,8 +1,5 @@ -mod data; - use std::collections::BTreeMap; -pub(crate) use data::Data; use futures_util::Future; use regex::RegexSet; use ruma::{ @@ -13,6 +10,10 @@ use tokio::sync::RwLock; use crate::Result; +mod data; + +pub(crate) use data::Data; + /// Compiled regular expressions for a namespace. #[derive(Clone, Debug)] pub(crate) struct NamespaceRegex { diff --git a/src/service/globals.rs b/src/service/globals.rs index a1662c9e..1026135c 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -1,4 +1,3 @@ -mod data; use std::{ collections::{BTreeMap, HashMap}, error::Error as StdError, @@ -15,7 +14,6 @@ use std::{ }; use base64::{engine::general_purpose, Engine as _}; -pub(crate) use data::{Data, SigningKeys}; use futures_util::FutureExt; use hyper::service::Service as _; use hyper_util::{ @@ -48,6 +46,10 @@ use crate::{ Config, Error, Result, }; +mod data; + +pub(crate) use data::{Data, SigningKeys}; + type WellKnownMap = HashMap; type TlsNameMap = HashMap, u16)>; // Time if last failed try, number of failed tries diff --git a/src/service/key_backups.rs b/src/service/key_backups.rs index 411199f4..4f4b4344 100644 --- a/src/service/key_backups.rs +++ b/src/service/key_backups.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/pusher.rs b/src/service/pusher.rs index adc56c2f..c2ae7c46 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -1,8 +1,6 @@ -mod data; use std::{fmt::Debug, mem}; use bytes::BytesMut; -pub(crate) use data::Data; use ruma::{ api::{ client::push::{set_pusher, Pusher, PusherKind}, @@ -29,6 +27,10 @@ use tracing::warn; use crate::{services, utils, Error, PduEvent, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 5dcf9275..c5a62a6d 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -1,16 +1,18 @@ -mod data; use std::{ collections::{BTreeSet, HashSet}, sync::Arc, }; -pub(crate) use data::Data; use ruma::{api::client::error::ErrorKind, EventId, RoomId}; use tracing::{debug, error, warn}; use super::short::ShortEventId; use crate::{services, utils::debug_slice_truncated, Error, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/directory.rs b/src/service/rooms/directory.rs index 411199f4..4f4b4344 100644 --- a/src/service/rooms/directory.rs +++ b/src/service/rooms/directory.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/edus/read_receipt.rs b/src/service/rooms/edus/read_receipt.rs index 411199f4..4f4b4344 100644 --- a/src/service/rooms/edus/read_receipt.rs +++ b/src/service/rooms/edus/read_receipt.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/lazy_loading.rs b/src/service/rooms/lazy_loading.rs index 10e0fff1..f2732790 100644 --- a/src/service/rooms/lazy_loading.rs +++ b/src/service/rooms/lazy_loading.rs @@ -1,13 +1,15 @@ -mod data; use std::collections::{HashMap, HashSet}; -pub(crate) use data::Data; use ruma::{DeviceId, OwnedDeviceId, OwnedRoomId, OwnedUserId, RoomId, UserId}; use tokio::sync::Mutex; use super::timeline::PduCount; use crate::Result; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, diff --git a/src/service/rooms/metadata.rs b/src/service/rooms/metadata.rs index 411199f4..4f4b4344 100644 --- a/src/service/rooms/metadata.rs +++ b/src/service/rooms/metadata.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/outlier.rs b/src/service/rooms/outlier.rs index 411199f4..4f4b4344 100644 --- a/src/service/rooms/outlier.rs +++ b/src/service/rooms/outlier.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index 767ada39..d3854523 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -1,7 +1,5 @@ -mod data; use std::sync::Arc; -pub(crate) use data::Data; use ruma::{ api::client::relations::get_relating_events, events::{relation::RelationType, TimelineEventType}, @@ -12,6 +10,10 @@ use serde::Deserialize; use super::timeline::PduCount; use crate::{services, PduEvent, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/search.rs b/src/service/rooms/search.rs index 411199f4..4f4b4344 100644 --- a/src/service/rooms/search.rs +++ b/src/service/rooms/search.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 97a7edc8..790b1d04 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,5 +1,7 @@ mod data; +pub(crate) use data::Data; + macro_rules! short_id_type { ($name:ident) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -23,5 +25,4 @@ short_id_type!(ShortEventId); short_id_type!(ShortStateHash); short_id_type!(ShortStateKey); -pub(crate) use data::Data; pub(crate) type Service = &'static dyn Data; diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index f45effd7..f19a9f9a 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -1,11 +1,9 @@ -mod data; use std::{ collections::{HashMap, HashSet}, iter, sync::Arc, }; -pub(crate) use data::Data; use ruma::{ api::client::error::ErrorKind, events::{ @@ -29,6 +27,10 @@ use crate::{ Error, PduEvent, Result, }; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index f27a69ee..c3160d96 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -1,10 +1,8 @@ -mod data; use std::{ collections::HashMap, sync::{Arc, Mutex}, }; -pub(crate) use data::Data; use lru_cache::LruCache; use ruma::{ events::{ @@ -35,6 +33,10 @@ use crate::{ Error, PduEvent, Result, }; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, pub(crate) server_visibility_cache: diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index a2b1aab2..980998a7 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -1,7 +1,5 @@ -mod data; use std::{collections::HashSet, sync::Arc}; -pub(crate) use data::Data; use ruma::{ events::{ ignored_user_list::IgnoredUserListEvent, @@ -16,6 +14,10 @@ use tracing::warn; use crate::{service::appservice::RegistrationInfo, services, Error, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 42dc8878..4232a325 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -8,6 +8,7 @@ use std::{ use lru_cache::LruCache; use ruma::{EventId, RoomId}; +use super::short::{ShortEventId, ShortStateHash, ShortStateKey}; use crate::{ observability::{FoundIn, Lookup, METRICS}, services, utils, Result, @@ -18,8 +19,6 @@ pub(crate) mod data; pub(crate) use data::Data; use data::StateDiff; -use super::short::{ShortEventId, ShortStateHash, ShortStateKey}; - #[derive(Clone)] pub(crate) struct CompressedStateLayer { pub(crate) shortstatehash: ShortStateHash, diff --git a/src/service/rooms/threads.rs b/src/service/rooms/threads.rs index c2e9ed11..5acfdec8 100644 --- a/src/service/rooms/threads.rs +++ b/src/service/rooms/threads.rs @@ -1,6 +1,3 @@ -mod data; - -pub(crate) use data::Data; use ruma::{ api::client::{error::ErrorKind, threads::get_threads::v1::IncludeThreads}, events::relation::BundledThread, @@ -10,6 +7,10 @@ use serde_json::json; use crate::{services, Error, PduEvent, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index c70c7033..933524e2 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -1,12 +1,9 @@ -mod data; - use std::{ cmp::Ordering, collections::{BTreeMap, HashSet}, sync::Arc, }; -pub(crate) use data::Data; use ruma::{ api::{client::error::ErrorKind, federation}, canonical_json::to_canonical_value, @@ -43,6 +40,10 @@ use crate::{ Error, PduEvent, Result, }; +mod data; + +pub(crate) use data::Data; + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct PduId { inner: Vec, diff --git a/src/service/rooms/user.rs b/src/service/rooms/user.rs index 411199f4..4f4b4344 100644 --- a/src/service/rooms/user.rs +++ b/src/service/rooms/user.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/sending.rs b/src/service/sending.rs index 2e3fcc5a..d02918d5 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -1,5 +1,3 @@ -mod data; - use std::{ collections::{BTreeMap, HashMap, HashSet}, fmt::Debug, @@ -8,7 +6,6 @@ use std::{ }; use base64::{engine::general_purpose, Engine as _}; -pub(crate) use data::Data; use federation::transactions::send_transaction_message; use futures_util::{stream::FuturesUnordered, StreamExt}; use ruma::{ @@ -50,6 +47,10 @@ use crate::{ Config, Error, PduEvent, Result, }; +mod data; + +pub(crate) use data::Data; + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum Destination { Appservice(String), diff --git a/src/service/transaction_ids.rs b/src/service/transaction_ids.rs index 411199f4..4f4b4344 100644 --- a/src/service/transaction_ids.rs +++ b/src/service/transaction_ids.rs @@ -1,4 +1,5 @@ mod data; pub(crate) use data::Data; + pub(crate) type Service = &'static dyn Data; diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 5ca95ed1..afa04650 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -1,6 +1,3 @@ -mod data; - -pub(crate) use data::Data; use ruma::{ api::client::{ error::ErrorKind, @@ -14,6 +11,10 @@ use crate::{ api::client_server::SESSION_ID_LENGTH, services, utils, Error, Result, }; +mod data; + +pub(crate) use data::Data; + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/users.rs b/src/service/users.rs index 47277600..3007f176 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -1,11 +1,9 @@ -mod data; use std::{ collections::{BTreeMap, BTreeSet}, mem, sync::{Arc, Mutex}, }; -pub(crate) use data::Data; use ruma::{ api::client::{ device::Device, @@ -24,6 +22,10 @@ use ruma::{ use crate::{services, Error, Result}; +mod data; + +pub(crate) use data::Data; + pub(crate) struct SlidingSyncCache { lists: BTreeMap, subscriptions: BTreeMap, diff --git a/src/utils.rs b/src/utils.rs index c9cafa56..e5d923d5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,3 @@ -pub(crate) mod error; -pub(crate) mod on_demand_hashmap; -pub(crate) mod room_version; - use std::{ borrow::Cow, cmp, fmt, @@ -21,6 +17,10 @@ use ruma::{ use crate::{Error, Result}; +pub(crate) mod error; +pub(crate) mod on_demand_hashmap; +pub(crate) mod room_version; + // Hopefully we have a better chat protocol in 530 years #[allow(clippy::as_conversions, clippy::cast_possible_truncation)] pub(crate) fn millis_since_unix_epoch() -> u64 { From e001356653222af20d24ae13cd1966dc78e6eaec Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Sun, 13 Oct 2024 20:58:52 -0400 Subject: [PATCH 413/617] Return local join error if all remote joins fail If all join requests to resident servers fail or if the joining server is the only resident server (i.e. the room is local-only), we would previously send a 500 error, even if the more correct response would be M_UNAUTHORIZED (e.g. if the user tries to join an invite-only room). To fix this, we now return the error generated by attempting the join locally, which correctly informs the client about why their request failed. --- book/changelog.md | 4 ++++ src/api/client_server/membership.rs | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index cd3af642..138329c3 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -186,6 +186,10 @@ This will be the first release of Grapevine since it was forked from Conduit ([!96](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/96)) 18. Fixed incoming HTTP/2 requests failing federation signature check. ([!104](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/104)) +19. Return 403 instead of 500 when joins to a local-only room are denied. + Consequently fixes Heisenbridge being unable to join puppeted users to its + rooms ([#85](https://gitlab.computer.surgery/matrix/grapevine/-/issues/85)). + ([!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127)) ### Added diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 00f1db9e..f9ce1163 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -639,8 +639,11 @@ async fn join_room_by_id_helper( "We couldn't do the join locally, maybe federation can help to \ satisfy the restricted join requirements" ); - let (make_join_response, remote_server) = - make_join_request(sender_user, room_id, servers).await?; + let Ok((make_join_response, remote_server)) = + make_join_request(sender_user, room_id, servers).await + else { + return Err(error); + }; let room_version_id = match make_join_response.room_version { Some(room_version_id) From 76a633cb663a37376ca89d703e18b5a8894130ee Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Sun, 13 Oct 2024 19:24:41 -0700 Subject: [PATCH 414/617] Log failed remote resident server join requests --- book/changelog.md | 3 ++- src/api/client_server/membership.rs | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 138329c3..10bc1688 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -107,7 +107,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!54](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/54), [!56](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/56), [!69](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/69), - [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102)) + [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102), + [!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127)) 5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/12)) 6. **BREAKING:** Allow federation by default. diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index f9ce1163..c07a07c9 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1158,11 +1158,18 @@ async fn make_join_request( ) .await; - make_join_response_and_server = - make_join_response.map(|r| (r, remote_server.clone())); - - if make_join_response_and_server.is_ok() { - break; + match make_join_response { + Ok(r) => { + return Ok((r, remote_server.clone())); + } + Err(error) => { + warn!( + %error, + server = %remote_server, + "Remote join request failed", + ); + make_join_response_and_server = Err(error); + } } } From 6a44d0af2baa97db50250cd8bbd72c1ff25e4f4a Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 20 Oct 2024 18:50:05 +0000 Subject: [PATCH 415/617] Fix u8_slice_to_hex() For bytes <0x10, this would omit the leading zero. --- src/utils.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index e5d923d5..1eca4b4b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -374,14 +374,14 @@ pub(crate) fn curlify(req: &http::Request) -> Option { /// whitespace or commas do not appear in the output). pub(crate) fn u8_slice_to_hex(slice: &[u8]) -> String { slice.iter().fold(String::new(), |mut acc, x| { - write!(acc, "{x:X}").expect("in-memory write should succeed"); + write!(acc, "{x:02X}").expect("in-memory write should succeed"); acc }) } #[cfg(test)] mod tests { - use crate::utils::dbg_truncate_str; + use crate::utils::{dbg_truncate_str, u8_slice_to_hex}; #[test] fn test_truncate_str() { @@ -395,4 +395,20 @@ mod tests { assert_eq!(dbg_truncate_str(ok_hand, ok_hand.len() - 1), "👌🏽"); assert_eq!(dbg_truncate_str(ok_hand, ok_hand.len()), "👌🏽"); } + + #[test] + fn test_slice_to_hex() { + assert_eq!(u8_slice_to_hex(&[]), ""); + assert_eq!(u8_slice_to_hex(&[0]), "00"); + assert_eq!(u8_slice_to_hex(&[0xFF]), "FF"); + assert_eq!(u8_slice_to_hex(&[1, 2, 3, 4]), "01020304"); + assert_eq!( + u8_slice_to_hex(&[0x42; 100]), + "4242424242424242424242424242424242424242\ + 4242424242424242424242424242424242424242\ + 4242424242424242424242424242424242424242\ + 4242424242424242424242424242424242424242\ + 4242424242424242424242424242424242424242" + ); + } } From e0cf16348694b8f54cb4b7db690fdd070366fe80 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 20:25:23 -0700 Subject: [PATCH 416/617] delete useless admin commands To clear caches, restart the server. We may want to consider adding the cache sizes and database memory usage as metrics in the future. --- book/changelog.md | 8 ++++ src/database/abstraction.rs | 4 -- src/database/abstraction/rocksdb.rs | 25 ++--------- src/database/key_value/globals.rs | 70 ----------------------------- src/service.rs | 60 ------------------------- src/service/admin.rs | 37 --------------- src/service/globals/data.rs | 2 - 7 files changed, 11 insertions(+), 195 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 10bc1688..cb2e70af 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -73,6 +73,14 @@ This will be the first release of Grapevine since it was forked from Conduit ([!48](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/48)) 7. **BREAKING:** Remove unstable room versions. ([!59](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/59)) +8. Remove `memory-usage`, `clear-database-caches`, and `clear-service-caches` + admin commands. + ([!123](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/123)) + * The `memory-usage` command wasn't particularly useful since it can't + actually give you an accurate value in bytes and isn't supported on all + database backends. + * The latter two commands had poor UX and didn't have any noticable effect on + memory consumption. ### Changed diff --git a/src/database/abstraction.rs b/src/database/abstraction.rs index db14c728..63714cd3 100644 --- a/src/database/abstraction.rs +++ b/src/database/abstraction.rs @@ -20,10 +20,6 @@ pub(crate) trait KeyValueDatabaseEngine: Send + Sync { fn cleanup(&self) -> Result<()> { Ok(()) } - fn memory_usage(&self) -> Result { - Ok("Current database engine does not support memory usage reporting." - .to_owned()) - } } pub(crate) trait KvTree: Send + Sync { diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index 72e676a1..90cdb06a 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -6,10 +6,9 @@ use std::{ }; use rocksdb::{ - perf::get_memory_usage_stats, BlockBasedOptions, BoundColumnFamily, Cache, - ColumnFamilyDescriptor, DBCompactionStyle, DBCompressionType, - DBRecoveryMode, DBWithThreadMode, Direction, IteratorMode, MultiThreaded, - Options, ReadOptions, WriteOptions, + BlockBasedOptions, BoundColumnFamily, Cache, ColumnFamilyDescriptor, + DBCompactionStyle, DBCompressionType, DBRecoveryMode, DBWithThreadMode, + Direction, IteratorMode, MultiThreaded, Options, ReadOptions, WriteOptions, }; use tracing::Level; @@ -143,24 +142,6 @@ impl KeyValueDatabaseEngine for Arc { write_lock: RwLock::new(()), })) } - - #[allow(clippy::as_conversions, clippy::cast_precision_loss)] - fn memory_usage(&self) -> Result { - let stats = - get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; - Ok(format!( - "Approximate memory usage of all the mem-tables: {:.3} \ - MB\nApproximate memory usage of un-flushed mem-tables: {:.3} \ - MB\nApproximate memory usage of all the table readers: {:.3} \ - MB\nApproximate memory usage by cache: {:.3} MB\nApproximate \ - memory usage by cache pinned: {:.3} MB\n", - stats.mem_table_total as f64 / 1024.0 / 1024.0, - stats.mem_table_unflushed as f64 / 1024.0 / 1024.0, - stats.mem_table_readers_total as f64 / 1024.0 / 1024.0, - stats.cache_total as f64 / 1024.0 / 1024.0, - self.cache.get_pinned_usage() as f64 / 1024.0 / 1024.0, - )) - } } impl RocksDbEngineTree<'_> { diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 819ef98c..e3293571 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -1,8 +1,5 @@ -use std::collections::HashMap; - use async_trait::async_trait; use futures_util::{stream::FuturesUnordered, StreamExt}; -use lru_cache::LruCache; use ruma::{ api::federation::discovery::{OldVerifyKey, ServerSigningKeys}, signatures::Ed25519KeyPair, @@ -138,73 +135,6 @@ impl service::globals::Data for KeyValueDatabase { self.db.cleanup() } - fn memory_usage(&self) -> String { - let pdu_cache = self.pdu_cache.lock().unwrap().len(); - let shorteventid_cache = self.shorteventid_cache.lock().unwrap().len(); - let auth_chain_cache = self.auth_chain_cache.lock().unwrap().len(); - let eventidshort_cache = self.eventidshort_cache.lock().unwrap().len(); - let statekeyshort_cache = - self.statekeyshort_cache.lock().unwrap().len(); - let our_real_users_cache = - self.our_real_users_cache.read().unwrap().len(); - let appservice_in_room_cache = - self.appservice_in_room_cache.read().unwrap().len(); - let lasttimelinecount_cache = - self.lasttimelinecount_cache.lock().unwrap().len(); - - let mut response = format!( - "\ -pdu_cache: {pdu_cache} -shorteventid_cache: {shorteventid_cache} -auth_chain_cache: {auth_chain_cache} -eventidshort_cache: {eventidshort_cache} -statekeyshort_cache: {statekeyshort_cache} -our_real_users_cache: {our_real_users_cache} -appservice_in_room_cache: {appservice_in_room_cache} -lasttimelinecount_cache: {lasttimelinecount_cache}\n" - ); - if let Ok(db_stats) = self.db.memory_usage() { - response += &db_stats; - } - - response - } - - fn clear_caches(&self, amount: u32) { - if amount > 0 { - let c = &mut *self.pdu_cache.lock().unwrap(); - *c = LruCache::new(c.capacity()); - } - if amount > 1 { - let c = &mut *self.shorteventid_cache.lock().unwrap(); - *c = LruCache::new(c.capacity()); - } - if amount > 2 { - let c = &mut *self.auth_chain_cache.lock().unwrap(); - *c = LruCache::new(c.capacity()); - } - if amount > 3 { - let c = &mut *self.eventidshort_cache.lock().unwrap(); - *c = LruCache::new(c.capacity()); - } - if amount > 4 { - let c = &mut *self.statekeyshort_cache.lock().unwrap(); - *c = LruCache::new(c.capacity()); - } - if amount > 5 { - let c = &mut *self.our_real_users_cache.write().unwrap(); - *c = HashMap::new(); - } - if amount > 6 { - let c = &mut *self.appservice_in_room_cache.write().unwrap(); - *c = HashMap::new(); - } - if amount > 7 { - let c = &mut *self.lasttimelinecount_cache.lock().unwrap(); - *c = HashMap::new(); - } - } - fn load_keypair(&self) -> Result { let keypair_bytes = self.global.get(b"keypair")?.map_or_else( || { diff --git a/src/service.rs b/src/service.rs index c2dcb0f8..1c45fbcd 100644 --- a/src/service.rs +++ b/src/service.rs @@ -168,64 +168,4 @@ impl Services { "Services::install was called more than once" ); } - - async fn memory_usage(&self) -> String { - let lazy_load_waiting = - self.rooms.lazy_loading.lazy_load_waiting.lock().await.len(); - let server_visibility_cache = self - .rooms - .state_accessor - .server_visibility_cache - .lock() - .unwrap() - .len(); - let user_visibility_cache = self - .rooms - .state_accessor - .user_visibility_cache - .lock() - .unwrap() - .len(); - let stateinfo_cache = - self.rooms.state_compressor.stateinfo_cache.lock().unwrap().len(); - let roomid_spacechunk_cache = - self.rooms.spaces.roomid_spacechunk_cache.lock().await.len(); - - format!( - "\ -lazy_load_waiting: {lazy_load_waiting} -server_visibility_cache: {server_visibility_cache} -user_visibility_cache: {user_visibility_cache} -stateinfo_cache: {stateinfo_cache} -roomid_spacechunk_cache: {roomid_spacechunk_cache}" - ) - } - - async fn clear_caches(&self, amount: u32) { - if amount > 0 { - self.rooms.lazy_loading.lazy_load_waiting.lock().await.clear(); - } - if amount > 1 { - self.rooms - .state_accessor - .server_visibility_cache - .lock() - .unwrap() - .clear(); - } - if amount > 2 { - self.rooms - .state_accessor - .user_visibility_cache - .lock() - .unwrap() - .clear(); - } - if amount > 3 { - self.rooms.state_compressor.stateinfo_cache.lock().unwrap().clear(); - } - if amount > 5 { - self.rooms.spaces.roomid_spacechunk_cache.lock().await.clear(); - } - } } diff --git a/src/service/admin.rs b/src/service/admin.rs index e46368ed..8fd0abdf 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -141,21 +141,6 @@ enum AdminCommand { event_id: Box, }, - /// Print database memory usage statistics - MemoryUsage, - - /// Clears all of Grapevine's database caches with index smaller than the - /// amount - ClearDatabaseCaches { - amount: u32, - }, - - /// Clears all of Grapevine's service caches with index smaller than the - /// amount - ClearServiceCaches { - amount: u32, - }, - /// Reset user password ResetPassword { /// Username of the user for whom the password should be reset @@ -661,28 +646,6 @@ impl Service { } } } - AdminCommand::MemoryUsage => { - let response1 = services().memory_usage().await; - let response2 = services().globals.db.memory_usage(); - - RoomMessageEventContent::text_plain(format!( - "Services:\n{response1}\n\nDatabase:\n{response2}" - )) - } - AdminCommand::ClearDatabaseCaches { - amount, - } => { - services().globals.db.clear_caches(amount); - - RoomMessageEventContent::text_plain("Done.") - } - AdminCommand::ClearServiceCaches { - amount, - } => { - services().clear_caches(amount).await; - - RoomMessageEventContent::text_plain("Done.") - } AdminCommand::ResetPassword { username, } => { diff --git a/src/service/globals/data.rs b/src/service/globals/data.rs index 28e7e512..124e81b3 100644 --- a/src/service/globals/data.rs +++ b/src/service/globals/data.rs @@ -90,8 +90,6 @@ pub(crate) trait Data: Send + Sync { async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()>; fn cleanup(&self) -> Result<()>; - fn memory_usage(&self) -> String; - fn clear_caches(&self, amount: u32); fn load_keypair(&self) -> Result; fn remove_keypair(&self) -> Result<()>; /// Only extends the cached keys, not moving any verify_keys to From a1fe0f3fff20d68ea51346310ed7f52a2c28e09f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 20:52:06 -0700 Subject: [PATCH 417/617] add service type for short This will be necessary in the near future. --- src/service.rs | 2 +- src/service/rooms/short.rs | 81 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/service.rs b/src/service.rs index 1c45fbcd..cad5e31b 100644 --- a/src/service.rs +++ b/src/service.rs @@ -93,7 +93,7 @@ impl Services { db, }, search: db, - short: db, + short: rooms::short::Service::new(db), state: rooms::state::Service { db, }, diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 790b1d04..d165ef9e 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,6 +1,8 @@ -mod data; +use std::sync::Arc; -pub(crate) use data::Data; +use ruma::{events::StateEventType, EventId, RoomId}; + +use crate::utils::error::Result; macro_rules! short_id_type { ($name:ident) => { @@ -25,4 +27,77 @@ short_id_type!(ShortEventId); short_id_type!(ShortStateHash); short_id_type!(ShortStateKey); -pub(crate) type Service = &'static dyn Data; +mod data; + +pub(crate) use data::Data; + +pub(crate) struct Service { + db: &'static dyn Data, +} + +impl Service { + pub(crate) fn new(db: &'static dyn Data) -> Self { + Self { + db, + } + } + + pub(crate) fn get_or_create_shorteventid( + &self, + event_id: &EventId, + ) -> Result { + self.db.get_or_create_shorteventid(event_id) + } + + pub(crate) fn get_shortstatekey( + &self, + event_type: &StateEventType, + state_key: &str, + ) -> Result> { + self.db.get_shortstatekey(event_type, state_key) + } + + pub(crate) fn get_or_create_shortstatekey( + &self, + event_type: &StateEventType, + state_key: &str, + ) -> Result { + self.db.get_or_create_shortstatekey(event_type, state_key) + } + + pub(crate) fn get_eventid_from_short( + &self, + shorteventid: ShortEventId, + ) -> Result> { + self.db.get_eventid_from_short(shorteventid) + } + + pub(crate) fn get_statekey_from_short( + &self, + shortstatekey: ShortStateKey, + ) -> Result<(StateEventType, String)> { + self.db.get_statekey_from_short(shortstatekey) + } + + /// Returns `(shortstatehash, already_existed)` + pub(crate) fn get_or_create_shortstatehash( + &self, + state_hash: &[u8], + ) -> Result<(ShortStateHash, bool)> { + self.db.get_or_create_shortstatehash(state_hash) + } + + pub(crate) fn get_shortroomid( + &self, + room_id: &RoomId, + ) -> Result> { + self.db.get_shortroomid(room_id) + } + + pub(crate) fn get_or_create_shortroomid( + &self, + room_id: &RoomId, + ) -> Result { + self.db.get_or_create_shortroomid(room_id) + } +} From fb534d81401798b7f04ce0f7da418dbf7edbfbe7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 13:38:25 -0700 Subject: [PATCH 418/617] move userdevicesessionid_uiaarequest to service --- src/database.rs | 9 ++----- src/database/key_value/uiaa.rs | 34 +----------------------- src/service.rs | 4 +-- src/service/uiaa.rs | 48 ++++++++++++++++++++++++++-------- src/service/uiaa/data.rs | 17 +----------- 5 files changed, 42 insertions(+), 70 deletions(-) diff --git a/src/database.rs b/src/database.rs index 641f1f7c..d3704497 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,5 +1,5 @@ use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{HashMap, HashSet}, fs, io::Write, mem::size_of, @@ -13,8 +13,7 @@ use ruma::{ push_rules::PushRulesEvent, GlobalAccountDataEventType, StateEventType, }, push::Ruleset, - CanonicalJsonValue, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, - OwnedUserId, RoomId, UserId, + EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, }; use tracing::{debug, error, info, info_span, warn, Instrument}; @@ -81,9 +80,6 @@ pub(crate) struct KeyValueDatabase { // Trees "owned" by `self::key_value::uiaa` // User-interactive authentication pub(super) userdevicesessionid_uiaainfo: Arc, - pub(super) userdevicesessionid_uiaarequest: RwLock< - BTreeMap<(OwnedUserId, OwnedDeviceId, String), CanonicalJsonValue>, - >, // Trees "owned" by `self::key_value::rooms::edus` // ReadReceiptId = RoomId + Count + UserId @@ -376,7 +372,6 @@ impl KeyValueDatabase { userdevicesessionid_uiaainfo: builder .open_tree("userdevicesessionid_uiaainfo")?, - userdevicesessionid_uiaarequest: RwLock::new(BTreeMap::new()), readreceiptid_readreceipt: builder .open_tree("readreceiptid_readreceipt")?, // "Private" read receipt diff --git a/src/database/key_value/uiaa.rs b/src/database/key_value/uiaa.rs index 8fac4a21..e97f915e 100644 --- a/src/database/key_value/uiaa.rs +++ b/src/database/key_value/uiaa.rs @@ -1,43 +1,11 @@ use ruma::{ api::client::{error::ErrorKind, uiaa::UiaaInfo}, - CanonicalJsonValue, DeviceId, UserId, + DeviceId, UserId, }; use crate::{database::KeyValueDatabase, service, Error, Result}; impl service::uiaa::Data for KeyValueDatabase { - fn set_uiaa_request( - &self, - user_id: &UserId, - device_id: &DeviceId, - session: &str, - request: &CanonicalJsonValue, - ) -> Result<()> { - self.userdevicesessionid_uiaarequest.write().unwrap().insert( - (user_id.to_owned(), device_id.to_owned(), session.to_owned()), - request.to_owned(), - ); - - Ok(()) - } - - fn get_uiaa_request( - &self, - user_id: &UserId, - device_id: &DeviceId, - session: &str, - ) -> Option { - self.userdevicesessionid_uiaarequest - .read() - .unwrap() - .get(&( - user_id.to_owned(), - device_id.to_owned(), - session.to_owned(), - )) - .map(ToOwned::to_owned) - } - fn update_uiaa_session( &self, user_id: &UserId, diff --git a/src/service.rs b/src/service.rs index cad5e31b..df7735aa 100644 --- a/src/service.rs +++ b/src/service.rs @@ -142,9 +142,7 @@ impl Services { user: db, }, transaction_ids: db, - uiaa: uiaa::Service { - db, - }, + uiaa: uiaa::Service::new(db), users: users::Service { db, connections: StdMutex::new(BTreeMap::new()), diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index afa04650..566db934 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -1,9 +1,11 @@ +use std::{collections::BTreeMap, sync::RwLock}; + use ruma::{ api::client::{ error::ErrorKind, uiaa::{AuthData, AuthType, Password, UiaaInfo, UserIdentifier}, }, - CanonicalJsonValue, DeviceId, UserId, + CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedUserId, UserId, }; use tracing::error; @@ -16,10 +18,20 @@ mod data; pub(crate) use data::Data; pub(crate) struct Service { - pub(crate) db: &'static dyn Data, + db: &'static dyn Data, + userdevicesessionid_uiaarequest: RwLock< + BTreeMap<(OwnedUserId, OwnedDeviceId, String), CanonicalJsonValue>, + >, } impl Service { + pub(crate) fn new(db: &'static dyn Data) -> Self { + Self { + db, + userdevicesessionid_uiaarequest: RwLock::new(BTreeMap::new()), + } + } + /// Creates a new Uiaa session. Make sure the session token is unique. pub(crate) fn create( &self, @@ -28,14 +40,20 @@ impl Service { uiaainfo: &UiaaInfo, json_body: &CanonicalJsonValue, ) -> Result<()> { - self.db.set_uiaa_request( - user_id, - device_id, - // TODO: better session error handling (why is it optional in - // ruma?) - uiaainfo.session.as_ref().expect("session should be set"), - json_body, - )?; + self.userdevicesessionid_uiaarequest.write().unwrap().insert( + ( + user_id.to_owned(), + device_id.to_owned(), + // TODO: better session error handling (why is it optional in + // ruma?) + uiaainfo + .session + .as_ref() + .expect("session should be set") + .to_owned(), + ), + json_body.to_owned(), + ); self.db.update_uiaa_session( user_id, device_id, @@ -160,6 +178,14 @@ impl Service { device_id: &DeviceId, session: &str, ) -> Option { - self.db.get_uiaa_request(user_id, device_id, session) + self.userdevicesessionid_uiaarequest + .read() + .unwrap() + .get(&( + user_id.to_owned(), + device_id.to_owned(), + session.to_owned(), + )) + .map(ToOwned::to_owned) } } diff --git a/src/service/uiaa/data.rs b/src/service/uiaa/data.rs index c8f41793..2af82e11 100644 --- a/src/service/uiaa/data.rs +++ b/src/service/uiaa/data.rs @@ -1,23 +1,8 @@ -use ruma::{api::client::uiaa::UiaaInfo, CanonicalJsonValue, DeviceId, UserId}; +use ruma::{api::client::uiaa::UiaaInfo, DeviceId, UserId}; use crate::Result; pub(crate) trait Data: Send + Sync { - fn set_uiaa_request( - &self, - user_id: &UserId, - device_id: &DeviceId, - session: &str, - request: &CanonicalJsonValue, - ) -> Result<()>; - - fn get_uiaa_request( - &self, - user_id: &UserId, - device_id: &DeviceId, - session: &str, - ) -> Option; - fn update_uiaa_session( &self, user_id: &UserId, From 7563360bee418f9cc7d9a0783a2993fb136b74d1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 20:37:50 -0700 Subject: [PATCH 419/617] move pdu_cache to service --- src/config.rs | 4 +-- src/database.rs | 9 +----- src/database/key_value/rooms/timeline.rs | 25 ++------------- src/service.rs | 5 +-- src/service/rooms/timeline.rs | 40 +++++++++++++++++++++--- src/service/rooms/timeline/data.rs | 1 - 6 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/config.rs b/src/config.rs index c980c26b..c53f1dd5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -49,7 +49,7 @@ pub(crate) struct Config { #[serde(default = "default_cache_capacity_modifier")] pub(crate) cache_capacity_modifier: f64, #[serde(default = "default_pdu_cache_capacity")] - pub(crate) pdu_cache_capacity: u32, + pub(crate) pdu_cache_capacity: usize, #[serde(default = "default_cleanup_second_interval")] pub(crate) cleanup_second_interval: u32, #[serde(default = "default_max_request_size")] @@ -390,7 +390,7 @@ fn default_rocksdb_max_open_files() -> i32 { 1000 } -fn default_pdu_cache_capacity() -> u32 { +fn default_pdu_cache_capacity() -> usize { 150_000 } diff --git a/src/database.rs b/src/database.rs index d3704497..ea1fe1c3 100644 --- a/src/database.rs +++ b/src/database.rs @@ -27,7 +27,7 @@ use crate::{ timeline::PduCount, }, }, - services, utils, Config, Error, PduEvent, Result, + services, utils, Config, Error, Result, }; pub(crate) mod abstraction; @@ -236,7 +236,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) pdu_cache: Mutex>>, pub(super) shorteventid_cache: Mutex>>, pub(super) auth_chain_cache: Mutex, Arc>>>, @@ -468,12 +467,6 @@ impl KeyValueDatabase { global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - pdu_cache: Mutex::new(LruCache::new( - config - .pdu_cache_capacity - .try_into() - .expect("pdu cache capacity fits into usize"), - )), #[allow( clippy::as_conversions, clippy::cast_sign_loss, diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 656598db..71b75652 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -132,14 +132,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase { /// Checks the `eventid_outlierpdu` Tree if not found in the timeline. #[tracing::instrument(skip(self))] fn get_pdu(&self, event_id: &EventId) -> Result>> { - let lookup = Lookup::Pdu; - - if let Some(p) = self.pdu_cache.lock().unwrap().get_mut(event_id) { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Some(Arc::clone(p))); - } - - if let Some(pdu) = self + Ok(self .get_non_outlier_pdu(event_id)? .map_or_else( || { @@ -154,18 +147,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase { }, |x| Ok(Some(x)), )? - .map(Arc::new) - { - METRICS.record_lookup(lookup, FoundIn::Database); - self.pdu_cache - .lock() - .unwrap() - .insert(event_id.to_owned(), Arc::clone(&pdu)); - Ok(Some(pdu)) - } else { - METRICS.record_lookup(lookup, FoundIn::Nothing); - Ok(None) - } + .map(Arc::new)) } /// Returns the pdu. @@ -241,7 +223,6 @@ impl service::rooms::timeline::Data for KeyValueDatabase { &self, pdu_id: &PduId, pdu_json: &CanonicalJsonObject, - pdu: &PduEvent, ) -> Result<()> { if self.pduid_pdu.get(pdu_id.as_bytes())?.is_some() { self.pduid_pdu.insert( @@ -256,8 +237,6 @@ impl service::rooms::timeline::Data for KeyValueDatabase { )); } - self.pdu_cache.lock().unwrap().remove(&(*pdu.event_id).to_owned()); - Ok(()) } diff --git a/src/service.rs b/src/service.rs index df7735aa..73633685 100644 --- a/src/service.rs +++ b/src/service.rs @@ -130,9 +130,10 @@ impl Services { (100.0 * config.cache_capacity_modifier) as usize, )), }, - timeline: rooms::timeline::Service { + timeline: rooms::timeline::Service::new( db, - }, + config.pdu_cache_capacity, + ), threads: rooms::threads::Service { db, }, diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 933524e2..341fb1c4 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -1,9 +1,10 @@ use std::{ cmp::Ordering, collections::{BTreeMap, HashSet}, - sync::Arc, + sync::{Arc, Mutex}, }; +use lru_cache::LruCache; use ruma::{ api::{client::error::ErrorKind, federation}, canonical_json::to_canonical_value, @@ -30,6 +31,7 @@ use tracing::{error, info, warn}; use super::{short::ShortRoomId, state_compressor::CompressedStateEvent}; use crate::{ api::server_server, + observability::{FoundIn, Lookup, METRICS}, service::{ appservice::NamespaceRegex, globals::{marker, SigningKeys}, @@ -111,10 +113,21 @@ impl Ord for PduCount { } pub(crate) struct Service { - pub(crate) db: &'static dyn Data, + db: &'static dyn Data, + pdu_cache: Mutex>>, } impl Service { + pub(crate) fn new( + db: &'static dyn Data, + pdu_cache_capacity: usize, + ) -> Self { + Self { + db, + pdu_cache: Mutex::new(LruCache::new(pdu_cache_capacity)), + } + } + #[tracing::instrument(skip(self))] pub(crate) fn first_pdu_in_room( &self, @@ -174,7 +187,24 @@ impl Service { &self, event_id: &EventId, ) -> Result>> { - self.db.get_pdu(event_id) + let lookup = Lookup::Pdu; + + if let Some(p) = self.pdu_cache.lock().unwrap().get_mut(event_id) { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Some(Arc::clone(p))); + } + + if let Some(pdu) = self.db.get_pdu(event_id)? { + METRICS.record_lookup(lookup, FoundIn::Database); + self.pdu_cache + .lock() + .unwrap() + .insert(event_id.to_owned(), Arc::clone(&pdu)); + Ok(Some(pdu)) + } else { + METRICS.record_lookup(lookup, FoundIn::Nothing); + Ok(None) + } } /// Returns the pdu. @@ -203,7 +233,9 @@ impl Service { pdu_json: &CanonicalJsonObject, pdu: &PduEvent, ) -> Result<()> { - self.db.replace_pdu(pdu_id, pdu_json, pdu) + self.db.replace_pdu(pdu_id, pdu_json)?; + self.pdu_cache.lock().unwrap().remove(&(*pdu.event_id).to_owned()); + Ok(()) } /// Creates a new persisted data unit and adds it to a room. diff --git a/src/service/rooms/timeline/data.rs b/src/service/rooms/timeline/data.rs index acd0b33c..a9a85760 100644 --- a/src/service/rooms/timeline/data.rs +++ b/src/service/rooms/timeline/data.rs @@ -76,7 +76,6 @@ pub(crate) trait Data: Send + Sync { &self, pdu_id: &PduId, pdu_json: &CanonicalJsonObject, - pdu: &PduEvent, ) -> Result<()>; /// Returns an iterator over all events and their tokens in a room that From 47502d1f361ef0efe8915c494e3f626b9c6f2f09 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 21:06:37 -0700 Subject: [PATCH 420/617] move shorteventid_cache to service --- src/database.rs | 9 ------- src/database/key_value/rooms/short.rs | 16 ------------ src/service.rs | 13 +++++++++- src/service/rooms/short.rs | 37 ++++++++++++++++++++++++--- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/database.rs b/src/database.rs index ea1fe1c3..ba3709cd 100644 --- a/src/database.rs +++ b/src/database.rs @@ -236,7 +236,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) shorteventid_cache: Mutex>>, pub(super) auth_chain_cache: Mutex, Arc>>>, pub(super) eventidshort_cache: Mutex>, @@ -480,14 +479,6 @@ impl KeyValueDatabase { clippy::cast_sign_loss, clippy::cast_possible_truncation )] - shorteventid_cache: Mutex::new(LruCache::new( - (100_000.0 * config.cache_capacity_modifier) as usize, - )), - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] eventidshort_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index ca499ed2..15840e6e 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -162,15 +162,6 @@ impl service::rooms::short::Data for KeyValueDatabase { &self, shorteventid: ShortEventId, ) -> Result> { - let lookup = Lookup::ShortToEventId; - - if let Some(id) = - self.shorteventid_cache.lock().unwrap().get_mut(&shorteventid) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Arc::clone(id)); - } - let bytes = self .shorteventid_eventid .get(&shorteventid.get().to_be_bytes())? @@ -189,13 +180,6 @@ impl service::rooms::short::Data for KeyValueDatabase { Error::bad_database("EventId in shorteventid_eventid is invalid.") })?; - METRICS.record_lookup(lookup, FoundIn::Database); - - self.shorteventid_cache - .lock() - .unwrap() - .insert(shorteventid, Arc::clone(&event_id)); - Ok(event_id) } diff --git a/src/service.rs b/src/service.rs index 73633685..6f038ecb 100644 --- a/src/service.rs +++ b/src/service.rs @@ -45,6 +45,7 @@ pub(crate) struct Services { } impl Services { + #[allow(clippy::too_many_lines)] pub(crate) fn build< D: appservice::Data + pusher::Data @@ -93,7 +94,17 @@ impl Services { db, }, search: db, - short: rooms::short::Service::new(db), + short: rooms::short::Service::new( + db, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + { + (100_000.0 * config.cache_capacity_modifier) as usize + }, + ), state: rooms::state::Service { db, }, diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index d165ef9e..87554692 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,8 +1,12 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use lru_cache::LruCache; use ruma::{events::StateEventType, EventId, RoomId}; -use crate::utils::error::Result; +use crate::{ + observability::{FoundIn, Lookup, METRICS}, + utils::error::Result, +}; macro_rules! short_id_type { ($name:ident) => { @@ -33,12 +37,19 @@ pub(crate) use data::Data; pub(crate) struct Service { db: &'static dyn Data, + shorteventid_cache: Mutex>>, } impl Service { - pub(crate) fn new(db: &'static dyn Data) -> Self { + pub(crate) fn new( + db: &'static dyn Data, + shorteventid_cache_size: usize, + ) -> Self { Self { db, + shorteventid_cache: Mutex::new(LruCache::new( + shorteventid_cache_size, + )), } } @@ -69,7 +80,25 @@ impl Service { &self, shorteventid: ShortEventId, ) -> Result> { - self.db.get_eventid_from_short(shorteventid) + let lookup = Lookup::ShortToEventId; + + if let Some(id) = + self.shorteventid_cache.lock().unwrap().get_mut(&shorteventid) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Arc::clone(id)); + } + + let event_id = self.db.get_eventid_from_short(shorteventid)?; + + METRICS.record_lookup(lookup, FoundIn::Database); + + self.shorteventid_cache + .lock() + .unwrap() + .insert(shorteventid, Arc::clone(&event_id)); + + Ok(event_id) } pub(crate) fn get_statekey_from_short( From 095ee483ac491c3086cd63f6c15d2d787f64c314 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 21:29:16 -0700 Subject: [PATCH 421/617] move auth_chain_cache to service --- src/database.rs | 10 ----- src/database/key_value/rooms/auth_chain.rs | 36 +++------------ src/service.rs | 12 ++++- src/service/rooms/auth_chain.rs | 51 +++++++++++++++++++--- src/service/rooms/auth_chain/data.rs | 8 ++-- 5 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/database.rs b/src/database.rs index ba3709cd..e506f4f1 100644 --- a/src/database.rs +++ b/src/database.rs @@ -236,8 +236,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) auth_chain_cache: - Mutex, Arc>>>, pub(super) eventidshort_cache: Mutex>, pub(super) statekeyshort_cache: Mutex>, @@ -466,14 +464,6 @@ impl KeyValueDatabase { global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - auth_chain_cache: Mutex::new(LruCache::new( - (100_000.0 * config.cache_capacity_modifier) as usize, - )), #[allow( clippy::as_conversions, clippy::cast_sign_loss, diff --git a/src/database/key_value/rooms/auth_chain.rs b/src/database/key_value/rooms/auth_chain.rs index b29149f8..b736e524 100644 --- a/src/database/key_value/rooms/auth_chain.rs +++ b/src/database/key_value/rooms/auth_chain.rs @@ -1,8 +1,7 @@ -use std::{collections::HashSet, mem::size_of, sync::Arc}; +use std::{collections::HashSet, mem::size_of}; use crate::{ database::KeyValueDatabase, - observability::{FoundIn, Lookup, METRICS}, service::{self, rooms::short::ShortEventId}, utils, Result, }; @@ -12,19 +11,9 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { fn get_cached_eventid_authchain( &self, key: &[ShortEventId], - ) -> Result>>> { - let lookup = Lookup::AuthChain; - - // Check RAM cache - if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Some(Arc::clone(result))); - } - + ) -> Result>> { // We only save auth chains for single events in the db if key.len() == 1 { - // Check DB cache let chain = self .shorteventid_authchain .get(&key[0].get().to_be_bytes())? @@ -40,28 +29,16 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { .collect() }); - if let Some(chain) = chain { - METRICS.record_lookup(lookup, FoundIn::Database); - let chain = Arc::new(chain); - - // Cache in RAM - self.auth_chain_cache - .lock() - .unwrap() - .insert(vec![key[0]], Arc::clone(&chain)); - - return Ok(Some(chain)); - } + return Ok(chain); } - METRICS.record_lookup(lookup, FoundIn::Nothing); Ok(None) } fn cache_auth_chain( &self, - key: Vec, - auth_chain: Arc>, + key: &[ShortEventId], + auth_chain: &HashSet, ) -> Result<()> { // Only persist single events in db if key.len() == 1 { @@ -74,9 +51,6 @@ impl service::rooms::auth_chain::Data for KeyValueDatabase { )?; } - // Cache in RAM - self.auth_chain_cache.lock().unwrap().insert(key, auth_chain); - Ok(()) } } diff --git a/src/service.rs b/src/service.rs index 6f038ecb..c5725f83 100644 --- a/src/service.rs +++ b/src/service.rs @@ -71,9 +71,17 @@ impl Services { }, rooms: rooms::Service { alias: rooms::alias::Service::new(db), - auth_chain: rooms::auth_chain::Service { + auth_chain: rooms::auth_chain::Service::new( db, - }, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + { + (100_000.0 * config.cache_capacity_modifier) as usize + }, + ), directory: db, edus: rooms::edus::Service { read_receipt: db, diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index c5a62a6d..97c95f11 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -1,28 +1,67 @@ use std::{ collections::{BTreeSet, HashSet}, - sync::Arc, + sync::{Arc, Mutex}, }; +use lru_cache::LruCache; use ruma::{api::client::error::ErrorKind, EventId, RoomId}; use tracing::{debug, error, warn}; use super::short::ShortEventId; -use crate::{services, utils::debug_slice_truncated, Error, Result}; +use crate::{ + observability::{FoundIn, Lookup, METRICS}, + services, + utils::debug_slice_truncated, + Error, Result, +}; mod data; pub(crate) use data::Data; pub(crate) struct Service { - pub(crate) db: &'static dyn Data, + db: &'static dyn Data, + auth_chain_cache: + Mutex, Arc>>>, } impl Service { + pub(crate) fn new( + db: &'static dyn Data, + auth_chain_cache_size: usize, + ) -> Self { + Self { + db, + auth_chain_cache: Mutex::new(LruCache::new(auth_chain_cache_size)), + } + } + pub(crate) fn get_cached_eventid_authchain( &self, key: &[ShortEventId], ) -> Result>>> { - self.db.get_cached_eventid_authchain(key) + let lookup = Lookup::AuthChain; + + if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Some(Arc::clone(result))); + } + + let Some(chain) = self.db.get_cached_eventid_authchain(key)? else { + METRICS.record_lookup(lookup, FoundIn::Nothing); + return Ok(None); + }; + + METRICS.record_lookup(lookup, FoundIn::Database); + let chain = Arc::new(chain); + + self.auth_chain_cache + .lock() + .unwrap() + .insert(vec![key[0]], Arc::clone(&chain)); + + Ok(Some(chain)) } #[tracing::instrument(skip(self))] @@ -31,7 +70,9 @@ impl Service { key: Vec, auth_chain: Arc>, ) -> Result<()> { - self.db.cache_auth_chain(key, auth_chain) + self.db.cache_auth_chain(&key, &auth_chain)?; + self.auth_chain_cache.lock().unwrap().insert(key, auth_chain); + Ok(()) } #[tracing::instrument( diff --git a/src/service/rooms/auth_chain/data.rs b/src/service/rooms/auth_chain/data.rs index 5d01b1e2..9dc855d2 100644 --- a/src/service/rooms/auth_chain/data.rs +++ b/src/service/rooms/auth_chain/data.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, sync::Arc}; +use std::collections::HashSet; use crate::{service::rooms::short::ShortEventId, Result}; @@ -6,10 +6,10 @@ pub(crate) trait Data: Send + Sync { fn get_cached_eventid_authchain( &self, shorteventid: &[ShortEventId], - ) -> Result>>>; + ) -> Result>>; fn cache_auth_chain( &self, - shorteventid: Vec, - auth_chain: Arc>, + shorteventid: &[ShortEventId], + auth_chain: &HashSet, ) -> Result<()>; } From 2b2b4169df0e0d62d9863dee706ece80e4abc33c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 30 Sep 2024 21:45:04 -0700 Subject: [PATCH 422/617] move eventidshort_cache to service --- src/database.rs | 11 +-------- src/database/key_value/rooms/short.rs | 35 +++++++-------------------- src/service.rs | 8 ++++++ src/service/rooms/short.rs | 31 ++++++++++++++++++++++-- src/service/rooms/short/data.rs | 3 ++- 5 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/database.rs b/src/database.rs index e506f4f1..d625a10f 100644 --- a/src/database.rs +++ b/src/database.rs @@ -13,7 +13,7 @@ use ruma::{ push_rules::PushRulesEvent, GlobalAccountDataEventType, StateEventType, }, push::Ruleset, - EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, + EventId, OwnedRoomId, OwnedUserId, RoomId, UserId, }; use tracing::{debug, error, info, info_span, warn, Instrument}; @@ -236,7 +236,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) eventidshort_cache: Mutex>, pub(super) statekeyshort_cache: Mutex>, pub(super) shortstatekey_cache: @@ -464,14 +463,6 @@ impl KeyValueDatabase { global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - eventidshort_cache: Mutex::new(LruCache::new( - (100_000.0 * config.cache_capacity_modifier) as usize, - )), #[allow( clippy::as_conversions, clippy::cast_sign_loss, diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 15840e6e..c60f1bb8 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -19,43 +19,26 @@ impl service::rooms::short::Data for KeyValueDatabase { fn get_or_create_shorteventid( &self, event_id: &EventId, - ) -> Result { - let lookup = Lookup::CreateEventIdToShort; - - if let Some(short) = - self.eventidshort_cache.lock().unwrap().get_mut(event_id) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(*short); - } - - let short = if let Some(shorteventid) = + ) -> Result<(ShortEventId, bool)> { + let (short, created) = if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? { - METRICS.record_lookup(lookup, FoundIn::Database); + let shorteventid = + utils::u64_from_bytes(&shorteventid).map_err(|_| { + Error::bad_database("Invalid shorteventid in db.") + })?; - utils::u64_from_bytes(&shorteventid).map_err(|_| { - Error::bad_database("Invalid shorteventid in db.") - })? + (shorteventid, false) } else { - METRICS.record_lookup(lookup, FoundIn::Nothing); - let shorteventid = services().globals.next_count()?; self.eventid_shorteventid .insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?; self.shorteventid_eventid .insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?; - shorteventid + (shorteventid, true) }; - let short = ShortEventId::new(short); - - self.eventidshort_cache - .lock() - .unwrap() - .insert(event_id.to_owned(), short); - - Ok(short) + Ok((ShortEventId::new(short), created)) } #[tracing::instrument(skip(self), fields(cache_result))] diff --git a/src/service.rs b/src/service.rs index c5725f83..d25dfe7c 100644 --- a/src/service.rs +++ b/src/service.rs @@ -112,6 +112,14 @@ impl Services { { (100_000.0 * config.cache_capacity_modifier) as usize }, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + { + (100_000.0 * config.cache_capacity_modifier) as usize + }, ), state: rooms::state::Service { db, diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 87554692..2c18e668 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; use lru_cache::LruCache; -use ruma::{events::StateEventType, EventId, RoomId}; +use ruma::{events::StateEventType, EventId, OwnedEventId, RoomId}; use crate::{ observability::{FoundIn, Lookup, METRICS}, @@ -38,18 +38,23 @@ pub(crate) use data::Data; pub(crate) struct Service { db: &'static dyn Data, shorteventid_cache: Mutex>>, + eventidshort_cache: Mutex>, } impl Service { pub(crate) fn new( db: &'static dyn Data, shorteventid_cache_size: usize, + eventidshort_cache_size: usize, ) -> Self { Self { db, shorteventid_cache: Mutex::new(LruCache::new( shorteventid_cache_size, )), + eventidshort_cache: Mutex::new(LruCache::new( + eventidshort_cache_size, + )), } } @@ -57,7 +62,29 @@ impl Service { &self, event_id: &EventId, ) -> Result { - self.db.get_or_create_shorteventid(event_id) + let lookup = Lookup::CreateEventIdToShort; + + if let Some(short) = + self.eventidshort_cache.lock().unwrap().get_mut(event_id) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(*short); + } + + let (short, created) = self.db.get_or_create_shorteventid(event_id)?; + + if created { + METRICS.record_lookup(lookup, FoundIn::Nothing); + } else { + METRICS.record_lookup(lookup, FoundIn::Database); + } + + self.eventidshort_cache + .lock() + .unwrap() + .insert(event_id.to_owned(), short); + + Ok(short) } pub(crate) fn get_shortstatekey( diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index 3650a0b1..7b3fe19e 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -6,10 +6,11 @@ use super::{ShortEventId, ShortRoomId, ShortStateHash, ShortStateKey}; use crate::Result; pub(crate) trait Data: Send + Sync { + /// The returned bool indicates whether it was created fn get_or_create_shorteventid( &self, event_id: &EventId, - ) -> Result; + ) -> Result<(ShortEventId, bool)>; fn get_shortstatekey( &self, From 190b7886837299d12beb1a44cc15afcb1a988f0e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 13:27:02 -0700 Subject: [PATCH 423/617] move statekeyshort_cache to service --- src/database.rs | 10 ----- src/database/key_value/rooms/short.rs | 61 +++++---------------------- src/service.rs | 8 ++++ src/service/rooms/short.rs | 61 ++++++++++++++++++++++++++- src/service/rooms/short/data.rs | 3 +- 5 files changed, 79 insertions(+), 64 deletions(-) diff --git a/src/database.rs b/src/database.rs index d625a10f..4979d1e7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -236,8 +236,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) statekeyshort_cache: - Mutex>, pub(super) shortstatekey_cache: Mutex>, pub(super) our_real_users_cache: @@ -471,14 +469,6 @@ impl KeyValueDatabase { shortstatekey_cache: Mutex::new(LruCache::new( (100_000.0 * config.cache_capacity_modifier) as usize, )), - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - statekeyshort_cache: Mutex::new(LruCache::new( - (100_000.0 * config.cache_capacity_modifier) as usize, - )), our_real_users_cache: RwLock::new(HashMap::new()), appservice_in_room_cache: RwLock::new(HashMap::new()), lasttimelinecount_cache: Mutex::new(HashMap::new()), diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index c60f1bb8..ec3fcf8d 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -47,18 +47,6 @@ impl service::rooms::short::Data for KeyValueDatabase { event_type: &StateEventType, state_key: &str, ) -> Result> { - let lookup = Lookup::StateKeyToShort; - - if let Some(short) = self - .statekeyshort_cache - .lock() - .unwrap() - .get_mut(&(event_type.clone(), state_key.to_owned())) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Some(*short)); - } - let mut db_key = event_type.to_string().as_bytes().to_vec(); db_key.push(0xFF); db_key.extend_from_slice(state_key.as_bytes()); @@ -75,17 +63,6 @@ impl service::rooms::short::Data for KeyValueDatabase { }) .transpose()?; - if let Some(s) = short { - METRICS.record_lookup(lookup, FoundIn::Database); - - self.statekeyshort_cache - .lock() - .unwrap() - .insert((event_type.clone(), state_key.to_owned()), s); - } else { - METRICS.record_lookup(lookup, FoundIn::Nothing); - } - Ok(short) } @@ -94,50 +71,32 @@ impl service::rooms::short::Data for KeyValueDatabase { &self, event_type: &StateEventType, state_key: &str, - ) -> Result { - let lookup = Lookup::CreateStateKeyToShort; - - if let Some(short) = self - .statekeyshort_cache - .lock() - .unwrap() - .get_mut(&(event_type.clone(), state_key.to_owned())) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(*short); - } - + ) -> Result<(ShortStateKey, bool)> { let mut db_key = event_type.to_string().as_bytes().to_vec(); db_key.push(0xFF); db_key.extend_from_slice(state_key.as_bytes()); - let short = if let Some(shortstatekey) = + let (short, created) = if let Some(shortstatekey) = self.statekey_shortstatekey.get(&db_key)? { - METRICS.record_lookup(lookup, FoundIn::Database); - - utils::u64_from_bytes(&shortstatekey).map_err(|_| { - Error::bad_database("Invalid shortstatekey in db.") - })? + ( + utils::u64_from_bytes(&shortstatekey).map_err(|_| { + Error::bad_database("Invalid shortstatekey in db.") + })?, + false, + ) } else { - METRICS.record_lookup(lookup, FoundIn::Nothing); - let shortstatekey = services().globals.next_count()?; self.statekey_shortstatekey .insert(&db_key, &shortstatekey.to_be_bytes())?; self.shortstatekey_statekey .insert(&shortstatekey.to_be_bytes(), &db_key)?; - shortstatekey + (shortstatekey, true) }; let short = ShortStateKey::new(short); - self.statekeyshort_cache - .lock() - .unwrap() - .insert((event_type.clone(), state_key.to_owned()), short); - - Ok(short) + Ok((short, created)) } #[tracing::instrument(skip(self))] diff --git a/src/service.rs b/src/service.rs index d25dfe7c..f99cac9f 100644 --- a/src/service.rs +++ b/src/service.rs @@ -120,6 +120,14 @@ impl Services { { (100_000.0 * config.cache_capacity_modifier) as usize }, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + { + (100_000.0 * config.cache_capacity_modifier) as usize + }, ), state: rooms::state::Service { db, diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 2c18e668..dc8ab423 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -39,6 +39,8 @@ pub(crate) struct Service { db: &'static dyn Data, shorteventid_cache: Mutex>>, eventidshort_cache: Mutex>, + statekeyshort_cache: + Mutex>, } impl Service { @@ -46,6 +48,7 @@ impl Service { db: &'static dyn Data, shorteventid_cache_size: usize, eventidshort_cache_size: usize, + statekeyshort_cache_size: usize, ) -> Self { Self { db, @@ -55,6 +58,9 @@ impl Service { eventidshort_cache: Mutex::new(LruCache::new( eventidshort_cache_size, )), + statekeyshort_cache: Mutex::new(LruCache::new( + statekeyshort_cache_size, + )), } } @@ -92,7 +98,32 @@ impl Service { event_type: &StateEventType, state_key: &str, ) -> Result> { - self.db.get_shortstatekey(event_type, state_key) + let lookup = Lookup::StateKeyToShort; + + if let Some(short) = self + .statekeyshort_cache + .lock() + .unwrap() + .get_mut(&(event_type.clone(), state_key.to_owned())) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Some(*short)); + } + + let short = self.db.get_shortstatekey(event_type, state_key)?; + + if let Some(short) = short { + METRICS.record_lookup(lookup, FoundIn::Database); + + self.statekeyshort_cache + .lock() + .unwrap() + .insert((event_type.clone(), state_key.to_owned()), short); + } else { + METRICS.record_lookup(lookup, FoundIn::Nothing); + } + + Ok(short) } pub(crate) fn get_or_create_shortstatekey( @@ -100,7 +131,33 @@ impl Service { event_type: &StateEventType, state_key: &str, ) -> Result { - self.db.get_or_create_shortstatekey(event_type, state_key) + let lookup = Lookup::CreateStateKeyToShort; + + if let Some(short) = self + .statekeyshort_cache + .lock() + .unwrap() + .get_mut(&(event_type.clone(), state_key.to_owned())) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(*short); + } + + let (short, created) = + self.db.get_or_create_shortstatekey(event_type, state_key)?; + + if created { + METRICS.record_lookup(lookup, FoundIn::Nothing); + } else { + METRICS.record_lookup(lookup, FoundIn::Database); + } + + self.statekeyshort_cache + .lock() + .unwrap() + .insert((event_type.clone(), state_key.to_owned()), short); + + Ok(short) } pub(crate) fn get_eventid_from_short( diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index 7b3fe19e..b21588be 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -18,11 +18,12 @@ pub(crate) trait Data: Send + Sync { state_key: &str, ) -> Result>; + /// The returned bool indicates whether it was created fn get_or_create_shortstatekey( &self, event_type: &StateEventType, state_key: &str, - ) -> Result; + ) -> Result<(ShortStateKey, bool)>; fn get_eventid_from_short( &self, From d3b62e598d891cbf4153453e2d00bd471fe2e6de Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 13:38:12 -0700 Subject: [PATCH 424/617] move shortstatekey_cache to service --- src/database.rs | 15 +-------------- src/database/key_value/rooms/short.rs | 21 +-------------------- src/service.rs | 8 ++++++++ src/service/rooms/short.rs | 26 +++++++++++++++++++++++++- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/database.rs b/src/database.rs index 4979d1e7..0b9d49a0 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,11 +7,8 @@ use std::{ sync::{Arc, Mutex, RwLock}, }; -use lru_cache::LruCache; use ruma::{ - events::{ - push_rules::PushRulesEvent, GlobalAccountDataEventType, StateEventType, - }, + events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, push::Ruleset, EventId, OwnedRoomId, OwnedUserId, RoomId, UserId, }; @@ -236,8 +233,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) shortstatekey_cache: - Mutex>, pub(super) our_real_users_cache: RwLock>>>, pub(super) appservice_in_room_cache: @@ -461,14 +456,6 @@ impl KeyValueDatabase { global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - shortstatekey_cache: Mutex::new(LruCache::new( - (100_000.0 * config.cache_capacity_modifier) as usize, - )), our_real_users_cache: RwLock::new(HashMap::new()), appservice_in_room_cache: RwLock::new(HashMap::new()), lasttimelinecount_cache: Mutex::new(HashMap::new()), diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index ec3fcf8d..ee137733 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -4,7 +4,6 @@ use ruma::{events::StateEventType, EventId, RoomId}; use crate::{ database::KeyValueDatabase, - observability::{FoundIn, Lookup, METRICS}, service::{ self, rooms::short::{ @@ -130,15 +129,6 @@ impl service::rooms::short::Data for KeyValueDatabase { &self, shortstatekey: ShortStateKey, ) -> Result<(StateEventType, String)> { - let lookup = Lookup::ShortToStateKey; - - if let Some(id) = - self.shortstatekey_cache.lock().unwrap().get_mut(&shortstatekey) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(id.clone()); - } - let bytes = self .shortstatekey_statekey .get(&shortstatekey.get().to_be_bytes())? @@ -168,16 +158,7 @@ impl service::rooms::short::Data for KeyValueDatabase { ) })?; - let result = (event_type, state_key); - - METRICS.record_lookup(lookup, FoundIn::Database); - - self.shortstatekey_cache - .lock() - .unwrap() - .insert(shortstatekey, result.clone()); - - Ok(result) + Ok((event_type, state_key)) } /// Returns `(shortstatehash, already_existed)` diff --git a/src/service.rs b/src/service.rs index f99cac9f..9abbfeaf 100644 --- a/src/service.rs +++ b/src/service.rs @@ -128,6 +128,14 @@ impl Services { { (100_000.0 * config.cache_capacity_modifier) as usize }, + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + { + (100_000.0 * config.cache_capacity_modifier) as usize + }, ), state: rooms::state::Service { db, diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index dc8ab423..917e5a85 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -41,6 +41,8 @@ pub(crate) struct Service { eventidshort_cache: Mutex>, statekeyshort_cache: Mutex>, + shortstatekey_cache: + Mutex>, } impl Service { @@ -49,6 +51,7 @@ impl Service { shorteventid_cache_size: usize, eventidshort_cache_size: usize, statekeyshort_cache_size: usize, + shortstatekey_cache_size: usize, ) -> Self { Self { db, @@ -61,6 +64,9 @@ impl Service { statekeyshort_cache: Mutex::new(LruCache::new( statekeyshort_cache_size, )), + shortstatekey_cache: Mutex::new(LruCache::new( + shortstatekey_cache_size, + )), } } @@ -189,7 +195,25 @@ impl Service { &self, shortstatekey: ShortStateKey, ) -> Result<(StateEventType, String)> { - self.db.get_statekey_from_short(shortstatekey) + let lookup = Lookup::ShortToStateKey; + + if let Some(id) = + self.shortstatekey_cache.lock().unwrap().get_mut(&shortstatekey) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(id.clone()); + } + + let x = self.db.get_statekey_from_short(shortstatekey)?; + + METRICS.record_lookup(lookup, FoundIn::Database); + + self.shortstatekey_cache + .lock() + .unwrap() + .insert(shortstatekey, x.clone()); + + Ok(x) } /// Returns `(shortstatehash, already_existed)` From 9d62865b28872456103d6b0a28c293cfe3e886c9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 15:21:07 -0700 Subject: [PATCH 425/617] move our_real_users_cache to service --- src/database.rs | 5 +- src/database/key_value/rooms/state_cache.rs | 35 +++----------- src/service.rs | 4 +- src/service/rooms/state_cache.rs | 51 ++++++++++++++++++--- src/service/rooms/state_cache/data.rs | 8 ++-- 5 files changed, 56 insertions(+), 47 deletions(-) diff --git a/src/database.rs b/src/database.rs index 0b9d49a0..bf22a0a3 100644 --- a/src/database.rs +++ b/src/database.rs @@ -10,7 +10,7 @@ use std::{ use ruma::{ events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, push::Ruleset, - EventId, OwnedRoomId, OwnedUserId, RoomId, UserId, + EventId, OwnedRoomId, RoomId, UserId, }; use tracing::{debug, error, info, info_span, warn, Instrument}; @@ -233,8 +233,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) our_real_users_cache: - RwLock>>>, pub(super) appservice_in_room_cache: RwLock>>, pub(super) lasttimelinecount_cache: Mutex>, @@ -456,7 +454,6 @@ impl KeyValueDatabase { global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - our_real_users_cache: RwLock::new(HashMap::new()), appservice_in_room_cache: RwLock::new(HashMap::new()), lasttimelinecount_cache: Mutex::new(HashMap::new()), }; diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 115511eb..78ee1fb7 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, sync::Arc}; +use std::collections::HashSet; use ruma::{ events::{AnyStrippedStateEvent, AnySyncStateEvent}, @@ -101,7 +101,10 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { Ok(()) } - fn update_joined_count(&self, room_id: &RoomId) -> Result<()> { + fn update_joined_count( + &self, + room_id: &RoomId, + ) -> Result> { let mut joinedcount = 0_u64; let mut invitedcount = 0_u64; let mut joined_servers = HashSet::new(); @@ -129,11 +132,6 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { self.roomid_invitedcount .insert(room_id.as_bytes(), &invitedcount.to_be_bytes())?; - self.our_real_users_cache - .write() - .unwrap() - .insert(room_id.to_owned(), Arc::new(real_users)); - for old_joined_server in self.room_servers(room_id).filter_map(Result::ok) { @@ -168,28 +166,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { self.appservice_in_room_cache.write().unwrap().remove(room_id); - Ok(()) - } - - #[tracing::instrument(skip(self))] - fn get_our_real_users( - &self, - room_id: &RoomId, - ) -> Result>> { - let lookup = Lookup::OurRealUsers; - - let maybe = - self.our_real_users_cache.read().unwrap().get(room_id).cloned(); - if let Some(users) = maybe { - METRICS.record_lookup(lookup, FoundIn::Cache); - Ok(users) - } else { - self.update_joined_count(room_id)?; - METRICS.record_lookup(lookup, FoundIn::Database); - Ok(Arc::clone( - self.our_real_users_cache.read().unwrap().get(room_id).unwrap(), - )) - } + Ok(real_users) } #[tracing::instrument( diff --git a/src/service.rs b/src/service.rs index 9abbfeaf..5ba3aa3d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -159,9 +159,7 @@ impl Services { (100.0 * config.cache_capacity_modifier) as usize, )), }, - state_cache: rooms::state_cache::Service { - db, - }, + state_cache: rooms::state_cache::Service::new(db), state_compressor: rooms::state_compressor::Service { db, #[allow( diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 980998a7..f3e6144c 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -1,4 +1,7 @@ -use std::{collections::HashSet, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + sync::{Arc, RwLock}, +}; use ruma::{ events::{ @@ -12,19 +15,32 @@ use ruma::{ }; use tracing::warn; -use crate::{service::appservice::RegistrationInfo, services, Error, Result}; +use crate::{ + observability::{FoundIn, Lookup, METRICS}, + service::appservice::RegistrationInfo, + services, Error, Result, +}; mod data; pub(crate) use data::Data; pub(crate) struct Service { - pub(crate) db: &'static dyn Data, + db: &'static dyn Data, + our_real_users_cache: + RwLock>>>, } type RoomsLeft = (OwnedRoomId, Vec>); impl Service { + pub(crate) fn new(db: &'static dyn Data) -> Self { + Self { + db, + our_real_users_cache: RwLock::new(HashMap::new()), + } + } + /// Update current membership data. #[tracing::instrument(skip(self, last_state))] pub(crate) fn update_membership( @@ -283,8 +299,18 @@ impl Service { } #[tracing::instrument(skip(self, room_id))] - pub(crate) fn update_joined_count(&self, room_id: &RoomId) -> Result<()> { - self.db.update_joined_count(room_id) + pub(crate) fn update_joined_count( + &self, + room_id: &RoomId, + ) -> Result>> { + let our_real_users = Arc::new(self.db.update_joined_count(room_id)?); + + self.our_real_users_cache + .write() + .unwrap() + .insert(room_id.to_owned(), our_real_users.clone()); + + Ok(our_real_users) } #[tracing::instrument(skip(self, room_id))] @@ -292,7 +318,20 @@ impl Service { &self, room_id: &RoomId, ) -> Result>> { - self.db.get_our_real_users(room_id) + let lookup = Lookup::OurRealUsers; + + if let Some(our_real_users) = + self.our_real_users_cache.read().unwrap().get(room_id).cloned() + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(our_real_users); + } + + let our_real_users = self.update_joined_count(room_id)?; + + METRICS.record_lookup(lookup, FoundIn::Database); + + Ok(our_real_users) } #[tracing::instrument(skip(self, room_id, appservice))] diff --git a/src/service/rooms/state_cache/data.rs b/src/service/rooms/state_cache/data.rs index 5369e42f..a265f99c 100644 --- a/src/service/rooms/state_cache/data.rs +++ b/src/service/rooms/state_cache/data.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, sync::Arc}; +use std::collections::HashSet; use ruma::{ events::{AnyStrippedStateEvent, AnySyncStateEvent}, @@ -23,12 +23,10 @@ pub(crate) trait Data: Send + Sync { ) -> Result<()>; fn mark_as_left(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>; - fn update_joined_count(&self, room_id: &RoomId) -> Result<()>; - - fn get_our_real_users( + fn update_joined_count( &self, room_id: &RoomId, - ) -> Result>>; + ) -> Result>; fn appservice_in_room( &self, From 107f4521e0dd622d49f8aad9d42010b62a1398b7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 15:41:17 -0700 Subject: [PATCH 426/617] move appservice_in_room_cache to service --- src/database.rs | 5 +-- src/database/key_value/rooms/state_cache.rs | 44 +++++---------------- src/service/rooms/state_cache.rs | 30 +++++++++++++- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/database.rs b/src/database.rs index bf22a0a3..66025b90 100644 --- a/src/database.rs +++ b/src/database.rs @@ -4,7 +4,7 @@ use std::{ io::Write, mem::size_of, path::Path, - sync::{Arc, Mutex, RwLock}, + sync::{Arc, Mutex}, }; use ruma::{ @@ -233,8 +233,6 @@ pub(crate) struct KeyValueDatabase { pub(super) senderkey_pusher: Arc, // Uncategorized trees - pub(super) appservice_in_room_cache: - RwLock>>, pub(super) lasttimelinecount_cache: Mutex>, } @@ -454,7 +452,6 @@ impl KeyValueDatabase { global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - appservice_in_room_cache: RwLock::new(HashMap::new()), lasttimelinecount_cache: Mutex::new(HashMap::new()), }; diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 78ee1fb7..2c2e215f 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -8,7 +8,6 @@ use ruma::{ use crate::{ database::KeyValueDatabase, - observability::{FoundIn, Lookup, METRICS}, service::{self, appservice::RegistrationInfo}, services, utils, Error, Result, }; @@ -164,8 +163,6 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { self.serverroomids.insert(&serverroom_id, &[])?; } - self.appservice_in_room_cache.write().unwrap().remove(room_id); - Ok(real_users) } @@ -178,44 +175,21 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { room_id: &RoomId, appservice: &RegistrationInfo, ) -> Result { - let lookup = Lookup::AppserviceInRoom; + let bridge_user_id = UserId::parse_with_server_name( + appservice.registration.sender_localpart.as_str(), + services().globals.server_name(), + ) + .ok(); - let maybe = self - .appservice_in_room_cache - .read() - .unwrap() - .get(room_id) - .and_then(|map| map.get(&appservice.registration.id)) - .copied(); - - if let Some(b) = maybe { - METRICS.record_lookup(lookup, FoundIn::Cache); - Ok(b) - } else { - let bridge_user_id = UserId::parse_with_server_name( - appservice.registration.sender_localpart.as_str(), - services().globals.server_name(), - ) - .ok(); - - let in_room = bridge_user_id.map_or(false, |id| { - self.is_joined(&id, room_id).unwrap_or(false) - }) || self.room_members(room_id).any(|userid| { + let in_room = bridge_user_id + .map_or(false, |id| self.is_joined(&id, room_id).unwrap_or(false)) + || self.room_members(room_id).any(|userid| { userid.map_or(false, |userid| { appservice.users.is_match(userid.as_str()) }) }); - METRICS.record_lookup(lookup, FoundIn::Database); - self.appservice_in_room_cache - .write() - .unwrap() - .entry(room_id.to_owned()) - .or_default() - .insert(appservice.registration.id.clone(), in_room); - - Ok(in_room) - } + Ok(in_room) } /// Makes a user forget a room. diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index f3e6144c..9cf264a9 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -27,6 +27,8 @@ pub(crate) use data::Data; pub(crate) struct Service { db: &'static dyn Data, + appservice_in_room_cache: + RwLock>>, our_real_users_cache: RwLock>>>, } @@ -38,6 +40,7 @@ impl Service { Self { db, our_real_users_cache: RwLock::new(HashMap::new()), + appservice_in_room_cache: RwLock::new(HashMap::new()), } } @@ -309,6 +312,7 @@ impl Service { .write() .unwrap() .insert(room_id.to_owned(), our_real_users.clone()); + self.appservice_in_room_cache.write().unwrap().remove(room_id); Ok(our_real_users) } @@ -340,7 +344,31 @@ impl Service { room_id: &RoomId, appservice: &RegistrationInfo, ) -> Result { - self.db.appservice_in_room(room_id, appservice) + let lookup = Lookup::AppserviceInRoom; + + if let Some(in_room) = self + .appservice_in_room_cache + .read() + .unwrap() + .get(room_id) + .and_then(|map| map.get(&appservice.registration.id)) + .copied() + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(in_room); + } + + let in_room = self.db.appservice_in_room(room_id, appservice)?; + + METRICS.record_lookup(lookup, FoundIn::Database); + self.appservice_in_room_cache + .write() + .unwrap() + .entry(room_id.to_owned()) + .or_default() + .insert(appservice.registration.id.clone(), in_room); + + Ok(in_room) } /// Makes a user forget a room. From ce7efc1effa4e2ae6c47760cbd4b74e18d0b26b2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 14:40:54 -0700 Subject: [PATCH 427/617] move lasttimelinecount_cache to service --- src/database.rs | 8 +--- src/database/key_value/rooms/timeline.rs | 49 +----------------------- src/service/rooms/timeline.rs | 42 ++++++++++++++++++-- src/service/rooms/timeline/data.rs | 7 ---- 4 files changed, 41 insertions(+), 65 deletions(-) diff --git a/src/database.rs b/src/database.rs index 66025b90..5205b9ca 100644 --- a/src/database.rs +++ b/src/database.rs @@ -4,7 +4,7 @@ use std::{ io::Write, mem::size_of, path::Path, - sync::{Arc, Mutex}, + sync::Arc, }; use ruma::{ @@ -21,7 +21,6 @@ use crate::{ rooms::{ short::{ShortEventId, ShortStateHash, ShortStateKey}, state_compressor::CompressedStateEvent, - timeline::PduCount, }, }, services, utils, Config, Error, Result, @@ -231,9 +230,6 @@ pub(crate) struct KeyValueDatabase { // Trees "owned" by `self::key_value::pusher` pub(super) senderkey_pusher: Arc, - - // Uncategorized trees - pub(super) lasttimelinecount_cache: Mutex>, } impl KeyValueDatabase { @@ -451,8 +447,6 @@ impl KeyValueDatabase { senderkey_pusher: builder.open_tree("senderkey_pusher")?, global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, - - lasttimelinecount_cache: Mutex::new(HashMap::new()), }; Ok(db) diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index 71b75652..b46e9bfe 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -1,59 +1,18 @@ -use std::{collections::hash_map, mem::size_of, sync::Arc}; +use std::{mem::size_of, sync::Arc}; use ruma::{ api::client::error::ErrorKind, CanonicalJsonObject, EventId, OwnedUserId, RoomId, UserId, }; use service::rooms::timeline::PduCount; -use tracing::error; use crate::{ database::KeyValueDatabase, - observability::{FoundIn, Lookup, METRICS}, service::{self, rooms::timeline::PduId}, services, utils, Error, PduEvent, Result, }; impl service::rooms::timeline::Data for KeyValueDatabase { - #[tracing::instrument(skip(self))] - fn last_timeline_count( - &self, - sender_user: &UserId, - room_id: &RoomId, - ) -> Result { - let lookup = Lookup::LastTimelineCount; - - match self - .lasttimelinecount_cache - .lock() - .unwrap() - .entry(room_id.to_owned()) - { - hash_map::Entry::Vacant(v) => { - if let Some(last_count) = self - .pdus_until(sender_user, room_id, PduCount::MAX)? - .find_map(|x| match x { - Ok(x) => Some(x), - Err(error) => { - error!(%error, "Bad pdu in pdus_since"); - None - } - }) - { - METRICS.record_lookup(lookup, FoundIn::Database); - Ok(*v.insert(last_count.0)) - } else { - METRICS.record_lookup(lookup, FoundIn::Nothing); - Ok(PduCount::Normal(0)) - } - } - hash_map::Entry::Occupied(o) => { - METRICS.record_lookup(lookup, FoundIn::Cache); - Ok(*o.get()) - } - } - } - /// Returns the `count` of this pdu's id. fn get_pdu_count(&self, event_id: &EventId) -> Result> { self.eventid_pduid @@ -180,7 +139,6 @@ impl service::rooms::timeline::Data for KeyValueDatabase { pdu_id: &PduId, pdu: &PduEvent, json: &CanonicalJsonObject, - count: u64, ) -> Result<()> { self.pduid_pdu.insert( pdu_id.as_bytes(), @@ -188,11 +146,6 @@ impl service::rooms::timeline::Data for KeyValueDatabase { .expect("CanonicalJsonObject is always a valid"), )?; - self.lasttimelinecount_cache - .lock() - .unwrap() - .insert(pdu.room_id.clone(), PduCount::Normal(count)); - self.eventid_pduid .insert(pdu.event_id.as_bytes(), pdu_id.as_bytes())?; self.eventid_outlierpdu.remove(pdu.event_id.as_bytes())?; diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 341fb1c4..d70fd0c2 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -1,6 +1,6 @@ use std::{ cmp::Ordering, - collections::{BTreeMap, HashSet}, + collections::{hash_map, BTreeMap, HashMap, HashSet}, sync::{Arc, Mutex}, }; @@ -115,6 +115,7 @@ impl Ord for PduCount { pub(crate) struct Service { db: &'static dyn Data, pdu_cache: Mutex>>, + lasttimelinecount_cache: Mutex>, } impl Service { @@ -125,6 +126,7 @@ impl Service { Self { db, pdu_cache: Mutex::new(LruCache::new(pdu_cache_capacity)), + lasttimelinecount_cache: Mutex::new(HashMap::new()), } } @@ -145,7 +147,37 @@ impl Service { sender_user: &UserId, room_id: &RoomId, ) -> Result { - self.db.last_timeline_count(sender_user, room_id) + let lookup = Lookup::LastTimelineCount; + + match self + .lasttimelinecount_cache + .lock() + .unwrap() + .entry(room_id.to_owned()) + { + hash_map::Entry::Vacant(v) => { + if let Some(last_count) = self + .pdus_until(sender_user, room_id, PduCount::MAX)? + .find_map(|x| match x { + Ok(x) => Some(x), + Err(error) => { + error!(%error, "Bad pdu in pdus_since"); + None + } + }) + { + METRICS.record_lookup(lookup, FoundIn::Database); + Ok(*v.insert(last_count.0)) + } else { + METRICS.record_lookup(lookup, FoundIn::Nothing); + Ok(PduCount::Normal(0)) + } + } + hash_map::Entry::Occupied(o) => { + METRICS.record_lookup(lookup, FoundIn::Cache); + Ok(*o.get()) + } + } } /// Returns the `count` of this pdu's id. @@ -334,7 +366,11 @@ impl Service { let pdu_id = PduId::new(pdu_id); // Insert pdu - self.db.append_pdu(&pdu_id, pdu, &pdu_json, count2)?; + self.db.append_pdu(&pdu_id, pdu, &pdu_json)?; + self.lasttimelinecount_cache + .lock() + .unwrap() + .insert(pdu.room_id.clone(), PduCount::Normal(count2)); drop(insert_token); diff --git a/src/service/rooms/timeline/data.rs b/src/service/rooms/timeline/data.rs index a9a85760..49b0c8a2 100644 --- a/src/service/rooms/timeline/data.rs +++ b/src/service/rooms/timeline/data.rs @@ -6,12 +6,6 @@ use super::PduCount; use crate::{service::rooms::timeline::PduId, PduEvent, Result}; pub(crate) trait Data: Send + Sync { - fn last_timeline_count( - &self, - sender_user: &UserId, - room_id: &RoomId, - ) -> Result; - /// Returns the `count` of this pdu's id. fn get_pdu_count(&self, event_id: &EventId) -> Result>; @@ -60,7 +54,6 @@ pub(crate) trait Data: Send + Sync { pdu_id: &PduId, pdu: &PduEvent, json: &CanonicalJsonObject, - count: u64, ) -> Result<()>; // Adds a new pdu to the backfilled timeline From b93c39ee93bf264bef848b51ba06d1fa9b3910a0 Mon Sep 17 00:00:00 2001 From: K900 Date: Thu, 24 Oct 2024 14:46:44 +0000 Subject: [PATCH 428/617] fix: use non-alias name in nixos module --- nix/modules/default/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 3bc86c56..d2efa327 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -18,11 +18,11 @@ in options.services.grapevine = { enable = lib.mkEnableOption "grapevine"; package = lib.mkPackageOption - inputs.self.packages.${pkgs.hostPlatform.system} + inputs.self.packages.${pkgs.stdenv.hostPlatform.system} "grapevine" { default = "default"; - pkgsText = "inputs.grapevine.packages.\${pkgs.hostPlatform.system}"; + pkgsText = "inputs.grapevine.packages.\${pkgs.stdenv.hostPlatform.system}"; }; settings = lib.mkOption { From b03c2a15b39188c8cc7164682d9ccf1a2be4fdd6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 10 Oct 2024 14:30:13 -0700 Subject: [PATCH 429/617] add observability infrastructure for cli subcmds --- src/cli.rs | 34 +++++++++++++++++++++++++++++++++- src/config.rs | 26 ++++++++++++++++++++------ src/config/env_filter_clone.rs | 17 +++++++++++++++-- src/error.rs | 3 +++ src/observability.rs | 26 +++++++++++++++++++++++++- 5 files changed, 96 insertions(+), 10 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index c997ad67..f697d258 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,7 +7,10 @@ use std::path::PathBuf; use clap::{Parser, Subcommand}; -use crate::error; +use crate::{ + config::{default_tracing_filter, EnvFilterClone, LogFormat}, + error, observability, +}; mod serve; @@ -51,6 +54,21 @@ pub(crate) struct ConfigArg { pub(crate) config: Option, } +/// Observability arguments for CLI subcommands +#[derive(clap::Args)] +struct ObservabilityArgs { + /// Log format + #[clap(long, default_value_t = LogFormat::Full)] + log_format: LogFormat, + + /// Log filter + /// + /// For information about the syntax, see here: + /// + #[clap(long, default_value_t = default_tracing_filter())] + log_filter: EnvFilterClone, +} + #[derive(clap::Args)] pub(crate) struct ServeArgs { #[clap(flatten)] @@ -59,9 +77,23 @@ pub(crate) struct ServeArgs { impl Args { pub(crate) async fn run(self) -> Result<(), error::Main> { + if let Some((format, filter)) = self.command.cli_observability_args() { + observability::init_for_cli(format, filter.into())?; + } + match self.command { Command::Serve(args) => serve::run(args).await?, } Ok(()) } } + +impl Command { + fn cli_observability_args(&self) -> Option<(LogFormat, EnvFilterClone)> { + // All subcommands other than `serve` should return `Some`. Keep these + // match arms sorted by the enum variant name. + match self { + Command::Serve(_) => None, + } + } +} diff --git a/src/config.rs b/src/config.rs index c53f1dd5..bf08c7c1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -180,20 +180,34 @@ impl Display for ListenConfig { } } -#[derive(Copy, Clone, Default, Debug, Deserialize)] +#[derive(Copy, Clone, Default, Debug, Deserialize, clap::ValueEnum)] #[serde(rename_all = "snake_case")] pub(crate) enum LogFormat { - /// Use the [`tracing_subscriber::fmt::format::Pretty`] formatter + /// Multiple lines per event, includes all information Pretty, - /// Use the [`tracing_subscriber::fmt::format::Full`] formatter + + /// One line per event, includes most information #[default] Full, - /// Use the [`tracing_subscriber::fmt::format::Compact`] formatter + + /// One line per event, includes less information Compact, - /// Use the [`tracing_subscriber::fmt::format::Json`] formatter + + /// One JSON object per line per event, includes most information Json, } +impl Display for LogFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogFormat::Pretty => write!(f, "pretty"), + LogFormat::Full => write!(f, "full"), + LogFormat::Compact => write!(f, "compact"), + LogFormat::Json => write!(f, "json"), + } + } +} + #[derive(Clone, Debug, Deserialize)] #[serde(default)] pub(crate) struct TurnConfig { @@ -404,7 +418,7 @@ fn default_max_request_size() -> u32 { 20 * 1024 * 1024 } -fn default_tracing_filter() -> EnvFilterClone { +pub(crate) fn default_tracing_filter() -> EnvFilterClone { "info,ruma_state_res=warn" .parse() .expect("hardcoded env filter should be valid") diff --git a/src/config/env_filter_clone.rs b/src/config/env_filter_clone.rs index 183c827f..9e8983b7 100644 --- a/src/config/env_filter_clone.rs +++ b/src/config/env_filter_clone.rs @@ -5,7 +5,7 @@ //! [0]: https://github.com/tokio-rs/tracing/pull/2956 #![warn(missing_docs, clippy::missing_docs_in_private_items)] -use std::str::FromStr; +use std::{fmt, str::FromStr}; use serde::{de, Deserialize, Deserializer}; use tracing_subscriber::EnvFilter; @@ -14,7 +14,7 @@ use tracing_subscriber::EnvFilter; /// /// Use [`FromStr`] or [`Deserialize`] to construct this type, then [`From`] or /// [`Into`] to convert it into an [`EnvFilter`] when needed. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct EnvFilterClone(String); impl FromStr for EnvFilterClone { @@ -26,6 +26,12 @@ impl FromStr for EnvFilterClone { } } +impl fmt::Display for EnvFilterClone { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + impl From<&EnvFilterClone> for EnvFilter { fn from(other: &EnvFilterClone) -> Self { EnvFilter::from_str(&other.0) @@ -33,6 +39,13 @@ impl From<&EnvFilterClone> for EnvFilter { } } +impl From for EnvFilter { + fn from(other: EnvFilterClone) -> Self { + EnvFilter::from_str(&other.0) + .expect("env filter syntax should have been validated already") + } +} + impl<'de> Deserialize<'de> for EnvFilterClone { fn deserialize(deserializer: D) -> Result where diff --git a/src/error.rs b/src/error.rs index 72bcd337..e0e53436 100644 --- a/src/error.rs +++ b/src/error.rs @@ -42,6 +42,9 @@ impl fmt::Display for DisplayWithSources<'_> { pub(crate) enum Main { #[error(transparent)] ServeCommand(#[from] ServeCommand), + + #[error("failed to install global default tracing subscriber")] + SetSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError), } /// Errors returned from the `serve` CLI subcommand. diff --git a/src/observability.rs b/src/observability.rs index 059ed436..f0baee1c 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -18,7 +18,7 @@ use opentelemetry_sdk::{ }; use strum::{AsRefStr, IntoStaticStr}; use tokio::time::Instant; -use tracing::Span; +use tracing::{subscriber::SetGlobalDefaultError, Span}; use tracing_flame::{FlameLayer, FlushGuard}; use tracing_opentelemetry::OtelData; use tracing_subscriber::{ @@ -469,3 +469,27 @@ pub(crate) async fn traceresponse_layer(req: Request, next: Next) -> Response { resp } + +/// Set up observability for CLI-oriented subcommands. +/// +/// Tracing spans and events will be sent to `stderr`. +pub(crate) fn init_for_cli( + log_format: LogFormat, + env_filter: EnvFilter, +) -> Result<(), SetGlobalDefaultError> { + let log_layer = + tracing_subscriber::fmt::Layer::new().with_writer(std::io::stderr); + + let log_layer = match log_format { + LogFormat::Pretty => log_layer.pretty().boxed(), + LogFormat::Full => log_layer.boxed(), + LogFormat::Compact => log_layer.compact().boxed(), + LogFormat::Json => log_layer.json().boxed(), + }; + + let log_layer = log_layer.with_filter(env_filter); + + let subscriber = Registry::default().with(log_layer); + + tracing::subscriber::set_global_default(subscriber).map_err(Into::into) +} From 86481fd6511902e82002c39e9c59dae4216b902c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 22 Oct 2024 12:17:03 -0700 Subject: [PATCH 430/617] make reload_handles optional for creating Services This will be useful for instantiating services in CLI subcommands, which have different requirements around observeability. --- src/cli/serve.rs | 2 +- src/service.rs | 2 +- src/service/admin.rs | 6 +++++- src/service/globals.rs | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 0991066b..8a2b57b9 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -80,7 +80,7 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .map_err(Error::DatabaseError)?, )); - Services::build(db, config, reload_handles) + Services::build(db, config, Some(reload_handles)) .map_err(Error::InitializeServices)? .install(); diff --git a/src/service.rs b/src/service.rs index 5ba3aa3d..4bea1998 100644 --- a/src/service.rs +++ b/src/service.rs @@ -62,7 +62,7 @@ impl Services { >( db: &'static D, config: Config, - reload_handles: FilterReloadHandles, + reload_handles: Option, ) -> Result { Ok(Self { appservice: appservice::Service::build(db)?, diff --git a/src/service/admin.rs b/src/service/admin.rs index 8fd0abdf..e8b9beae 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1134,7 +1134,11 @@ impl Service { backend, filter, } => { - let handles = &services().globals.reload_handles; + let Some(handles) = &services().globals.reload_handles else { + return Ok(RoomMessageEventContent::text_plain( + "Reloading filters is disabled", + )); + }; let handle = match backend { TracingBackend::Log => &handles.log, TracingBackend::Flame => &handles.flame, diff --git a/src/service/globals.rs b/src/service/globals.rs index 1026135c..f3ac9a8e 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -66,7 +66,7 @@ pub(crate) mod marker { pub(crate) struct Service { pub(crate) db: &'static dyn Data, - pub(crate) reload_handles: FilterReloadHandles, + pub(crate) reload_handles: Option, // actual_destination, host pub(crate) actual_destination_cache: Arc>, @@ -200,7 +200,7 @@ impl Service { pub(crate) fn load( db: &'static dyn Data, config: Config, - reload_handles: FilterReloadHandles, + reload_handles: Option, ) -> Result { let keypair = db.load_keypair(); From 99f3e2aecd01d80639115b2e7fc6fa8357719235 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 15 Sep 2024 15:12:49 +0000 Subject: [PATCH 431/617] Refactor server listener spawning --- src/cli/serve.rs | 188 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 53 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 8a2b57b9..e0612576 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -1,22 +1,26 @@ use std::{ - collections::HashSet, future::Future, net::SocketAddr, sync::atomic, - time::Duration, + collections::HashSet, convert::Infallible, future::Future, net::SocketAddr, + sync::atomic, time::Duration, }; use axum::{ extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, response::IntoResponse, - routing::{any, get, on, MethodFilter}, + routing::{any, get, on, IntoMakeService, MethodFilter, Route}, Router, }; use axum_server::{ - bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle, + accept::Accept, + bind, + service::SendService, + tls_rustls::{RustlsAcceptor, RustlsConfig}, + Handle as ServerHandle, Server, }; -use futures_util::FutureExt; use http::{ header::{self, HeaderName}, Method, StatusCode, Uri, }; +use hyper::body::Incoming; use ruma::api::{ client::{ error::{Error as RumaError, ErrorBody, ErrorKind}, @@ -25,8 +29,13 @@ use ruma::api::{ federation::discovery::get_server_version, IncomingRequest, }; -use tokio::{signal, task::JoinSet}; -use tower::ServiceBuilder; +use tokio::{ + io::{AsyncRead, AsyncWrite}, + net::TcpStream, + signal, + task::JoinSet, +}; +use tower::{Layer, Service, ServiceBuilder}; use tower_http::{ cors::{self, CorsLayer}, trace::TraceLayer, @@ -42,7 +51,7 @@ use crate::{ server_server::{self, AllowLoopbackRequests, LogRequestError}, well_known, }, - config::{self, Config, ListenComponent, ListenTransport}, + config::{self, Config, ListenComponent, ListenConfig, ListenTransport}, database::KeyValueDatabase, error, observability, services, set_application_state, utils::{ @@ -124,6 +133,120 @@ async fn federation_self_test() -> Result<()> { Ok(()) } +struct ServerSpawner<'cfg, M> { + config: &'cfg Config, + middlewares: M, + + tls_config: Option, + servers: JoinSet<(ListenConfig, std::io::Result<()>)>, + handles: Vec, +} + +impl<'cfg, M> ServerSpawner<'cfg, M> +where + M: Layer + Clone + Send + 'static, + M::Service: Service< + axum::extract::Request, + Response = axum::response::Response, + Error = Infallible, + > + Clone + + Send + + 'static, + >::Future: Send + 'static, +{ + async fn new( + config: &'cfg Config, + middlewares: M, + ) -> Result { + let tls_config = if let Some(tls) = &config.tls { + Some( + RustlsConfig::from_pem_file(&tls.certs, &tls.key) + .await + .map_err(|err| error::Serve::LoadCerts { + certs: tls.certs.clone(), + key: tls.key.clone(), + err, + })?, + ) + } else { + None + }; + + Ok(Self { + config, + middlewares, + tls_config, + servers: JoinSet::new(), + handles: Vec::new(), + }) + } + + /// Returns a function that transforms a lower-layer acceptor into a TLS + /// acceptor. + fn tls_acceptor_factory( + &self, + listen: &ListenConfig, + ) -> Result RustlsAcceptor, error::Serve> { + let config = self + .tls_config + .clone() + .ok_or_else(|| error::Serve::NoTlsCerts(listen.clone()))?; + + Ok(|inner| RustlsAcceptor::new(config).acceptor(inner)) + } + + fn spawn_server_inner( + &mut self, + listen: ListenConfig, + server: Server, + app: IntoMakeService, + ) where + A: Accept + Clone + Send + Sync + 'static, + A::Stream: AsyncRead + AsyncWrite + Unpin + Send + Sync, + A::Service: SendService> + Send, + A::Future: Send, + { + let handle = ServerHandle::new(); + let server = server.handle(handle.clone()).serve(app); + self.servers.spawn(async move { + let result = server.await; + + (listen, result) + }); + self.handles.push(handle); + } + + fn spawn_server( + &mut self, + listen: ListenConfig, + ) -> Result<(), error::Serve> { + let app = routes(self.config, &listen.components) + .layer(self.middlewares.clone()) + .into_make_service(); + + match listen.transport { + ListenTransport::Tcp { + address, + port, + tls, + } => { + let addr = SocketAddr::from((address, port)); + let server = bind(addr); + + if tls { + let server = + server.map(self.tls_acceptor_factory(&listen)?); + self.spawn_server_inner(listen, server, app); + } else { + self.spawn_server_inner(listen, server, app); + } + + Ok(()) + } + } + } +} + #[allow(clippy::too_many_lines)] async fn run_server() -> Result<(), error::Serve> { use error::Serve as Error; @@ -200,20 +323,7 @@ async fn run_server() -> Result<(), error::Serve> { .layer(axum::middleware::from_fn(observability::http_metrics_layer)) .layer(axum::middleware::from_fn(observability::traceresponse_layer)); - let mut handles = Vec::new(); - let mut servers = JoinSet::new(); - - let tls_config = if let Some(tls) = &config.tls { - Some(RustlsConfig::from_pem_file(&tls.certs, &tls.key).await.map_err( - |err| Error::LoadCerts { - certs: tls.certs.clone(), - key: tls.key.clone(), - err, - }, - )?) - } else { - None - }; + let mut spawner = ServerSpawner::new(config, middlewares).await?; if config.listen.is_empty() { return Err(Error::NoListeners); @@ -221,38 +331,10 @@ async fn run_server() -> Result<(), error::Serve> { for listen in &config.listen { info!(listener = %listen, "Listening for incoming traffic"); - let app = routes(config, &listen.components) - .layer(middlewares.clone()) - .into_make_service(); - - match listen.transport { - ListenTransport::Tcp { - address, - port, - tls, - } => { - let addr = SocketAddr::from((address, port)); - let handle = ServerHandle::new(); - handles.push(handle.clone()); - let server = if tls { - let tls_config = tls_config - .clone() - .ok_or_else(|| Error::NoTlsCerts(listen.clone()))?; - bind_rustls(addr, tls_config) - .handle(handle) - .serve(app) - .left_future() - } else { - bind(addr).handle(handle).serve(app).right_future() - }; - servers.spawn( - server.then(|result| async { (listen.clone(), result) }), - ); - } - } + spawner.spawn_server(listen.clone())?; } - tokio::spawn(handle_signals(tls_config, handles)); + tokio::spawn(handle_signals(spawner.tls_config, spawner.handles)); if config.federation.enable && config.federation.self_test { federation_self_test() @@ -263,7 +345,7 @@ async fn run_server() -> Result<(), error::Serve> { set_application_state(ApplicationState::Ready); - while let Some(result) = servers.join_next().await { + while let Some(result) = spawner.servers.join_next().await { let (listen, result) = result.expect("should be able to join server task"); result.map_err(|err| Error::Listen(err, listen))?; From 3247c64cd89c349008febb0a619778646a64522e Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 15 Sep 2024 15:55:47 +0000 Subject: [PATCH 432/617] Add support for HAProxy proxy protocol for listeners --- Cargo.lock | 12 +++ Cargo.toml | 2 + book/changelog.md | 3 + src/cli/serve.rs | 39 +++++++-- src/config.rs | 30 +++++-- src/utils.rs | 1 + src/utils/proxy_protocol.rs | 161 ++++++++++++++++++++++++++++++++++++ 7 files changed, 234 insertions(+), 14 deletions(-) create mode 100644 src/utils/proxy_protocol.rs diff --git a/Cargo.lock b/Cargo.lock index caff42d9..65aa7f4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -827,7 +827,9 @@ dependencies = [ "opentelemetry_sdk", "parking_lot", "phf", + "pin-project-lite", "prometheus", + "proxy-header", "rand", "regex", "reqwest", @@ -1856,6 +1858,16 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "proxy-header" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1493f63ddddfba840c3169e997c2905d09538ace72d64e84af6324c6e0e065" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "quick-error" version = "1.2.3" diff --git a/Cargo.toml b/Cargo.toml index b406358b..eebcbd2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,7 +113,9 @@ opentelemetry-prometheus = "0.17.0" opentelemetry_sdk = { version = "0.24.0", features = ["rt-tokio"] } parking_lot = { version = "0.12.3", optional = true } phf = { version = "0.11.2", features = ["macros"] } +pin-project-lite = "0.2.14" prometheus = "0.13.4" +proxy-header = { version = "0.1.2", features = ["tokio"] } rand = "0.8.5" regex = "1.10.6" reqwest = { version = "0.12.7", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } diff --git a/book/changelog.md b/book/changelog.md index cb2e70af..a8951562 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -269,3 +269,6 @@ This will be the first release of Grapevine since it was forked from Conduit ([!97](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/97)) 22. Added a federation self-test, perfomed automatically on startup. ([!106](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/106)) +23. Added support for HAProxy [proxy protocol](http://www.haproxy.org/download/3.0/doc/proxy-protocol.txt) + listeners. + ([!97](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/97)) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index e0612576..aa507522 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -57,6 +57,7 @@ use crate::{ utils::{ self, error::{Error, Result}, + proxy_protocol::{ProxyAcceptor, ProxyAcceptorConfig}, }, ApplicationState, Services, }; @@ -138,6 +139,7 @@ struct ServerSpawner<'cfg, M> { middlewares: M, tls_config: Option, + proxy_config: ProxyAcceptorConfig, servers: JoinSet<(ListenConfig, std::io::Result<()>)>, handles: Vec, } @@ -172,10 +174,13 @@ where None }; + let proxy_config = ProxyAcceptorConfig::default(); + Ok(Self { config, middlewares, tls_config, + proxy_config, servers: JoinSet::new(), handles: Vec::new(), }) @@ -195,6 +200,14 @@ where Ok(|inner| RustlsAcceptor::new(config).acceptor(inner)) } + /// Returns a function that transforms a lower-layer acceptor into a Proxy + /// Protocol acceptor. + fn proxy_acceptor_factory(&self) -> impl FnOnce(A) -> ProxyAcceptor { + let config = self.proxy_config.clone(); + + |inner| ProxyAcceptor::new(inner, config) + } + fn spawn_server_inner( &mut self, listen: ListenConfig, @@ -229,16 +242,30 @@ where address, port, tls, + proxy_protocol, } => { let addr = SocketAddr::from((address, port)); let server = bind(addr); - if tls { - let server = - server.map(self.tls_acceptor_factory(&listen)?); - self.spawn_server_inner(listen, server, app); - } else { - self.spawn_server_inner(listen, server, app); + match (tls, proxy_protocol) { + (false, false) => { + self.spawn_server_inner(listen, server, app); + } + (false, true) => { + let server = server.map(self.proxy_acceptor_factory()); + self.spawn_server_inner(listen, server, app); + } + (true, false) => { + let server = + server.map(self.tls_acceptor_factory(&listen)?); + self.spawn_server_inner(listen, server, app); + } + (true, true) => { + let server = server + .map(self.proxy_acceptor_factory()) + .map(self.tls_acceptor_factory(&listen)?); + self.spawn_server_inner(listen, server, app); + } } Ok(()) diff --git a/src/config.rs b/src/config.rs index bf08c7c1..4c3d5a44 100644 --- a/src/config.rs +++ b/src/config.rs @@ -137,22 +137,35 @@ pub(crate) enum ListenTransport { port: u16, #[serde(default = "false_fn")] tls: bool, + #[serde(default = "false_fn")] + proxy_protocol: bool, }, } impl Display for ListenTransport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { + match *self { ListenTransport::Tcp { address, port, - tls: false, - } => write!(f, "http://{address}:{port}"), - ListenTransport::Tcp { - address, - port, - tls: true, - } => write!(f, "https://{address}:{port}"), + tls, + proxy_protocol, + } => { + let scheme = format!( + "{}{}", + if proxy_protocol { + "proxy+" + } else { + "" + }, + if tls { + "https" + } else { + "http" + } + ); + write!(f, "{scheme}://{address}:{port}") + } } } } @@ -379,6 +392,7 @@ fn default_listen() -> Vec { address: default_address(), port: default_port(), tls: false, + proxy_protocol: false, }, }] } diff --git a/src/utils.rs b/src/utils.rs index 1eca4b4b..45211399 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,6 +19,7 @@ use crate::{Error, Result}; pub(crate) mod error; pub(crate) mod on_demand_hashmap; +pub(crate) mod proxy_protocol; pub(crate) mod room_version; // Hopefully we have a better chat protocol in 530 years diff --git a/src/utils/proxy_protocol.rs b/src/utils/proxy_protocol.rs new file mode 100644 index 00000000..53021755 --- /dev/null +++ b/src/utils/proxy_protocol.rs @@ -0,0 +1,161 @@ +use std::{ + future::Future, io::ErrorKind, pin::Pin, task::Poll, time::Duration, +}; + +use axum::{middleware::AddExtension, Extension}; +use axum_server::accept::Accept; +use pin_project_lite::pin_project; +use proxy_header::{io::ProxiedStream, ParseConfig, ProxyHeader}; +use tokio::{ + io::AsyncRead, + time::{timeout, Timeout}, +}; +use tower::Layer; +use tracing::warn; + +#[derive(Debug, Clone)] +pub(crate) struct ProxyAcceptorConfig { + pub(crate) header_timeout: Duration, + pub(crate) parse_config: ParseConfig, +} + +impl Default for ProxyAcceptorConfig { + fn default() -> Self { + Self { + header_timeout: Duration::from_secs(5), + parse_config: ParseConfig::default(), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ProxyAcceptor { + inner: A, + config: ProxyAcceptorConfig, +} + +impl ProxyAcceptor { + pub(crate) fn new(inner: A, config: ProxyAcceptorConfig) -> Self { + Self { + inner, + config, + } + } +} + +impl Accept for ProxyAcceptor +where + A: Accept, + A::Stream: AsyncRead + Unpin + Send + 'static, +{ + type Future = AcceptorFuture; + type Service = AddExtension>; + type Stream = ProxiedStream; + + fn accept(&self, stream: I, service: S) -> Self::Future { + let inner_future = self.inner.accept(stream, service); + let config = self.config.clone(); + + AcceptorFuture::new(inner_future, config) + } +} + +/// Future returned by [`ProxiedStream::create_from_tokio()`]. +type StreamFuture = + Pin>> + Send>>; + +pin_project! { + #[project = AcceptorFutureProj] + pub(crate) enum AcceptorFuture { + Inner { + #[pin] + inner_future: F, + config: ProxyAcceptorConfig, + }, + Proxy { + #[pin] + stream_future: Timeout>, + service: Option, + }, + } +} + +impl AcceptorFuture { + fn new(inner_future: F, config: ProxyAcceptorConfig) -> Self { + Self::Inner { + inner_future, + config, + } + } +} + +impl Future for AcceptorFuture +where + F: Future>, + I: AsyncRead + Unpin + Send + 'static, +{ + type Output = std::io::Result<( + ProxiedStream, + AddExtension>, + )>; + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + loop { + match self.as_mut().project() { + AcceptorFutureProj::Inner { + inner_future, + config, + } => { + let Poll::Ready((stream, service)) = + inner_future.poll(cx)? + else { + return Poll::Pending; + }; + + let stream_future: StreamFuture = + Box::pin(ProxiedStream::create_from_tokio( + stream, + config.parse_config, + )); + let stream_future = + timeout(config.header_timeout, stream_future); + + self.set(AcceptorFuture::Proxy { + stream_future, + service: Some(service), + }); + } + AcceptorFutureProj::Proxy { + stream_future, + service, + } => { + let Poll::Ready(ret) = stream_future.poll(cx) else { + return Poll::Pending; + }; + + let stream = ret + .map_err(|e| { + warn!( + "Timed out waiting for HAProxy protocol header" + ); + std::io::Error::new(ErrorKind::TimedOut, e) + })? + .inspect_err(|error| { + warn!(%error, "Failed to parse HAProxy protocol header"); + })?; + + let service = + Extension(stream.proxy_header().clone().into_owned()) + .layer(service.take().expect( + "future should not be polled after ready", + )); + + return Poll::Ready(Ok((stream, service))); + } + } + } + } +} From 70ee206031649490d6b48bc38a3fbe215c54036c Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 15 Sep 2024 17:04:47 +0000 Subject: [PATCH 433/617] Extract source address for requests --- Cargo.lock | 2 ++ Cargo.toml | 2 +- src/cli/serve.rs | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65aa7f4e..80ea091b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,6 +158,7 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sync_wrapper 1.0.1", + "tokio", "tower 0.5.1", "tower-layer", "tower-service", @@ -3069,6 +3070,7 @@ dependencies = [ "futures-util", "pin-project-lite", "sync_wrapper 0.1.2", + "tokio", "tower-layer", "tower-service", ] diff --git a/Cargo.toml b/Cargo.toml index eebcbd2a..6157e966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ workspace = true [dependencies] argon2 = "0.5.3" async-trait = "0.1.82" -axum = { version = "0.7.6", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } +axum = { version = "0.7.6", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } axum-extra = { version = "0.9.4", features = ["typed-header"] } axum-server = { version = "0.7.1", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" diff --git a/src/cli/serve.rs b/src/cli/serve.rs index aa507522..69d5eb2b 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -4,9 +4,13 @@ use std::{ }; use axum::{ - extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, + extract::{ + connect_info::IntoMakeServiceWithConnectInfo, ConnectInfo, + DefaultBodyLimit, FromRequestParts, MatchedPath, + }, + middleware::AddExtension, response::IntoResponse, - routing::{any, get, on, IntoMakeService, MethodFilter, Route}, + routing::{any, get, on, MethodFilter, Route}, Router, }; use axum_server::{ @@ -21,6 +25,7 @@ use http::{ Method, StatusCode, Uri, }; use hyper::body::Incoming; +use proxy_header::ProxyHeader; use ruma::api::{ client::{ error::{Error as RumaError, ErrorBody, ErrorKind}, @@ -212,9 +217,13 @@ where &mut self, listen: ListenConfig, server: Server, - app: IntoMakeService, + app: IntoMakeServiceWithConnectInfo, ) where - A: Accept + Clone + Send + Sync + 'static, + A: Accept>> + + Clone + + Send + + Sync + + 'static, A::Stream: AsyncRead + AsyncWrite + Unpin + Send + Sync, A::Service: SendService> + Send, A::Future: Send, @@ -235,7 +244,7 @@ where ) -> Result<(), error::Serve> { let app = routes(self.config, &listen.components) .layer(self.middlewares.clone()) - .into_make_service(); + .into_make_service_with_connect_info::(); match listen.transport { ListenTransport::Tcp { @@ -298,11 +307,28 @@ async fn run_server() -> Result<(), error::Serve> { let method = request.method(); + let source_address = request + .extensions() + .get::>() + .map_or_else( + || { + request + .extensions() + .get::>() + .map(|&ConnectInfo(addr)| addr) + }, + |h| h.proxied_address().map(|addr| addr.source), + ); + tracing::info_span!( "http_request", otel.name = format!("{method} {endpoint}"), %method, %endpoint, + source_address = source_address.map_or( + "[unknown]".to_owned(), + |a| a.to_string() + ), ) }) .on_request( From 26ba489aa3965c302e1faa05cf398d70aa1579b4 Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Fri, 4 Oct 2024 10:49:08 -0400 Subject: [PATCH 434/617] Add a "check-config" command to validate config files & tests for it --- Cargo.lock | 193 ++++++++++++++++++ Cargo.toml | 11 + nix/shell.nix | 2 + src/cli.rs | 20 ++ src/cli/check_config.rs | 11 + src/error.rs | 13 ++ tests/integrations/check_config.rs | 95 +++++++++ .../fixtures/check_config/invalid-keys.toml | 2 + .../fixtures/check_config/invalid-values.toml | 2 + .../fixtures/check_config/minimal-valid.toml | 8 + .../fixtures/check_config/valid.toml | 21 ++ tests/integrations/main.rs | 4 + ...heck_config__invalid_keys@status_code.snap | 8 + ...ns__check_config__invalid_keys@stderr.snap | 12 ++ ...ns__check_config__invalid_keys@stdout.snap | 6 + ...ck_config__invalid_values@status_code.snap | 8 + ...__check_config__invalid_values@stderr.snap | 12 ++ ...__check_config__invalid_values@stdout.snap | 6 + ...fig__minimal_valid_config@status_code.snap | 8 + ...k_config__minimal_valid_config@stderr.snap | 15 ++ ...k_config__minimal_valid_config@stdout.snap | 6 + ...heck_config__valid_config@status_code.snap | 8 + ...ns__check_config__valid_config@stderr.snap | 15 ++ ...ns__check_config__valid_config@stdout.snap | 6 + 24 files changed, 492 insertions(+) create mode 100644 src/cli/check_config.rs create mode 100644 tests/integrations/check_config.rs create mode 100644 tests/integrations/fixtures/check_config/invalid-keys.toml create mode 100644 tests/integrations/fixtures/check_config/invalid-values.toml create mode 100644 tests/integrations/fixtures/check_config/minimal-valid.toml create mode 100644 tests/integrations/fixtures/check_config/valid.toml create mode 100755 tests/integrations/main.rs create mode 100644 tests/integrations/snapshots/integrations__check_config__invalid_keys@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__invalid_keys@stdout.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__invalid_values@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__invalid_values@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__invalid_values@stdout.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__minimal_valid_config@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stdout.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__valid_config@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__valid_config@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__valid_config@stdout.snap diff --git a/Cargo.lock b/Cargo.lock index 80ea091b..591033a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38fa22307249f86fb7fad906fcae77f2564caeb56d7209103c551cd1cf4798f" +[[package]] +name = "assert_cmd" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "assign" version = "1.1.1" @@ -316,6 +332,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata 0.4.7", + "serde", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -445,6 +472,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -559,6 +598,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -570,6 +615,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "ed25519" version = "2.2.3" @@ -601,6 +652,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -666,6 +723,15 @@ dependencies = [ "miniz_oxide 0.8.0", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -801,6 +867,7 @@ name = "grapevine" version = "0.1.0" dependencies = [ "argon2", + "assert_cmd", "async-trait", "axum", "axum-extra", @@ -816,6 +883,7 @@ dependencies = [ "hyper", "hyper-util", "image", + "insta", "jsonwebtoken", "lru-cache", "nix", @@ -829,6 +897,7 @@ dependencies = [ "parking_lot", "phf", "pin-project-lite", + "predicates", "prometheus", "proxy-header", "rand", @@ -1153,6 +1222,22 @@ dependencies = [ "serde", ] +[[package]] +name = "insta" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "pest", + "pest_derive", + "regex", + "serde", + "similar", +] + [[package]] name = "ipconfig" version = "0.3.2" @@ -1461,6 +1546,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1679,6 +1770,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "phf" version = "0.11.2" @@ -1797,6 +1933,36 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -2673,6 +2839,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + [[package]] name = "simple_asn1" version = "0.6.2" @@ -2805,6 +2977,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thiserror" version = "1.0.64" @@ -3281,6 +3459,12 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -3359,6 +3543,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 6157e966..d340dca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,6 +148,17 @@ xdg = "2.5.2" [target.'cfg(unix)'.dependencies] nix = { version = "0.29", features = ["resource", "time"] } +[dev-dependencies] +assert_cmd = "2.0.16" +insta = { version = "1.40.0", features = ["filters", "json", "redactions"] } +predicates = "3.1.2" + +[profile.dev.package.insta] +opt-level = 3 + +[profile.dev.package.similar] +opt-level = 3 + [features] default = ["rocksdb", "sqlite", "systemd"] diff --git a/nix/shell.nix b/nix/shell.nix index 130d63e6..91388d90 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,6 +1,7 @@ # Keep sorted { buildPlatform , default +, cargo-insta , engage , inputs , jq @@ -28,6 +29,7 @@ mkShell { inputs.fenix.packages.${buildPlatform.system}.latest.rustfmt # Keep sorted + cargo-insta engage jq lychee diff --git a/src/cli.rs b/src/cli.rs index f697d258..99641790 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,6 +12,7 @@ use crate::{ error, observability, }; +mod check_config; mod serve; /// Command line arguments @@ -29,6 +30,18 @@ pub(crate) struct Args { pub(crate) enum Command { /// Run the server. Serve(ServeArgs), + + /// Check the configuration file for syntax and semantic errors. + CheckConfig(CheckConfigArgs), +} + +#[derive(clap::Args)] +pub(crate) struct CheckConfigArgs { + #[clap(flatten)] + config: ConfigArg, + + #[clap(flatten)] + observability: ObservabilityArgs, } /// Wrapper for the `--config` arg. @@ -83,6 +96,9 @@ impl Args { match self.command { Command::Serve(args) => serve::run(args).await?, + Command::CheckConfig(args) => { + check_config::run(args.config).await?; + } } Ok(()) } @@ -93,6 +109,10 @@ impl Command { // All subcommands other than `serve` should return `Some`. Keep these // match arms sorted by the enum variant name. match self { + Command::CheckConfig(args) => Some(( + args.observability.log_format, + args.observability.log_filter.clone(), + )), Command::Serve(_) => None, } } diff --git a/src/cli/check_config.rs b/src/cli/check_config.rs new file mode 100644 index 00000000..9dd0dba3 --- /dev/null +++ b/src/cli/check_config.rs @@ -0,0 +1,11 @@ +use tracing::info; + +use crate::{cli::ConfigArg, config, error}; + +pub(crate) async fn run( + args: ConfigArg, +) -> Result<(), error::CheckConfigCommand> { + let _config = config::load(args.config.as_ref()).await?; + info!("Configuration looks good"); + Ok(()) +} diff --git a/src/error.rs b/src/error.rs index e0e53436..ce8ebb37 100644 --- a/src/error.rs +++ b/src/error.rs @@ -45,6 +45,9 @@ pub(crate) enum Main { #[error("failed to install global default tracing subscriber")] SetSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError), + + #[error(transparent)] + CheckConfigCommand(#[from] CheckConfigCommand), } /// Errors returned from the `serve` CLI subcommand. @@ -72,6 +75,16 @@ pub(crate) enum ServeCommand { ServerNameChanged(#[from] ServerNameChanged), } +/// Errors returned from the `check-config` CLI subcommand. +// Missing docs are allowed here since that kind of information should be +// encoded in the error messages themselves anyway. +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub(crate) enum CheckConfigCommand { + #[error("failed to validate configuration")] + Config(#[from] Config), +} + /// Error generated if `server_name` has changed or if checking this failed // Missing docs are allowed here since that kind of information should be // encoded in the error messages themselves anyway. diff --git a/tests/integrations/check_config.rs b/tests/integrations/check_config.rs new file mode 100644 index 00000000..03474266 --- /dev/null +++ b/tests/integrations/check_config.rs @@ -0,0 +1,95 @@ +use std::{ + path::{Path, PathBuf}, + process::Output, +}; + +use assert_cmd::Command; + +type TestError = Box; +type TestResult = Result<(), TestError>; + +fn fixture_path

(name: P) -> PathBuf +where + P: AsRef, +{ + PathBuf::from("tests/integrations/fixtures/check_config").join(name) +} + +fn run

(file: P) -> Result +where + P: AsRef, +{ + Command::cargo_bin("grapevine")? + .args(["check-config", "--log-format=json", "-c"]) + .arg(fixture_path(file)) + .output() + .map_err(Into::into) +} + +macro_rules! make_snapshot_test { + ($name:ident, $description:expr, $fixture_name:expr $(,)?) => { + #[test] + fn $name() -> TestResult { + let output = run($fixture_name)?; + + let stdout = String::from_utf8(output.stdout)?; + let stderr = String::from_utf8(output.stderr)?; + let status_code = output.status.code(); + + insta::with_settings!({ + description => $description, + omit_expression => true, + snapshot_suffix => "stdout", + }, { + insta::assert_snapshot!(stdout); + }); + + let stderr_parse = serde_json::Deserializer::from_str(&stderr) + .into_iter::() + .collect::, _>>(); + insta::with_settings!({ + description => $description, + omit_expression => true, + snapshot_suffix => "stderr", + }, { + if let Ok(stderr_json) = stderr_parse { + insta::assert_json_snapshot!(stderr_json, { + ".*.timestamp" => "[timestamp]" + }); + } else { + insta::assert_snapshot!(stderr); + } + }); + + insta::with_settings!({ + description => $description, + omit_expression => true, + snapshot_suffix => "status_code", + }, { + insta::assert_debug_snapshot!(status_code); + }); + + Ok(()) + } + }; +} + +make_snapshot_test!(valid_config, "A normal config is valid", "valid.toml"); + +make_snapshot_test!( + minimal_valid_config, + "A configuration containing only the required keys is valid", + "minimal-valid.toml", +); + +make_snapshot_test!( + invalid_keys, + "A config with invalid keys fails", + "invalid-keys.toml", +); + +make_snapshot_test!( + invalid_values, + "A config with invalid values fails", + "invalid-values.toml", +); diff --git a/tests/integrations/fixtures/check_config/invalid-keys.toml b/tests/integrations/fixtures/check_config/invalid-keys.toml new file mode 100644 index 00000000..4f9ad938 --- /dev/null +++ b/tests/integrations/fixtures/check_config/invalid-keys.toml @@ -0,0 +1,2 @@ +some_name = "example.com" +prort = 6167 diff --git a/tests/integrations/fixtures/check_config/invalid-values.toml b/tests/integrations/fixtures/check_config/invalid-values.toml new file mode 100644 index 00000000..54f9ff6c --- /dev/null +++ b/tests/integrations/fixtures/check_config/invalid-values.toml @@ -0,0 +1,2 @@ +server_name = 6667 +port = "ircd" diff --git a/tests/integrations/fixtures/check_config/minimal-valid.toml b/tests/integrations/fixtures/check_config/minimal-valid.toml new file mode 100644 index 00000000..e8a93d0e --- /dev/null +++ b/tests/integrations/fixtures/check_config/minimal-valid.toml @@ -0,0 +1,8 @@ +server_name = "example.com" + +[server_discovery] +client.base_url = "https://matrix.example.com" + +[database] +backend = "rocksdb" +path = "/var/lib/grapevine" diff --git a/tests/integrations/fixtures/check_config/valid.toml b/tests/integrations/fixtures/check_config/valid.toml new file mode 100644 index 00000000..8d8a0bae --- /dev/null +++ b/tests/integrations/fixtures/check_config/valid.toml @@ -0,0 +1,21 @@ +server_name = "example.com" + +allow_registration = false + +max_request_size = 20_000_000 + +[server_discovery] +server.authority = "matrix.example.com:443" +client.base_url = "https://matrix.example.com" + +[database] +backend = "rocksdb" +path = "/var/lib/grapevine" + +[federation] +enable = true +trusted_servers = ["matrix.org"] + +[[listen]] + type="tcp" + address = "0.0.0.0" diff --git a/tests/integrations/main.rs b/tests/integrations/main.rs new file mode 100755 index 00000000..908b0bac --- /dev/null +++ b/tests/integrations/main.rs @@ -0,0 +1,4 @@ +// +#![allow(clippy::tests_outside_test_module)] + +mod check_config; diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_keys@status_code.snap b/tests/integrations/snapshots/integrations__check_config__invalid_keys@status_code.snap new file mode 100644 index 00000000..2278288a --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__invalid_keys@status_code.snap @@ -0,0 +1,8 @@ +--- +source: tests/integrations/check_config.rs +description: A config with invalid keys fails +snapshot_kind: text +--- +Some( + 1, +) diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap new file mode 100644 index 00000000..7cf08682 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap @@ -0,0 +1,12 @@ +--- +source: tests/integrations/check_config.rs +description: A config with invalid keys fails +snapshot_kind: text +--- +Error: failed to validate configuration + Caused by: failed to parse configuration file "tests/integrations/fixtures/check_config/invalid-keys.toml" + Caused by: TOML parse error at line 1, column 1 + | +1 | some_name = "example.com" + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +missing field `server_name` diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stdout.snap b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stdout.snap new file mode 100644 index 00000000..b17d0451 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stdout.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A config with invalid keys fails +snapshot_kind: text +--- + diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_values@status_code.snap b/tests/integrations/snapshots/integrations__check_config__invalid_values@status_code.snap new file mode 100644 index 00000000..27ae9ec0 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__invalid_values@status_code.snap @@ -0,0 +1,8 @@ +--- +source: tests/integrations/check_config.rs +description: A config with invalid values fails +snapshot_kind: text +--- +Some( + 1, +) diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_values@stderr.snap b/tests/integrations/snapshots/integrations__check_config__invalid_values@stderr.snap new file mode 100644 index 00000000..5e46f095 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__invalid_values@stderr.snap @@ -0,0 +1,12 @@ +--- +source: tests/integrations/check_config.rs +description: A config with invalid values fails +snapshot_kind: text +--- +Error: failed to validate configuration + Caused by: failed to parse configuration file "tests/integrations/fixtures/check_config/invalid-values.toml" + Caused by: TOML parse error at line 1, column 15 + | +1 | server_name = 6667 + | ^^^^ +invalid type: integer `6667`, expected a string diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_values@stdout.snap b/tests/integrations/snapshots/integrations__check_config__invalid_values@stdout.snap new file mode 100644 index 00000000..a67fbf13 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__invalid_values@stdout.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A config with invalid values fails +snapshot_kind: text +--- + diff --git a/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@status_code.snap b/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@status_code.snap new file mode 100644 index 00000000..c58f09d0 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@status_code.snap @@ -0,0 +1,8 @@ +--- +source: tests/integrations/check_config.rs +description: A configuration containing only the required keys is valid +snapshot_kind: text +--- +Some( + 0, +) diff --git a/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stderr.snap b/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stderr.snap new file mode 100644 index 00000000..0a90a7d2 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stderr.snap @@ -0,0 +1,15 @@ +--- +source: tests/integrations/check_config.rs +description: A configuration containing only the required keys is valid +snapshot_kind: text +--- +[ + { + "fields": { + "message": "Configuration looks good" + }, + "level": "INFO", + "target": "grapevine::cli::check_config", + "timestamp": "[timestamp]" + } +] diff --git a/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stdout.snap b/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stdout.snap new file mode 100644 index 00000000..ca3c5651 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__minimal_valid_config@stdout.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A configuration containing only the required keys is valid +snapshot_kind: text +--- + diff --git a/tests/integrations/snapshots/integrations__check_config__valid_config@status_code.snap b/tests/integrations/snapshots/integrations__check_config__valid_config@status_code.snap new file mode 100644 index 00000000..529e7e1c --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__valid_config@status_code.snap @@ -0,0 +1,8 @@ +--- +source: tests/integrations/check_config.rs +description: A normal config is valid +snapshot_kind: text +--- +Some( + 0, +) diff --git a/tests/integrations/snapshots/integrations__check_config__valid_config@stderr.snap b/tests/integrations/snapshots/integrations__check_config__valid_config@stderr.snap new file mode 100644 index 00000000..b5651e25 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__valid_config@stderr.snap @@ -0,0 +1,15 @@ +--- +source: tests/integrations/check_config.rs +description: A normal config is valid +snapshot_kind: text +--- +[ + { + "fields": { + "message": "Configuration looks good" + }, + "level": "INFO", + "target": "grapevine::cli::check_config", + "timestamp": "[timestamp]" + } +] diff --git a/tests/integrations/snapshots/integrations__check_config__valid_config@stdout.snap b/tests/integrations/snapshots/integrations__check_config__valid_config@stdout.snap new file mode 100644 index 00000000..deb44a46 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__valid_config@stdout.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A normal config is valid +snapshot_kind: text +--- + From a02c551a5e99bf873b684e0e1dc77ac1d51fad53 Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Wed, 30 Oct 2024 11:11:29 -0400 Subject: [PATCH 435/617] Disallow any unknown fields in configuration files This will break backwards compatibility of configurations, but ensures that a previously-configured setting won't get dropped arbitrarily. Pretty much worth it, I think. --- src/config.rs | 18 ++++++++++++++++++ ...ons__check_config__invalid_keys@stderr.snap | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4c3d5a44..5c9ae2ee 100644 --- a/src/config.rs +++ b/src/config.rs @@ -29,6 +29,7 @@ pub(crate) static DEFAULT_PATH: Lazy = #[allow(clippy::struct_excessive_bools)] #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct Config { #[serde(default = "false_fn")] pub(crate) conduit_compat: bool, @@ -77,6 +78,7 @@ pub(crate) struct Config { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct ServerDiscovery { /// Server-server discovery configuration #[serde(default)] @@ -88,6 +90,7 @@ pub(crate) struct ServerDiscovery { /// Server-server discovery configuration #[derive(Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct ServerServerDiscovery { /// The alternative authority to make server-server API requests to pub(crate) authority: Option, @@ -95,6 +98,7 @@ pub(crate) struct ServerServerDiscovery { /// Client-server discovery configuration #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct ClientServerDiscovery { /// The base URL to make client-server API requests to pub(crate) base_url: Url, @@ -104,6 +108,7 @@ pub(crate) struct ClientServerDiscovery { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct TlsConfig { pub(crate) certs: String, pub(crate) key: String, @@ -114,6 +119,7 @@ pub(crate) struct TlsConfig { )] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] +#[serde(deny_unknown_fields)] pub(crate) enum ListenComponent { Client, Federation, @@ -129,6 +135,7 @@ impl ListenComponent { #[derive(Clone, Debug, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] +#[serde(deny_unknown_fields)] pub(crate) enum ListenTransport { Tcp { #[serde(default = "default_address")] @@ -171,6 +178,7 @@ impl Display for ListenTransport { } #[derive(Clone, Debug, Deserialize)] +// Incompatible with deny_unknown_fields due to serde(flatten). pub(crate) struct ListenConfig { #[serde(default = "ListenComponent::all_components")] pub(crate) components: HashSet, @@ -194,6 +202,7 @@ impl Display for ListenConfig { } #[derive(Copy, Clone, Default, Debug, Deserialize, clap::ValueEnum)] +#[serde(deny_unknown_fields)] #[serde(rename_all = "snake_case")] pub(crate) enum LogFormat { /// Multiple lines per event, includes all information @@ -222,6 +231,7 @@ impl Display for LogFormat { } #[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct TurnConfig { pub(crate) username: String, @@ -244,6 +254,7 @@ impl Default for TurnConfig { } #[derive(Clone, Copy, Debug, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(rename_all = "lowercase")] pub(crate) enum DatabaseBackend { #[cfg(feature = "rocksdb")] @@ -264,6 +275,7 @@ impl Display for DatabaseBackend { } #[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub(crate) struct DatabaseConfig { pub(crate) backend: DatabaseBackend, pub(crate) path: String, @@ -275,12 +287,14 @@ pub(crate) struct DatabaseConfig { } #[derive(Clone, Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct MetricsConfig { pub(crate) enable: bool, } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct OtelTraceConfig { pub(crate) enable: bool, @@ -301,6 +315,7 @@ impl Default for OtelTraceConfig { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct FlameConfig { pub(crate) enable: bool, @@ -319,6 +334,7 @@ impl Default for FlameConfig { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct LogConfig { pub(crate) filter: EnvFilterClone, @@ -339,6 +355,7 @@ impl Default for LogConfig { } #[derive(Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct ObservabilityConfig { /// Prometheus metrics @@ -352,6 +369,7 @@ pub(crate) struct ObservabilityConfig { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(default)] pub(crate) struct FederationConfig { pub(crate) enable: bool, diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap index 7cf08682..942cce3b 100644 --- a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap +++ b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap @@ -8,5 +8,5 @@ Error: failed to validate configuration Caused by: TOML parse error at line 1, column 1 | 1 | some_name = "example.com" - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -missing field `server_name` + | ^^^^^^^^^ +unknown field `some_name`, expected one of `conduit_compat`, `listen`, `tls`, `server_name`, `server_discovery`, `database`, `federation`, `cache_capacity_modifier`, `pdu_cache_capacity`, `cleanup_second_interval`, `max_request_size`, `allow_registration`, `registration_token`, `allow_encryption`, `allow_room_creation`, `serve_media_unauthenticated`, `default_room_version`, `proxy`, `jwt_secret`, `observability`, `turn`, `emergency_password` From dcf64f03fbf511ec493059445c66cf196c407abc Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Wed, 30 Oct 2024 11:19:10 -0400 Subject: [PATCH 436/617] Validate generated config file in the nixos module This uses the usual pkgs.runCommand pattern to ensure that no non-parseable config files can make it to the command line. --- nix/modules/default/default.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index d2efa327..d29a7f1f 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -11,6 +11,10 @@ let cfg = config.services.grapevine; configFile = format.generate "config.toml" cfg.settings; + validateConfig = file: pkgs.runCommand "grapevine-checked-config" {} '' + ${lib.getExe cfg.package} check-config -c ${lib.escapeShellArg file} + ln -s ${lib.escapeShellArg file} "$out" + ''; format = pkgs.formats.toml {}; in @@ -79,7 +83,7 @@ in # Keep sorted serviceConfig = { DynamicUser = true; - ExecStart = "${lib.getExe cfg.package} serve --config ${configFile}"; + ExecStart = "${lib.getExe cfg.package} serve --config ${validateConfig configFile}"; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; From 9529d43a21f3693fa8bb588d25569b5456eeaa1c Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Fri, 1 Nov 2024 12:16:49 -0400 Subject: [PATCH 437/617] ChangeLog entry for check-config subcmd --- book/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index a8951562..5b2da825 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -272,3 +272,6 @@ This will be the first release of Grapevine since it was forked from Conduit 23. Added support for HAProxy [proxy protocol](http://www.haproxy.org/download/3.0/doc/proxy-protocol.txt) listeners. ([!97](https://gitlab.computer.surgery/matrix/grapevine-fork/-/merge_requests/97)) +24. Add a `check-config` CLI subcommand to check whether the configuration file + is valid. + ([!121](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/121)) From 5be1e20eb4c0b6e7a9286bed606711b0f73f46f0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 21 Oct 2024 11:25:08 -0700 Subject: [PATCH 438/617] call maximize_fd_limit at top of main This way we don't shoot ourselves in the foot by forgetting to do it for other subcommands (e.g. that manipulate the database) in the future. --- src/cli/serve.rs | 29 ----------------------------- src/main.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 69d5eb2b..310c97a2 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -78,17 +78,6 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { let (_guard, reload_handles) = observability::init(&config)?; - // This is needed for opening lots of file descriptors, which tends to - // happen more often when using RocksDB and making lots of federation - // connections at startup. The soft limit is usually 1024, and the hard - // limit is usually 512000; I've personally seen it hit >2000. - // - // * https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6 - // * https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741 - #[cfg(unix)] - maximize_fd_limit() - .expect("should be able to increase the soft limit to the hard limit"); - info!("Loading database"); let db = Box::leak(Box::new( KeyValueDatabase::load_or_create(&config) @@ -908,21 +897,3 @@ fn method_to_filter(method: Method) -> MethodFilter { m => panic!("Unsupported HTTP method: {m:?}"), } } - -#[cfg(unix)] -#[tracing::instrument(err)] -fn maximize_fd_limit() -> Result<(), nix::errno::Errno> { - use nix::sys::resource::{getrlimit, setrlimit, Resource}; - - let res = Resource::RLIMIT_NOFILE; - - let (soft_limit, hard_limit) = getrlimit(res)?; - - debug!(soft_limit, "Current nofile soft limit"); - - setrlimit(res, hard_limit, hard_limit)?; - - debug!(hard_limit, "Increased nofile soft limit to the hard limit"); - - Ok(()) -} diff --git a/src/main.rs b/src/main.rs index 2726b4bd..8f4268a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,6 +84,17 @@ fn set_application_state(state: ApplicationState) { #[tokio::main] async fn main() -> ExitCode { + // This is needed for opening lots of file descriptors, which tends to + // happen more often when using RocksDB and making lots of federation + // connections at startup. The soft limit is usually 1024, and the hard + // limit is usually 512000; I've personally seen it hit >2000. + // + // * https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6 + // * https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741 + #[cfg(unix)] + maximize_fd_limit() + .expect("should be able to increase the soft limit to the hard limit"); + let args = cli::Args::parse(); let Err(e) = args.run().await else { return ExitCode::SUCCESS; @@ -99,3 +110,22 @@ async fn main() -> ExitCode { ExitCode::FAILURE } + +#[cfg(unix)] +#[tracing::instrument(err)] +fn maximize_fd_limit() -> Result<(), nix::errno::Errno> { + use nix::sys::resource::{getrlimit, setrlimit, Resource}; + use tracing::debug; + + let res = Resource::RLIMIT_NOFILE; + + let (soft_limit, hard_limit) = getrlimit(res)?; + + debug!(soft_limit, "Current nofile soft limit"); + + setrlimit(res, hard_limit, hard_limit)?; + + debug!(hard_limit, "Increased nofile soft limit to the hard limit"); + + Ok(()) +} From b18df8de706a6b5832f985da67a32336a900cb2e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:07:03 -0700 Subject: [PATCH 439/617] rename appservice service constructor --- src/service.rs | 2 +- src/service/appservice.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service.rs b/src/service.rs index 4bea1998..7a279080 100644 --- a/src/service.rs +++ b/src/service.rs @@ -65,7 +65,7 @@ impl Services { reload_handles: Option, ) -> Result { Ok(Self { - appservice: appservice::Service::build(db)?, + appservice: appservice::Service::new(db)?, pusher: pusher::Service { db, }, diff --git a/src/service/appservice.rs b/src/service/appservice.rs index d0b1626d..8174d2e4 100644 --- a/src/service/appservice.rs +++ b/src/service/appservice.rs @@ -117,7 +117,7 @@ pub(crate) struct Service { } impl Service { - pub(crate) fn build(db: &'static dyn Data) -> Result { + pub(crate) fn new(db: &'static dyn Data) -> Result { let mut registration_info = BTreeMap::new(); // Inserting registrations into cache for appservice in db.all()? { From 3b28d0cfda4e166d4927f3167d5ec35e19b6fe61 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:08:51 -0700 Subject: [PATCH 440/617] add constructor for typing service --- src/service.rs | 8 ++------ src/service/rooms/edus/typing.rs | 15 +++++++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/service.rs b/src/service.rs index 7a279080..754f4634 100644 --- a/src/service.rs +++ b/src/service.rs @@ -4,7 +4,7 @@ use std::{ }; use lru_cache::LruCache; -use tokio::sync::{broadcast, Mutex, RwLock}; +use tokio::sync::Mutex; use crate::{observability::FilterReloadHandles, Config, Result}; @@ -85,11 +85,7 @@ impl Services { directory: db, edus: rooms::edus::Service { read_receipt: db, - typing: rooms::edus::typing::Service { - typing: RwLock::new(BTreeMap::new()), - last_typing_update: RwLock::new(BTreeMap::new()), - typing_update_sender: broadcast::channel(100).0, - }, + typing: rooms::edus::typing::Service::new(), }, event_handler: rooms::event_handler::Service, lazy_loading: rooms::lazy_loading::Service { diff --git a/src/service/rooms/edus/typing.rs b/src/service/rooms/edus/typing.rs index 49b5de95..097fed27 100644 --- a/src/service/rooms/edus/typing.rs +++ b/src/service/rooms/edus/typing.rs @@ -11,14 +11,21 @@ use crate::{services, utils, Result}; pub(crate) struct Service { // u64 is unix timestamp of timeout - pub(crate) typing: - RwLock>>, + typing: RwLock>>, // timestamp of the last change to typing users - pub(crate) last_typing_update: RwLock>, - pub(crate) typing_update_sender: broadcast::Sender, + last_typing_update: RwLock>, + typing_update_sender: broadcast::Sender, } impl Service { + pub(crate) fn new() -> Self { + Self { + typing: RwLock::new(BTreeMap::new()), + last_typing_update: RwLock::new(BTreeMap::new()), + typing_update_sender: broadcast::channel(100).0, + } + } + /// Sets a user as typing until the timeout timestamp is reached or /// `roomtyping_remove` is called. #[tracing::instrument(skip(self))] From a083ff920078216b94aee21fd6ee04933d58fb91 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:10:30 -0700 Subject: [PATCH 441/617] add constructor for lazy-loading service --- src/service.rs | 7 ++----- src/service/rooms/lazy_loading.rs | 11 +++++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/service.rs b/src/service.rs index 754f4634..76a25985 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,5 +1,5 @@ use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, sync::{Arc, Mutex as StdMutex, OnceLock}, }; @@ -88,10 +88,7 @@ impl Services { typing: rooms::edus::typing::Service::new(), }, event_handler: rooms::event_handler::Service, - lazy_loading: rooms::lazy_loading::Service { - db, - lazy_load_waiting: Mutex::new(HashMap::new()), - }, + lazy_loading: rooms::lazy_loading::Service::new(db), metadata: db, outlier: db, pdu_metadata: rooms::pdu_metadata::Service { diff --git a/src/service/rooms/lazy_loading.rs b/src/service/rooms/lazy_loading.rs index f2732790..4c0508f1 100644 --- a/src/service/rooms/lazy_loading.rs +++ b/src/service/rooms/lazy_loading.rs @@ -11,10 +11,10 @@ mod data; pub(crate) use data::Data; pub(crate) struct Service { - pub(crate) db: &'static dyn Data, + db: &'static dyn Data, #[allow(clippy::type_complexity)] - pub(crate) lazy_load_waiting: Mutex< + lazy_load_waiting: Mutex< HashMap< (OwnedUserId, OwnedDeviceId, OwnedRoomId, PduCount), HashSet, @@ -23,6 +23,13 @@ pub(crate) struct Service { } impl Service { + pub(crate) fn new(db: &'static dyn Data) -> Self { + Self { + db, + lazy_load_waiting: Mutex::new(HashMap::new()), + } + } + #[tracing::instrument(skip(self))] pub(crate) fn lazy_load_was_sent_before( &self, From c6e2f8372ca9ec5b175cf0737ed29a335318cc2f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:15:24 -0700 Subject: [PATCH 442/617] add constructor for state accessor service --- src/service.rs | 16 ++++++++-------- src/service/rooms/state_accessor.rs | 23 +++++++++++++++++++---- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/service.rs b/src/service.rs index 76a25985..b5f356aa 100644 --- a/src/service.rs +++ b/src/service.rs @@ -133,25 +133,25 @@ impl Services { state: rooms::state::Service { db, }, - state_accessor: rooms::state_accessor::Service { + state_accessor: rooms::state_accessor::Service::new( db, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] - server_visibility_cache: StdMutex::new(LruCache::new( - (100.0 * config.cache_capacity_modifier) as usize, - )), + { + (100.0 * config.cache_capacity_modifier) as usize + }, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] - user_visibility_cache: StdMutex::new(LruCache::new( - (100.0 * config.cache_capacity_modifier) as usize, - )), - }, + { + (100.0 * config.cache_capacity_modifier) as usize + }, + ), state_cache: rooms::state_cache::Service::new(db), state_compressor: rooms::state_compressor::Service { db, diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index c3160d96..a3922249 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -38,14 +38,29 @@ mod data; pub(crate) use data::Data; pub(crate) struct Service { - pub(crate) db: &'static dyn Data, - pub(crate) server_visibility_cache: + db: &'static dyn Data, + server_visibility_cache: Mutex>, - pub(crate) user_visibility_cache: - Mutex>, + user_visibility_cache: Mutex>, } impl Service { + pub(crate) fn new( + db: &'static dyn Data, + server_visibility_cache_size: usize, + user_visibility_cache_size: usize, + ) -> Self { + Self { + db, + server_visibility_cache: Mutex::new(LruCache::new( + server_visibility_cache_size, + )), + user_visibility_cache: Mutex::new(LruCache::new( + user_visibility_cache_size, + )), + } + } + /// 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))] From f702b6cccdc18f26486180c4f6fded6257d8b351 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:16:54 -0700 Subject: [PATCH 443/617] add constructor for state compressor service --- src/service.rs | 10 +++++----- src/service/rooms/state_compressor.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/service.rs b/src/service.rs index b5f356aa..07a48520 100644 --- a/src/service.rs +++ b/src/service.rs @@ -153,17 +153,17 @@ impl Services { }, ), state_cache: rooms::state_cache::Service::new(db), - state_compressor: rooms::state_compressor::Service { + state_compressor: rooms::state_compressor::Service::new( db, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] - stateinfo_cache: StdMutex::new(LruCache::new( - (100.0 * config.cache_capacity_modifier) as usize, - )), - }, + { + (100.0 * config.cache_capacity_modifier) as usize + }, + ), timeline: rooms::timeline::Service::new( db, config.pdu_cache_capacity, diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 4232a325..cac73fe0 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -72,6 +72,16 @@ impl CompressedStateEvent { } impl Service { + pub(crate) fn new( + db: &'static dyn Data, + stateinfo_cache_size: usize, + ) -> Self { + Self { + db, + stateinfo_cache: Mutex::new(LruCache::new(stateinfo_cache_size)), + } + } + /// Returns a stack with info on shortstatehash, full state, added diff and /// removed diff for the selected shortstatehash and each parent layer. #[allow(clippy::type_complexity)] From f771d319b27992df4f41690e53024c82fa4bb0b0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:31:57 -0700 Subject: [PATCH 444/617] add constructor for spaces service Also adds a public function to invalidate the cache rather than exposing the entire cache publicly. --- src/service.rs | 7 +------ src/service/rooms/spaces.rs | 14 +++++++++++++- src/service/rooms/state.rs | 6 ++---- src/service/rooms/timeline.rs | 8 +++----- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/service.rs b/src/service.rs index 07a48520..42638b2d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -3,9 +3,6 @@ use std::{ sync::{Arc, Mutex as StdMutex, OnceLock}, }; -use lru_cache::LruCache; -use tokio::sync::Mutex; - use crate::{observability::FilterReloadHandles, Config, Result}; pub(crate) mod account_data; @@ -171,9 +168,7 @@ impl Services { threads: rooms::threads::Service { db, }, - spaces: rooms::spaces::Service { - roomid_spacechunk_cache: Mutex::new(LruCache::new(200)), - }, + spaces: rooms::spaces::Service::new(200), user: db, }, transaction_ids: db, diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 57655564..ad5f4da0 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -45,11 +45,19 @@ pub(crate) struct CachedSpaceChunk { } pub(crate) struct Service { - pub(crate) roomid_spacechunk_cache: + roomid_spacechunk_cache: Mutex>>, } impl Service { + pub(crate) fn new(roomid_spacechunk_cache_size: usize) -> Self { + Self { + roomid_spacechunk_cache: Mutex::new(LruCache::new( + roomid_spacechunk_cache_size, + )), + } + } + #[allow(clippy::too_many_lines)] #[tracing::instrument(skip(self))] pub(crate) async fn get_hierarchy( @@ -518,6 +526,10 @@ impl Service { }) } + pub(crate) async fn invalidate_cache(&self, room_id: &RoomId) { + self.roomid_spacechunk_cache.lock().await.remove(room_id); + } + fn translate_joinrule(join_rule: &JoinRule) -> Result { match join_rule { JoinRule::Invite => Ok(SpaceRoomJoinRule::Invite), diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index f19a9f9a..4702a76b 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -103,10 +103,8 @@ impl Service { services() .rooms .spaces - .roomid_spacechunk_cache - .lock() - .await - .remove(&pdu.room_id); + .invalidate_cache(&pdu.room_id) + .await; } _ => continue, } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index d70fd0c2..0cd751dd 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -508,14 +508,12 @@ impl Service { }; } TimelineEventType::SpaceChild => { - if let Some(_state_key) = &pdu.state_key { + if pdu.state_key.is_some() { services() .rooms .spaces - .roomid_spacechunk_cache - .lock() - .await - .remove(&pdu.room_id); + .invalidate_cache(&pdu.room_id) + .await; } } TimelineEventType::RoomMember => { From e06d126d4eeb7bae18d66ddcf3849d5de68f7dae Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:36:26 -0700 Subject: [PATCH 445/617] add constructor for users service --- src/service.rs | 10 ++-------- src/service/users.rs | 7 +++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/service.rs b/src/service.rs index 42638b2d..5a3af3de 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,7 +1,4 @@ -use std::{ - collections::BTreeMap, - sync::{Arc, Mutex as StdMutex, OnceLock}, -}; +use std::sync::{Arc, OnceLock}; use crate::{observability::FilterReloadHandles, Config, Result}; @@ -173,10 +170,7 @@ impl Services { }, transaction_ids: db, uiaa: uiaa::Service::new(db), - users: users::Service { - db, - connections: StdMutex::new(BTreeMap::new()), - }, + users: users::Service::new(db), account_data: db, admin: admin::Service::build(), key_backups: db, diff --git a/src/service/users.rs b/src/service/users.rs index 3007f176..088fbb12 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -46,6 +46,13 @@ pub(crate) struct Service { } impl Service { + pub(crate) fn new(db: &'static dyn Data) -> Self { + Self { + db, + connections: Mutex::new(BTreeMap::new()), + } + } + /// Check if a user has an account on this homeserver. pub(crate) fn exists(&self, user_id: &UserId) -> Result { self.db.exists(user_id) From 4faa8ea37c749b551d377eeec9c3cc92c1c8e7cd Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:36:45 -0700 Subject: [PATCH 446/617] rename constructor for admin service --- src/service.rs | 2 +- src/service/admin.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service.rs b/src/service.rs index 5a3af3de..4176a525 100644 --- a/src/service.rs +++ b/src/service.rs @@ -172,7 +172,7 @@ impl Services { uiaa: uiaa::Service::new(db), users: users::Service::new(db), account_data: db, - admin: admin::Service::build(), + admin: admin::Service::new(), key_backups: db, media: media::Service { db, diff --git a/src/service/admin.rs b/src/service/admin.rs index e8b9beae..e406d0f7 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -230,7 +230,7 @@ enum TracingBackend { } impl Service { - pub(crate) fn build() -> Arc { + pub(crate) fn new() -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); Arc::new(Self { sender, From 55b605f046e54c85984100bb2d2a6b0e2befc625 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:37:18 -0700 Subject: [PATCH 447/617] rename constructor for sending service --- src/service.rs | 2 +- src/service/sending.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service.rs b/src/service.rs index 4176a525..e3983721 100644 --- a/src/service.rs +++ b/src/service.rs @@ -177,7 +177,7 @@ impl Services { media: media::Service { db, }, - sending: sending::Service::build(db, &config), + sending: sending::Service::new(db, &config), globals: globals::Service::load(db, config, reload_handles)?, }) diff --git a/src/service/sending.rs b/src/service/sending.rs index d02918d5..1c3b6ecb 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -159,7 +159,7 @@ enum SelectedEvents { } impl Service { - pub(crate) fn build(db: &'static dyn Data, config: &Config) -> Arc { + pub(crate) fn new(db: &'static dyn Data, config: &Config) -> Arc { let (sender, receiver) = mpsc::unbounded_channel(); Arc::new(Self { db, From fefc84e8c795a0c2ed84fa0c30ac0b2a1d3fa022 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:37:38 -0700 Subject: [PATCH 448/617] rename constructor for globals service --- src/service.rs | 2 +- src/service/globals.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service.rs b/src/service.rs index e3983721..63b93f7f 100644 --- a/src/service.rs +++ b/src/service.rs @@ -179,7 +179,7 @@ impl Services { }, sending: sending::Service::new(db, &config), - globals: globals::Service::load(db, config, reload_handles)?, + globals: globals::Service::new(db, config, reload_handles)?, }) } diff --git a/src/service/globals.rs b/src/service/globals.rs index f3ac9a8e..0881ce13 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -197,7 +197,7 @@ impl Resolve for Resolver { impl Service { #[tracing::instrument(skip_all)] - pub(crate) fn load( + pub(crate) fn new( db: &'static dyn Data, config: Config, reload_handles: Option, From 4083451a10d36d5fbf6fd4ee8af7a5dd223b8a26 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 25 Oct 2024 14:29:15 -0700 Subject: [PATCH 449/617] rename Services constructor --- src/cli/serve.rs | 2 +- src/service.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 310c97a2..d8521bcb 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -84,7 +84,7 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .map_err(Error::DatabaseError)?, )); - Services::build(db, config, Some(reload_handles)) + Services::new(db, config, Some(reload_handles)) .map_err(Error::InitializeServices)? .install(); diff --git a/src/service.rs b/src/service.rs index 63b93f7f..04088773 100644 --- a/src/service.rs +++ b/src/service.rs @@ -40,7 +40,7 @@ pub(crate) struct Services { impl Services { #[allow(clippy::too_many_lines)] - pub(crate) fn build< + pub(crate) fn new< D: appservice::Data + pusher::Data + rooms::Data From 9fab7538a0779d58aaa7940e3f6db8bfddd497d8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 16:19:30 -0700 Subject: [PATCH 450/617] scale roomid_spacechunk_cache by modifier Not scaling this was probably unintentional. --- src/service.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/service.rs b/src/service.rs index 04088773..56a1c8a6 100644 --- a/src/service.rs +++ b/src/service.rs @@ -165,7 +165,16 @@ impl Services { threads: rooms::threads::Service { db, }, - spaces: rooms::spaces::Service::new(200), + spaces: rooms::spaces::Service::new( + #[allow( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + { + (200.0 * config.cache_capacity_modifier) as usize + }, + ), user: db, }, transaction_ids: db, From 1148c6004f0569d78f412e48506ae8d14f701497 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 19:49:21 -0700 Subject: [PATCH 451/617] make all caches individually configurable Also: * Removes the `cache_capacity_modifier` option * Renames the `pdu_cache_capacity` option to `cache.pdu` --- book/changelog.md | 39 +++++---- src/config.rs | 47 +++++++--- src/service.rs | 86 +++---------------- ...ns__check_config__invalid_keys@stderr.snap | 3 +- 4 files changed, 65 insertions(+), 110 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 5b2da825..039ed467 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -81,27 +81,26 @@ This will be the first release of Grapevine since it was forked from Conduit database backends. * The latter two commands had poor UX and didn't have any noticable effect on memory consumption. +9. **BREAKING:** Remove the `global.conduit_cache_capacity_modifier` and + `global.pdu_cache_capacity` configuration options. + ([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124)) + * Instead, it is now possible to configure each cache capacity individually. ### Changed -1. **BREAKING:** Rename `conduit_cache_capacity_modifier` configuration option - to `cache_capacity_modifier`. - ([5619d7e](https://gitlab.computer.surgery/matrix/grapevine/-/commit/5619d7e3180661731800e253b558b88b407d2ae7)) - * If you are explicitly setting this configuration option, make sure to - change its name before updating. -2. **BREAKING:** Rename Conduit to Grapevine. +1. **BREAKING:** Rename Conduit to Grapevine. ([360e020](https://gitlab.computer.surgery/matrix/grapevine/-/commit/360e020b644bd012ed438708b661a25fbd124f68)) * The `CONDUIT_VERSION_EXTRA` build-time environment variable has been renamed to `GRAPEVINE_VERSION_EXTRA`. This change only affects distribution packagers or non-Nix users who are building from source. If you fall into one of those categories *and* were explicitly setting this environment variable, make sure to change its name before building Grapevine. -3. **BREAKING:** Change the default port from 8000 to 6167. +2. **BREAKING:** Change the default port from 8000 to 6167. ([f205280](https://gitlab.computer.surgery/matrix/grapevine/-/commit/f2052805201f0685d850592b1c96f4861c58fb22)) * If you relied on the default port being 8000, either update your other configuration to use the new port, or explicitly configure Grapevine's port to 8000. -4. Improve tracing spans and events. +3. Improve tracing spans and events. ([!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) (merged as [5172f66](https://gitlab.computer.surgery/matrix/grapevine/-/commit/5172f66c1a90e0e97b67be2897ae59fbc00208a4)), [!11 (a275db3)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/11/diffs?commit_id=a275db3847b8d5aaa0c651a686c19cfbf9fdb8b5) @@ -117,31 +116,31 @@ This will be the first release of Grapevine since it was forked from Conduit [!69](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/69), [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102), [!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127)) -5. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. +4. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/12)) -6. **BREAKING:** Allow federation by default. +5. **BREAKING:** Allow federation by default. ([!24](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/24)) * If you relied on federation being disabled by default, make sure to explicitly disable it before upgrading. -7. **BREAKING:** Remove the `[global]` section from the configuration file. +6. **BREAKING:** Remove the `[global]` section from the configuration file. ([!38](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/38)) * Details on how to migrate can be found in the merge request's description. -8. **BREAKING:** Allow specifying multiple transport listeners in the +7. **BREAKING:** Allow specifying multiple transport listeners in the configuration file. ([!39](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/39)) * Details on how to migrate can be found in the merge request's description. -9. Increase default log level so that span information is included. +8. Increase default log level so that span information is included. ([!50](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/50)) -10. **BREAKING:** Reorganize config into sections. - ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) - * Details on how to migrate can be found in the merge request's description. -11. Try to generate thumbnails for remote media ourselves if the federation +9. **BREAKING:** Reorganize config into sections. + ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) + * Details on how to migrate can be found in the merge request's description. +10. Try to generate thumbnails for remote media ourselves if the federation thumbnail request fails. ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58)) -12. **BREAKING:** Disable unauthenticated access to media by default, set the +11. **BREAKING:** Disable unauthenticated access to media by default, set the `serve_media_unauthenticated` config option to `true` to enable it. ([!103](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/103)) -13. **BREAKING:** Split CLI into multiple subcommands. The CLI invocation to run +12. **BREAKING:** Split CLI into multiple subcommands. The CLI invocation to run the server is now behind the `serve` command, so `grapevine --config ...` becomes `grapevine serve --config ...`. ([!108](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/108)) @@ -275,3 +274,5 @@ This will be the first release of Grapevine since it was forked from Conduit 24. Add a `check-config` CLI subcommand to check whether the configuration file is valid. ([!121](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/121)) +25. Add configuration options to tune the value of each cache individually. + ([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124)) diff --git a/src/config.rs b/src/config.rs index 5c9ae2ee..54ef0e79 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,11 +46,8 @@ pub(crate) struct Config { pub(crate) database: DatabaseConfig, #[serde(default)] pub(crate) federation: FederationConfig, - - #[serde(default = "default_cache_capacity_modifier")] - pub(crate) cache_capacity_modifier: f64, - #[serde(default = "default_pdu_cache_capacity")] - pub(crate) pdu_cache_capacity: usize, + #[serde(default)] + pub(crate) cache: CacheConfig, #[serde(default = "default_cleanup_second_interval")] pub(crate) cleanup_second_interval: u32, #[serde(default = "default_max_request_size")] @@ -77,6 +74,38 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, } +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields, default)] +pub(crate) struct CacheConfig { + pub(crate) pdu: usize, + pub(crate) auth_chain: usize, + pub(crate) short_eventid: usize, + pub(crate) eventid_short: usize, + pub(crate) statekey_short: usize, + pub(crate) short_statekey: usize, + pub(crate) server_visibility: usize, + pub(crate) user_visibility: usize, + pub(crate) state_info: usize, + pub(crate) roomid_spacechunk: usize, +} + +impl Default for CacheConfig { + fn default() -> Self { + Self { + pdu: 150_000, + auth_chain: 100_000, + short_eventid: 100_000, + eventid_short: 100_000, + statekey_short: 100_000, + short_statekey: 100_000, + server_visibility: 100, + user_visibility: 100, + state_info: 100, + roomid_spacechunk: 200, + } + } +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub(crate) struct ServerDiscovery { @@ -427,19 +456,11 @@ fn default_db_cache_capacity_mb() -> f64 { 300.0 } -fn default_cache_capacity_modifier() -> f64 { - 1.0 -} - #[cfg(feature = "rocksdb")] fn default_rocksdb_max_open_files() -> i32 { 1000 } -fn default_pdu_cache_capacity() -> usize { - 150_000 -} - fn default_cleanup_second_interval() -> u32 { // every minute 60 diff --git a/src/service.rs b/src/service.rs index 56a1c8a6..85e7a302 100644 --- a/src/service.rs +++ b/src/service.rs @@ -67,14 +67,7 @@ impl Services { alias: rooms::alias::Service::new(db), auth_chain: rooms::auth_chain::Service::new( db, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100_000.0 * config.cache_capacity_modifier) as usize - }, + config.cache.auth_chain, ), directory: db, edus: rooms::edus::Service { @@ -91,89 +84,30 @@ impl Services { search: db, short: rooms::short::Service::new( db, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100_000.0 * config.cache_capacity_modifier) as usize - }, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100_000.0 * config.cache_capacity_modifier) as usize - }, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100_000.0 * config.cache_capacity_modifier) as usize - }, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100_000.0 * config.cache_capacity_modifier) as usize - }, + config.cache.short_eventid, + config.cache.eventid_short, + config.cache.statekey_short, + config.cache.short_statekey, ), state: rooms::state::Service { db, }, state_accessor: rooms::state_accessor::Service::new( db, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100.0 * config.cache_capacity_modifier) as usize - }, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100.0 * config.cache_capacity_modifier) as usize - }, + config.cache.server_visibility, + config.cache.user_visibility, ), state_cache: rooms::state_cache::Service::new(db), state_compressor: rooms::state_compressor::Service::new( db, - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (100.0 * config.cache_capacity_modifier) as usize - }, - ), - timeline: rooms::timeline::Service::new( - db, - config.pdu_cache_capacity, + config.cache.state_info, ), + timeline: rooms::timeline::Service::new(db, config.cache.pdu), threads: rooms::threads::Service { db, }, spaces: rooms::spaces::Service::new( - #[allow( - clippy::as_conversions, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - { - (200.0 * config.cache_capacity_modifier) as usize - }, + config.cache.roomid_spacechunk, ), user: db, }, diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap index 942cce3b..2b7ce043 100644 --- a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap +++ b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap @@ -1,7 +1,6 @@ --- source: tests/integrations/check_config.rs description: A config with invalid keys fails -snapshot_kind: text --- Error: failed to validate configuration Caused by: failed to parse configuration file "tests/integrations/fixtures/check_config/invalid-keys.toml" @@ -9,4 +8,4 @@ Error: failed to validate configuration | 1 | some_name = "example.com" | ^^^^^^^^^ -unknown field `some_name`, expected one of `conduit_compat`, `listen`, `tls`, `server_name`, `server_discovery`, `database`, `federation`, `cache_capacity_modifier`, `pdu_cache_capacity`, `cleanup_second_interval`, `max_request_size`, `allow_registration`, `registration_token`, `allow_encryption`, `allow_room_creation`, `serve_media_unauthenticated`, `default_room_version`, `proxy`, `jwt_secret`, `observability`, `turn`, `emergency_password` +unknown field `some_name`, expected one of `conduit_compat`, `listen`, `tls`, `server_name`, `server_discovery`, `database`, `federation`, `cache`, `cleanup_second_interval`, `max_request_size`, `allow_registration`, `registration_token`, `allow_encryption`, `allow_room_creation`, `serve_media_unauthenticated`, `default_room_version`, `proxy`, `jwt_secret`, `observability`, `turn`, `emergency_password` From d42a5ec1f0e40d79f0569363cad487e3960287d1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 8 Oct 2024 22:28:52 -0700 Subject: [PATCH 452/617] avoid overhead when cache sizes are zero Don't even try taking locks, inserting or removing anything, etc. --- src/service/rooms/auth_chain.rs | 26 ++--- src/service/rooms/short.rs | 135 +++++++++++++------------- src/service/rooms/spaces.rs | 102 +++++++++---------- src/service/rooms/state_accessor.rs | 72 ++++++++------ src/service/rooms/state_compressor.rs | 23 ++--- 5 files changed, 184 insertions(+), 174 deletions(-) diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 97c95f11..3c8e4b38 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -21,8 +21,9 @@ pub(crate) use data::Data; pub(crate) struct Service { db: &'static dyn Data, + #[allow(clippy::type_complexity)] auth_chain_cache: - Mutex, Arc>>>, + Option, Arc>>>>, } impl Service { @@ -32,7 +33,8 @@ impl Service { ) -> Self { Self { db, - auth_chain_cache: Mutex::new(LruCache::new(auth_chain_cache_size)), + auth_chain_cache: (auth_chain_cache_size > 0) + .then(|| Mutex::new(LruCache::new(auth_chain_cache_size))), } } @@ -42,10 +44,11 @@ impl Service { ) -> Result>>> { let lookup = Lookup::AuthChain; - if let Some(result) = self.auth_chain_cache.lock().unwrap().get_mut(key) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Some(Arc::clone(result))); + if let Some(cache) = &self.auth_chain_cache { + if let Some(result) = cache.lock().unwrap().get_mut(key) { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Some(Arc::clone(result))); + } } let Some(chain) = self.db.get_cached_eventid_authchain(key)? else { @@ -56,10 +59,9 @@ impl Service { METRICS.record_lookup(lookup, FoundIn::Database); let chain = Arc::new(chain); - self.auth_chain_cache - .lock() - .unwrap() - .insert(vec![key[0]], Arc::clone(&chain)); + if let Some(cache) = &self.auth_chain_cache { + cache.lock().unwrap().insert(vec![key[0]], Arc::clone(&chain)); + } Ok(Some(chain)) } @@ -71,7 +73,9 @@ impl Service { auth_chain: Arc>, ) -> Result<()> { self.db.cache_auth_chain(&key, &auth_chain)?; - self.auth_chain_cache.lock().unwrap().insert(key, auth_chain); + if let Some(cache) = &self.auth_chain_cache { + cache.lock().unwrap().insert(key, auth_chain); + } Ok(()) } diff --git a/src/service/rooms/short.rs b/src/service/rooms/short.rs index 917e5a85..ce4d207c 100644 --- a/src/service/rooms/short.rs +++ b/src/service/rooms/short.rs @@ -37,12 +37,12 @@ pub(crate) use data::Data; pub(crate) struct Service { db: &'static dyn Data, - shorteventid_cache: Mutex>>, - eventidshort_cache: Mutex>, + shorteventid_cache: Option>>>, + eventidshort_cache: Option>>, statekeyshort_cache: - Mutex>, + Option>>, shortstatekey_cache: - Mutex>, + Option>>, } impl Service { @@ -55,18 +55,14 @@ impl Service { ) -> Self { Self { db, - shorteventid_cache: Mutex::new(LruCache::new( - shorteventid_cache_size, - )), - eventidshort_cache: Mutex::new(LruCache::new( - eventidshort_cache_size, - )), - statekeyshort_cache: Mutex::new(LruCache::new( - statekeyshort_cache_size, - )), - shortstatekey_cache: Mutex::new(LruCache::new( - shortstatekey_cache_size, - )), + shorteventid_cache: (shorteventid_cache_size > 0) + .then(|| Mutex::new(LruCache::new(shorteventid_cache_size))), + eventidshort_cache: (eventidshort_cache_size > 0) + .then(|| Mutex::new(LruCache::new(eventidshort_cache_size))), + statekeyshort_cache: (statekeyshort_cache_size > 0) + .then(|| Mutex::new(LruCache::new(statekeyshort_cache_size))), + shortstatekey_cache: (shortstatekey_cache_size > 0) + .then(|| Mutex::new(LruCache::new(shortstatekey_cache_size))), } } @@ -76,11 +72,11 @@ impl Service { ) -> Result { let lookup = Lookup::CreateEventIdToShort; - if let Some(short) = - self.eventidshort_cache.lock().unwrap().get_mut(event_id) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(*short); + if let Some(cache) = &self.eventidshort_cache { + if let Some(short) = cache.lock().unwrap().get_mut(event_id) { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(*short); + } } let (short, created) = self.db.get_or_create_shorteventid(event_id)?; @@ -91,10 +87,9 @@ impl Service { METRICS.record_lookup(lookup, FoundIn::Database); } - self.eventidshort_cache - .lock() - .unwrap() - .insert(event_id.to_owned(), short); + if let Some(cache) = &self.eventidshort_cache { + cache.lock().unwrap().insert(event_id.to_owned(), short); + } Ok(short) } @@ -106,14 +101,15 @@ impl Service { ) -> Result> { let lookup = Lookup::StateKeyToShort; - if let Some(short) = self - .statekeyshort_cache - .lock() - .unwrap() - .get_mut(&(event_type.clone(), state_key.to_owned())) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Some(*short)); + if let Some(cache) = &self.statekeyshort_cache { + if let Some(short) = cache + .lock() + .unwrap() + .get_mut(&(event_type.clone(), state_key.to_owned())) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Some(*short)); + } } let short = self.db.get_shortstatekey(event_type, state_key)?; @@ -121,10 +117,12 @@ impl Service { if let Some(short) = short { METRICS.record_lookup(lookup, FoundIn::Database); - self.statekeyshort_cache - .lock() - .unwrap() - .insert((event_type.clone(), state_key.to_owned()), short); + if let Some(cache) = &self.statekeyshort_cache { + cache + .lock() + .unwrap() + .insert((event_type.clone(), state_key.to_owned()), short); + } } else { METRICS.record_lookup(lookup, FoundIn::Nothing); } @@ -139,14 +137,15 @@ impl Service { ) -> Result { let lookup = Lookup::CreateStateKeyToShort; - if let Some(short) = self - .statekeyshort_cache - .lock() - .unwrap() - .get_mut(&(event_type.clone(), state_key.to_owned())) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(*short); + if let Some(cache) = &self.statekeyshort_cache { + if let Some(short) = cache + .lock() + .unwrap() + .get_mut(&(event_type.clone(), state_key.to_owned())) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(*short); + } } let (short, created) = @@ -158,10 +157,12 @@ impl Service { METRICS.record_lookup(lookup, FoundIn::Database); } - self.statekeyshort_cache - .lock() - .unwrap() - .insert((event_type.clone(), state_key.to_owned()), short); + if let Some(cache) = &self.statekeyshort_cache { + cache + .lock() + .unwrap() + .insert((event_type.clone(), state_key.to_owned()), short); + } Ok(short) } @@ -172,21 +173,19 @@ impl Service { ) -> Result> { let lookup = Lookup::ShortToEventId; - if let Some(id) = - self.shorteventid_cache.lock().unwrap().get_mut(&shorteventid) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(Arc::clone(id)); + if let Some(cache) = &self.shorteventid_cache { + if let Some(id) = cache.lock().unwrap().get_mut(&shorteventid) { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(Arc::clone(id)); + } } - let event_id = self.db.get_eventid_from_short(shorteventid)?; METRICS.record_lookup(lookup, FoundIn::Database); - self.shorteventid_cache - .lock() - .unwrap() - .insert(shorteventid, Arc::clone(&event_id)); + if let Some(cache) = &self.shorteventid_cache { + cache.lock().unwrap().insert(shorteventid, Arc::clone(&event_id)); + } Ok(event_id) } @@ -197,21 +196,19 @@ impl Service { ) -> Result<(StateEventType, String)> { let lookup = Lookup::ShortToStateKey; - if let Some(id) = - self.shortstatekey_cache.lock().unwrap().get_mut(&shortstatekey) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(id.clone()); + if let Some(cache) = &self.shortstatekey_cache { + if let Some(id) = cache.lock().unwrap().get_mut(&shortstatekey) { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(id.clone()); + } } - let x = self.db.get_statekey_from_short(shortstatekey)?; METRICS.record_lookup(lookup, FoundIn::Database); - self.shortstatekey_cache - .lock() - .unwrap() - .insert(shortstatekey, x.clone()); + if let Some(cache) = &self.shortstatekey_cache { + cache.lock().unwrap().insert(shortstatekey, x.clone()); + } Ok(x) } diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index ad5f4da0..2011d5b5 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -46,15 +46,15 @@ pub(crate) struct CachedSpaceChunk { pub(crate) struct Service { roomid_spacechunk_cache: - Mutex>>, + Option>>>, } impl Service { pub(crate) fn new(roomid_spacechunk_cache_size: usize) -> Self { Self { - roomid_spacechunk_cache: Mutex::new(LruCache::new( - roomid_spacechunk_cache_size, - )), + roomid_spacechunk_cache: (roomid_spacechunk_cache_size > 0).then( + || Mutex::new(LruCache::new(roomid_spacechunk_cache_size)), + ), } } @@ -90,35 +90,32 @@ impl Service { break; } - if let Some(cached) = self - .roomid_spacechunk_cache - .lock() - .await - .get_mut(¤t_room.clone()) - .as_ref() - { - if let Some(cached) = cached { - let allowed = match &cached.join_rule { - CachedJoinRule::Full(f) => self.handle_join_rule( - f, - sender_user, - ¤t_room, - )?, - }; - if allowed { - if left_to_skip > 0 { - left_to_skip -= 1; - } else { - results.push(cached.chunk.clone()); - } - if rooms_in_path.len() < max_depth { - stack.push(cached.children.clone()); + if let Some(cache) = &self.roomid_spacechunk_cache { + if let Some(cached) = + cache.lock().await.get_mut(¤t_room.clone()).as_ref() + { + if let Some(cached) = cached { + let allowed = match &cached.join_rule { + CachedJoinRule::Full(f) => self.handle_join_rule( + f, + sender_user, + ¤t_room, + )?, + }; + if allowed { + if left_to_skip > 0 { + left_to_skip -= 1; + } else { + results.push(cached.chunk.clone()); + } + if rooms_in_path.len() < max_depth { + stack.push(cached.children.clone()); + } } } + continue; } - continue; } - if let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(¤t_room)? { @@ -201,14 +198,16 @@ impl Service { .transpose()? .unwrap_or(JoinRule::Invite); - self.roomid_spacechunk_cache.lock().await.insert( - current_room.clone(), - Some(CachedSpaceChunk { - chunk, - children: children_ids.clone(), - join_rule: CachedJoinRule::Full(join_rule), - }), - ); + if let Some(cache) = &self.roomid_spacechunk_cache { + cache.lock().await.insert( + current_room.clone(), + Some(CachedSpaceChunk { + chunk, + children: children_ids.clone(), + join_rule: CachedJoinRule::Full(join_rule), + }), + ); + } } if rooms_in_path.len() < max_depth { @@ -307,19 +306,18 @@ impl Service { } } - self.roomid_spacechunk_cache.lock().await.insert( - current_room.clone(), - Some(CachedSpaceChunk { - chunk, - children, - join_rule: CachedJoinRule::Full(join_rule), - }), - ); - } else { - self.roomid_spacechunk_cache - .lock() - .await - .insert(current_room.clone(), None); + if let Some(cache) = &self.roomid_spacechunk_cache { + cache.lock().await.insert( + current_room.clone(), + Some(CachedSpaceChunk { + chunk, + children, + join_rule: CachedJoinRule::Full(join_rule), + }), + ); + } + } else if let Some(cache) = &self.roomid_spacechunk_cache { + cache.lock().await.insert(current_room.clone(), None); } } } @@ -527,7 +525,9 @@ impl Service { } pub(crate) async fn invalidate_cache(&self, room_id: &RoomId) { - self.roomid_spacechunk_cache.lock().await.remove(room_id); + if let Some(cache) = &self.roomid_spacechunk_cache { + cache.lock().await.remove(room_id); + } } fn translate_joinrule(join_rule: &JoinRule) -> Result { diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index a3922249..108e510d 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -40,8 +40,9 @@ pub(crate) use data::Data; pub(crate) struct Service { db: &'static dyn Data, server_visibility_cache: - Mutex>, - user_visibility_cache: Mutex>, + Option>>, + user_visibility_cache: + Option>>, } impl Service { @@ -52,12 +53,11 @@ impl Service { ) -> Self { Self { db, - server_visibility_cache: Mutex::new(LruCache::new( - server_visibility_cache_size, - )), - user_visibility_cache: Mutex::new(LruCache::new( - user_visibility_cache_size, - )), + server_visibility_cache: (server_visibility_cache_size > 0).then( + || Mutex::new(LruCache::new(server_visibility_cache_size)), + ), + user_visibility_cache: (user_visibility_cache_size > 0) + .then(|| Mutex::new(LruCache::new(user_visibility_cache_size))), } } @@ -165,14 +165,15 @@ impl Service { return Ok(true); }; - if let Some(visibility) = self - .server_visibility_cache - .lock() - .unwrap() - .get_mut(&(origin.to_owned(), shortstatehash)) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(*visibility); + if let Some(cache) = &self.server_visibility_cache { + if let Some(visibility) = cache + .lock() + .unwrap() + .get_mut(&(origin.to_owned(), shortstatehash)) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(*visibility); + } } let history_visibility = self @@ -223,10 +224,13 @@ impl Service { }; METRICS.record_lookup(lookup, FoundIn::Database); - self.server_visibility_cache - .lock() - .unwrap() - .insert((origin.to_owned(), shortstatehash), visibility); + + if let Some(cache) = &self.server_visibility_cache { + cache + .lock() + .unwrap() + .insert((origin.to_owned(), shortstatehash), visibility); + } Ok(visibility) } @@ -246,14 +250,15 @@ impl Service { return Ok(true); }; - if let Some(visibility) = self - .user_visibility_cache - .lock() - .unwrap() - .get_mut(&(user_id.to_owned(), shortstatehash)) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(*visibility); + if let Some(cache) = &self.user_visibility_cache { + if let Some(visibility) = cache + .lock() + .unwrap() + .get_mut(&(user_id.to_owned(), shortstatehash)) + { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(*visibility); + } } let currently_member = @@ -296,10 +301,13 @@ impl Service { }; METRICS.record_lookup(lookup, FoundIn::Database); - self.user_visibility_cache - .lock() - .unwrap() - .insert((user_id.to_owned(), shortstatehash), visibility); + + if let Some(cache) = &self.user_visibility_cache { + cache + .lock() + .unwrap() + .insert((user_id.to_owned(), shortstatehash), visibility); + } Ok(visibility) } diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index cac73fe0..60fbb9c5 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -32,7 +32,7 @@ pub(crate) struct Service { #[allow(clippy::type_complexity)] pub(crate) stateinfo_cache: - Mutex>>, + Option>>>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -78,7 +78,8 @@ impl Service { ) -> Self { Self { db, - stateinfo_cache: Mutex::new(LruCache::new(stateinfo_cache_size)), + stateinfo_cache: (stateinfo_cache_size > 0) + .then(|| Mutex::new(LruCache::new(stateinfo_cache_size))), } } @@ -92,11 +93,11 @@ impl Service { ) -> Result> { let lookup = Lookup::StateInfo; - if let Some(r) = - self.stateinfo_cache.lock().unwrap().get_mut(&shortstatehash) - { - METRICS.record_lookup(lookup, FoundIn::Cache); - return Ok(r.clone()); + if let Some(cache) = &self.stateinfo_cache { + if let Some(r) = cache.lock().unwrap().get_mut(&shortstatehash) { + METRICS.record_lookup(lookup, FoundIn::Cache); + return Ok(r.clone()); + } } let StateDiff { @@ -131,10 +132,10 @@ impl Service { }; METRICS.record_lookup(lookup, FoundIn::Database); - self.stateinfo_cache - .lock() - .unwrap() - .insert(shortstatehash, response.clone()); + + if let Some(cache) = &self.stateinfo_cache { + cache.lock().unwrap().insert(shortstatehash, response.clone()); + } Ok(response) } From f07c8c2b6f4eb5a7022226f511054025a72cd55a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 12:15:09 -0700 Subject: [PATCH 453/617] update flake.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And some supporting changes: * crane: It removed its dependency on nixpkgs and made overrideToolchain take a function for splicing reasons, but we're doing splicing ourselves so we can just ignore the function argument. These changes are in `flake.nix`. * [NixOS/nixpkgs#347228][0]: linkerFor* were removed because the linker no longer needs to be different in some edge cases. Based on the diff of the PR that introduced this change, ccFor* are the proper replacements. These changes are in `cross-compilation-env.nix` in the compiler-and-linker-choosing section. * [NixOS/nixpkgs#350299][1]: buildPlatform isn't at the top level anymore, we have to go through stdenv now. These changes are in `nix/shell.nix`. * rocksdb: nixpkgs has 9.6.1 now so we need to upgrade our rust library to use the matching version. These changes are in `Cargo.toml`, `Cargo.lock`, `nix/pkgs/default/default.nix`, and `cross-compilation-env.nix` in the linker flags section. [0]: https://github.com/NixOS/nixpkgs/pull/347228 [1]: https://github.com/NixOS/nixpkgs/pull/350299 Flake lock file updates: • Updated input 'attic': 'github:zhaofengli/attic/4dbdbee45728d8ce5788db6461aaaa89d98081f0' (2024-03-29) → 'github:zhaofengli/attic/48c8b395bfbc6b76c7eae74df6c74351255a095c' (2024-10-30) • Updated input 'attic/crane': 'github:ipetkov/crane/7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb' (2023-12-18) → 'github:ipetkov/crane/4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4' (2024-08-06) • Updated input 'attic/flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9' (2023-01-17) → 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) • Added input 'attic/flake-parts': 'github:hercules-ci/flake-parts/8471fe90ad337a8074e957b69ca4d0089218391d' (2024-08-01) • Added input 'attic/flake-parts/nixpkgs-lib': follows 'attic/nixpkgs' • Removed input 'attic/flake-utils' • Updated input 'attic/nixpkgs': 'github:NixOS/nixpkgs/07262b18b97000d16a4bdb003418bd2fb067a932' (2024-03-25) → 'github:NixOS/nixpkgs/159be5db480d1df880a0135ca0bfed84c2f88353' (2024-09-11) • Updated input 'attic/nixpkgs-stable': 'github:NixOS/nixpkgs/44733514b72e732bd49f5511bd0203dea9b9a434' (2024-03-26) → 'github:NixOS/nixpkgs/797f7dc49e0bc7fab4b57c021cdf68f595e47841' (2024-08-22) • Added input 'attic/nix-github-actions': 'github:nix-community/nix-github-actions/e04df33f62cdcf93d73e9a04142464753a16db67' (2024-10-24) • Added input 'attic/nix-github-actions/nixpkgs': follows 'attic/nixpkgs' • Updated input 'crane': 'github:ipetkov/crane/109987da061a1bf452f435f1653c47511587d919' (2024-05-24) → 'github:ipetkov/crane/498d9f122c413ee1154e8131ace5a35a80d8fa76' (2024-10-27) • Removed input 'crane/nixpkgs' • Updated input 'fenix': 'github:nix-community/fenix/b6fc5035b28e36a98370d0eac44f4ef3fd323df6' (2024-05-22) → 'github:nix-community/fenix/87b4d20f896c99018dde4702a9c6157b516f2a76' (2024-11-01) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/21ec8f523812b88418b2bfc64240c62b3dd967bd' (2024-05-19) → 'github:rust-lang/rust-analyzer/0ba893e1a00d92557ac91efb771d72eee36ca687' (2024-10-31) • Updated input 'flake-utils': 'github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a' (2024-03-11) → 'github:numtide/flake-utils/c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a' (2024-09-17) • Updated input 'nix-filter': 'github:numtide/nix-filter/3342559a24e85fc164b295c3444e8a139924675b' (2024-03-11) → 'github:numtide/nix-filter/776e68c1d014c3adde193a18db9d738458cd2ba4' (2024-10-29) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/5710852ba686cc1fd0d3b8e22b3117d43ba374c2' (2024-05-21) → 'github:NixOS/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd' (2024-10-29) --- Cargo.lock | 8 +- Cargo.toml | 2 +- flake.lock | 123 ++++++++++++--------- flake.nix | 4 +- nix/pkgs/default/cross-compilation-env.nix | 37 +++---- nix/pkgs/default/default.nix | 3 + nix/shell.nix | 6 +- 7 files changed, 102 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 591033a0..e3c2baa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2484,9 +2484,9 @@ dependencies = [ [[package]] name = "rust-librocksdb-sys" -version = "0.22.0+9.2.1" +version = "0.26.0+9.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eacd840bcfbba938d74f67981a32bc908fe57d1cf6f39b131cb8c3f33fe67d1c" +checksum = "4508cf0cb12feb8185556cebc1bf2e53925b415e7b5cb3bcaaff5d90f57eae4e" dependencies = [ "bindgen", "bzip2-sys", @@ -2500,9 +2500,9 @@ dependencies = [ [[package]] name = "rust-rocksdb" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa60811a194b1d25b8b7761786fe8a4a3fb962ccb15ec246a337187ebcd9b8fd" +checksum = "98735c6ebacc6796c0f74814de76d99dd379df7afa7389c801ed11bba5782884" dependencies = [ "libc", "rust-librocksdb-sys", diff --git a/Cargo.toml b/Cargo.toml index d340dca5..e989f459 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,7 @@ rand = "0.8.5" regex = "1.10.6" reqwest = { version = "0.12.7", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.8" -rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +rocksdb = { package = "rust-rocksdb", version = "0.30.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } rustls = { version = "0.23.13", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } diff --git a/flake.lock b/flake.lock index c63aebc0..892de814 100644 --- a/flake.lock +++ b/flake.lock @@ -4,16 +4,17 @@ "inputs": { "crane": "crane", "flake-compat": "flake-compat", - "flake-utils": "flake-utils", + "flake-parts": "flake-parts", + "nix-github-actions": "nix-github-actions", "nixpkgs": "nixpkgs", "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1711742460, - "narHash": "sha256-0O4v6e4a1toxXZ2gf5INhg4WPE5C5T+SVvsBt+45Mcc=", + "lastModified": 1730257295, + "narHash": "sha256-OQl+aAsKiyygvpzck1u0sZf/R4T9zM903CgNDFmmzA8=", "owner": "zhaofengli", "repo": "attic", - "rev": "4dbdbee45728d8ce5788db6461aaaa89d98081f0", + "rev": "48c8b395bfbc6b76c7eae74df6c74351255a095c", "type": "github" }, "original": { @@ -31,11 +32,11 @@ ] }, "locked": { - "lastModified": 1702918879, - "narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=", + "lastModified": 1722960479, + "narHash": "sha256-NhCkJJQhD5GUib8zN9JrmYGMwt4lCRp6ZVNzIiYCl0Y=", "owner": "ipetkov", "repo": "crane", - "rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb", + "rev": "4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4", "type": "github" }, "original": { @@ -45,17 +46,12 @@ } }, "crane_2": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1716569590, - "narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=", + "lastModified": 1730060262, + "narHash": "sha256-RMgSVkZ9H03sxC+Vh4jxtLTCzSjPq18UWpiM0gq6shQ=", "owner": "ipetkov", "repo": "crane", - "rev": "109987da061a1bf452f435f1653c47511587d919", + "rev": "498d9f122c413ee1154e8131ace5a35a80d8fa76", "type": "github" }, "original": { @@ -73,11 +69,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1716359173, - "narHash": "sha256-pYcjP6Gy7i6jPWrjiWAVV0BCQp+DdmGaI/k65lBb/kM=", + "lastModified": 1730442928, + "narHash": "sha256-U1DWb5c3EfkA7pqx5V1H4AWRA+EaE6UJ0lIRvK1RxgM=", "owner": "nix-community", "repo": "fenix", - "rev": "b6fc5035b28e36a98370d0eac44f4ef3fd323df6", + "rev": "87b4d20f896c99018dde4702a9c6157b516f2a76", "type": "github" }, "original": { @@ -90,11 +86,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -120,31 +116,37 @@ "type": "github" } }, - "flake-utils": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "attic", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "lastModified": 1722555600, + "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "hercules-ci", + "repo": "flake-parts", "type": "github" } }, - "flake-utils_2": { + "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "type": "github" }, "original": { @@ -156,11 +158,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1710156097, - "narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=", + "lastModified": 1730207686, + "narHash": "sha256-SCHiL+1f7q9TAnxpasriP6fMarWE5H43t25F5/9e28I=", "owner": "numtide", "repo": "nix-filter", - "rev": "3342559a24e85fc164b295c3444e8a139924675b", + "rev": "776e68c1d014c3adde193a18db9d738458cd2ba4", "type": "github" }, "original": { @@ -170,13 +172,34 @@ "type": "github" } }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "attic", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729742964, + "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "e04df33f62cdcf93d73e9a04142464753a16db67", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1711401922, - "narHash": "sha256-QoQqXoj8ClGo0sqD/qWKFWezgEwUL0SUh37/vY2jNhc=", + "lastModified": 1726042813, + "narHash": "sha256-LnNKCCxnwgF+575y0pxUdlGZBO/ru1CtGHIqQVfvjlA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "07262b18b97000d16a4bdb003418bd2fb067a932", + "rev": "159be5db480d1df880a0135ca0bfed84c2f88353", "type": "github" }, "original": { @@ -188,27 +211,27 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1711460390, - "narHash": "sha256-akSgjDZL6pVHEfSE6sz1DNSXuYX6hq+P/1Z5IoYWs7E=", + "lastModified": 1724316499, + "narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "44733514b72e732bd49f5511bd0203dea9b9a434", + "rev": "797f7dc49e0bc7fab4b57c021cdf68f595e47841", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1716330097, - "narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=", + "lastModified": 1730200266, + "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5710852ba686cc1fd0d3b8e22b3117d43ba374c2", + "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", "type": "github" }, "original": { @@ -224,7 +247,7 @@ "crane": "crane_2", "fenix": "fenix", "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", "nix-filter": "nix-filter", "nixpkgs": "nixpkgs_2", "rust-manifest": "rust-manifest" @@ -233,11 +256,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1716107283, - "narHash": "sha256-NJgrwLiLGHDrCia5AeIvZUHUY7xYGVryee0/9D3Ir1I=", + "lastModified": 1730386175, + "narHash": "sha256-0Uq+/B8eu7pw8B8pxuGdFYKjcVLwNMcHfDxU9sXh7rg=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "21ec8f523812b88418b2bfc64240c62b3dd967bd", + "rev": "0ba893e1a00d92557ac91efb771d72eee36ca687", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 50fe2135..80621ee3 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ # Keep sorted inputs = { attic.url = "github:zhaofengli/attic?ref=main"; - crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; }; + crane.url = "github:ipetkov/crane?ref=master"; fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; flake-compat = { url = "github:edolstra/flake-compat?ref=master"; flake = false; }; flake-utils.url = "github:numtide/flake-utils?ref=main"; @@ -21,7 +21,7 @@ # Keep sorted mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: { craneLib = - (inputs.crane.mkLib pkgs).overrideToolchain self.toolchain; + (inputs.crane.mkLib pkgs).overrideToolchain (_: self.toolchain); default = self.callPackage ./nix/pkgs/default {}; diff --git a/nix/pkgs/default/cross-compilation-env.nix b/nix/pkgs/default/cross-compilation-env.nix index fac85e02..b10b9a26 100644 --- a/nix/pkgs/default/cross-compilation-env.nix +++ b/nix/pkgs/default/cross-compilation-env.nix @@ -1,6 +1,8 @@ +# Keep sorted { lib , pkgsBuildHost , rust +, snappy , stdenv }: @@ -20,29 +22,23 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic { ++ lib.optionals stdenv.hostPlatform.isStatic [ "-C" "relocation-model=static" ] + + # I'm not sure why any of this is necessary but it is so *shrug* ++ lib.optionals (stdenv.buildPlatform.config != stdenv.hostPlatform.config) - [ "-l" "c" ] - ++ lib.optionals - # This check has to match the one [here][0]. We only need to set - # these flags when using a different linker. Don't ask me why, though, - # because I don't know. All I know is it breaks otherwise. - # - # [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L37-L40 - ( - # Nixpkgs doesn't check for x86_64 here but we do, because I - # observed a failure building statically for x86_64 without - # including it here. Linkers are weird. - (stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64) - && stdenv.hostPlatform.isStatic - && !stdenv.isDarwin - && !stdenv.cc.bintools.isLLVM - ) [ + "-l" + "c" + "-l" "stdc++" "-L" "${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib" + + "-l" + "snappy" + "-L" + "${snappy}/lib" ] ); } @@ -52,7 +48,7 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic { # case of build scripts that need native code compiled and run on the build # platform (I think). # -# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L57-L80 +# [0]: https://github.com/NixOS/nixpkgs/blob/2768c7d042a37de65bb1b5b3268fc987e534c49d/pkgs/build-support/rust/lib/default.nix#L45-L68 // ( let @@ -68,8 +64,7 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic { { "CC_${cargoEnvVarTarget}" = envVars.ccForTarget; "CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget; - "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = - envVars.linkerForTarget; + "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.ccForTarget; } ) // @@ -80,7 +75,7 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic { { "CC_${cargoEnvVarTarget}" = envVars.ccForHost; "CXX_${cargoEnvVarTarget}" = envVars.cxxForHost; - "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost; + "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.ccForHost; CARGO_BUILD_TARGET = rustcTarget; } ) @@ -92,7 +87,7 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic { { "CC_${cargoEnvVarTarget}" = envVars.ccForBuild; "CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild; - "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild; + "CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.ccForBuild; HOST_CC = "${pkgsBuildHost.stdenv.cc}/bin/cc"; HOST_CXX = "${pkgsBuildHost.stdenv.cc}/bin/c++"; } diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index fe31cc74..67b55b95 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -7,6 +7,7 @@ , rocksdb , rust , rust-jemalloc-sys +, snappy , stdenv # Options (keep sorted) @@ -48,6 +49,7 @@ let rocksdb' = rocksdb.override { jemalloc = rust-jemalloc-sys'; enableJemalloc = featureEnabled "jemalloc"; + enableLiburing = false; }; in { @@ -63,6 +65,7 @@ let lib pkgsBuildHost rust + snappy stdenv; }); diff --git a/nix/shell.nix b/nix/shell.nix index 91388d90..6b4ff81a 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,6 +1,5 @@ # Keep sorted -{ buildPlatform -, default +{ default , cargo-insta , engage , inputs @@ -9,6 +8,7 @@ , markdownlint-cli , mdbook , mkShell +, stdenv , toolchain }: @@ -26,7 +26,7 @@ mkShell { # # This needs to come before `toolchain` in this list, otherwise # `$PATH` will have stable rustfmt instead. - inputs.fenix.packages.${buildPlatform.system}.latest.rustfmt + inputs.fenix.packages.${stdenv.buildPlatform.system}.latest.rustfmt # Keep sorted cargo-insta From 2e6bf86a423650e238ad633874017fe306b19f38 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 14:51:16 -0700 Subject: [PATCH 454/617] move rust-jemalloc-sys to its own file --- flake.nix | 4 ++++ nix/pkgs/default/default.nix | 14 ++------------ nix/pkgs/rust-jemalloc-sys/default.nix | 12 ++++++++++++ 3 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 nix/pkgs/rust-jemalloc-sys/default.nix diff --git a/flake.nix b/flake.nix index 80621ee3..d8e4365a 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,10 @@ default = prev.default.override args; }); + rust-jemalloc-sys = self.callPackage ./nix/pkgs/rust-jemalloc-sys { + inherit (pkgs) rust-jemalloc-sys; + }; + shell = self.callPackage ./nix/shell.nix {}; # The Rust toolchain to use diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 67b55b95..d46a3d81 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -34,20 +34,10 @@ let featureEnabled = feature : builtins.elem feature features'; - # This derivation will set the JEMALLOC_OVERRIDE variable, causing the - # tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's - # own. In order for this to work, we need to set flags on the build that match - # whatever flags tikv-jemalloc-sys was going to use. These are dependent on - # which features we enable in tikv-jemalloc-sys. - rust-jemalloc-sys' = (rust-jemalloc-sys.override { - # tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms feature - unprefixed = true; - }); - buildDepsOnlyEnv = let rocksdb' = rocksdb.override { - jemalloc = rust-jemalloc-sys'; + jemalloc = rust-jemalloc-sys; enableJemalloc = featureEnabled "jemalloc"; enableLiburing = false; }; @@ -92,7 +82,7 @@ let dontStrip = profile == "dev"; - buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys'; + buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys; nativeBuildInputs = [ # bindgen needs the build platform's libclang. Apparently due to "splicing diff --git a/nix/pkgs/rust-jemalloc-sys/default.nix b/nix/pkgs/rust-jemalloc-sys/default.nix new file mode 100644 index 00000000..7d1b178d --- /dev/null +++ b/nix/pkgs/rust-jemalloc-sys/default.nix @@ -0,0 +1,12 @@ +# This derivation will set the JEMALLOC_OVERRIDE variable, causing the +# tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's +# own. In order for this to work, we need to set flags on the build that match +# whatever flags tikv-jemalloc-sys was going to use. These are dependent on +# which features we enable in tikv-jemalloc-sys. + +{ rust-jemalloc-sys }: + +rust-jemalloc-sys.override { + # tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms feature + unprefixed = true; +} From 9f4a1578aa9beaf5f45f1d3ff321accb8a03b579 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 14:54:39 -0700 Subject: [PATCH 455/617] move rocksdb to its own file --- flake.nix | 4 ++++ nix/pkgs/default/default.nix | 2 -- nix/pkgs/rocksdb/default.nix | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 nix/pkgs/rocksdb/default.nix diff --git a/flake.nix b/flake.nix index d8e4365a..55d0f9b8 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,10 @@ default = prev.default.override args; }); + rocksdb = self.callPackage ./nix/pkgs/rocksdb { + inherit (pkgs) rocksdb; + }; + rust-jemalloc-sys = self.callPackage ./nix/pkgs/rust-jemalloc-sys { inherit (pkgs) rust-jemalloc-sys; }; diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index d46a3d81..7aa9151e 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -37,9 +37,7 @@ let buildDepsOnlyEnv = let rocksdb' = rocksdb.override { - jemalloc = rust-jemalloc-sys; enableJemalloc = featureEnabled "jemalloc"; - enableLiburing = false; }; in { diff --git a/nix/pkgs/rocksdb/default.nix b/nix/pkgs/rocksdb/default.nix new file mode 100644 index 00000000..ad18fdcc --- /dev/null +++ b/nix/pkgs/rocksdb/default.nix @@ -0,0 +1,14 @@ +# Dependencies (keep sorted) +{ rocksdb +, rust-jemalloc-sys + +# Options (keep sorted) +, enableJemalloc ? false +}: + +rocksdb.override { + jemalloc = rust-jemalloc-sys; + + enableLiburing = false; + inherit enableJemalloc; +} From a550d8db1f4aca50a6e08d1495e1cd215f219f49 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 15:20:04 -0700 Subject: [PATCH 456/617] assert that rocksdb versions match during eval --- nix/pkgs/rocksdb/default.nix | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/nix/pkgs/rocksdb/default.nix b/nix/pkgs/rocksdb/default.nix index ad18fdcc..4468c4d6 100644 --- a/nix/pkgs/rocksdb/default.nix +++ b/nix/pkgs/rocksdb/default.nix @@ -1,14 +1,39 @@ # Dependencies (keep sorted) -{ rocksdb +{ lib +, rocksdb , rust-jemalloc-sys # Options (keep sorted) , enableJemalloc ? false }: -rocksdb.override { - jemalloc = rust-jemalloc-sys; +let + rocksdb' = rocksdb.override { + jemalloc = rust-jemalloc-sys; - enableLiburing = false; - inherit enableJemalloc; -} + enableLiburing = false; + inherit enableJemalloc; + }; + + cVersion = rocksdb'.version; + + rustVersion = builtins.elemAt + (lib.splitString + "+" + (lib.findSingle + (x: x.name == "rust-librocksdb-sys") + (builtins.throw "Multiple rust-librocksdb-sys versions in Cargo.lock") + (builtins.throw "No rust-librocksdb-sys versions in Cargo.lock") + (builtins.fromTOML (builtins.readFile ../../../Cargo.lock)).package + ).version + ) + 1; +in + +if cVersion == rustVersion + then rocksdb' + else builtins.throw + (builtins.concatStringsSep " " [ + "C version (${cVersion}) and Rust version (${rustVersion}) of RocksDB do" + "not match" + ]) From e74c8687f523073d113b1009699853a00e057b8c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 15:31:04 -0700 Subject: [PATCH 457/617] drop dependency on once-cell --- Cargo.lock | 1 - Cargo.toml | 1 - src/config.rs | 6 +++--- src/observability.rs | 14 +++++++++----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3c2baa4..1f6f9c36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -888,7 +888,6 @@ dependencies = [ "lru-cache", "nix", "num_cpus", - "once_cell", "opentelemetry", "opentelemetry-jaeger-propagator", "opentelemetry-otlp", diff --git a/Cargo.toml b/Cargo.toml index e989f459..dfd7e7f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,6 @@ image = { version = "0.25.2", default-features = false, features = ["jpeg", "png jsonwebtoken = "9.3.0" lru-cache = "0.1.2" num_cpus = "1.16.0" -once_cell = "1.19.0" opentelemetry = "0.24.0" opentelemetry-jaeger-propagator = "0.3.0" opentelemetry-otlp = "0.17.0" diff --git a/src/config.rs b/src/config.rs index 54ef0e79..a440a455 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,9 +4,9 @@ use std::{ fmt::{self, Display}, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, + sync::LazyLock, }; -use once_cell::sync::Lazy; use reqwest::Url; use ruma::{ api::federation::discovery::OldVerifyKey, OwnedServerName, @@ -24,8 +24,8 @@ pub(crate) use env_filter_clone::EnvFilterClone; use proxy::ProxyConfig; /// The default configuration file path -pub(crate) static DEFAULT_PATH: Lazy = - Lazy::new(|| [env!("CARGO_PKG_NAME"), "config.toml"].iter().collect()); +pub(crate) static DEFAULT_PATH: LazyLock = + LazyLock::new(|| [env!("CARGO_PKG_NAME"), "config.toml"].iter().collect()); #[allow(clippy::struct_excessive_bools)] #[derive(Debug, Deserialize)] diff --git a/src/observability.rs b/src/observability.rs index f0baee1c..f2503f61 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -1,7 +1,12 @@ //! Facilities for observing runtime behavior #![warn(missing_docs, clippy::missing_docs_in_private_items)] -use std::{collections::HashSet, fs::File, io::BufWriter, sync::Arc}; +use std::{ + collections::HashSet, + fs::File, + io::BufWriter, + sync::{Arc, LazyLock}, +}; use axum::{ extract::{MatchedPath, Request}, @@ -9,7 +14,6 @@ use axum::{ response::Response, }; use http::Method; -use once_cell::sync::Lazy; use opentelemetry::{metrics::MeterProvider, trace::TracerProvider, KeyValue}; use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::{ @@ -34,7 +38,7 @@ use crate::{ }; /// Globally accessible metrics state -pub(crate) static METRICS: Lazy = Lazy::new(Metrics::new); +pub(crate) static METRICS: LazyLock = LazyLock::new(Metrics::new); /// Cleans up resources relating to observability when [`Drop`]ped pub(crate) struct Guard { @@ -385,8 +389,8 @@ impl Metrics { /// Track HTTP metrics by converting this into an [`axum`] layer pub(crate) async fn http_metrics_layer(req: Request, next: Next) -> Response { /// Routes that should not be included in the metrics - static IGNORED_ROUTES: Lazy> = - Lazy::new(|| [(&Method::GET, "/metrics")].into_iter().collect()); + static IGNORED_ROUTES: LazyLock> = + LazyLock::new(|| [(&Method::GET, "/metrics")].into_iter().collect()); let matched_path = req.extensions().get::().map(|x| x.as_str().to_owned()); From d565b22da9cbe4e98da9f2800b5be4550b5308ee Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 15:32:53 -0700 Subject: [PATCH 458/617] run `cargo upgrade && cargo update` There are a few available incompatible updates not covered here: * RocksDB * Ruma * OTel stuff --- Cargo.lock | 412 ++++++++++++++++++++++++----------------------------- Cargo.toml | 40 +++--- 2 files changed, 210 insertions(+), 242 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f6f9c36..1bae555b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,13 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -46,15 +40,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "arc-swap" @@ -104,9 +98,9 @@ checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -115,9 +109,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -126,9 +120,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -143,15 +137,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -183,9 +177,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -258,7 +252,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.0", + "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", @@ -284,14 +278,14 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools", "lazy_static", "lazycell", "proc-macro2", @@ -339,7 +333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "serde", ] @@ -351,9 +345,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -369,9 +363,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "bzip2-sys" @@ -386,9 +380,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -429,9 +423,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -439,9 +433,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstyle", "clap_lex", @@ -492,9 +486,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7782af8f90fe69a4bb41e460abe1727d493403d8b2cc43201a3a3e906b24379f" +checksum = "013b6c2c3a14d678f38cd23994b02da3a1a1b6a5d1eedddfe63a5a5f11b13a81" [[package]] name = "core-foundation" @@ -700,9 +694,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" dependencies = [ "simd-adler32", ] @@ -715,12 +709,12 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -749,24 +743,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -775,15 +769,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -792,21 +786,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", @@ -852,9 +846,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -941,7 +935,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -963,6 +957,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashlink" version = "0.9.1" @@ -1082,9 +1082,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1094,9 +1094,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -1124,7 +1124,7 @@ dependencies = [ "hyper", "hyper-util", "rustls", - "rustls-native-certs 0.8.0", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", @@ -1146,9 +1146,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1159,7 +1159,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -1186,9 +1185,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae" dependencies = [ "bytemuck", "byteorder-lite", @@ -1212,20 +1211,20 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "serde", ] [[package]] name = "insta" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -1251,9 +1250,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "itertools" @@ -1264,15 +1263,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -1290,9 +1280,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -1364,9 +1354,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -1439,9 +1429,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.11.0" +version = "1.11.1+lz4-1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb44a01837a858d47e5a630d2ccf304c8efcc4b83b8f9f75b7a9ee4fcc6e57d" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" dependencies = [ "cc", "libc", @@ -1492,16 +1482,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", - "simd-adler32", -] - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1509,6 +1489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1607,18 +1588,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl-probe" @@ -1858,18 +1839,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -1878,9 +1859,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1900,21 +1881,21 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide", ] [[package]] @@ -1973,9 +1954,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -2012,7 +1993,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools", "proc-macro2", "quote", "syn", @@ -2077,10 +2058,11 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780" dependencies = [ + "cfg_aliases", "libc", "once_cell", "socket2", @@ -2129,23 +2111,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2159,13 +2141,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2176,15 +2158,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -2206,7 +2188,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls", - "rustls-native-certs 0.7.3", + "rustls-native-certs", "rustls-pemfile", "rustls-pki-types", "serde", @@ -2315,7 +2297,7 @@ dependencies = [ "bytes", "form_urlencoded", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "js_int", "konst", "percent-encoding", @@ -2341,7 +2323,7 @@ version = "0.28.1" source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ "as_variant", - "indexmap 2.5.0", + "indexmap 2.6.0", "js_int", "js_option", "percent-encoding", @@ -2457,7 +2439,7 @@ name = "ruma-state-res" version = "0.11.0" source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" dependencies = [ - "itertools 0.12.1", + "itertools", "js_int", "ruma-common", "ruma-events", @@ -2536,9 +2518,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags 2.6.0", "errno", @@ -2549,9 +2531,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "log", "once_cell", @@ -2562,19 +2544,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-native-certs" version = "0.8.0" @@ -2590,19 +2559,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -2617,9 +2585,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -2629,9 +2597,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -2644,9 +2612,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sd-notify" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4646d6f919800cd25c50edb49438a1381e2cd4833c027e75e8897981c50b8b5e" +checksum = "1be20c5f7f393ee700f8b2f28ea35812e4e212f40774b550cd2a93ea91684451" [[package]] name = "security-framework" @@ -2679,18 +2647,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -2704,7 +2672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" dependencies = [ "form_urlencoded", - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -2712,9 +2680,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -2734,9 +2702,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2759,7 +2727,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -2942,9 +2910,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.77" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -2968,12 +2936,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2984,18 +2952,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", @@ -3080,9 +3048,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -3176,11 +3144,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -3189,9 +3157,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", @@ -3254,9 +3222,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41515cc9e193536d93fd0dbbea0c73819c08eca76e0b30909a325c3ec90985bb" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" dependencies = [ "bitflags 2.6.0", "bytes", @@ -3466,9 +3434,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -3517,9 +3485,9 @@ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] @@ -3568,9 +3536,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -3579,9 +3547,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -3594,9 +3562,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -3606,9 +3574,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3616,9 +3584,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -3629,15 +3597,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -3667,9 +3635,9 @@ checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "wildmatch" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19" +checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd" [[package]] name = "winapi" @@ -3873,9 +3841,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index dfd7e7f3..ef130acc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,21 +87,21 @@ workspace = true # Keep sorted [dependencies] argon2 = "0.5.3" -async-trait = "0.1.82" -axum = { version = "0.7.6", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } +async-trait = "0.1.83" +axum = { version = "0.7.7", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } axum-extra = { version = "0.9.4", features = ["typed-header"] } axum-server = { version = "0.7.1", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" -bytes = "1.7.2" -clap = { version = "4.5.18", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } -futures-util = { version = "0.3.30", default-features = false } +bytes = "1.8.0" +clap = { version = "4.5.20", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } +futures-util = { version = "0.3.31", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" http = "1.1.0" http-body-util = "0.1.2" -hyper = "1.4.1" -hyper-util = { version = "0.1.8", features = ["client", "client-legacy", "service"] } -image = { version = "0.25.2", default-features = false, features = ["jpeg", "png", "gif"] } +hyper = "1.5.0" +hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "service"] } +image = { version = "0.25.4", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.3.0" lru-cache = "0.1.2" num_cpus = "1.16.0" @@ -109,34 +109,34 @@ opentelemetry = "0.24.0" opentelemetry-jaeger-propagator = "0.3.0" opentelemetry-otlp = "0.17.0" opentelemetry-prometheus = "0.17.0" -opentelemetry_sdk = { version = "0.24.0", features = ["rt-tokio"] } +opentelemetry_sdk = { version = "0.24.1", features = ["rt-tokio"] } parking_lot = { version = "0.12.3", optional = true } phf = { version = "0.11.2", features = ["macros"] } -pin-project-lite = "0.2.14" +pin-project-lite = "0.2.15" prometheus = "0.13.4" proxy-header = { version = "0.1.2", features = ["tokio"] } rand = "0.8.5" -regex = "1.10.6" -reqwest = { version = "0.12.7", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } +regex = "1.11.1" +reqwest = { version = "0.12.9", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.30.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } -rustls = { version = "0.23.13", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } -sd-notify = { version = "0.4.2", optional = true } -serde = { version = "1.0.210", features = ["rc"] } +rustls = { version = "0.23.16", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } +sd-notify = { version = "0.4.3", optional = true } +serde = { version = "1.0.214", features = ["rc"] } serde_html_form = "0.2.6" -serde_json = { version = "1.0.128", features = ["raw_value"] } +serde_json = { version = "1.0.132", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" strum = { version = "0.26.3", features = ["derive"] } -thiserror = "1.0.64" +thiserror = "1.0.66" thread_local = "1.1.8" tikv-jemallocator = { version = "0.6.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -tokio = { version = "1.40.0", features = ["fs", "macros", "signal", "sync"] } +tokio = { version = "1.41.0", features = ["fs", "macros", "signal", "sync"] } toml = "0.8.19" tower = { version = "0.5.1", features = ["util"] } -tower-http = { version = "0.6.0", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } +tower-http = { version = "0.6.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.40", features = [] } tracing-flame = "0.2.0" tracing-opentelemetry = "0.25.0" @@ -149,7 +149,7 @@ nix = { version = "0.29", features = ["resource", "time"] } [dev-dependencies] assert_cmd = "2.0.16" -insta = { version = "1.40.0", features = ["filters", "json", "redactions"] } +insta = { version = "1.41.1", features = ["filters", "json", "redactions"] } predicates = "3.1.2" [profile.dev.package.insta] From 2dbb101140b56721aa00f02e3d6d1be26e3982c7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 15:57:02 -0700 Subject: [PATCH 459/617] upgrade to latest rust-rocksdb We gotta overrideAttrs to set the src and version to a newer version than nixpkgs has now. --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- flake.lock | 18 ++++++++++++++++++ flake.nix | 1 + nix/pkgs/rocksdb/default.nix | 12 ++++++++++-- 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bae555b..c5ea2de4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2465,9 +2465,9 @@ dependencies = [ [[package]] name = "rust-librocksdb-sys" -version = "0.26.0+9.6.1" +version = "0.29.0+9.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4508cf0cb12feb8185556cebc1bf2e53925b415e7b5cb3bcaaff5d90f57eae4e" +checksum = "7431f14c28485bd13140e5b27298c22a3b96a0cc9f60a4f5318ae782b7288e9c" dependencies = [ "bindgen", "bzip2-sys", @@ -2481,9 +2481,9 @@ dependencies = [ [[package]] name = "rust-rocksdb" -version = "0.30.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98735c6ebacc6796c0f74814de76d99dd379df7afa7389c801ed11bba5782884" +checksum = "c4412bfff73ff8f0c458041934bee4f0bbf92488271e8e5d767679f4a670df44" dependencies = [ "libc", "rust-librocksdb-sys", diff --git a/Cargo.toml b/Cargo.toml index ef130acc..92b2c6db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,7 +119,7 @@ rand = "0.8.5" regex = "1.11.1" reqwest = { version = "0.12.9", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.8" -rocksdb = { package = "rust-rocksdb", version = "0.30.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +rocksdb = { package = "rust-rocksdb", version = "0.33.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } rustls = { version = "0.23.16", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } diff --git a/flake.lock b/flake.lock index 892de814..f2016994 100644 --- a/flake.lock +++ b/flake.lock @@ -241,6 +241,23 @@ "type": "github" } }, + "rocksdb": { + "flake": false, + "locked": { + "lastModified": 1730475155, + "narHash": "sha256-u5uuShM2SxHc9/zL4UU56IhCcR/ZQbzde0LgOYS44bM=", + "owner": "facebook", + "repo": "rocksdb", + "rev": "3c27a3dde0993210c5cc30d99717093f7537916f", + "type": "github" + }, + "original": { + "owner": "facebook", + "ref": "v9.7.4", + "repo": "rocksdb", + "type": "github" + } + }, "root": { "inputs": { "attic": "attic", @@ -250,6 +267,7 @@ "flake-utils": "flake-utils", "nix-filter": "nix-filter", "nixpkgs": "nixpkgs_2", + "rocksdb": "rocksdb", "rust-manifest": "rust-manifest" } }, diff --git a/flake.nix b/flake.nix index 55d0f9b8..aae3883b 100644 --- a/flake.nix +++ b/flake.nix @@ -8,6 +8,7 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + rocksdb = { url = "github:facebook/rocksdb?ref=v9.7.4"; flake = false; }; rust-manifest = { # Keep version in sync with rust-toolchain.toml diff --git a/nix/pkgs/rocksdb/default.nix b/nix/pkgs/rocksdb/default.nix index 4468c4d6..9aa62353 100644 --- a/nix/pkgs/rocksdb/default.nix +++ b/nix/pkgs/rocksdb/default.nix @@ -1,5 +1,6 @@ # Dependencies (keep sorted) -{ lib +{ inputs +, lib , rocksdb , rust-jemalloc-sys @@ -8,7 +9,14 @@ }: let - rocksdb' = rocksdb.override { + rocksdb' = (rocksdb.overrideAttrs (old: { + src = inputs.rocksdb; + version = lib.removePrefix + "v" + (builtins.fromJSON + (builtins.readFile ../../../flake.lock) + ).nodes.rocksdb.original.ref; + })).override { jemalloc = rust-jemalloc-sys; enableLiburing = false; From c9c30fba302abbb0c9523654cbc9201a7f31b2ed Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Oct 2024 16:03:37 -0700 Subject: [PATCH 460/617] upgrade to latest ruma --- Cargo.lock | 131 +++++++++++++++++++------------- src/api/client_server/keys.rs | 15 +++- src/api/client_server/media.rs | 3 + src/api/client_server/push.rs | 44 +---------- src/api/server_server.rs | 4 +- src/database/key_value/users.rs | 17 +++-- src/service/users.rs | 12 +-- src/service/users/data.rs | 12 +-- 8 files changed, 119 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5ea2de4..ab5ae6c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,7 +285,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "proc-macro2", @@ -908,7 +908,7 @@ dependencies = [ "serde_yaml", "sha-1", "strum", - "thiserror", + "thiserror 1.0.66", "thread_local", "tikv-jemallocator", "tokio", @@ -1263,6 +1263,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1618,7 +1627,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.66", ] [[package]] @@ -1643,7 +1652,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost", - "thiserror", + "thiserror 1.0.66", "tokio", "tonic", ] @@ -1689,7 +1698,7 @@ dependencies = [ "percent-encoding", "rand", "serde_json", - "thiserror", + "thiserror 1.0.66", "tokio", "tokio-stream", ] @@ -1757,7 +1766,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.66", "ucd-trie", ] @@ -1973,7 +1982,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror", + "thiserror 1.0.66", ] [[package]] @@ -1993,7 +2002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools", + "itertools 0.12.1", "proc-macro2", "quote", "syn", @@ -2034,7 +2043,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "socket2", - "thiserror", + "thiserror 1.0.66", "tokio", "tracing", ] @@ -2051,7 +2060,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "slab", - "thiserror", + "thiserror 1.0.66", "tinyvec", "tracing", ] @@ -2233,8 +2242,8 @@ dependencies = [ [[package]] name = "ruma" -version = "0.10.1" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.11.1" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "assign", "js_int", @@ -2254,8 +2263,8 @@ dependencies = [ [[package]] name = "ruma-appservice-api" -version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.11.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "js_int", "ruma-common", @@ -2266,8 +2275,8 @@ dependencies = [ [[package]] name = "ruma-client-api" -version = "0.18.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.19.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "as_variant", "assign", @@ -2282,15 +2291,15 @@ dependencies = [ "serde", "serde_html_form", "serde_json", - "thiserror", + "thiserror 2.0.1", "url", "web-time", ] [[package]] name = "ruma-common" -version = "0.13.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.14.1" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "as_variant", "base64 0.22.1", @@ -2308,7 +2317,7 @@ dependencies = [ "serde", "serde_html_form", "serde_json", - "thiserror", + "thiserror 2.0.1", "time", "tracing", "url", @@ -2319,8 +2328,8 @@ dependencies = [ [[package]] name = "ruma-events" -version = "0.28.1" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.29.1" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "as_variant", "indexmap 2.6.0", @@ -2333,7 +2342,7 @@ dependencies = [ "ruma-macros", "serde", "serde_json", - "thiserror", + "thiserror 2.0.1", "tracing", "url", "web-time", @@ -2342,8 +2351,8 @@ dependencies = [ [[package]] name = "ruma-federation-api" -version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.10.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "bytes", "http", @@ -2360,17 +2369,17 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" -version = "0.9.5" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.10.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "js_int", - "thiserror", + "thiserror 2.0.1", ] [[package]] name = "ruma-identity-service-api" -version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.10.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "js_int", "ruma-common", @@ -2379,8 +2388,8 @@ dependencies = [ [[package]] name = "ruma-macros" -version = "0.13.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.14.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "cfg-if", "once_cell", @@ -2395,8 +2404,8 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" -version = "0.9.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.10.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "js_int", "ruma-common", @@ -2407,21 +2416,21 @@ dependencies = [ [[package]] name = "ruma-server-util" -version = "0.3.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.4.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "headers", "http", "http-auth", "ruma-common", - "thiserror", + "thiserror 2.0.1", "tracing", ] [[package]] name = "ruma-signatures" -version = "0.15.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.16.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2431,21 +2440,21 @@ dependencies = [ "serde_json", "sha2", "subslice", - "thiserror", + "thiserror 2.0.1", ] [[package]] name = "ruma-state-res" -version = "0.11.0" -source = "git+https://github.com/ruma/ruma?branch=main#1ae98db9c44f46a590f4c76baf5cef70ebb6970d" +version = "0.12.0" +source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" dependencies = [ - "itertools", + "itertools 0.13.0", "js_int", "ruma-common", "ruma-events", "serde", "serde_json", - "thiserror", + "thiserror 2.0.1", "tracing", ] @@ -2820,7 +2829,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.66", "time", ] @@ -2910,9 +2919,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.86" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2956,7 +2965,16 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.66", +] + +[[package]] +name = "thiserror" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c1e40dd48a282ae8edc36c732cbc219144b87fb6a4c7316d611c6b1f06ec0c" +dependencies = [ + "thiserror-impl 2.0.1", ] [[package]] @@ -2970,6 +2988,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874aa7e446f1da8d9c3a5c95b1c5eb41d800045252121dc7f8e0ba370cee55f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -3093,7 +3122,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.66", "tokio", ] @@ -3371,7 +3400,7 @@ dependencies = [ "once_cell", "rand", "smallvec", - "thiserror", + "thiserror 1.0.66", "tinyvec", "tokio", "tracing", @@ -3393,7 +3422,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror", + "thiserror 1.0.66", "tokio", "tracing", "trust-dns-proto", diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 0e423e72..c4422079 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -17,7 +17,7 @@ use ruma::{ federation, }, serde::Raw, - DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId, + OneTimeKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId, }; use serde_json::json; use tracing::debug; @@ -474,7 +474,16 @@ pub(crate) async fn get_keys_helper bool>( let (_, our_master_key) = services() .users .parse_master_key(&user, &our_master_key)?; - master_key.signatures.extend(our_master_key.signatures); + + for (entity, v) in &*our_master_key.signatures { + for (key_identifier, value) in v { + master_key.signatures.insert_signature( + entity.clone(), + key_identifier.clone(), + value.clone(), + ); + } + } } let json = serde_json::to_value(master_key) .expect("to_value always works"); @@ -525,7 +534,7 @@ fn add_unsigned_device_display_name( pub(crate) async fn claim_keys_helper( one_time_keys_input: &BTreeMap< OwnedUserId, - BTreeMap, + BTreeMap, >, ) -> Result { let mut one_time_keys = BTreeMap::new(); diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index ab11894c..e0928cf2 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -670,11 +670,13 @@ pub(crate) async fn get_content_thumbnail_legacy_route( AmResponse { file, content_type, + .. }: AmResponse, ) -> LegacyResponse { LegacyResponse { file, content_type, + content_disposition: None, cross_origin_resource_policy: Some("cross-origin".to_owned()), } } @@ -833,6 +835,7 @@ async fn get_content_thumbnail_route_ruma( authenticated_media_client::get_content_thumbnail::v1::Response { file, content_type, + content_disposition: None, } }; diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 39a2cd13..9d2b5956 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -4,7 +4,7 @@ use ruma::{ push::{ delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled, get_pushrules_all, set_pusher, set_pushrule, - set_pushrule_actions, set_pushrule_enabled, RuleScope, + set_pushrule_actions, set_pushrule_enabled, }, }, events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, @@ -89,13 +89,6 @@ pub(crate) async fn set_pushrule_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let body = body.body; - if body.scope != RuleScope::Global { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Scopes other than 'global' are not supported.", - )); - } - let event = services() .account_data .get( @@ -168,13 +161,6 @@ pub(crate) async fn get_pushrule_actions_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if body.scope != RuleScope::Global { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Scopes other than 'global' are not supported.", - )); - } - let event = services() .account_data .get( @@ -213,13 +199,6 @@ pub(crate) async fn set_pushrule_actions_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if body.scope != RuleScope::Global { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Scopes other than 'global' are not supported.", - )); - } - let event = services() .account_data .get( @@ -268,13 +247,6 @@ pub(crate) async fn get_pushrule_enabled_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if body.scope != RuleScope::Global { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Scopes other than 'global' are not supported.", - )); - } - let event = services() .account_data .get( @@ -314,13 +286,6 @@ pub(crate) async fn set_pushrule_enabled_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if body.scope != RuleScope::Global { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Scopes other than 'global' are not supported.", - )); - } - let event = services() .account_data .get( @@ -369,13 +334,6 @@ pub(crate) async fn delete_pushrule_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - if body.scope != RuleScope::Global { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Scopes other than 'global' are not supported.", - )); - } - let event = services() .account_data .get( diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 9030bf99..95b40fea 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -59,7 +59,7 @@ use ruma::{ uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedServerSigningKeyId, OwnedSigningKeyId, OwnedUserId, RoomId, - ServerName, + ServerName, Signatures, }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; @@ -617,7 +617,7 @@ pub(crate) async fn get_server_keys_route() -> Result { server_name: services().globals.server_name().to_owned(), verify_keys, old_verify_keys, - signatures: BTreeMap::new(), + signatures: Signatures::new(), valid_until_ts: keys.valid_until_ts, }) .expect("static conversion, no errors"), diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 3040e237..97558f10 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -5,8 +5,9 @@ use ruma::{ encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::{AnyToDeviceEvent, StateEventType}, serde::Raw, - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, - OwnedDeviceId, OwnedDeviceKeyId, OwnedMxcUri, OwnedUserId, UInt, UserId, + DeviceId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyName, + OwnedDeviceId, OwnedKeyId, OwnedMxcUri, OwnedOneTimeKeyId, OwnedUserId, + UInt, UserId, }; use tracing::warn; @@ -369,7 +370,7 @@ impl service::users::Data for KeyValueDatabase { &self, user_id: &UserId, device_id: &DeviceId, - one_time_key_key: &DeviceKeyId, + one_time_key_key: &OwnedKeyId, one_time_key_value: &Raw, ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); @@ -388,7 +389,7 @@ impl service::users::Data for KeyValueDatabase { // anymore) key.extend_from_slice( serde_json::to_string(one_time_key_key) - .expect("DeviceKeyId::to_string always works") + .expect("OwnedKeyId::to_string always works") .as_bytes(), ); @@ -410,8 +411,8 @@ impl service::users::Data for KeyValueDatabase { &self, user_id: &UserId, device_id: &DeviceId, - key_algorithm: &DeviceKeyAlgorithm, - ) -> Result)>> { + key_algorithm: &OneTimeKeyAlgorithm, + ) -> Result)>> { let mut prefix = user_id.as_bytes().to_vec(); prefix.push(0xFF); prefix.extend_from_slice(device_id.as_bytes()); @@ -455,7 +456,7 @@ impl service::users::Data for KeyValueDatabase { &self, user_id: &UserId, device_id: &DeviceId, - ) -> Result> { + ) -> Result> { let mut userdeviceid = user_id.as_bytes().to_vec(); userdeviceid.push(0xFF); userdeviceid.extend_from_slice(device_id.as_bytes()); @@ -467,7 +468,7 @@ impl service::users::Data for KeyValueDatabase { .scan_prefix(userdeviceid) .map(|(bytes, _)| { Ok::<_, Error>( - serde_json::from_slice::( + serde_json::from_slice::( bytes.rsplit(|&b| b == 0xFF).next().ok_or_else( || { Error::bad_database( diff --git a/src/service/users.rs b/src/service/users.rs index 088fbb12..614514ac 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -16,8 +16,8 @@ use ruma::{ encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::AnyToDeviceEvent, serde::Raw, - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, - OwnedMxcUri, OwnedRoomId, OwnedUserId, UInt, UserId, + DeviceId, OneTimeKeyAlgorithm, OneTimeKeyName, OwnedDeviceId, OwnedKeyId, + OwnedMxcUri, OwnedOneTimeKeyId, OwnedRoomId, OwnedUserId, UInt, UserId, }; use crate::{services, Error, Result}; @@ -423,7 +423,7 @@ impl Service { &self, user_id: &UserId, device_id: &DeviceId, - one_time_key_key: &DeviceKeyId, + one_time_key_key: &OwnedKeyId, one_time_key_value: &Raw, ) -> Result<()> { self.db.add_one_time_key( @@ -438,8 +438,8 @@ impl Service { &self, user_id: &UserId, device_id: &DeviceId, - key_algorithm: &DeviceKeyAlgorithm, - ) -> Result)>> { + key_algorithm: &OneTimeKeyAlgorithm, + ) -> Result)>> { self.db.take_one_time_key(user_id, device_id, key_algorithm) } @@ -447,7 +447,7 @@ impl Service { &self, user_id: &UserId, device_id: &DeviceId, - ) -> Result> { + ) -> Result> { self.db.count_one_time_keys(user_id, device_id) } diff --git a/src/service/users/data.rs b/src/service/users/data.rs index ea3959ed..0ff9a162 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -5,8 +5,8 @@ use ruma::{ encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::AnyToDeviceEvent, serde::Raw, - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, - OwnedMxcUri, OwnedUserId, UInt, UserId, + DeviceId, OneTimeKeyAlgorithm, OneTimeKeyName, OwnedDeviceId, OwnedKeyId, + OwnedMxcUri, OwnedOneTimeKeyId, OwnedUserId, UInt, UserId, }; use crate::Result; @@ -112,7 +112,7 @@ pub(crate) trait Data: Send + Sync { &self, user_id: &UserId, device_id: &DeviceId, - one_time_key_key: &DeviceKeyId, + one_time_key_key: &OwnedKeyId, one_time_key_value: &Raw, ) -> Result<()>; @@ -120,14 +120,14 @@ pub(crate) trait Data: Send + Sync { &self, user_id: &UserId, device_id: &DeviceId, - key_algorithm: &DeviceKeyAlgorithm, - ) -> Result)>>; + key_algorithm: &OneTimeKeyAlgorithm, + ) -> Result)>>; fn count_one_time_keys( &self, user_id: &UserId, device_id: &DeviceId, - ) -> Result>; + ) -> Result>; fn add_device_keys( &self, From a4e1522875358ce620e9cc3baaa86ac324211587 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 8 Nov 2024 18:11:11 -0800 Subject: [PATCH 461/617] generalize `get_room_version` There are other fields of `m.room.create` events that are useful to individually extract without caring about the values of other fields. --- src/api/client_server/membership.rs | 7 ++++-- src/api/server_server.rs | 22 +++++++++++++---- src/service/rooms/state.rs | 38 ++++++++++++++++++++++------- src/service/rooms/timeline.rs | 26 ++++++++++++++------ 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index c07a07c9..7d6c9cdb 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -38,6 +38,7 @@ use crate::{ service::{ globals::SigningKeys, pdu::{gen_event_id_canonical_json, PduBuilder}, + rooms::state::ExtractVersion, }, services, utils, Ar, Error, PduEvent, Ra, Result, }; @@ -1324,8 +1325,10 @@ pub(crate) async fn invite_helper( (pdu, pdu_json, invite_room_state) }; - let room_version_id = - services().rooms.state.get_room_version(room_id)?; + let room_version_id = services() + .rooms + .state + .get_create_content::(room_id)?; let response = services() .sending diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 95b40fea..edba5291 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -72,6 +72,7 @@ use crate::{ service::{ globals::SigningKeys, pdu::{gen_event_id_canonical_json, PduBuilder}, + rooms::state::ExtractVersion, }, services, utils::{self, dbg_truncate_str, MxcData}, @@ -712,7 +713,10 @@ pub(crate) fn parse_incoming_pdu( "Invalid room id in pdu", ))?; - let room_version_id = services().rooms.state.get_room_version(&room_id)?; + let room_version_id = services() + .rooms + .state + .get_create_content::(&room_id)?; let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) @@ -754,7 +758,12 @@ pub(crate) async fn send_transaction_message_route( "Invalid room id in pdu", ))?; - if services().rooms.state.get_room_version(&room_id).is_err() { + if services() + .rooms + .state + .get_create_content::(&room_id) + .is_err() + { debug!(%room_id, "This server is not in the room"); continue; } @@ -1514,8 +1523,10 @@ pub(crate) async fn create_join_event_template_route( } } - let room_version_id = - services().rooms.state.get_room_version(&body.room_id)?; + let room_version_id = services() + .rooms + .state + .get_create_content::(&body.room_id)?; if !body.ver.contains(&room_version_id) { return Err(Error::BadRequest( ErrorKind::IncompatibleRoomVersion { @@ -1620,7 +1631,8 @@ async fn create_join_event( // We do not add the event_id field to the pdu here because of signature and // hashes checks - let room_version_id = services().rooms.state.get_room_version(room_id)?; + let room_version_id = + services().rooms.state.get_create_content::(room_id)?; let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 4702a76b..72741417 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -7,14 +7,14 @@ use std::{ use ruma::{ api::client::error::ErrorKind, events::{ - room::{create::RoomCreateEventContent, member::MembershipState}, - AnyStrippedStateEvent, StateEventType, TimelineEventType, + room::member::MembershipState, AnyStrippedStateEvent, StateEventType, + TimelineEventType, }, serde::Raw, state_res::{self, StateMap}, EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId, }; -use serde::Deserialize; +use serde::{de::DeserializeOwned, Deserialize}; use tracing::warn; use super::{short::ShortStateHash, state_compressor::CompressedStateEvent}; @@ -31,6 +31,26 @@ mod data; pub(crate) use data::Data; +pub(crate) trait ExtractCreateContent: DeserializeOwned { + type Extract; + + fn extract(self) -> Self::Extract; +} + +/// Extract the `room_version` from an `m.room.create` event +#[derive(Deserialize)] +pub(crate) struct ExtractVersion { + room_version: RoomVersionId, +} + +impl ExtractCreateContent for ExtractVersion { + type Extract = RoomVersionId; + + fn extract(self) -> Self::Extract { + self.room_version + } +} + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } @@ -315,22 +335,22 @@ impl Service { self.db.set_room_state(room_id, shortstatehash) } - /// Returns the room's version. + /// Returns the value of a field of an `m.room.create` event's `content`. #[tracing::instrument(skip(self))] - pub(crate) fn get_room_version( + pub(crate) fn get_create_content( &self, room_id: &RoomId, - ) -> Result { + ) -> Result { let create_event = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomCreate, "", )?; - let create_event_content: RoomCreateEventContent = create_event + let content_field = create_event .as_ref() .map(|create_event| { - serde_json::from_str(create_event.content.get()).map_err( + serde_json::from_str::(create_event.content.get()).map_err( |error| { warn!(%error, "Invalid create event"); Error::BadDatabase("Invalid create event in db.") @@ -345,7 +365,7 @@ impl Service { ) })?; - Ok(create_event_content.room_version) + Ok(content_field.extract()) } #[tracing::instrument(skip(self))] diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 0cd751dd..382c3aca 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -36,6 +36,7 @@ use crate::{ appservice::NamespaceRegex, globals::{marker, SigningKeys}, pdu::{EventHash, PduBuilder}, + rooms::state::ExtractVersion, }, services, utils::{self, on_demand_hashmap::KeyToken, room_version::RoomVersion}, @@ -476,8 +477,10 @@ impl Service { match pdu.kind { TimelineEventType::RoomRedaction => { - let room_version_id = - services().rooms.state.get_room_version(&pdu.room_id)?; + let room_version_id = services() + .rooms + .state + .get_create_content::(&pdu.room_id)?; let room_version = RoomVersion::try_from(&room_version_id)?; if room_version.redaction_event_redacts_in_content { let content = serde_json::from_str::< @@ -774,8 +777,11 @@ impl Service { .collect(); // If there was no create event yet, assume we are creating a room - let room_version_id = - services().rooms.state.get_room_version(room_id).or_else(|_| { + let room_version_id = services() + .rooms + .state + .get_create_content::(room_id) + .or_else(|_| { if event_type == TimelineEventType::RoomCreate { let content = serde_json::from_str::( @@ -1064,8 +1070,10 @@ impl Service { // If redaction event is not authorized, do not append it to the // timeline if pdu.kind == TimelineEventType::RoomRedaction { - let room_version_id = - services().rooms.state.get_room_version(&pdu.room_id)?; + let room_version_id = services() + .rooms + .state + .get_create_content::(&pdu.room_id)?; let room_version = RoomVersion::try_from(&room_version_id)?; if room_version.redaction_event_redacts_in_content { let content = @@ -1257,8 +1265,10 @@ impl Service { )?; } - let room_version_id = - services().rooms.state.get_room_version(&pdu.room_id)?; + let room_version_id = services() + .rooms + .state + .get_create_content::(&pdu.room_id)?; pdu.redact(room_version_id, reason)?; self.replace_pdu( From 9d0cf428a58085465d9ca3cefbd7c31ab2535719 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 8 Nov 2024 18:27:47 -0800 Subject: [PATCH 462/617] stop passing the entire create event around This gets rid of 3 instances of re-parsing the room version. There's one place where we need the event ID of the room create event to verify federation responses, so now we just look up the event ID at that point instead. --- src/service/rooms/event_handler.rs | 101 ++++++++++++----------------- 1 file changed, 42 insertions(+), 59 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index d5e7a6a4..1543e423 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -20,7 +20,6 @@ use ruma::{ }, events::{ room::{ - create::RoomCreateEventContent, redaction::RoomRedactionEventContent, server_acl::RoomServerAclEventContent, }, @@ -42,7 +41,7 @@ use super::{ timeline::PduId, }; use crate::{ - service::{globals::SigningKeys, pdu}, + service::{globals::SigningKeys, pdu, rooms::state::ExtractVersion}, services, utils::{debug_slice_truncated, room_version::RoomVersion}, Error, PduEvent, Result, @@ -115,34 +114,22 @@ impl Service { return Ok(Some(pdu_id.clone())); } - let create_event = services() - .rooms - .state_accessor - .room_state_get(room_id, &StateEventType::RoomCreate, "")? - .ok_or_else(|| { - Error::bad_database("Failed to find create event in db.") - })?; - - let create_event_content: RoomCreateEventContent = - serde_json::from_str(create_event.content.get()).map_err( - |error| { - error!(%error, "Invalid create event"); - Error::BadDatabase("Invalid create event.") - }, - )?; - let room_version_id = &create_event_content.room_version; - let first_pdu_in_room = services().rooms.timeline.first_pdu_in_room(room_id)?.ok_or_else( || Error::bad_database("Failed to find first pdu in db."), )?; + let room_version_id = services() + .rooms + .state + .get_create_content::(room_id)?; + let (incoming_pdu, val) = self .handle_outlier_pdu( origin, - &create_event, event_id, room_id, + &room_version_id, value, false, pub_key_map, @@ -165,9 +152,8 @@ impl Service { let (sorted_prev_events, mut eventid_info) = self .fetch_unknown_prev_events( origin, - &create_event, room_id, - room_version_id, + &room_version_id, pub_key_map, incoming_pdu.prev_events.clone(), ) @@ -245,9 +231,9 @@ impl Service { .upgrade_outlier_to_timeline_pdu( pdu, json, - &create_event, origin, room_id, + &room_version_id, pub_key_map, ) .await @@ -296,9 +282,9 @@ impl Service { .upgrade_outlier_to_timeline_pdu( incoming_pdu, val, - &create_event, origin, room_id, + &room_version_id, pub_key_map, ) .await; @@ -317,9 +303,9 @@ impl Service { fn handle_outlier_pdu<'a>( &'a self, origin: &'a ServerName, - create_event: &'a PduEvent, event_id: &'a EventId, room_id: &'a RoomId, + room_version_id: &'a RoomVersionId, mut value: CanonicalJsonObject, auth_events_known: bool, pub_key_map: &'a RwLock>, @@ -330,15 +316,7 @@ impl Service { // 2. Check signatures, otherwise drop // 3. check content hash, redact if doesn't match - let create_event_content: RoomCreateEventContent = - serde_json::from_str(create_event.content.get()).map_err( - |error| { - error!(%error, "Invalid create event"); - Error::BadDatabase("Invalid create event in db") - }, - )?; - let room_version_id = &create_event_content.room_version; let ruma_room_version = state_res::RoomVersion::new(room_version_id).map_err(|_| { Error::UnsupportedRoomVersion(room_version_id.clone()) @@ -470,7 +448,6 @@ impl Service { .iter() .map(|x| Arc::from(&**x)) .collect::>(), - create_event, room_id, room_version_id, pub_key_map, @@ -560,15 +537,14 @@ impl Service { #[tracing::instrument(skip_all, fields( incoming_pdu = %incoming_pdu.event_id, - create_event = %create_event.event_id, ))] pub(crate) async fn upgrade_outlier_to_timeline_pdu( &self, incoming_pdu: Arc, val: CanonicalJsonObject, - create_event: &PduEvent, origin: &ServerName, room_id: &RoomId, + room_version_id: &RoomVersionId, pub_key_map: &RwLock>, ) -> Result> { // Skip the PDU if we already have it as a timeline event @@ -594,15 +570,6 @@ impl Service { "Upgrading event to timeline pdu", ); - let create_event_content: RoomCreateEventContent = - serde_json::from_str(create_event.content.get()).map_err( - |error| { - warn!(%error, "Invalid create event"); - Error::BadDatabase("Invalid create event in db") - }, - )?; - - let room_version_id = &create_event_content.room_version; let room_version = RoomVersion::try_from(room_version_id)?; let ruma_room_version = state_res::RoomVersion::new(room_version_id) .map_err(|_| { @@ -822,7 +789,6 @@ impl Service { .fetch_and_handle_outliers( origin, &collect, - create_event, room_id, room_version_id, pub_key_map, @@ -859,16 +825,36 @@ impl Service { } } - // The original create event must still be in the state - let create_shortstatekey = services() - .rooms - .short - .get_shortstatekey(&StateEventType::RoomCreate, "")? - .expect("Room exists"); + let new_create_event = state + .get( + &services() + .rooms + .short + .get_shortstatekey( + &StateEventType::RoomCreate, + "", + )? + .expect("Room exists"), + ) + .map(|x| &**x) + .ok_or(Error::BadServerResponse( + "state_ids response did not contain an \ + m.room.create event", + ))?; - if state.get(&create_shortstatekey) - != Some(&create_event.event_id) - { + let original_create_event = &*services() + .rooms + .state_accessor + .room_state_get( + room_id, + &StateEventType::RoomCreate, + "", + )? + .expect("Room exists") + .event_id; + + // The original create event must still be in the state + if new_create_event != original_create_event { return Err(Error::bad_database( "Incoming event refers to wrong create event.", )); @@ -1228,7 +1214,6 @@ impl Service { &'a self, origin: &'a ServerName, events: &'a [Arc], - create_event: &'a PduEvent, room_id: &'a RoomId, room_version_id: &'a RoomVersionId, pub_key_map: &'a RwLock>, @@ -1403,9 +1388,9 @@ impl Service { match self .handle_outlier_pdu( origin, - create_event, next_id, room_id, + room_version_id, value.clone(), true, pub_key_map, @@ -1437,7 +1422,6 @@ impl Service { async fn fetch_unknown_prev_events( &self, origin: &ServerName, - create_event: &PduEvent, room_id: &RoomId, room_version_id: &RoomVersionId, pub_key_map: &RwLock>, @@ -1462,7 +1446,6 @@ impl Service { .fetch_and_handle_outliers( origin, &[prev_event_id.clone()], - create_event, room_id, room_version_id, pub_key_map, From 51b30d9ba3426ef1e4070406fa9153d99bbea5d0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Tue, 5 Nov 2024 11:34:24 -0800 Subject: [PATCH 463/617] largely stop using `RoomCreateEventContent` This became a problem because #foundation-office:matrix.org has a malformed create event with its `predecessor` set to a string instead of a map. The solution to this is, unfortunately, to do more shotgun parsing to extract only the desired fields rather than trying to parse the entire content every time. To prevent this kind of problem from happening again, `RoomCreateEventContent` must only be used for creating new PDUs, existing PDUs must be shotgun-parsed. --- src/api/client_server/directory.rs | 20 +++++------------- src/service/rooms/spaces.rs | 24 ++++------------------ src/service/rooms/state.rs | 33 ++++++++++++++++++++++++++++-- src/service/rooms/state_cache.rs | 24 +++++++++------------- 4 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index 3d4e1e06..ed95e034 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -15,7 +15,6 @@ use ruma::{ room::{ avatar::RoomAvatarEventContent, canonical_alias::RoomCanonicalAliasEventContent, - create::RoomCreateEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, history_visibility::{ HistoryVisibility, RoomHistoryVisibilityEventContent, @@ -29,7 +28,9 @@ use ruma::{ }; use tracing::{error, info, warn}; -use crate::{services, Ar, Error, Ra, Result}; +use crate::{ + service::rooms::state::ExtractType, services, Ar, Error, Ra, Result, +}; /// # `POST /_matrix/client/r0/publicRooms` /// @@ -382,19 +383,8 @@ fn room_id_to_chunk(room_id: ruma::OwnedRoomId) -> Result { Error::bad_database("Missing room join rule event for room.") })?; - let room_type = services() - .rooms - .state_accessor - .room_state_get(&room_id, &StateEventType::RoomCreate, "")? - .map(|s| { - serde_json::from_str::(s.content.get()) - .map_err(|error| { - error!(%error, "Invalid room create event in database"); - Error::BadDatabase("Invalid room create event in database.") - }) - }) - .transpose()? - .and_then(|e| e.room_type); + let room_type = + services().rooms.state.get_create_content::(&room_id)?; Ok(PublicRoomsChunk { canonical_alias, diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 2011d5b5..88b0c4a9 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -13,7 +13,6 @@ use ruma::{ room::{ avatar::RoomAvatarEventContent, canonical_alias::RoomCanonicalAliasEventContent, - create::RoomCreateEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, history_visibility::{ HistoryVisibility, RoomHistoryVisibilityEventContent, @@ -32,6 +31,7 @@ use ruma::{ use tokio::sync::Mutex; use tracing::{debug, error, warn}; +use super::state::ExtractType; use crate::{services, Error, PduEvent, Result}; pub(crate) enum CachedJoinRule { @@ -496,27 +496,11 @@ impl Service { Self::translate_joinrule(&join_rule)? }, + room_type: services() .rooms - .state_accessor - .room_state_get(room_id, &StateEventType::RoomCreate, "")? - .map(|s| { - serde_json::from_str::( - s.content.get(), - ) - .map_err(|error| { - error!( - %error, - event_id = %s.event_id, - "Invalid room create event", - ); - Error::BadDatabase( - "Invalid room create event in database.", - ) - }) - }) - .transpose()? - .and_then(|e| e.room_type), + .state + .get_create_content::(room_id)?, children_state: children .into_iter() .map(|pdu| pdu.to_stripped_spacechild_state_event()) diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 72741417..5db98287 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -7,9 +7,10 @@ use std::{ use ruma::{ api::client::error::ErrorKind, events::{ - room::member::MembershipState, AnyStrippedStateEvent, StateEventType, - TimelineEventType, + room::{create::PreviousRoom, member::MembershipState}, + AnyStrippedStateEvent, StateEventType, TimelineEventType, }, + room::RoomType, serde::Raw, state_res::{self, StateMap}, EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId, @@ -51,6 +52,34 @@ impl ExtractCreateContent for ExtractVersion { } } +/// Extract the `type` from an `m.room.create` event +#[derive(Deserialize)] +pub(crate) struct ExtractType { + #[serde(rename = "type")] + kind: Option, +} + +impl ExtractCreateContent for ExtractType { + type Extract = Option; + + fn extract(self) -> Self::Extract { + self.kind + } +} + +#[derive(Deserialize)] +pub(crate) struct ExtractPredecessor { + predecessor: Option, +} + +impl ExtractCreateContent for ExtractPredecessor { + type Extract = Option; + + fn extract(self) -> Self::Extract { + self.predecessor + } +} + pub(crate) struct Service { pub(crate) db: &'static dyn Data, } diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 9cf264a9..e3783d74 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -5,10 +5,9 @@ use std::{ use ruma::{ events::{ - ignored_user_list::IgnoredUserListEvent, - room::{create::RoomCreateEventContent, member::MembershipState}, + ignored_user_list::IgnoredUserListEvent, room::member::MembershipState, AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, - RoomAccountDataEventType, StateEventType, + RoomAccountDataEventType, }, serde::Raw, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, @@ -25,6 +24,8 @@ mod data; pub(crate) use data::Data; +use super::state::ExtractPredecessor; + pub(crate) struct Service { db: &'static dyn Data, appservice_in_room_cache: @@ -72,18 +73,13 @@ impl Service { // Check if the room has a predecessor if let Some(predecessor) = services() .rooms - .state_accessor - .room_state_get( - room_id, - &StateEventType::RoomCreate, - "", - )? - .and_then(|create| { - serde_json::from_str(create.content.get()).ok() - }) - .and_then(|content: RoomCreateEventContent| { - content.predecessor + .state + .get_create_content::(room_id) + .inspect_err(|error| { + warn!(%error, "Failed to get room predecessor"); }) + .ok() + .flatten() { self.copy_upgraded_account_data( user_id, From 93c714a199f06d90a3a226d80f1d73ad0787b8f4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 8 Nov 2024 23:00:56 -0800 Subject: [PATCH 464/617] update changelog --- book/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 039ed467..66b990d0 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -198,6 +198,9 @@ This will be the first release of Grapevine since it was forked from Conduit Consequently fixes Heisenbridge being unable to join puppeted users to its rooms ([#85](https://gitlab.computer.surgery/matrix/grapevine/-/issues/85)). ([!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127)) +20. Fix handling of v11 rooms with `m.room.create` event content that passes + the authorization rules but doesn't match other parts of the spec. + ([!139](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/139)) ### Added From 887e59cf03efb70bfa68b56dda32c411ba6cd5fa Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 16 Nov 2024 21:15:16 -0800 Subject: [PATCH 465/617] update name and email in mailmap --- .mailmap | 1 + 1 file changed, 1 insertion(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..a4ac7d66 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Olivia Lee From 7fc8f224eb83716f8451749d4da57d2e1141ce60 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 16 Nov 2024 15:11:31 -0800 Subject: [PATCH 466/617] update flake.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'attic': 'github:zhaofengli/attic/48c8b395bfbc6b76c7eae74df6c74351255a095c' (2024-10-30) → 'github:zhaofengli/attic/47752427561f1c34debb16728a210d378f0ece36' (2024-11-10) • Updated input 'crane': 'github:ipetkov/crane/498d9f122c413ee1154e8131ace5a35a80d8fa76' (2024-10-27) → 'github:ipetkov/crane/ef80ead953c1b28316cc3f8613904edc2eb90c28' (2024-11-08) • Updated input 'fenix': 'github:nix-community/fenix/87b4d20f896c99018dde4702a9c6157b516f2a76' (2024-11-01) → 'github:nix-community/fenix/e10ba121773f754a30d31b6163919a3e404a434f' (2024-11-16) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/0ba893e1a00d92557ac91efb771d72eee36ca687' (2024-10-31) → 'github:rust-lang/rust-analyzer/1b90e979aeee8d1db7fe14603a00834052505497' (2024-11-15) • Updated input 'flake-utils': 'github:numtide/flake-utils/c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a' (2024-09-17) → 'github:numtide/flake-utils/11707dc2f618dd54ca8739b309ec4fc024de578b' (2024-11-13) • Updated input 'nix-filter': 'github:numtide/nix-filter/776e68c1d014c3adde193a18db9d738458cd2ba4' (2024-10-29) → 'github:numtide/nix-filter/f7653272fd234696ae94229839a99b73c9ab7de0' (2024-11-13) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd' (2024-10-29) → 'github:NixOS/nixpkgs/5e4fbfb6b3de1aa2872b76d49fafc942626e2add' (2024-11-15) --- flake.lock | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/flake.lock b/flake.lock index f2016994..abebfbde 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1730257295, - "narHash": "sha256-OQl+aAsKiyygvpzck1u0sZf/R4T9zM903CgNDFmmzA8=", + "lastModified": 1731270564, + "narHash": "sha256-6KMC/NH/VWP5Eb+hA56hz0urel3jP6Y6cF2PX6xaTkk=", "owner": "zhaofengli", "repo": "attic", - "rev": "48c8b395bfbc6b76c7eae74df6c74351255a095c", + "rev": "47752427561f1c34debb16728a210d378f0ece36", "type": "github" }, "original": { @@ -47,11 +47,11 @@ }, "crane_2": { "locked": { - "lastModified": 1730060262, - "narHash": "sha256-RMgSVkZ9H03sxC+Vh4jxtLTCzSjPq18UWpiM0gq6shQ=", + "lastModified": 1731098351, + "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", "owner": "ipetkov", "repo": "crane", - "rev": "498d9f122c413ee1154e8131ace5a35a80d8fa76", + "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", "type": "github" }, "original": { @@ -69,11 +69,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1730442928, - "narHash": "sha256-U1DWb5c3EfkA7pqx5V1H4AWRA+EaE6UJ0lIRvK1RxgM=", + "lastModified": 1731738660, + "narHash": "sha256-tIXhc9lX1b030v812yVJanSR37OnpTb/OY5rU3TbShA=", "owner": "nix-community", "repo": "fenix", - "rev": "87b4d20f896c99018dde4702a9c6157b516f2a76", + "rev": "e10ba121773f754a30d31b6163919a3e404a434f", "type": "github" }, "original": { @@ -142,11 +142,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -158,11 +158,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1730207686, - "narHash": "sha256-SCHiL+1f7q9TAnxpasriP6fMarWE5H43t25F5/9e28I=", + "lastModified": 1731533336, + "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=", "owner": "numtide", "repo": "nix-filter", - "rev": "776e68c1d014c3adde193a18db9d738458cd2ba4", + "rev": "f7653272fd234696ae94229839a99b73c9ab7de0", "type": "github" }, "original": { @@ -227,11 +227,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1730200266, - "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", + "lastModified": 1731676054, + "narHash": "sha256-OZiZ3m8SCMfh3B6bfGC/Bm4x3qc1m2SVEAlkV6iY7Yg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", + "rev": "5e4fbfb6b3de1aa2872b76d49fafc942626e2add", "type": "github" }, "original": { @@ -274,11 +274,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1730386175, - "narHash": "sha256-0Uq+/B8eu7pw8B8pxuGdFYKjcVLwNMcHfDxU9sXh7rg=", + "lastModified": 1731693936, + "narHash": "sha256-uHUUS1WPyW6ohp5Bt3dAZczUlQ22vOn7YZF8vaPKIEw=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "0ba893e1a00d92557ac91efb771d72eee36ca687", + "rev": "1b90e979aeee8d1db7fe14603a00834052505497", "type": "github" }, "original": { From af15f0c596844a18304ada8818d1e60ebef6c845 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 16 Nov 2024 15:22:23 -0800 Subject: [PATCH 467/617] update rust dependencies Except OTel stuff, as usual. The main point is to bump `ruma-state-res` for a state resolution fix. --- Cargo.lock | 507 ++++++++++++++++++++++++++++++++++++---------- Cargo.toml | 16 +- book/changelog.md | 6 +- 3 files changed, 418 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab5ae6c4..a79bcae8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "arc-swap" @@ -143,9 +143,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", @@ -198,25 +198,26 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.4" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" +checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" dependencies = [ "axum", "axum-core", "bytes", + "fastrand", "futures-util", "headers", "http", "http-body", "http-body-util", "mime", + "multer", "pin-project-lite", "serde", "tower 0.5.1", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -328,12 +329,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "serde", ] @@ -380,9 +381,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", @@ -423,9 +424,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -433,9 +434,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstyle", "clap_lex", @@ -456,9 +457,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "color_quant" @@ -508,9 +509,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -609,6 +610,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -652,6 +664,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -692,6 +713,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + [[package]] name = "fdeflate" version = "0.3.6" @@ -709,9 +736,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -908,7 +935,7 @@ dependencies = [ "serde_yaml", "sha-1", "strum", - "thiserror 1.0.66", + "thiserror 2.0.3", "thread_local", "tikv-jemallocator", "tokio", @@ -959,9 +986,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "hashlink" @@ -1133,9 +1160,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper", "hyper-util", @@ -1163,6 +1190,124 @@ dependencies = [ "tracing", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "idna" version = "0.4.0" @@ -1175,19 +1320,30 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "image" -version = "0.25.4" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" dependencies = [ "bytemuck", "byteorder-lite", @@ -1216,7 +1372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -1363,9 +1519,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libloading" @@ -1411,6 +1567,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -1513,6 +1675,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + [[package]] name = "nix" version = "0.29.0" @@ -1627,7 +1806,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror 1.0.66", + "thiserror 1.0.69", ] [[package]] @@ -1652,7 +1831,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost", - "thiserror 1.0.66", + "thiserror 1.0.69", "tokio", "tonic", ] @@ -1698,7 +1877,7 @@ dependencies = [ "percent-encoding", "rand", "serde_json", - "thiserror 1.0.66", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -1766,7 +1945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror 1.0.66", + "thiserror 1.0.69", "ucd-trie", ] @@ -1982,7 +2161,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror 1.0.66", + "thiserror 1.0.69", ] [[package]] @@ -2002,7 +2181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", "syn", @@ -2032,9 +2211,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -2043,33 +2222,36 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "socket2", - "thiserror 1.0.66", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash 2.0.0", "rustls", + "rustls-pki-types", "slab", - "thiserror 1.0.66", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780" +checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" dependencies = [ "cfg_aliases", "libc", @@ -2135,7 +2317,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -2150,9 +2332,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2243,7 +2425,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.11.1" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "assign", "js_int", @@ -2264,7 +2446,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "js_int", "ruma-common", @@ -2276,7 +2458,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.19.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "as_variant", "assign", @@ -2291,7 +2473,7 @@ dependencies = [ "serde", "serde_html_form", "serde_json", - "thiserror 2.0.1", + "thiserror 2.0.3", "url", "web-time", ] @@ -2299,7 +2481,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.14.1" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "as_variant", "base64 0.22.1", @@ -2317,7 +2499,7 @@ dependencies = [ "serde", "serde_html_form", "serde_json", - "thiserror 2.0.1", + "thiserror 2.0.3", "time", "tracing", "url", @@ -2329,7 +2511,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.29.1" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "as_variant", "indexmap 2.6.0", @@ -2342,7 +2524,7 @@ dependencies = [ "ruma-macros", "serde", "serde_json", - "thiserror 2.0.1", + "thiserror 2.0.3", "tracing", "url", "web-time", @@ -2352,7 +2534,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "bytes", "http", @@ -2370,16 +2552,16 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "js_int", - "thiserror 2.0.1", + "thiserror 2.0.3", ] [[package]] name = "ruma-identity-service-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "js_int", "ruma-common", @@ -2389,7 +2571,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.14.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "cfg-if", "once_cell", @@ -2405,7 +2587,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "js_int", "ruma-common", @@ -2417,20 +2599,20 @@ dependencies = [ [[package]] name = "ruma-server-util" version = "0.4.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "headers", "http", "http-auth", "ruma-common", - "thiserror 2.0.1", + "thiserror 2.0.3", "tracing", ] [[package]] name = "ruma-signatures" version = "0.16.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2440,13 +2622,13 @@ dependencies = [ "serde_json", "sha2", "subslice", - "thiserror 2.0.1", + "thiserror 2.0.3", ] [[package]] name = "ruma-state-res" version = "0.12.0" -source = "git+https://github.com/ruma/ruma?branch=main#a77a6ad2138363deaeac544759af1ef0b31d1e0a" +source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" dependencies = [ "itertools 0.13.0", "js_int", @@ -2454,7 +2636,7 @@ dependencies = [ "ruma-events", "serde", "serde_json", - "thiserror 2.0.1", + "thiserror 2.0.3", "tracing", ] @@ -2527,9 +2709,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -2540,9 +2722,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" dependencies = [ "log", "once_cell", @@ -2580,6 +2762,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -2640,9 +2825,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -2656,18 +2841,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -2829,7 +3014,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror 1.0.66", + "thiserror 1.0.69", "time", ] @@ -2880,6 +3065,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strum" version = "0.26.3" @@ -2943,6 +3134,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "terminal_size" version = "0.4.0" @@ -2961,27 +3163,27 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.66", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c1e40dd48a282ae8edc36c732cbc219144b87fb6a4c7316d611c6b1f06ec0c" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" dependencies = [ - "thiserror-impl 2.0.1", + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -2990,9 +3192,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874aa7e446f1da8d9c3a5c95b1c5eb41d800045252121dc7f8e0ba370cee55f5" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -3060,6 +3262,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -3077,9 +3289,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -3122,7 +3334,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror 1.0.66", + "thiserror 1.0.69", "tokio", ] @@ -3400,7 +3612,7 @@ dependencies = [ "once_cell", "rand", "smallvec", - "thiserror 1.0.66", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -3422,7 +3634,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror 1.0.66", + "thiserror 1.0.69", "tokio", "tracing", "trust-dns-proto", @@ -3496,22 +3708,34 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.11.0" @@ -3887,12 +4111,48 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xdg" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -3914,12 +4174,55 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zstd-sys" version = "2.0.13+zstd.1.5.6" diff --git a/Cargo.toml b/Cargo.toml index 92b2c6db..61d84f13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,12 +88,12 @@ workspace = true [dependencies] argon2 = "0.5.3" async-trait = "0.1.83" -axum = { version = "0.7.7", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } -axum-extra = { version = "0.9.4", features = ["typed-header"] } +axum = { version = "0.7.9", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } +axum-extra = { version = "0.9.6", features = ["typed-header"] } axum-server = { version = "0.7.1", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" bytes = "1.8.0" -clap = { version = "4.5.20", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } +clap = { version = "4.5.21", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } futures-util = { version = "0.3.31", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" @@ -101,7 +101,7 @@ http = "1.1.0" http-body-util = "0.1.2" hyper = "1.5.0" hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "service"] } -image = { version = "0.25.4", default-features = false, features = ["jpeg", "png", "gif"] } +image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.3.0" lru-cache = "0.1.2" num_cpus = "1.16.0" @@ -122,18 +122,18 @@ ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.33.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } -rustls = { version = "0.23.16", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } +rustls = { version = "0.23.17", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.3", optional = true } -serde = { version = "1.0.214", features = ["rc"] } +serde = { version = "1.0.215", features = ["rc"] } serde_html_form = "0.2.6" serde_json = { version = "1.0.132", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" strum = { version = "0.26.3", features = ["derive"] } -thiserror = "1.0.66" +thiserror = "2.0.3" thread_local = "1.1.8" tikv-jemallocator = { version = "0.6.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -tokio = { version = "1.41.0", features = ["fs", "macros", "signal", "sync"] } +tokio = { version = "1.41.1", features = ["fs", "macros", "signal", "sync"] } toml = "0.8.19" tower = { version = "0.5.1", features = ["util"] } tower-http = { version = "0.6.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } diff --git a/book/changelog.md b/book/changelog.md index 66b990d0..c1886584 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -115,7 +115,8 @@ This will be the first release of Grapevine since it was forked from Conduit [!56](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/56), [!69](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/69), [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102), - [!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127)) + [!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127), + [!141](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/141)) 4. Stop returning unnecessary member counts from `/_matrix/client/{r0,v3}/sync`. ([!12](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/12)) 5. **BREAKING:** Allow federation by default. @@ -201,6 +202,9 @@ This will be the first release of Grapevine since it was forked from Conduit 20. Fix handling of v11 rooms with `m.room.create` event content that passes the authorization rules but doesn't match other parts of the spec. ([!139](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/139)) +21. Fix tiebreaking comparisons between events during state resolution. This + will reduce the rate at which servers disagree about the state of rooms. + ([!141](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/141)) ### Added From 2bf1975e759388df63f299f6afe0252959e2b312 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 22 Nov 2024 11:16:33 -0800 Subject: [PATCH 468/617] add flake output for the website root --- .gitlab-ci.yml | 3 ++- book.toml | 2 +- flake.nix | 3 +++ nix/pkgs/website-root/default.nix | 28 ++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 nix/pkgs/website-root/default.nix diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 08bee17e..30019862 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,7 +57,8 @@ pages: stage: deploy image: nixos/nix:2.18.2 script: - - direnv exec . mdbook build + - nix build .#website-root + - cp --recursive --dereference result public artifacts: paths: - public diff --git a/book.toml b/book.toml index 038e5202..eb6c2bdf 100644 --- a/book.toml +++ b/book.toml @@ -5,7 +5,7 @@ multilingual = false src = "book" [build] -build-dir = "public" +build-dir = "target/book" [output.html] git-repository-icon = "fa-git-square" diff --git a/flake.nix b/flake.nix index aae3883b..0d6f516e 100644 --- a/flake.nix +++ b/flake.nix @@ -65,6 +65,8 @@ (fenix.targets.${target}.fromManifestFile inputs.rust-manifest) .withComponents components) targets); + + website-root = self.callPackage ./nix/pkgs/website-root {}; }); in inputs.flake-utils.lib.eachDefaultSystem (system: @@ -81,6 +83,7 @@ { packages = { default = (mkScope pkgs).default; + website-root = (mkScope pkgs).website-root; } // builtins.listToAttrs diff --git a/nix/pkgs/website-root/default.nix b/nix/pkgs/website-root/default.nix new file mode 100644 index 00000000..bbb93a25 --- /dev/null +++ b/nix/pkgs/website-root/default.nix @@ -0,0 +1,28 @@ +# Keep sorted +{ inputs +, lib +, mdbook +, stdenv +}: + +stdenv.mkDerivation { + name = "website-root"; + + src = let filter = inputs.nix-filter.lib; in filter { + root = inputs.self; + + # Keep sorted + include = [ + "book" + "book.toml" + ]; + }; + + buildPhase = '' + ${lib.getExe mdbook} build + ''; + + installPhase = '' + mv target/book $out + ''; +} From 103a4fb56b4fd3fd852076e72de96d87304dc14e Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Wed, 27 Nov 2024 22:10:06 -0800 Subject: [PATCH 469/617] handle media keys where thumbnail size contains 0xFF Our current code should never write these, because we have an allowlist of thumbnail sizes. None of the allowed sizes contain a 0xFF byte. We have observed keys with a 0xFF in the thumbnail size a couple times on real servers, and believe an early version of conduit wrote these before the allowlist was added. These keys were originally handled correctly, and were broken by e2cba15ed2ec1681cc455d66a7bb2053cc204f23. Before that commit, we were parsing media keys backwards, and never tried to read the thumbnail size or mxc url. --- src/database/key_value/media.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 9fc15e54..713c8b05 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -24,18 +24,37 @@ impl TryFrom<&MediaFileKey> for MediaFileKeyParts { fields(key = utils::u8_slice_to_hex(key.as_bytes())), )] fn try_from(key: &MediaFileKey) -> Result { - let mut parts = key.as_bytes().split(|&b| b == 0xFF); - // Extract parts + // Extracting mxc url and thumbnail size is a bit fiddly, because the + // thumbnail size may contain 0xFF bytes. + let mut parts = key.as_bytes().splitn(2, |&b| b == 0xFF); + let mxc_bytes = parts .next() .ok_or_else(|| Error::BadDatabase("Missing MXC URI bytes"))?; - let thumbnail_size_bytes = parts.next().ok_or_else(|| { + let rest = parts.next().ok_or_else(|| { Error::BadDatabase("Missing thumbnail size bytes") })?; + // Thumbnail size is always 8 bytes + let (thumbnail_size_bytes, rest) = + rest.split_at_checked(8).ok_or_else(|| { + Error::BadDatabase("Missing thumbnail size bytes") + })?; + + // And always followed immediately by a 0xFF separator + let mut parts = rest.split(|&b| b == 0xFF); + + let thumbnail_size_rest = parts.next().ok_or_else(|| { + Error::BadDatabase("Missing Content-Disposition bytes") + })?; + if !thumbnail_size_rest.is_empty() { + return Err(Error::BadDatabase("Thumbnail size part is too long")); + } + + // The remaining parts are straightforward 0xFF-delimited fields let content_disposition_bytes = parts.next().ok_or_else(|| { Error::BadDatabase("Missing Content-Disposition bytes") })?; From 79bc2525e35bb93fc5823acd2685f08f71ad51d3 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Wed, 27 Nov 2024 22:13:25 -0800 Subject: [PATCH 470/617] add unit tests for media key parsing We've had at least a couple media key parsing bugs. Let's get a *little* more confidence that this code works :) --- src/database/key_value/media.rs | 140 ++++++++++++++++++++++++++++++++ src/service/media.rs | 1 + 2 files changed, 141 insertions(+) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 713c8b05..1e7866d5 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -9,6 +9,7 @@ use crate::{ utils, Error, Result, }; +#[derive(Debug, Eq, PartialEq)] struct MediaFileKeyParts { mxc: OwnedMxcUri, width: u32, @@ -218,3 +219,142 @@ impl service::media::Data for KeyValueDatabase { ) } } + +#[cfg(test)] +mod test { + use super::{FileMeta, MediaFileKey, MediaFileKeyParts}; + + #[test] + fn parse_key_basic() { + let mut key = b"mxc://example.com/someid".to_vec(); + key.push(0xFF); + key.extend_from_slice(&640_u32.to_be_bytes()); + key.extend_from_slice(&480_u32.to_be_bytes()); + key.push(0xFF); + key.extend_from_slice(b"inline"); + key.push(0xFF); + key.extend_from_slice(b"image/png"); + let key = MediaFileKey::new(key); + + assert_eq!( + MediaFileKeyParts::try_from(&key).unwrap(), + MediaFileKeyParts { + mxc: "mxc://example.com/someid".into(), + width: 640, + height: 480, + meta: FileMeta { + content_disposition: Some("inline".to_owned()), + content_type: Some("image/png".to_owned()), + } + } + ); + } + + #[test] + fn parse_key_no_content_type() { + let mut key = b"mxc://example.com/someid".to_vec(); + key.push(0xFF); + key.extend_from_slice(&640_u32.to_be_bytes()); + key.extend_from_slice(&480_u32.to_be_bytes()); + key.push(0xFF); + key.extend_from_slice(b"inline"); + key.push(0xFF); + // No content type + let key = MediaFileKey::new(key); + + assert_eq!( + MediaFileKeyParts::try_from(&key).unwrap(), + MediaFileKeyParts { + mxc: "mxc://example.com/someid".into(), + width: 640, + height: 480, + meta: FileMeta { + content_disposition: Some("inline".to_owned()), + content_type: None, + } + } + ); + } + + #[test] + fn parse_key_no_content_disposition() { + let mut key = b"mxc://example.com/someid".to_vec(); + key.push(0xFF); + key.extend_from_slice(&640_u32.to_be_bytes()); + key.extend_from_slice(&480_u32.to_be_bytes()); + key.push(0xFF); + // No content disposition + key.push(0xFF); + key.extend_from_slice(b"image/png"); + let key = MediaFileKey::new(key); + + assert_eq!( + MediaFileKeyParts::try_from(&key).unwrap(), + MediaFileKeyParts { + mxc: "mxc://example.com/someid".into(), + width: 640, + height: 480, + meta: FileMeta { + content_disposition: None, + content_type: Some("image/png".to_owned()), + } + } + ); + } + + #[test] + fn parse_key_no_content_disposition_or_type() { + let mut key = b"mxc://example.com/someid".to_vec(); + key.push(0xFF); + key.extend_from_slice(&640_u32.to_be_bytes()); + key.extend_from_slice(&480_u32.to_be_bytes()); + key.push(0xFF); + // No content disposition + key.push(0xFF); + // No content type + let key = MediaFileKey::new(key); + + assert_eq!( + MediaFileKeyParts::try_from(&key).unwrap(), + MediaFileKeyParts { + mxc: "mxc://example.com/someid".into(), + width: 640, + height: 480, + meta: FileMeta { + content_disposition: None, + content_type: None, + } + } + ); + } + + // Our current media service code has an allowlist of thumbnail dimensions, + // and so we don't expect to create new thumbnails with dimensions + // containing a 0xFF byte. Thumbnails with a 0xFF in the dimensions may + // have been created previously, so we need to be able to read them. + #[test] + fn parse_key_separator_in_thumbnail_dims() { + let mut key = b"mxc://example.com/someid".to_vec(); + key.push(0xFF); + key.extend_from_slice(&[0x0, 0x0, 0xFF, 0xFF]); + key.extend_from_slice(&[0x0, 0x0, 0x10, 0xFF]); + key.push(0xFF); + key.extend_from_slice(b"inline"); + key.push(0xFF); + key.extend_from_slice(b"image/png"); + let key = MediaFileKey::new(key); + + assert_eq!( + MediaFileKeyParts::try_from(&key).unwrap(), + MediaFileKeyParts { + mxc: "mxc://example.com/someid".into(), + width: 0xFFFF, + height: 0x10FF, + meta: FileMeta { + content_disposition: Some("inline".to_owned()), + content_type: Some("image/png".to_owned()), + } + } + ); + } +} diff --git a/src/service/media.rs b/src/service/media.rs index 03109004..399dc1dd 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -14,6 +14,7 @@ mod data; pub(crate) use data::Data; +#[derive(Debug, Eq, PartialEq)] pub(crate) struct FileMeta { // This gets written to the database but we no longer read it // From ed789e653675765e7c753e823a8d45ede8d62413 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Thu, 28 Nov 2024 15:55:20 -0800 Subject: [PATCH 471/617] show count of corrupted media keys in delete-remote-media command --- src/service/admin.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index e406d0f7..32d18f9d 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -806,8 +806,10 @@ impl Service { } }); + let mut failed_keys = 0; while let Some(mxc) = rx.recv().await { let Ok(mxc) = mxc else { + failed_keys += 1; continue; }; @@ -828,11 +830,25 @@ impl Service { } } - let message = if dry_run { + let mut message = if dry_run { format!("{count} media objects would be deleted.") } else { format!("{count} media objects deleted.") }; + + if failed_keys != 0 { + write!( + message, + "\n{failed_keys} corrupted media keys found in the \ + database." + ) + .unwrap(); + write!( + message, + "\nCheck the server logs for more details." + ) + .unwrap(); + } RoomMessageEventContent::text_plain(message) } AdminCommand::DeactivateUser { From bdb623cb07e81bcda9d07df193a4f98d7c41fbb2 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Thu, 28 Nov 2024 16:46:54 -0800 Subject: [PATCH 472/617] add comment explaining that failed media keys are logged --- src/service/admin.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/service/admin.rs b/src/service/admin.rs index 32d18f9d..16d8a086 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -809,6 +809,7 @@ impl Service { let mut failed_keys = 0; while let Some(mxc) = rx.recv().await { let Ok(mxc) = mxc else { + // Error details are logged by media::iter_all failed_keys += 1; continue; }; From 11b50556476b447d9c7acc65229cb99e8b74213a Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Thu, 28 Nov 2024 16:47:22 -0800 Subject: [PATCH 473/617] log details of failed media deletion --- src/service/media.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 399dc1dd..f5dfda4d 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -8,7 +8,7 @@ use tokio::{ }; use tracing::{debug, warn}; -use crate::{services, Result}; +use crate::{services, utils, Result}; mod data; @@ -114,14 +114,29 @@ impl Service { /// Deletes a media object and all associated thumbnails. #[tracing::instrument(skip(self))] pub(crate) async fn delete(&self, mxc: OwnedMxcUri) -> Result<()> { - let (_, key) = self.db.search_file_metadata(mxc.clone(), 0, 0)?; + let (_, key) = + self.db.search_file_metadata(mxc.clone(), 0, 0).inspect_err( + |error| warn!(%error, "Failed to find original media key"), + )?; let thumbnails = self.db.search_thumbnails_metadata(mxc)?; for (_, thumbnail_key) in thumbnails { - self.delete_by_key(thumbnail_key).await?; + self.delete_by_key(thumbnail_key.clone()).await.inspect_err( + |error| { + warn!( + thumbnail_key = utils::u8_slice_to_hex( + thumbnail_key.as_bytes() + ), + %error, + "Failed to delete thumbnail media" + ); + }, + )?; } - self.delete_by_key(key).await?; + self.delete_by_key(key).await.inspect_err( + |error| warn!(%error, "Failed to delete original media"), + )?; Ok(()) } From 8fcec6396eb8e5e928dba43fdf45db3788b20053 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Thu, 28 Nov 2024 16:47:52 -0800 Subject: [PATCH 474/617] keep going when one deletion fails in delete-remote-media We *should* ensure that media deletion is always successful, but when a bug causes a single object to fail deletion it's better to try to delete the remaining objects than to give up entirely. --- src/service/admin.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 16d8a086..7fc67118 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -807,6 +807,7 @@ impl Service { }); let mut failed_keys = 0; + let mut failed_deletes = 0; while let Some(mxc) = rx.recv().await { let Ok(mxc) = mxc else { // Error details are logged by media::iter_all @@ -825,10 +826,18 @@ impl Service { } } - count += 1; + // Technically this can be collapsed, but relying on && + // short-circuiting to avoid the delete side-effect is + // confusing. + #[allow(clippy::collapsible_if)] if !dry_run { - services().media.delete(mxc).await?; + if services().media.delete(mxc).await.is_err() { + // Error details are logged by media::delete + failed_deletes += 1; + continue; + } } + count += 1; } let mut message = if dry_run { @@ -844,6 +853,17 @@ impl Service { database." ) .unwrap(); + } + + if failed_deletes != 0 { + write!( + message, + "\n{failed_deletes} media objects failed to delete." + ) + .unwrap(); + } + + if failed_keys != 0 || failed_deletes != 0 { write!( message, "\nCheck the server logs for more details." From 2f8e0e3e52620bf36756989f2d71d03b75fe8f50 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 29 Nov 2024 10:33:13 -0800 Subject: [PATCH 475/617] update rust dependencies Ruma dropped a couple dependencies and includes a stateres performance improvement. May as well pull in everything else (except OTel) while we're at it. --- Cargo.lock | 229 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 14 ++-- 2 files changed, 120 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a79bcae8..d7ccc059 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,7 +167,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tower 0.5.1", "tower-layer", @@ -190,7 +190,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", @@ -346,9 +346,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -364,9 +364,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bzip2-sys" @@ -381,9 +381,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ "jobserver", "libc", @@ -493,9 +493,9 @@ checksum = "013b6c2c3a14d678f38cd23994b02da3a1a1b6a5d1eedddfe63a5a5f11b13a81" [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -509,9 +509,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -693,12 +693,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -952,9 +952,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -986,9 +986,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" @@ -1121,9 +1121,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -1372,7 +1372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -1430,9 +1430,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -1445,9 +1445,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "fb15147158e79fd8b8afd0252522769c4f48725460b37338544d8379d94fc8f9" dependencies = [ "wasm-bindgen", ] @@ -1487,9 +1487,9 @@ dependencies = [ [[package]] name = "konst" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9" +checksum = "b65f00fb3910881e52bf0850ae2a82aea411488a557e1c02820ceaa60963dce3" dependencies = [ "const_panic", "konst_kernel", @@ -1498,9 +1498,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.9" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b" +checksum = "599c1232f55c72c7fc378335a3efe1c878c92720838c8e6a4fd87784ef7764de" dependencies = [ "typewit", ] @@ -1519,15 +1519,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.164" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1569,9 +1569,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -1665,11 +1665,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", @@ -2142,9 +2141,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2385,7 +2384,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tokio-rustls", "tokio-socks", @@ -2425,7 +2424,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.11.1" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "assign", "js_int", @@ -2446,7 +2445,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", @@ -2458,7 +2457,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.19.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "as_variant", "assign", @@ -2481,7 +2480,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.14.1" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "as_variant", "base64 0.22.1", @@ -2511,7 +2510,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.29.1" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "as_variant", "indexmap 2.6.0", @@ -2534,7 +2533,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "bytes", "http", @@ -2552,7 +2551,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "thiserror 2.0.3", @@ -2561,7 +2560,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", @@ -2571,10 +2570,9 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.14.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "cfg-if", - "once_cell", "proc-macro-crate", "proc-macro2", "quote", @@ -2587,7 +2585,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", @@ -2599,7 +2597,7 @@ dependencies = [ [[package]] name = "ruma-server-util" version = "0.4.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "headers", "http", @@ -2612,7 +2610,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.16.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2628,9 +2626,8 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.12.0" -source = "git+https://github.com/ruma/ruma?branch=main#42aae76a2d3b09779cd15148dbe858f282ac9d70" +source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ - "itertools 0.13.0", "js_int", "ruma-common", "ruma-events", @@ -2709,9 +2706,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -2722,9 +2719,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.17" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", "once_cell", @@ -2737,12 +2734,11 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework", @@ -2791,9 +2787,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -2812,9 +2808,9 @@ checksum = "1be20c5f7f393ee700f8b2f28ea35812e4e212f40774b550cd2a93ea91684451" [[package]] name = "security-framework" -version = "2.11.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" dependencies = [ "bitflags 2.6.0", "core-foundation", @@ -2874,9 +2870,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3041,9 +3037,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3110,9 +3106,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -3127,9 +3123,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -3463,9 +3459,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "bitflags 2.6.0", "bytes", @@ -3492,9 +3488,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -3504,9 +3500,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3515,9 +3511,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3565,9 +3561,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -3575,9 +3571,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -3654,9 +3650,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typewit" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24" +checksum = "d51dbd25812f740f45e2a9769f84711982e000483b13b73a8a1852e092abac8c" dependencies = [ "typewit_proc_macros", ] @@ -3681,9 +3677,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -3708,9 +3704,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna 1.0.3", @@ -3789,9 +3785,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b" dependencies = [ "cfg-if", "once_cell", @@ -3800,9 +3796,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283" dependencies = [ "bumpalo", "log", @@ -3815,21 +3811,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "951fe82312ed48443ac78b66fa43eded9999f738f6022e67aead7b708659e49a" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3837,9 +3834,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6" dependencies = [ "proc-macro2", "quote", @@ -3850,15 +3847,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "476364ff87d0ae6bfb661053a9104ab312542658c3d8f963b7ace80b6f9b26b9" dependencies = [ "js-sys", "wasm-bindgen", @@ -4131,9 +4128,9 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -4143,9 +4140,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -4176,18 +4173,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 61d84f13..053f605a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,14 +92,14 @@ axum = { version = "0.7.9", default-features = false, features = ["form", "http1 axum-extra = { version = "0.9.6", features = ["typed-header"] } axum-server = { version = "0.7.1", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" -bytes = "1.8.0" +bytes = "1.9.0" clap = { version = "4.5.21", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } futures-util = { version = "0.3.31", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" http = "1.1.0" http-body-util = "0.1.2" -hyper = "1.5.0" +hyper = "1.5.1" hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "service"] } image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.3.0" @@ -122,11 +122,11 @@ ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.33.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } -rustls = { version = "0.23.17", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } +rustls = { version = "0.23.19", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.3", optional = true } serde = { version = "1.0.215", features = ["rc"] } serde_html_form = "0.2.6" -serde_json = { version = "1.0.132", features = ["raw_value"] } +serde_json = { version = "1.0.133", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" strum = { version = "0.26.3", features = ["derive"] } @@ -136,11 +136,11 @@ tikv-jemallocator = { version = "0.6.0", features = ["unprefixed_malloc_on_suppo tokio = { version = "1.41.1", features = ["fs", "macros", "signal", "sync"] } toml = "0.8.19" tower = { version = "0.5.1", features = ["util"] } -tower-http = { version = "0.6.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } -tracing = { version = "0.1.40", features = [] } +tower-http = { version = "0.6.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } +tracing = { version = "0.1.41", features = [] } tracing-flame = "0.2.0" tracing-opentelemetry = "0.25.0" -tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } +tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] } trust-dns-resolver = "0.23.2" xdg = "2.5.2" From f0e41ade595c41a865e02ae77b0c3d1da727c08d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 1 Nov 2024 11:28:10 -0700 Subject: [PATCH 476/617] set rules for gitlab ci jobs This should give us access to more CI variables. This also replaces our usage of `only`, which is apparently deprecated. --- .gitlab-ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 30019862..6ecc662d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,6 +38,9 @@ before_script: ci: stage: ci image: nixos/nix:2.18.2 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - ./bin/nix-build-and-cache ci @@ -50,17 +53,20 @@ ci: artifacts: stage: artifacts image: nixos/nix:2.18.2 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - ./bin/nix-build-and-cache packages pages: stage: deploy image: nixos/nix:2.18.2 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH script: - nix build .#website-root - cp --recursive --dereference result public artifacts: paths: - public - only: - - main From ec01a84efbb9c8dcf55e82fe94e47362fa7f4093 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 25 Oct 2024 19:54:41 -0700 Subject: [PATCH 477/617] add custom release profile with debuginfo enabled This is primarily useful for profiling. Also change the nix package to decide whether to strip based on whether the "release" profile is used. --- Cargo.toml | 4 ++++ nix/pkgs/default/default.nix | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 053f605a..e5f30f91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -166,3 +166,7 @@ jemalloc = ["dep:tikv-jemallocator"] rocksdb = ["dep:rocksdb"] sqlite = ["dep:rusqlite", "dep:parking_lot", "tokio/signal"] systemd = ["dep:sd-notify"] + +[profile.release-debug] +inherits = "release" +debug = true diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 7aa9151e..cb7be038 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -78,7 +78,7 @@ let ]; }; - dontStrip = profile == "dev"; + dontStrip = profile != "release"; buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys; From 861016ce0f331ef6b48020b3159e84e8ebaf0482 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 10 Oct 2024 22:13:24 -0700 Subject: [PATCH 478/617] inline state accessor service code from data trait These were all calling into services() and not actually directly accessing the database at all anyway. --- .../key_value/rooms/state_accessor.rs | 181 +----------------- src/service/rooms/state_accessor.rs | 116 ++++++++++- src/service/rooms/state_accessor/data.rs | 65 +------ 3 files changed, 114 insertions(+), 248 deletions(-) diff --git a/src/database/key_value/rooms/state_accessor.rs b/src/database/key_value/rooms/state_accessor.rs index b2265ba2..1e9e1b64 100644 --- a/src/database/key_value/rooms/state_accessor.rs +++ b/src/database/key_value/rooms/state_accessor.rs @@ -1,139 +1,12 @@ -use std::{collections::HashMap, sync::Arc}; - -use async_trait::async_trait; -use ruma::{events::StateEventType, EventId, RoomId}; +use ruma::EventId; use crate::{ database::KeyValueDatabase, - service::{ - self, - rooms::short::{ShortStateHash, ShortStateKey}, - }, - services, utils, Error, PduEvent, Result, + service::{self, rooms::short::ShortStateHash}, + utils, Error, Result, }; -#[async_trait] impl service::rooms::state_accessor::Data for KeyValueDatabase { - async fn state_full_ids( - &self, - shortstatehash: ShortStateHash, - ) -> Result>> { - let full_state = services() - .rooms - .state_compressor - .load_shortstatehash_info(shortstatehash)? - .pop() - .expect("there is always one layer") - .full_state; - let mut result = HashMap::new(); - let mut i = 0; - for compressed in full_state.iter() { - let parsed = services() - .rooms - .state_compressor - .parse_compressed_state_event(compressed)?; - result.insert(parsed.0, parsed.1); - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } - Ok(result) - } - - async fn state_full( - &self, - shortstatehash: ShortStateHash, - ) -> Result>> { - let full_state = services() - .rooms - .state_compressor - .load_shortstatehash_info(shortstatehash)? - .pop() - .expect("there is always one layer") - .full_state; - - let mut result = HashMap::new(); - let mut i = 0; - for compressed in full_state.iter() { - let (_, eventid) = services() - .rooms - .state_compressor - .parse_compressed_state_event(compressed)?; - if let Some(pdu) = services().rooms.timeline.get_pdu(&eventid)? { - result.insert( - ( - pdu.kind.to_string().into(), - pdu.state_key - .as_ref() - .ok_or_else(|| { - Error::bad_database( - "State event has no state key.", - ) - })? - .clone(), - ), - pdu, - ); - } - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } - - Ok(result) - } - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn state_get_id( - &self, - shortstatehash: ShortStateHash, - event_type: &StateEventType, - state_key: &str, - ) -> Result>> { - let Some(shortstatekey) = - services().rooms.short.get_shortstatekey(event_type, state_key)? - else { - return Ok(None); - }; - let full_state = services() - .rooms - .state_compressor - .load_shortstatehash_info(shortstatehash)? - .pop() - .expect("there is always one layer") - .full_state; - Ok(full_state - .iter() - .find(|compressed| compressed.state == shortstatekey) - .and_then(|compressed| { - services() - .rooms - .state_compressor - .parse_compressed_state_event(compressed) - .ok() - .map(|(_, id)| id) - })) - } - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn state_get( - &self, - shortstatehash: ShortStateHash, - event_type: &StateEventType, - state_key: &str, - ) -> Result>> { - self.state_get_id(shortstatehash, event_type, state_key)? - .map_or(Ok(None), |event_id| { - services().rooms.timeline.get_pdu(&event_id) - }) - } - /// Returns the state hash for this pdu. fn pdu_shortstatehash( &self, @@ -158,52 +31,4 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { }, ) } - - /// Returns the full room state. - async fn room_state_full( - &self, - room_id: &RoomId, - ) -> Result>> { - if let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - { - self.state_full(current_shortstatehash).await - } else { - Ok(HashMap::new()) - } - } - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn room_state_get_id( - &self, - room_id: &RoomId, - event_type: &StateEventType, - state_key: &str, - ) -> Result>> { - if let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - { - self.state_get_id(current_shortstatehash, event_type, state_key) - } else { - Ok(None) - } - } - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn room_state_get( - &self, - room_id: &RoomId, - event_type: &StateEventType, - state_key: &str, - ) -> Result>> { - if let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - { - self.state_get(current_shortstatehash, event_type, state_key) - } else { - Ok(None) - } - } } diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 108e510d..e9794e53 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -68,7 +68,28 @@ impl Service { &self, shortstatehash: ShortStateHash, ) -> Result>> { - self.db.state_full_ids(shortstatehash).await + let full_state = services() + .rooms + .state_compressor + .load_shortstatehash_info(shortstatehash)? + .pop() + .expect("there is always one layer") + .full_state; + let mut result = HashMap::new(); + let mut i = 0; + for compressed in full_state.iter() { + let parsed = services() + .rooms + .state_compressor + .parse_compressed_state_event(compressed)?; + result.insert(parsed.0, parsed.1); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } + Ok(result) } #[tracing::instrument(skip(self))] @@ -76,7 +97,45 @@ impl Service { &self, shortstatehash: ShortStateHash, ) -> Result>> { - self.db.state_full(shortstatehash).await + let full_state = services() + .rooms + .state_compressor + .load_shortstatehash_info(shortstatehash)? + .pop() + .expect("there is always one layer") + .full_state; + + let mut result = HashMap::new(); + let mut i = 0; + for compressed in full_state.iter() { + let (_, eventid) = services() + .rooms + .state_compressor + .parse_compressed_state_event(compressed)?; + if let Some(pdu) = services().rooms.timeline.get_pdu(&eventid)? { + result.insert( + ( + pdu.kind.to_string().into(), + pdu.state_key + .as_ref() + .ok_or_else(|| { + Error::bad_database( + "State event has no state key.", + ) + })? + .clone(), + ), + pdu, + ); + } + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } + + Ok(result) } /// Returns a single PDU from `room_id` with key (`event_type`, @@ -88,7 +147,29 @@ impl Service { event_type: &StateEventType, state_key: &str, ) -> Result>> { - self.db.state_get_id(shortstatehash, event_type, state_key) + let Some(shortstatekey) = + services().rooms.short.get_shortstatekey(event_type, state_key)? + else { + return Ok(None); + }; + let full_state = services() + .rooms + .state_compressor + .load_shortstatehash_info(shortstatehash)? + .pop() + .expect("there is always one layer") + .full_state; + Ok(full_state + .iter() + .find(|compressed| compressed.state == shortstatekey) + .and_then(|compressed| { + services() + .rooms + .state_compressor + .parse_compressed_state_event(compressed) + .ok() + .map(|(_, id)| id) + })) } /// Returns a single PDU from `room_id` with key (`event_type`, @@ -100,7 +181,10 @@ impl Service { event_type: &StateEventType, state_key: &str, ) -> Result>> { - self.db.state_get(shortstatehash, event_type, state_key) + self.state_get_id(shortstatehash, event_type, state_key)? + .map_or(Ok(None), |event_id| { + services().rooms.timeline.get_pdu(&event_id) + }) } /// Get membership for given user in state @@ -360,7 +444,13 @@ impl Service { &self, room_id: &RoomId, ) -> Result>> { - self.db.room_state_full(room_id).await + if let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + { + self.state_full(current_shortstatehash).await + } else { + Ok(HashMap::new()) + } } /// Returns a single PDU from `room_id` with key (`event_type`, @@ -372,7 +462,13 @@ impl Service { event_type: &StateEventType, state_key: &str, ) -> Result>> { - self.db.room_state_get_id(room_id, event_type, state_key) + if let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + { + self.state_get_id(current_shortstatehash, event_type, state_key) + } else { + Ok(None) + } } /// Returns a single PDU from `room_id` with key (`event_type`, @@ -384,7 +480,13 @@ impl Service { event_type: &StateEventType, state_key: &str, ) -> Result>> { - self.db.room_state_get(room_id, event_type, state_key) + if let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + { + self.state_get(current_shortstatehash, event_type, state_key) + } else { + Ok(None) + } } #[tracing::instrument(skip(self))] diff --git a/src/service/rooms/state_accessor/data.rs b/src/service/rooms/state_accessor/data.rs index 4fd1b2af..4399dc66 100644 --- a/src/service/rooms/state_accessor/data.rs +++ b/src/service/rooms/state_accessor/data.rs @@ -1,72 +1,11 @@ -use std::{collections::HashMap, sync::Arc}; +use ruma::EventId; -use async_trait::async_trait; -use ruma::{events::StateEventType, EventId, RoomId}; +use crate::{service::rooms::short::ShortStateHash, Result}; -use crate::{ - service::rooms::short::{ShortStateHash, ShortStateKey}, - PduEvent, Result, -}; - -#[async_trait] pub(crate) trait Data: Send + Sync { - /// Builds a StateMap by iterating over all keys that start - /// with state_hash, this gives the full state for the given state_hash. - async fn state_full_ids( - &self, - shortstatehash: ShortStateHash, - ) -> Result>>; - - async fn state_full( - &self, - shortstatehash: ShortStateHash, - ) -> Result>>; - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn state_get_id( - &self, - shortstatehash: ShortStateHash, - event_type: &StateEventType, - state_key: &str, - ) -> Result>>; - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn state_get( - &self, - shortstatehash: ShortStateHash, - event_type: &StateEventType, - state_key: &str, - ) -> Result>>; - /// Returns the state hash for this pdu. fn pdu_shortstatehash( &self, event_id: &EventId, ) -> Result>; - - /// Returns the full room state. - async fn room_state_full( - &self, - room_id: &RoomId, - ) -> Result>>; - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn room_state_get_id( - &self, - room_id: &RoomId, - event_type: &StateEventType, - state_key: &str, - ) -> Result>>; - - /// Returns a single PDU from `room_id` with key (`event_type`, - /// `state_key`). - fn room_state_get( - &self, - room_id: &RoomId, - event_type: &StateEventType, - state_key: &str, - ) -> Result>>; } From f0f81db99b67850026b0debb97051554af771838 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 14:59:38 -0800 Subject: [PATCH 479/617] return Option from media::data::search_file_metadata This is useful to easily distinguish missing files from corrupted keys. All existing usage sites have been modified so there is no behavior change in this commit. --- src/database/key_value/media.rs | 14 +++++++------- src/service/media.rs | 23 +++++++++++++++-------- src/service/media/data.rs | 2 +- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 1e7866d5..8f7ca2d7 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -1,4 +1,4 @@ -use ruma::{api::client::error::ErrorKind, OwnedMxcUri}; +use ruma::OwnedMxcUri; use crate::{ database::KeyValueDatabase, @@ -158,21 +158,21 @@ impl service::media::Data for KeyValueDatabase { mxc: OwnedMxcUri, width: u32, height: u32, - ) -> Result<(FileMeta, MediaFileKey)> { + ) -> Result> { let mut prefix = mxc.as_bytes().to_vec(); prefix.push(0xFF); prefix.extend_from_slice(&width.to_be_bytes()); prefix.extend_from_slice(&height.to_be_bytes()); prefix.push(0xFF); - let (key, _) = - self.mediaid_file.scan_prefix(prefix).next().ok_or( - Error::BadRequest(ErrorKind::NotFound, "Media not found"), - )?; + let Some((key, _)) = self.mediaid_file.scan_prefix(prefix).next() + else { + return Ok(None); + }; let key = MediaFileKey::new(key); let parts = MediaFileKeyParts::try_from(&key)?; - Ok((parts.meta, key)) + Ok(Some((parts.meta, key))) } fn delete_file_metadata(&self, key: MediaFileKey) -> Result<()> { diff --git a/src/service/media.rs b/src/service/media.rs index f5dfda4d..74cc0f12 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -1,14 +1,17 @@ use std::io::Cursor; use image::imageops::FilterType; -use ruma::{http_headers::ContentDisposition, OwnedMxcUri}; +use ruma::{ + api::client::error::ErrorKind, http_headers::ContentDisposition, + OwnedMxcUri, +}; use tokio::{ fs::{self, File}, io::{AsyncReadExt, AsyncWriteExt}, }; use tracing::{debug, warn}; -use crate::{services, utils, Result}; +use crate::{services, utils, Error, Result}; mod data; @@ -96,7 +99,7 @@ impl Service { &self, mxc: OwnedMxcUri, ) -> Result)>> { - if let Ok((meta, key)) = self.db.search_file_metadata(mxc, 0, 0) { + if let Ok(Some((meta, key))) = self.db.search_file_metadata(mxc, 0, 0) { let path = services().globals.get_media_file(&key); let mut file_data = Vec::new(); let Ok(mut file) = File::open(path).await else { @@ -114,10 +117,13 @@ impl Service { /// Deletes a media object and all associated thumbnails. #[tracing::instrument(skip(self))] pub(crate) async fn delete(&self, mxc: OwnedMxcUri) -> Result<()> { - let (_, key) = - self.db.search_file_metadata(mxc.clone(), 0, 0).inspect_err( + let (_, key) = self + .db + .search_file_metadata(mxc.clone(), 0, 0) + .inspect_err( |error| warn!(%error, "Failed to find original media key"), - )?; + )? + .ok_or(Error::BadRequest(ErrorKind::NotFound, "Media not found"))?; let thumbnails = self.db.search_thumbnails_metadata(mxc)?; for (_, thumbnail_key) in thumbnails { @@ -292,7 +298,7 @@ impl Service { let (width, height, crop) = Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); - if let Ok((meta, key)) = + if let Ok(Some((meta, key))) = self.db.search_file_metadata(mxc.clone(), width, height) { debug!("Using saved thumbnail"); @@ -303,7 +309,8 @@ impl Service { return Ok(Some((meta, file.clone()))); } - let Ok((meta, key)) = self.db.search_file_metadata(mxc.clone(), 0, 0) + let Ok(Some((meta, key))) = + self.db.search_file_metadata(mxc.clone(), 0, 0) else { debug!("Original image not found, can't generate thumbnail"); return Ok(None); diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 139032dc..01b2ceb1 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -17,7 +17,7 @@ pub(crate) trait Data: Send + Sync { mxc: OwnedMxcUri, width: u32, height: u32, - ) -> Result<(FileMeta, MediaFileKey)>; + ) -> Result>; fn delete_file_metadata(&self, key: MediaFileKey) -> Result<()>; From 14b44064b3003a631cb75c0d1a28ab027e687a3f Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 15:03:59 -0800 Subject: [PATCH 480/617] propagate corrupted media key errors Now that we are able to distinguish between corrupted media keys and missing files, it makes more sense to propagate the corrupted keys up to the caller. --- src/service/media.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 74cc0f12..b9ccddae 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -99,7 +99,7 @@ impl Service { &self, mxc: OwnedMxcUri, ) -> Result)>> { - if let Ok(Some((meta, key))) = self.db.search_file_metadata(mxc, 0, 0) { + if let Some((meta, key)) = self.db.search_file_metadata(mxc, 0, 0)? { let path = services().globals.get_media_file(&key); let mut file_data = Vec::new(); let Ok(mut file) = File::open(path).await else { @@ -298,8 +298,8 @@ impl Service { let (width, height, crop) = Self::thumbnail_properties(width, height).unwrap_or((0, 0, false)); - if let Ok(Some((meta, key))) = - self.db.search_file_metadata(mxc.clone(), width, height) + if let Some((meta, key)) = + self.db.search_file_metadata(mxc.clone(), width, height)? { debug!("Using saved thumbnail"); let path = services().globals.get_media_file(&key); @@ -309,8 +309,8 @@ impl Service { return Ok(Some((meta, file.clone()))); } - let Ok(Some((meta, key))) = - self.db.search_file_metadata(mxc.clone(), 0, 0) + let Some((meta, key)) = + self.db.search_file_metadata(mxc.clone(), 0, 0)? else { debug!("Original image not found, can't generate thumbnail"); return Ok(None); From 230192be1b595d7a8cb29d221d9345479100d4fe Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Thu, 28 Nov 2024 15:21:15 -0800 Subject: [PATCH 481/617] skip thumbnails with only one nonzero dimension in all_file_metadata Previously we were only skipping thumbnails that had both dimensions nonzero. I think the assumption was that only the dimensions allowed by services::media::thumbnail_properties would be used. This is not the case because we have used arbitrary thumbnail dimensions when requesting remote thumbnails. --- src/database/key_value/media.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index 8f7ca2d7..a7272042 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -208,7 +208,7 @@ impl service::media::Data for KeyValueDatabase { let key = MediaFileKey::new(key); let parts = MediaFileKeyParts::try_from(&key)?; - if parts.width != 0 && parts.height != 0 { + if parts.width != 0 || parts.height != 0 { // Skip thumbnails return Ok(None); }; From 916088a22fee4cca76de67d72224d7fab045cc1a Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Fri, 29 Nov 2024 11:33:13 -0800 Subject: [PATCH 482/617] include mxcs from dangling thumbnails in service::media::iter_all When requesting remote thumbnails over federation, we can end up with a thumbnail in the media db without an associated original file. Because of this, skipping thumbnails is insufficient to get a list of all MXCs. --- src/database/key_value/media.rs | 5 ----- src/service/media.rs | 20 ++++++++++++++++++-- src/service/media/data.rs | 4 +--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index a7272042..53e2146e 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -208,11 +208,6 @@ impl service::media::Data for KeyValueDatabase { let key = MediaFileKey::new(key); let parts = MediaFileKeyParts::try_from(&key)?; - if parts.width != 0 || parts.height != 0 { - // Skip thumbnails - return Ok(None); - }; - Ok(Some((parts.mxc, parts.meta, key))) }) .filter_map(Result::transpose), diff --git a/src/service/media.rs b/src/service/media.rs index b9ccddae..836e8ef9 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -169,11 +169,27 @@ impl Service { /// List all media stored in the database. /// - /// Each MXC is list once. Thumbnails are not included separately from the + /// Each MXC is listed once. Thumbnails are not included separately from the /// original media. #[tracing::instrument(skip(self))] pub(crate) fn iter_all(&self) -> impl Iterator> { - self.db.all_file_metadata().map(|media| media.map(|(mxc, ..)| mxc)) + let mut prev_mxc = None; + self.db + .all_file_metadata() + .map(|media| media.map(|(mxc, ..)| mxc)) + .filter(move |mxc| { + if let Ok(mxc) = mxc { + // Skip mxcs that we have already seen. All files associated + // with a given mxc should appear consecutively in the db + // iterator, so we only need to check against the previous + // value. + if prev_mxc.as_ref() == Some(mxc) { + return false; + } + prev_mxc = Some(mxc.clone()); + } + true + }) } /// Returns width, height of the thumbnail and whether it should be cropped. diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 01b2ceb1..c3cff8cb 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -30,9 +30,7 @@ pub(crate) trait Data: Send + Sync { mxc: OwnedMxcUri, ) -> Result>; - /// Returns an iterator over metadata for all media. - /// - /// Thumbnails are not included. + /// Returns an iterator over metadata for all media, including thumbnails. fn all_file_metadata( &self, ) -> Box< From 46e8a63489fe4ca0505f1f0ab10cb00910673b1b Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 00:40:55 -0800 Subject: [PATCH 483/617] allow deleting dangling thumbnails Previously attempting to delete an MXC that is only associated with dangling thumbnails would fail, because it assumes that every thumbnail must have a corresponding original in the db, and errors out if it can't find the original. This is incorrect because we create dangling thumbnails when requesting a remote thumbnail over federation when we don't have the original file. --- src/service/media.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 836e8ef9..0d231c67 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -117,16 +117,11 @@ impl Service { /// Deletes a media object and all associated thumbnails. #[tracing::instrument(skip(self))] pub(crate) async fn delete(&self, mxc: OwnedMxcUri) -> Result<()> { - let (_, key) = self - .db - .search_file_metadata(mxc.clone(), 0, 0) - .inspect_err( - |error| warn!(%error, "Failed to find original media key"), - )? - .ok_or(Error::BadRequest(ErrorKind::NotFound, "Media not found"))?; + let mut any_files = false; - let thumbnails = self.db.search_thumbnails_metadata(mxc)?; + let thumbnails = self.db.search_thumbnails_metadata(mxc.clone())?; for (_, thumbnail_key) in thumbnails { + any_files = true; self.delete_by_key(thumbnail_key.clone()).await.inspect_err( |error| { warn!( @@ -140,11 +135,25 @@ impl Service { )?; } - self.delete_by_key(key).await.inspect_err( - |error| warn!(%error, "Failed to delete original media"), - )?; + if let Some((_, key)) = + self.db.search_file_metadata(mxc, 0, 0).inspect_err( + |error| warn!(%error, "Failed to find original media key"), + )? + { + any_files = true; + self.delete_by_key(key).await.inspect_err( + |error| warn!(%error, "Failed to delete original media"), + )?; + } - Ok(()) + if any_files { + Ok(()) + } else { + let error = + Error::BadRequest(ErrorKind::NotFound, "Media not found"); + warn!(%error, "Failed to delete media"); + Err(error) + } } /// Deletes a specific media key, which may or may not be a thumbnail. From a5fe9129f13844fe1ad67b5aab1d00d4bd7fa9e9 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 15:14:17 -0800 Subject: [PATCH 484/617] update media deletion entry in changelog --- book/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index c1886584..3cd11429 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -264,7 +264,8 @@ This will be the first release of Grapevine since it was forked from Conduit ([!96](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/96)) 18. Added admin commands to delete media ([!99](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/99), - [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102)) + [!102](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/102), + [!148](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/148)) 19. Allow configuring the served API components per listener. ([!109](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/109)) 20. Include the [`traceresponse` header](https://w3c.github.io/trace-context/#traceresponse-header) From 26e986a89b772003e4bb08ac5d704011e7bcacdf Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 16:48:58 -0800 Subject: [PATCH 485/617] update olivia's gitlab and matrix usernames --- book/contributing.md | 2 +- book/introduction.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/contributing.md b/book/contributing.md index 6eb64d38..e332e56b 100644 --- a/book/contributing.md +++ b/book/contributing.md @@ -38,7 +38,7 @@ the Grapevine maintainers in one of the following ways: * Open a GitLab issue that's marked as confidential * Create a private, invite-only, E2EE Matrix room and invite the following users: - * `@benjamin:computer.surgery` + * `@olivia:computer.surgery` * `@charles:computer.surgery` * `@xiretza:xiretza.xyz` diff --git a/book/introduction.md b/book/introduction.md index 72b8a871..3f183042 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -56,7 +56,7 @@ The project's current maintainers[^1] are: | Matrix username | GitLab username | |-|-| | `@charles:computer.surgery` | `charles` | -| `@benjamin:computer.surgery` | `benjamin` | +| `@olivia:computer.surgery` | `olivia` | | `@xiretza:xiretza.xyz` | `Lambda` | We would like to expand this list in the future as social trust is built and From 54c25ceb3ca0516b9297e05ae7655045b37ee9a6 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 16:57:04 -0800 Subject: [PATCH 486/617] update docs link in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4de02de..ae3f9a70 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ A Matrix homeserver. [Click here to read the latest version.][0] -[0]: https://matrix.pages.gitlab.computer.surgery/grapevine/ +[0]: https://grapevine.computer.surgery/ From eda34adef20291d61aee6ec03d11793875e84f42 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 17:53:25 -0800 Subject: [PATCH 487/617] add conduit migration section to docs Most of the database compatibility info came from Charles' notes in #17. --- book/SUMMARY.md | 1 + book/migration.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 book/migration.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 478d6135..289236ad 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -2,5 +2,6 @@ * [Introduction](./introduction.md) * [Code of conduct](./code-of-conduct.md) +* [Migration to/from conduit](./migration.md) * [Contributing](./contributing.md) * [Changelog](./changelog.md) diff --git a/book/migration.md b/book/migration.md new file mode 100644 index 00000000..e1651531 --- /dev/null +++ b/book/migration.md @@ -0,0 +1,69 @@ +# Migration to/from conduit + +Before migrating a conduit instance to grapevine, make sure to read through +all of the breaking changes listed in [the changelog](./changelog.md). + +In order to migrate an existing conduit instance to/from grapevine, the +grapevine config must include `conduit_compat = true`. This parameter cannot +currently be modified after creating the database for the first time, so make +sure to set it when creating a fresh grapevine instance that you may want to +migrate to a different implementation in the future. + +## Config + +Grapevine includes several breaking changes to the config schema. We don't +currently have docs on how to migrate an existing config. All breaking config +changes are mentioned in [the changelog](./changelog.md), so the best current +option is to read through those. Feel free to ask for config migration help in +[#grapevine:computer.surgery][room] if something is unclear. + +We plan to add [a config migration tool][config-migration-issue] to support +automatically migrating existing configs to the new schema. + +[room]: https://matrix.to/#/#grapevine:computer.surgery +[config-migration-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/38 + +## Database + +Grapevine is currently compatible with the Conduit 0.7.0 database format. It is +still possible to migrate to or from some newer conduit versions, but it may +require manual intervention or break some functionality. + +We plan to add [a migration tool][db-compatibility-mr] to support cleanly +migrating to or from conduit versions we are not internally compatible with. + +| Is migrating from | to | workable? | +|-|-|-| +| Conduit <=0.8.0 | Grapevine | Yes | +| Conduit 0.9.0 | Grapevine | [Yes, with caveats](#conduit-090-to-grapevine) | +| Grapevine | Conduit 0.7.0 | Yes | +| Grapevine | Conduit 0.8.0 | [Yes, with caveats](#grapevine-to-conduit-080-or-090) | +| Grapevine | Conduit 0.9.0 | [Yes, with caveats](#grapevine-to-conduit-080-or-090) | + +[db-compatibility-mr]: https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/85 + +### Conduit 0.9.0 to Grapevine + +Conduit 0.9.0 includes [a database migration][conduit-db-16-migration] that +modifies data that Grapevine doesn't read. Grapevine does not currently +recognize the new db version, and will fail to start against a conduit 0.9.0 +database. Grapevine can start and run without issues if the version recorded in +the db is rolled back from 16 to 13. It is possible to do this by editing the +db manually, or by modifying grapevine to change the version. [This +patch][conduit-db-16-patch] is an example of the latter approach. + +[conduit-db-16-migration]: https://gitlab.com/famedly/conduit/-/blob/f8d7ef04e664580e882bac852877b68e7bd3ab1e/src/database/mod.rs#L945 +[conduit-db-16-patch]: https://gitlab.computer.surgery/matrix/grapevine/-/commit/fdaa30f0d670c6f04f4e6be5d193f9146d179d95 + +### Grapevine to Conduit 0.8.0 or 0.9.0 + +Conduit 0.8.0 added [a new database table][alias_userid-commit] to track which +users created each room alias. Grapevine does not write to this table, so it is +not possible to delete aliases created in grapevine through the normal +client-server API after migrating to conduit 0.8.0. It is possible to delete +aliases with the `remove-alias` admin command. Not that this issue also applies +to migrations from conduit 0.7.0 to conduit 0.8.0. + +There are no additional known issues when migrating to conduit 0.9.0. + +[alias_userid-commit]: https://gitlab.com/famedly/conduit/-/commit/144d548ef739324ca97db12e8cada60ca3e43e09 From c7011fb70d9c2d1611655b98ac701a90cf9de5d3 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 18:15:02 -0800 Subject: [PATCH 488/617] added "can I use it" section to the docs This was based on the similar section in the hedgedoc. I dropped the bit about grapevine users getting banned from the conduwuit rooms cause we haven't heard about that happening for a while and the original case was pretty unclear. --- book/introduction.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/book/introduction.md b/book/introduction.md index 3f183042..055ee6b8 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -73,6 +73,22 @@ best. Additionally, due to our goals, the development of new features may be slower than alternatives. We find this to be an acceptable tradeoff considering the importance of the reliability of a project like this. +## Can I use it? + +Theoretically yes, but it's not in a good state for general users yet. There +[isn't very much user-facing docs yet][docs-issue], we +[don't have releases yet][releases-issue], we don't currently publish any binary +builds, and there are several breaking changes to the conduit config that +haven't been documented in detail yet. If building from source and figuring out +the config by reading source code/commit messages doesn't scare you away, go +for it! + +If you use nixos, [here's an example][nixos-example]. + +[docs-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/21 +[releases-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/18 +[nixos-example]: https://gitlab.computer.surgery/charles/servy-fleet/-/blob/main/config/grapevine/default.nix + --- [^1]: A "maintainer" is someone who has the ability to close issues opened by From 79783ebe565543fc8ba81bac4c6c7a742a9f9402 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 30 May 2024 22:06:55 +0000 Subject: [PATCH 489/617] sync: split into separate files --- src/api/client_server/sync.rs | 1695 +------------------------ src/api/client_server/sync/msc3575.rs | 673 ++++++++++ src/api/client_server/sync/v3.rs | 1050 +++++++++++++++ src/cli/serve.rs | 4 +- 4 files changed, 1730 insertions(+), 1692 deletions(-) create mode 100644 src/api/client_server/sync/msc3575.rs create mode 100644 src/api/client_server/sync/v3.rs diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 252036a5..2a17aa17 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1,1053 +1,12 @@ -use std::{ - collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - time::Duration, -}; - -use ruma::{ - api::client::{ - filter::{FilterDefinition, LazyLoadOptions}, - sync::sync_events::{ - self, - v3::{ - Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, - JoinedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, - Rooms, State, Timeline, ToDevice, - }, - v4::SlidingOp, - DeviceLists, UnreadNotificationsCount, - }, - uiaa::UiaaResponse, - }, - events::{ - room::member::{MembershipState, RoomMemberEventContent}, - StateEventType, TimelineEventType, - }, - uint, DeviceId, EventId, JsOption, OwnedUserId, RoomId, UInt, UserId, -}; -use tracing::{debug, error}; +use ruma::{events::StateEventType, RoomId, UserId}; +use tracing::error; use crate::{ - service::{pdu::EventHash, rooms::timeline::PduCount}, - services, utils, Ar, Error, PduEvent, Ra, Result, + service::rooms::timeline::PduCount, services, Error, PduEvent, Result, }; -/// # `GET /_matrix/client/r0/sync` -/// -/// Synchronize the client's state with the latest state on the server. -/// -/// - This endpoint takes a `since` parameter which should be the `next_batch` -/// value from a previous request for incremental syncs. -/// -/// Calling this endpoint without a `since` parameter returns: -/// - Some of the most recent events of each timeline -/// - Notification counts for each room -/// - Joined and invited member counts, heroes -/// - All state events -/// -/// Calling this endpoint with a `since` parameter from a previous `next_batch` -/// returns: For joined rooms: -/// - Some of the most recent events of each timeline that happened after -/// `since` -/// - If user joined the room after `since`: All state events (unless lazy -/// loading is activated) and all device list updates in that room -/// - If the user was already in the room: A list of all events that are in the -/// state now, but were not in the state at `since` -/// - If the state we send contains a member event: Joined and invited member -/// counts, heroes -/// - Device list updates that happened after `since` -/// - If there are events in the timeline we send or the user send updated their -/// read mark: Notification counts -/// - EDUs that are active now (read receipts, typing updates, presence) -/// - TODO: Allow multiple sync streams to support Pantalaimon -/// -/// For invited rooms: -/// - If the user was invited after `since`: A subset of the state of the room -/// at the point of the invite -/// -/// For left rooms: -/// - If the user left after `since`: `prev_batch` token, empty state (TODO: -/// subset of the state at the point of the leave) -#[allow(clippy::too_many_lines)] -pub(crate) async fn sync_events_route( - body: Ar, -) -> Result, Ra> { - let sender_user = body.sender_user.expect("user is authenticated"); - let sender_device = body.sender_device.expect("user is authenticated"); - let body = 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 next_batchcount = PduCount::Normal(next_batch); - let next_batch_string = next_batch.to_string(); - - // Load filter - let filter = match body.filter { - None => FilterDefinition::default(), - Some(Filter::FilterDefinition(filter)) => filter, - Some(Filter::FilterId(filter_id)) => services() - .users - .get_filter(&sender_user, &filter_id)? - .unwrap_or_default(), - }; - - let (lazy_load_enabled, lazy_load_send_redundant) = - match filter.room.state.lazy_load_options { - LazyLoadOptions::Enabled { - include_redundant_members: redundant, - } => (true, redundant), - LazyLoadOptions::Disabled => (false, false), - }; - - let full_state = body.full_state; - - let mut joined_rooms = BTreeMap::new(); - let since = - body.since.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); - let sincecount = PduCount::Normal(since); - - // Users that have left any encrypted rooms the sender was in - let mut left_encrypted_users = HashSet::new(); - let mut device_list_updates = HashSet::new(); - let mut device_list_left = HashSet::new(); - - // Look for device list updates of this account - device_list_updates.extend( - services() - .users - .keys_changed(sender_user.as_ref(), since, None) - .filter_map(Result::ok), - ); - - let all_joined_rooms = services() - .rooms - .state_cache - .rooms_joined(&sender_user) - .collect::>(); - for room_id in all_joined_rooms { - let room_id = room_id?; - if let Ok(joined_room) = load_joined_room( - &sender_user, - &sender_device, - &room_id, - since, - sincecount, - next_batch, - next_batchcount, - lazy_load_enabled, - lazy_load_send_redundant, - full_state, - &mut device_list_updates, - &mut left_encrypted_users, - ) - .await - { - if !joined_room.is_empty() { - joined_rooms.insert(room_id.clone(), joined_room); - } - } - } - - let mut left_rooms = BTreeMap::new(); - let all_left_rooms: Vec<_> = - services().rooms.state_cache.rooms_left(&sender_user).collect(); - for result in all_left_rooms { - let (room_id, _) = result?; - - { - // Get and drop the lock to wait for remaining operations to finish - let room_token = services() - .globals - .roomid_mutex_insert - .lock_key(room_id.clone()) - .await; - drop(room_token); - } - - let left_count = services() - .rooms - .state_cache - .get_left_count(&room_id, &sender_user)?; - - // Left before last sync - if Some(since) >= left_count { - continue; - } - - if !services().rooms.metadata.exists(&room_id)? { - // This is just a rejected invite, not a room we know - let event = PduEvent { - event_id: EventId::new(services().globals.server_name()).into(), - sender: sender_user.clone(), - origin_server_ts: utils::millis_since_unix_epoch() - .try_into() - .expect("Timestamp is valid js_int value"), - kind: TimelineEventType::RoomMember, - content: serde_json::from_str(r#"{ "membership": "leave"}"#) - .unwrap(), - state_key: Some(sender_user.to_string()), - unsigned: None, - // The following keys are dropped on conversion - room_id: room_id.clone(), - prev_events: vec![], - depth: uint!(1), - auth_events: vec![], - redacts: None, - hashes: EventHash { - sha256: String::new(), - }, - signatures: None, - }; - - left_rooms.insert( - room_id, - LeftRoom { - account_data: RoomAccountData { - events: Vec::new(), - }, - timeline: Timeline { - limited: false, - prev_batch: Some(next_batch_string.clone()), - events: Vec::new(), - }, - state: State { - events: vec![event.to_sync_state_event()], - }, - }, - ); - - continue; - } - - let mut left_state_events = Vec::new(); - - let since_shortstatehash = - services().rooms.user.get_token_shortstatehash(&room_id, since)?; - - let since_state_ids = match since_shortstatehash { - Some(s) => { - services().rooms.state_accessor.state_full_ids(s).await? - } - None => HashMap::new(), - }; - - let Some(left_event_id) = - services().rooms.state_accessor.room_state_get_id( - &room_id, - &StateEventType::RoomMember, - sender_user.as_str(), - )? - else { - error!("Left room but no left state event"); - continue; - }; - - let Some(left_shortstatehash) = services() - .rooms - .state_accessor - .pdu_shortstatehash(&left_event_id)? - else { - error!("Leave event has no state"); - continue; - }; - - let mut left_state_ids = services() - .rooms - .state_accessor - .state_full_ids(left_shortstatehash) - .await?; - - let leave_shortstatekey = - services().rooms.short.get_or_create_shortstatekey( - &StateEventType::RoomMember, - sender_user.as_str(), - )?; - - left_state_ids.insert(leave_shortstatekey, left_event_id); - - let mut i = 0; - for (key, event_id) in left_state_ids { - if full_state || since_state_ids.get(&key) != Some(&event_id) { - let (event_type, state_key) = - services().rooms.short.get_statekey_from_short(key)?; - - if !lazy_load_enabled - || event_type != StateEventType::RoomMember - || full_state - // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 - || *sender_user == state_key - { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - - left_state_events.push(pdu.to_sync_state_event()); - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } - } - } - - left_rooms.insert( - room_id.clone(), - LeftRoom { - account_data: RoomAccountData { - events: Vec::new(), - }, - timeline: Timeline { - limited: false, - prev_batch: Some(next_batch_string.clone()), - events: Vec::new(), - }, - state: State { - events: left_state_events, - }, - }, - ); - } - - let mut invited_rooms = BTreeMap::new(); - let all_invited_rooms: Vec<_> = - services().rooms.state_cache.rooms_invited(&sender_user).collect(); - for result in all_invited_rooms { - let (room_id, invite_state_events) = result?; - - { - // Get and drop the lock to wait for remaining operations to finish - let room_token = services() - .globals - .roomid_mutex_insert - .lock_key(room_id.clone()) - .await; - drop(room_token); - } - - let invite_count = services() - .rooms - .state_cache - .get_invite_count(&room_id, &sender_user)?; - - // Invited before last sync - if Some(since) >= invite_count { - continue; - } - - invited_rooms.insert( - room_id.clone(), - InvitedRoom { - invite_state: InviteState { - events: invite_state_events, - }, - }, - ); - } - - for user_id in left_encrypted_users { - let dont_share_encrypted_room = services() - .rooms - .user - .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? - .filter_map(Result::ok) - .filter_map(|other_room_id| { - Some( - services() - .rooms - .state_accessor - .room_state_get( - &other_room_id, - &StateEventType::RoomEncryption, - "", - ) - .ok()? - .is_some(), - ) - }) - .all(|encrypted| !encrypted); - // If the user doesn't share an encrypted room with the target anymore, - // we need to tell them - if dont_share_encrypted_room { - device_list_left.insert(user_id); - } - } - - // Remove all to-device events the device received *last time* - services().users.remove_to_device_events( - &sender_user, - &sender_device, - since, - )?; - - let response = sync_events::v3::Response { - next_batch: next_batch_string, - rooms: Rooms { - leave: left_rooms, - join: joined_rooms, - invite: invited_rooms, - // TODO - knock: BTreeMap::new(), - }, - presence: Presence::default(), - account_data: GlobalAccountData { - events: services() - .account_data - .changes_since(None, &sender_user, since)? - .into_iter() - .filter_map(|(_, v)| { - serde_json::from_str(v.json().get()) - .map_err(|_| { - Error::bad_database( - "Invalid account event in database.", - ) - }) - .ok() - }) - .collect(), - }, - device_lists: DeviceLists { - changed: device_list_updates.into_iter().collect(), - left: device_list_left.into_iter().collect(), - }, - device_one_time_keys_count: services() - .users - .count_one_time_keys(&sender_user, &sender_device)?, - to_device: ToDevice { - events: services() - .users - .get_to_device_events(&sender_user, &sender_device)?, - }, - // Fallback keys are not yet supported - device_unused_fallback_key_types: None, - }; - - // TODO: Retry the endpoint instead of returning (waiting for #118) - if !full_state - && response.rooms.is_empty() - && response.presence.is_empty() - && response.account_data.is_empty() - && response.device_lists.is_empty() - && response.to_device.is_empty() - { - // Hang a few seconds so requests are not spammed - // Stop hanging if new info arrives - let mut duration = body.timeout.unwrap_or_default(); - if duration.as_secs() > 30 { - duration = Duration::from_secs(30); - } - match tokio::time::timeout(duration, watcher).await { - Ok(x) => x.expect("watcher should succeed"), - Err(error) => debug!(%error, "Timed out"), - }; - } - Ok(Ra(response)) -} - -#[tracing::instrument(skip_all, fields(room_id = %room_id))] -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] -async fn load_joined_room( - sender_user: &UserId, - sender_device: &DeviceId, - room_id: &RoomId, - since: u64, - sincecount: PduCount, - next_batch: u64, - next_batchcount: PduCount, - lazy_load_enabled: bool, - lazy_load_send_redundant: bool, - full_state: bool, - device_list_updates: &mut HashSet, - left_encrypted_users: &mut HashSet, -) -> Result { - { - // Get and drop the lock to wait for remaining operations to finish - // This will make sure the we have all events until next_batch - let room_token = services() - .globals - .roomid_mutex_insert - .lock_key(room_id.to_owned()) - .await; - drop(room_token); - } - - let (timeline_pdus, limited) = - load_timeline(sender_user, room_id, sincecount, 10)?; - - let send_notification_counts = !timeline_pdus.is_empty() - || services() - .rooms - .user - .last_notification_read(sender_user, room_id)? - > since; - - let mut timeline_users = HashSet::new(); - for (_, event) in &timeline_pdus { - timeline_users.insert(event.sender.as_str().to_owned()); - } - - services() - .rooms - .lazy_loading - .lazy_load_confirm_delivery( - sender_user, - sender_device, - room_id, - sincecount, - ) - .await?; - - // Database queries: - - let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - else { - error!("Room has no state"); - return Err(Error::BadDatabase("Room has no state")); - }; - - let since_shortstatehash = - services().rooms.user.get_token_shortstatehash(room_id, since)?; - - let ( - heroes, - joined_member_count, - invited_member_count, - joined_since_last_sync, - state_events, - ) = if timeline_pdus.is_empty() - && since_shortstatehash == Some(current_shortstatehash) - { - // No state changes - (Vec::new(), None, None, false, Vec::new()) - } else { - // Calculates joined_member_count, invited_member_count and heroes - let calculate_counts = || { - let joined_member_count = services() - .rooms - .state_cache - .room_joined_count(room_id)? - .unwrap_or(0); - let invited_member_count = services() - .rooms - .state_cache - .room_invited_count(room_id)? - .unwrap_or(0); - - // Recalculate heroes (first 5 members) - let mut heroes = Vec::new(); - - if joined_member_count + invited_member_count <= 5 { - // Go through all PDUs and for each member event, check if the - // user is still joined or invited until we have - // 5 or we reach the end - - for hero in services() - .rooms - .timeline - .all_pdus(sender_user, room_id)? - .filter_map(Result::ok) - .filter(|(_, pdu)| { - pdu.kind == TimelineEventType::RoomMember - }) - .map(|(_, pdu)| { - let content: RoomMemberEventContent = - serde_json::from_str(pdu.content.get()).map_err( - |_| { - Error::bad_database( - "Invalid member event in database.", - ) - }, - )?; - - if let Some(state_key) = &pdu.state_key { - let user_id = UserId::parse(state_key.clone()) - .map_err(|_| { - Error::bad_database( - "Invalid UserId in member PDU.", - ) - })?; - - // The membership was and still is invite or join - if matches!( - content.membership, - MembershipState::Join | MembershipState::Invite - ) && (services() - .rooms - .state_cache - .is_joined(&user_id, room_id)? - || services() - .rooms - .state_cache - .is_invited(&user_id, room_id)?) - { - Ok::<_, Error>(Some(state_key.parse().expect( - "`state_key` should be a valid user ID", - ))) - } else { - Ok(None) - } - } else { - Ok(None) - } - }) - .filter_map(Result::ok) - .flatten() - { - if heroes.contains(&hero) || hero == sender_user.as_str() { - continue; - } - - heroes.push(hero); - } - } - - Ok::<_, Error>(( - Some(joined_member_count), - Some(invited_member_count), - heroes, - )) - }; - - let since_sender_member: Option = - since_shortstatehash - .and_then(|shortstatehash| { - services() - .rooms - .state_accessor - .state_get( - shortstatehash, - &StateEventType::RoomMember, - sender_user.as_str(), - ) - .transpose() - }) - .transpose()? - .and_then(|pdu| { - serde_json::from_str(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid PDU in database.") - }) - .ok() - }); - - let joined_since_last_sync = since_sender_member - .map_or(true, |member| member.membership != MembershipState::Join); - - if since_shortstatehash.is_none() || joined_since_last_sync { - // Probably since = 0, we will do an initial sync - - let (joined_member_count, invited_member_count, heroes) = - calculate_counts()?; - - let current_state_ids = services() - .rooms - .state_accessor - .state_full_ids(current_shortstatehash) - .await?; - - let mut state_events = Vec::new(); - let mut lazy_loaded = HashSet::new(); - - let mut i = 0; - for (shortstatekey, event_id) in current_state_ids { - let (event_type, state_key) = services() - .rooms - .short - .get_statekey_from_short(shortstatekey)?; - - if event_type != StateEventType::RoomMember { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - state_events.push(pdu); - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } else if !lazy_load_enabled - || full_state - || timeline_users.contains(&state_key) - // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 - || *sender_user == state_key - { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - - // This check is in case a bad user ID made it into the - // database - if let Ok(uid) = UserId::parse(&state_key) { - lazy_loaded.insert(uid); - } - state_events.push(pdu); - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } - } - - // Reset lazy loading because this is an initial sync - services().rooms.lazy_loading.lazy_load_reset( - sender_user, - sender_device, - room_id, - )?; - - // The state_events above should contain all timeline_users, let's - // mark them as lazy loaded. - services() - .rooms - .lazy_loading - .lazy_load_mark_sent( - sender_user, - sender_device, - room_id, - lazy_loaded, - next_batchcount, - ) - .await; - - ( - heroes, - joined_member_count, - invited_member_count, - true, - state_events, - ) - } else { - // Incremental /sync - let since_shortstatehash = since_shortstatehash.unwrap(); - - let mut delta_state_events = Vec::new(); - - if since_shortstatehash != current_shortstatehash { - let current_state_ids = services() - .rooms - .state_accessor - .state_full_ids(current_shortstatehash) - .await?; - let since_state_ids = services() - .rooms - .state_accessor - .state_full_ids(since_shortstatehash) - .await?; - - for (key, event_id) in current_state_ids { - if full_state - || since_state_ids.get(&key) != Some(&event_id) - { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - - delta_state_events.push(pdu); - tokio::task::yield_now().await; - } - } - } - - let encrypted_room = services() - .rooms - .state_accessor - .state_get( - current_shortstatehash, - &StateEventType::RoomEncryption, - "", - )? - .is_some(); - - let since_encryption = services().rooms.state_accessor.state_get( - since_shortstatehash, - &StateEventType::RoomEncryption, - "", - )?; - - // Calculations: - let new_encrypted_room = - encrypted_room && since_encryption.is_none(); - - let send_member_count = delta_state_events - .iter() - .any(|event| event.kind == TimelineEventType::RoomMember); - - if encrypted_room { - for state_event in &delta_state_events { - if state_event.kind != TimelineEventType::RoomMember { - continue; - } - - if let Some(state_key) = &state_event.state_key { - let user_id = UserId::parse(state_key.clone()) - .map_err(|_| { - Error::bad_database( - "Invalid UserId in member PDU.", - ) - })?; - - if user_id == sender_user { - continue; - } - - let new_membership = - serde_json::from_str::( - state_event.content.get(), - ) - .map_err(|_| { - Error::bad_database("Invalid PDU in database.") - })? - .membership; - - match new_membership { - MembershipState::Join => { - // A new user joined an encrypted room - if !share_encrypted_room( - sender_user, - &user_id, - room_id, - )? { - device_list_updates.insert(user_id); - } - } - MembershipState::Leave => { - // Write down users that have left encrypted - // rooms we are in - left_encrypted_users.insert(user_id); - } - _ => {} - } - } - } - } - - if joined_since_last_sync && encrypted_room || new_encrypted_room { - // If the user is in a new encrypted room, give them all joined - // users - device_list_updates.extend( - services() - .rooms - .state_cache - .room_members(room_id) - .flatten() - .filter(|user_id| { - // Don't send key updates from the sender to the - // sender - sender_user != user_id - }) - .filter(|user_id| { - // Only send keys if the sender doesn't share an - // encrypted room with the target already - !share_encrypted_room(sender_user, user_id, room_id) - .unwrap_or(false) - }), - ); - } - - let (joined_member_count, invited_member_count, heroes) = - if send_member_count { - calculate_counts()? - } else { - (None, None, Vec::new()) - }; - - let mut state_events = delta_state_events; - let mut lazy_loaded = HashSet::new(); - - // Mark all member events we're returning as lazy-loaded - for pdu in &state_events { - if pdu.kind == TimelineEventType::RoomMember { - match UserId::parse( - pdu.state_key - .as_ref() - .expect("State event has state key") - .clone(), - ) { - Ok(state_key_userid) => { - lazy_loaded.insert(state_key_userid); - } - Err(error) => { - error!( - event_id = %pdu.event_id, - %error, - "Invalid state key for member event", - ); - } - } - } - } - - // Fetch contextual member state events for events from the - // timeline, and mark them as lazy-loaded as well. - for (_, event) in &timeline_pdus { - if lazy_loaded.contains(&event.sender) { - continue; - } - - if !services().rooms.lazy_loading.lazy_load_was_sent_before( - sender_user, - sender_device, - room_id, - &event.sender, - )? || lazy_load_send_redundant - { - if let Some(member_event) = - services().rooms.state_accessor.room_state_get( - room_id, - &StateEventType::RoomMember, - event.sender.as_str(), - )? - { - lazy_loaded.insert(event.sender.clone()); - state_events.push(member_event); - } - } - } - - services() - .rooms - .lazy_loading - .lazy_load_mark_sent( - sender_user, - sender_device, - room_id, - lazy_loaded, - next_batchcount, - ) - .await; - - ( - heroes, - joined_member_count, - invited_member_count, - joined_since_last_sync, - state_events, - ) - } - }; - - // Look for device list updates in this room - device_list_updates.extend( - services() - .users - .keys_changed(room_id.as_ref(), since, None) - .filter_map(Result::ok), - ); - - let notification_count = send_notification_counts - .then(|| services().rooms.user.notification_count(sender_user, room_id)) - .transpose()? - .map(|x| x.try_into().expect("notification count can't go that high")); - - let highlight_count = send_notification_counts - .then(|| services().rooms.user.highlight_count(sender_user, room_id)) - .transpose()? - .map(|x| x.try_into().expect("highlight count can't go that high")); - - 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 mut edus: Vec<_> = services() - .rooms - .edus - .read_receipt - .readreceipts_since(room_id, since) - .filter_map(Result::ok) - .map(|(_, _, v)| v) - .collect(); - - if services().rooms.edus.typing.last_typing_update(room_id).await? > since { - edus.push( - serde_json::from_str( - &serde_json::to_string( - &services().rooms.edus.typing.typings_all(room_id).await?, - ) - .expect("event is valid, we just created it"), - ) - .expect("event is valid, we just created it"), - ); - } - - // Save the state after this sync so we can send the correct state diff next - // sync - services().rooms.user.associate_token_shortstatehash( - room_id, - next_batch, - current_shortstatehash, - )?; - - Ok(JoinedRoom { - account_data: RoomAccountData { - events: services() - .account_data - .changes_since(Some(room_id), sender_user, since)? - .into_iter() - .filter_map(|(_, v)| { - serde_json::from_str(v.json().get()) - .map_err(|_| { - Error::bad_database( - "Invalid account event in database.", - ) - }) - .ok() - }) - .collect(), - }, - summary: RoomSummary { - heroes, - joined_member_count: joined_member_count.map(UInt::new_saturating), - invited_member_count: invited_member_count - .map(UInt::new_saturating), - }, - unread_notifications: UnreadNotificationsCount { - highlight_count, - notification_count, - }, - timeline: Timeline { - limited: limited || joined_since_last_sync, - prev_batch, - events: room_events, - }, - state: State { - events: state_events - .iter() - .map(|pdu| pdu.to_sync_state_event()) - .collect(), - }, - ephemeral: Ephemeral { - events: edus, - }, - unread_thread_notifications: BTreeMap::new(), - }) -} +pub(crate) mod msc3575; +pub(crate) mod v3; fn load_timeline( sender_user: &UserId, @@ -1120,647 +79,3 @@ fn share_encrypted_room( }) .any(|encrypted| encrypted)) } - -#[allow(clippy::too_many_lines)] -pub(crate) async fn sync_events_v4_route( - body: Ar, -) -> Result, Ra> { - let sender_user = body.sender_user.expect("user is authenticated"); - let sender_device = body.sender_device.expect("user is authenticated"); - let mut body = 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.next_count()?; - - let globalsince = - body.pos.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); - - if globalsince == 0 { - if let Some(conn_id) = &body.conn_id { - services().users.forget_sync_request_connection( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - ); - } - } - - // Get sticky parameters from cache - let known_rooms = services().users.update_sync_request_with_cache( - sender_user.clone(), - sender_device.clone(), - &mut body, - ); - - let all_joined_rooms = services() - .rooms - .state_cache - .rooms_joined(&sender_user) - .filter_map(Result::ok) - .collect::>(); - - if body.extensions.to_device.enabled.unwrap_or(false) { - services().users.remove_to_device_events( - &sender_user, - &sender_device, - globalsince, - )?; - } - - // Users that have left any encrypted rooms the sender was in - let mut left_encrypted_users = HashSet::new(); - let mut device_list_changes = HashSet::new(); - let mut device_list_left = HashSet::new(); - - if body.extensions.e2ee.enabled.unwrap_or(false) { - // Look for device list updates of this account - device_list_changes.extend( - services() - .users - .keys_changed(sender_user.as_ref(), globalsince, None) - .filter_map(Result::ok), - ); - - for room_id in &all_joined_rooms { - let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - else { - error!(%room_id, "Room has no state"); - continue; - }; - - let since_shortstatehash = services() - .rooms - .user - .get_token_shortstatehash(room_id, globalsince)?; - - let since_sender_member: Option = - since_shortstatehash - .and_then(|shortstatehash| { - services() - .rooms - .state_accessor - .state_get( - shortstatehash, - &StateEventType::RoomMember, - sender_user.as_str(), - ) - .transpose() - }) - .transpose()? - .and_then(|pdu| { - serde_json::from_str(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid PDU in database.") - }) - .ok() - }); - - let encrypted_room = services() - .rooms - .state_accessor - .state_get( - current_shortstatehash, - &StateEventType::RoomEncryption, - "", - )? - .is_some(); - - if let Some(since_shortstatehash) = since_shortstatehash { - // Skip if there are only timeline changes - if since_shortstatehash == current_shortstatehash { - continue; - } - - let since_encryption = - services().rooms.state_accessor.state_get( - since_shortstatehash, - &StateEventType::RoomEncryption, - "", - )?; - - let joined_since_last_sync = since_sender_member - .map_or(true, |member| { - member.membership != MembershipState::Join - }); - - let new_encrypted_room = - encrypted_room && since_encryption.is_none(); - if encrypted_room { - let current_state_ids = services() - .rooms - .state_accessor - .state_full_ids(current_shortstatehash) - .await?; - let since_state_ids = services() - .rooms - .state_accessor - .state_full_ids(since_shortstatehash) - .await?; - - for (key, event_id) in current_state_ids { - if since_state_ids.get(&key) != Some(&event_id) { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - if pdu.kind == TimelineEventType::RoomMember { - if let Some(state_key) = &pdu.state_key { - let user_id = - UserId::parse(state_key.clone()) - .map_err(|_| { - Error::bad_database( - "Invalid UserId in member \ - PDU.", - ) - })?; - - if user_id == sender_user { - continue; - } - - let new_membership = - serde_json::from_str::< - RoomMemberEventContent, - >( - pdu.content.get() - ) - .map_err(|_| { - Error::bad_database( - "Invalid PDU in database.", - ) - })? - .membership; - - match new_membership { - MembershipState::Join => { - // A new user joined an encrypted - // room - if !share_encrypted_room( - &sender_user, - &user_id, - room_id, - )? { - device_list_changes - .insert(user_id); - } - } - MembershipState::Leave => { - // Write down users that have left - // encrypted rooms we are in - left_encrypted_users - .insert(user_id); - } - _ => {} - } - } - } - } - } - if joined_since_last_sync || new_encrypted_room { - // If the user is in a new encrypted room, give them all - // joined users - device_list_changes.extend( - services() - .rooms - .state_cache - .room_members(room_id) - .flatten() - .filter(|user_id| { - // Don't send key updates from the sender to - // the sender - &sender_user != user_id - }) - .filter(|user_id| { - // Only send keys if the sender doesn't - // share an encrypted room with the target - // already - !share_encrypted_room( - &sender_user, - user_id, - room_id, - ) - .unwrap_or(false) - }), - ); - } - } - } - // Look for device list updates in this room - device_list_changes.extend( - services() - .users - .keys_changed(room_id.as_ref(), globalsince, None) - .filter_map(Result::ok), - ); - } - for user_id in left_encrypted_users { - let dont_share_encrypted_room = services() - .rooms - .user - .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? - .filter_map(Result::ok) - .filter_map(|other_room_id| { - Some( - services() - .rooms - .state_accessor - .room_state_get( - &other_room_id, - &StateEventType::RoomEncryption, - "", - ) - .ok()? - .is_some(), - ) - }) - .all(|encrypted| !encrypted); - // If the user doesn't share an encrypted room with the target - // anymore, we need to tell them - if dont_share_encrypted_room { - device_list_left.insert(user_id); - } - } - } - - let mut lists = BTreeMap::new(); - // and required state - let mut todo_rooms = BTreeMap::new(); - - for (list_id, list) in body.lists { - if list.filters.and_then(|f| f.is_invite).unwrap_or(false) { - continue; - } - - let mut new_known_rooms = BTreeSet::new(); - - lists.insert( - list_id.clone(), - sync_events::v4::SyncList { - ops: list - .ranges - .into_iter() - .map(|mut r| { - r.0 = r.0.clamp( - uint!(0), - UInt::try_from(all_joined_rooms.len() - 1) - .unwrap_or(UInt::MAX), - ); - r.1 = r.1.clamp( - r.0, - UInt::try_from(all_joined_rooms.len() - 1) - .unwrap_or(UInt::MAX), - ); - let room_ids = all_joined_rooms[r - .0 - .try_into() - .unwrap_or(usize::MAX) - ..=r.1.try_into().unwrap_or(usize::MAX)] - .to_vec(); - new_known_rooms.extend(room_ids.iter().cloned()); - for room_id in &room_ids { - let todo_room = todo_rooms - .entry(room_id.clone()) - .or_insert((BTreeSet::new(), 0, u64::MAX)); - let limit = list - .room_details - .timeline_limit - .map_or(10, u64::from) - .min(100); - todo_room.0.extend( - list.room_details - .required_state - .iter() - .cloned(), - ); - todo_room.1 = todo_room.1.max(limit); - // 0 means unknown because it got out of date - todo_room.2 = todo_room.2.min( - known_rooms - .get(&list_id) - .and_then(|k| k.get(room_id)) - .copied() - .unwrap_or(0), - ); - } - sync_events::v4::SyncOp { - op: SlidingOp::Sync, - range: Some(r), - index: None, - room_ids, - room_id: None, - } - }) - .collect(), - count: UInt::try_from(all_joined_rooms.len()) - .unwrap_or(UInt::MAX), - }, - ); - - if let Some(conn_id) = &body.conn_id { - services().users.update_sync_known_rooms( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - list_id, - new_known_rooms, - globalsince, - ); - } - } - - let mut known_subscription_rooms = BTreeSet::new(); - for (room_id, room) in &body.room_subscriptions { - if !services().rooms.metadata.exists(room_id)? { - continue; - } - let todo_room = todo_rooms.entry(room_id.clone()).or_insert(( - BTreeSet::new(), - 0, - u64::MAX, - )); - let limit = room.timeline_limit.map_or(10, u64::from).min(100); - todo_room.0.extend(room.required_state.iter().cloned()); - todo_room.1 = todo_room.1.max(limit); - // 0 means unknown because it got out of date - todo_room.2 = todo_room.2.min( - known_rooms - .get("subscriptions") - .and_then(|k| k.get(room_id)) - .copied() - .unwrap_or(0), - ); - known_subscription_rooms.insert(room_id.clone()); - } - - for r in body.unsubscribe_rooms { - known_subscription_rooms.remove(&r); - body.room_subscriptions.remove(&r); - } - - if let Some(conn_id) = &body.conn_id { - services().users.update_sync_known_rooms( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - "subscriptions".to_owned(), - known_subscription_rooms, - globalsince, - ); - } - - if let Some(conn_id) = &body.conn_id { - services().users.update_sync_subscriptions( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - body.room_subscriptions, - ); - } - - let mut rooms = BTreeMap::new(); - for (room_id, (required_state_request, timeline_limit, roomsince)) in - &todo_rooms - { - let roomsincecount = PduCount::Normal(*roomsince); - - let (timeline_pdus, limited) = load_timeline( - &sender_user, - room_id, - roomsincecount, - *timeline_limit, - )?; - - if roomsince != &0 && timeline_pdus.is_empty() { - continue; - } - - 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(), - })) - })? - .or_else(|| (roomsince != &0).then(|| roomsince.to_string())); - - let room_events: Vec<_> = timeline_pdus - .iter() - .map(|(_, pdu)| pdu.to_sync_room_event()) - .collect(); - - let required_state = required_state_request - .iter() - .filter_map(|state| { - services() - .rooms - .state_accessor - .room_state_get(room_id, &state.0, &state.1) - .ok() - .flatten() - .map(|state| state.to_sync_state_event()) - }) - .collect(); - - // Heroes - let heroes = services() - .rooms - .state_cache - .room_members(room_id) - .filter_map(Result::ok) - .filter(|member| member != &sender_user) - .filter_map(|member| { - services() - .rooms - .state_accessor - .get_member(room_id, &member) - .ok() - .flatten() - .map(|memberevent| { - ( - memberevent - .displayname - .unwrap_or_else(|| member.to_string()), - memberevent.avatar_url, - ) - }) - }) - .take(5) - .collect::>(); - let name = match &*heroes { - [] => None, - [only] => Some(only.0.clone()), - [firsts @ .., last] => Some({ - let firsts = firsts - .iter() - .map(|h| h.0.clone()) - .collect::>() - .join(", "); - - format!("{firsts} and {}", last.0) - }), - }; - - let avatar = if let [only] = &*heroes { - only.1.clone() - } else { - None - }; - - rooms.insert( - room_id.clone(), - sync_events::v4::SlidingSyncRoom { - name: services() - .rooms - .state_accessor - .get_name(room_id)? - .or(name), - avatar: if let Some(avatar) = avatar { - JsOption::Some(avatar) - } else { - match services().rooms.state_accessor.get_avatar(room_id)? { - JsOption::Some(avatar) => { - JsOption::from_option(avatar.url) - } - JsOption::Null => JsOption::Null, - JsOption::Undefined => JsOption::Undefined, - } - }, - initial: Some(roomsince == &0), - is_dm: None, - invite_state: None, - unread_notifications: UnreadNotificationsCount { - highlight_count: Some( - services() - .rooms - .user - .highlight_count(&sender_user, room_id)? - .try_into() - .expect("notification count can't go that high"), - ), - notification_count: Some( - services() - .rooms - .user - .notification_count(&sender_user, room_id)? - .try_into() - .expect("notification count can't go that high"), - ), - }, - timeline: room_events, - required_state, - prev_batch, - limited, - joined_count: Some( - services() - .rooms - .state_cache - .room_joined_count(room_id)? - .map(UInt::new_saturating) - .unwrap_or(uint!(0)), - ), - invited_count: Some( - services() - .rooms - .state_cache - .room_invited_count(room_id)? - .map(UInt::new_saturating) - .unwrap_or(uint!(0)), - ), - // Count events in timeline greater than global sync counter - num_live: None, - timestamp: None, - // TODO - heroes: 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); - } - match tokio::time::timeout(duration, watcher).await { - Ok(x) => x.expect("watcher should succeed"), - Err(error) => debug!(%error, "Timed out"), - }; - } - - Ok(Ra(sync_events::v4::Response { - initial: globalsince == 0, - txn_id: body.txn_id.clone(), - pos: next_batch.to_string(), - lists, - rooms, - extensions: sync_events::v4::Extensions { - to_device: body - .extensions - .to_device - .enabled - .unwrap_or(false) - .then(|| { - services() - .users - .get_to_device_events(&sender_user, &sender_device) - .map(|events| sync_events::v4::ToDevice { - events, - next_batch: next_batch.to_string(), - }) - }) - .transpose()?, - e2ee: sync_events::v4::E2EE { - device_lists: DeviceLists { - changed: device_list_changes.into_iter().collect(), - left: device_list_left.into_iter().collect(), - }, - device_one_time_keys_count: services() - .users - .count_one_time_keys(&sender_user, &sender_device)?, - // Fallback keys are not yet supported - device_unused_fallback_key_types: None, - }, - account_data: sync_events::v4::AccountData { - global: if body.extensions.account_data.enabled.unwrap_or(false) - { - services() - .account_data - .changes_since(None, &sender_user, globalsince)? - .into_iter() - .filter_map(|(_, v)| { - serde_json::from_str(v.json().get()) - .map_err(|_| { - Error::bad_database( - "Invalid account event in database.", - ) - }) - .ok() - }) - .collect() - } else { - 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/sync/msc3575.rs b/src/api/client_server/sync/msc3575.rs new file mode 100644 index 00000000..1de1c7d1 --- /dev/null +++ b/src/api/client_server/sync/msc3575.rs @@ -0,0 +1,673 @@ +//! [MSC3575], aka Sliding Sync, aka Sync v3 (even though the endpoint is called +//! /v4) support +//! +//! [MSC3575]: https://github.com/matrix-org/matrix-spec-proposals/pull/3575 + +use std::{ + collections::{BTreeMap, BTreeSet, HashSet}, + time::Duration, +}; + +use ruma::{ + api::client::{ + sync::sync_events::{ + self, v4::SlidingOp, DeviceLists, UnreadNotificationsCount, + }, + uiaa::UiaaResponse, + }, + events::{ + room::member::{MembershipState, RoomMemberEventContent}, + StateEventType, TimelineEventType, + }, + uint, JsOption, UInt, UserId, +}; +use tracing::{debug, error}; + +use super::{load_timeline, share_encrypted_room}; +use crate::{ + service::rooms::timeline::PduCount, services, Ar, Error, Ra, Result, +}; + +#[allow(clippy::too_many_lines)] +pub(crate) async fn sync_events_v4_route( + body: Ar, +) -> Result, Ra> { + let sender_user = body.sender_user.expect("user is authenticated"); + let sender_device = body.sender_device.expect("user is authenticated"); + let mut body = 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.next_count()?; + + let globalsince = + body.pos.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); + + if globalsince == 0 { + if let Some(conn_id) = &body.conn_id { + services().users.forget_sync_request_connection( + sender_user.clone(), + sender_device.clone(), + conn_id.clone(), + ); + } + } + + // Get sticky parameters from cache + let known_rooms = services().users.update_sync_request_with_cache( + sender_user.clone(), + sender_device.clone(), + &mut body, + ); + + let all_joined_rooms = services() + .rooms + .state_cache + .rooms_joined(&sender_user) + .filter_map(Result::ok) + .collect::>(); + + if body.extensions.to_device.enabled.unwrap_or(false) { + services().users.remove_to_device_events( + &sender_user, + &sender_device, + globalsince, + )?; + } + + // Users that have left any encrypted rooms the sender was in + let mut left_encrypted_users = HashSet::new(); + let mut device_list_changes = HashSet::new(); + let mut device_list_left = HashSet::new(); + + if body.extensions.e2ee.enabled.unwrap_or(false) { + // Look for device list updates of this account + device_list_changes.extend( + services() + .users + .keys_changed(sender_user.as_ref(), globalsince, None) + .filter_map(Result::ok), + ); + + for room_id in &all_joined_rooms { + let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + else { + error!(%room_id, "Room has no state"); + continue; + }; + + let since_shortstatehash = services() + .rooms + .user + .get_token_shortstatehash(room_id, globalsince)?; + + let since_sender_member: Option = + since_shortstatehash + .and_then(|shortstatehash| { + services() + .rooms + .state_accessor + .state_get( + shortstatehash, + &StateEventType::RoomMember, + sender_user.as_str(), + ) + .transpose() + }) + .transpose()? + .and_then(|pdu| { + serde_json::from_str(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid PDU in database.") + }) + .ok() + }); + + let encrypted_room = services() + .rooms + .state_accessor + .state_get( + current_shortstatehash, + &StateEventType::RoomEncryption, + "", + )? + .is_some(); + + if let Some(since_shortstatehash) = since_shortstatehash { + // Skip if there are only timeline changes + if since_shortstatehash == current_shortstatehash { + continue; + } + + let since_encryption = + services().rooms.state_accessor.state_get( + since_shortstatehash, + &StateEventType::RoomEncryption, + "", + )?; + + let joined_since_last_sync = since_sender_member + .map_or(true, |member| { + member.membership != MembershipState::Join + }); + + let new_encrypted_room = + encrypted_room && since_encryption.is_none(); + if encrypted_room { + let current_state_ids = services() + .rooms + .state_accessor + .state_full_ids(current_shortstatehash) + .await?; + let since_state_ids = services() + .rooms + .state_accessor + .state_full_ids(since_shortstatehash) + .await?; + + for (key, event_id) in current_state_ids { + if since_state_ids.get(&key) != Some(&event_id) { + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); + continue; + }; + if pdu.kind == TimelineEventType::RoomMember { + if let Some(state_key) = &pdu.state_key { + let user_id = + UserId::parse(state_key.clone()) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in member \ + PDU.", + ) + })?; + + if user_id == sender_user { + continue; + } + + let new_membership = + serde_json::from_str::< + RoomMemberEventContent, + >( + pdu.content.get() + ) + .map_err(|_| { + Error::bad_database( + "Invalid PDU in database.", + ) + })? + .membership; + + match new_membership { + MembershipState::Join => { + // A new user joined an encrypted + // room + if !share_encrypted_room( + &sender_user, + &user_id, + room_id, + )? { + device_list_changes + .insert(user_id); + } + } + MembershipState::Leave => { + // Write down users that have left + // encrypted rooms we are in + left_encrypted_users + .insert(user_id); + } + _ => {} + } + } + } + } + } + if joined_since_last_sync || new_encrypted_room { + // If the user is in a new encrypted room, give them all + // joined users + device_list_changes.extend( + services() + .rooms + .state_cache + .room_members(room_id) + .flatten() + .filter(|user_id| { + // Don't send key updates from the sender to + // the sender + &sender_user != user_id + }) + .filter(|user_id| { + // Only send keys if the sender doesn't + // share an encrypted room with the target + // already + !share_encrypted_room( + &sender_user, + user_id, + room_id, + ) + .unwrap_or(false) + }), + ); + } + } + } + // Look for device list updates in this room + device_list_changes.extend( + services() + .users + .keys_changed(room_id.as_ref(), globalsince, None) + .filter_map(Result::ok), + ); + } + for user_id in left_encrypted_users { + let dont_share_encrypted_room = services() + .rooms + .user + .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? + .filter_map(Result::ok) + .filter_map(|other_room_id| { + Some( + services() + .rooms + .state_accessor + .room_state_get( + &other_room_id, + &StateEventType::RoomEncryption, + "", + ) + .ok()? + .is_some(), + ) + }) + .all(|encrypted| !encrypted); + // If the user doesn't share an encrypted room with the target + // anymore, we need to tell them + if dont_share_encrypted_room { + device_list_left.insert(user_id); + } + } + } + + let mut lists = BTreeMap::new(); + // and required state + let mut todo_rooms = BTreeMap::new(); + + for (list_id, list) in body.lists { + if list.filters.and_then(|f| f.is_invite).unwrap_or(false) { + continue; + } + + let mut new_known_rooms = BTreeSet::new(); + + lists.insert( + list_id.clone(), + sync_events::v4::SyncList { + ops: list + .ranges + .into_iter() + .map(|mut r| { + r.0 = r.0.clamp( + uint!(0), + UInt::try_from(all_joined_rooms.len() - 1) + .unwrap_or(UInt::MAX), + ); + r.1 = r.1.clamp( + r.0, + UInt::try_from(all_joined_rooms.len() - 1) + .unwrap_or(UInt::MAX), + ); + let room_ids = all_joined_rooms[r + .0 + .try_into() + .unwrap_or(usize::MAX) + ..=r.1.try_into().unwrap_or(usize::MAX)] + .to_vec(); + new_known_rooms.extend(room_ids.iter().cloned()); + for room_id in &room_ids { + let todo_room = todo_rooms + .entry(room_id.clone()) + .or_insert((BTreeSet::new(), 0, u64::MAX)); + let limit = list + .room_details + .timeline_limit + .map_or(10, u64::from) + .min(100); + todo_room.0.extend( + list.room_details + .required_state + .iter() + .cloned(), + ); + todo_room.1 = todo_room.1.max(limit); + // 0 means unknown because it got out of date + todo_room.2 = todo_room.2.min( + known_rooms + .get(&list_id) + .and_then(|k| k.get(room_id)) + .copied() + .unwrap_or(0), + ); + } + sync_events::v4::SyncOp { + op: SlidingOp::Sync, + range: Some(r), + index: None, + room_ids, + room_id: None, + } + }) + .collect(), + count: UInt::try_from(all_joined_rooms.len()) + .unwrap_or(UInt::MAX), + }, + ); + + if let Some(conn_id) = &body.conn_id { + services().users.update_sync_known_rooms( + sender_user.clone(), + sender_device.clone(), + conn_id.clone(), + list_id, + new_known_rooms, + globalsince, + ); + } + } + + let mut known_subscription_rooms = BTreeSet::new(); + for (room_id, room) in &body.room_subscriptions { + if !services().rooms.metadata.exists(room_id)? { + continue; + } + let todo_room = todo_rooms.entry(room_id.clone()).or_insert(( + BTreeSet::new(), + 0, + u64::MAX, + )); + let limit = room.timeline_limit.map_or(10, u64::from).min(100); + todo_room.0.extend(room.required_state.iter().cloned()); + todo_room.1 = todo_room.1.max(limit); + // 0 means unknown because it got out of date + todo_room.2 = todo_room.2.min( + known_rooms + .get("subscriptions") + .and_then(|k| k.get(room_id)) + .copied() + .unwrap_or(0), + ); + known_subscription_rooms.insert(room_id.clone()); + } + + for r in body.unsubscribe_rooms { + known_subscription_rooms.remove(&r); + body.room_subscriptions.remove(&r); + } + + if let Some(conn_id) = &body.conn_id { + services().users.update_sync_known_rooms( + sender_user.clone(), + sender_device.clone(), + conn_id.clone(), + "subscriptions".to_owned(), + known_subscription_rooms, + globalsince, + ); + } + + if let Some(conn_id) = &body.conn_id { + services().users.update_sync_subscriptions( + sender_user.clone(), + sender_device.clone(), + conn_id.clone(), + body.room_subscriptions, + ); + } + + let mut rooms = BTreeMap::new(); + for (room_id, (required_state_request, timeline_limit, roomsince)) in + &todo_rooms + { + let roomsincecount = PduCount::Normal(*roomsince); + + let (timeline_pdus, limited) = load_timeline( + &sender_user, + room_id, + roomsincecount, + *timeline_limit, + )?; + + if roomsince != &0 && timeline_pdus.is_empty() { + continue; + } + + 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(), + })) + })? + .or_else(|| (roomsince != &0).then(|| roomsince.to_string())); + + let room_events: Vec<_> = timeline_pdus + .iter() + .map(|(_, pdu)| pdu.to_sync_room_event()) + .collect(); + + let required_state = required_state_request + .iter() + .filter_map(|state| { + services() + .rooms + .state_accessor + .room_state_get(room_id, &state.0, &state.1) + .ok() + .flatten() + .map(|state| state.to_sync_state_event()) + }) + .collect(); + + // Heroes + let heroes = services() + .rooms + .state_cache + .room_members(room_id) + .filter_map(Result::ok) + .filter(|member| member != &sender_user) + .filter_map(|member| { + services() + .rooms + .state_accessor + .get_member(room_id, &member) + .ok() + .flatten() + .map(|memberevent| { + ( + memberevent + .displayname + .unwrap_or_else(|| member.to_string()), + memberevent.avatar_url, + ) + }) + }) + .take(5) + .collect::>(); + let name = match &*heroes { + [] => None, + [only] => Some(only.0.clone()), + [firsts @ .., last] => Some({ + let firsts = firsts + .iter() + .map(|h| h.0.clone()) + .collect::>() + .join(", "); + + format!("{firsts} and {}", last.0) + }), + }; + + let avatar = if let [only] = &*heroes { + only.1.clone() + } else { + None + }; + + rooms.insert( + room_id.clone(), + sync_events::v4::SlidingSyncRoom { + name: services() + .rooms + .state_accessor + .get_name(room_id)? + .or(name), + avatar: if let Some(avatar) = avatar { + JsOption::Some(avatar) + } else { + match services().rooms.state_accessor.get_avatar(room_id)? { + JsOption::Some(avatar) => { + JsOption::from_option(avatar.url) + } + JsOption::Null => JsOption::Null, + JsOption::Undefined => JsOption::Undefined, + } + }, + initial: Some(roomsince == &0), + is_dm: None, + invite_state: None, + unread_notifications: UnreadNotificationsCount { + highlight_count: Some( + services() + .rooms + .user + .highlight_count(&sender_user, room_id)? + .try_into() + .expect("notification count can't go that high"), + ), + notification_count: Some( + services() + .rooms + .user + .notification_count(&sender_user, room_id)? + .try_into() + .expect("notification count can't go that high"), + ), + }, + timeline: room_events, + required_state, + prev_batch, + limited, + joined_count: Some( + services() + .rooms + .state_cache + .room_joined_count(room_id)? + .map(UInt::new_saturating) + .unwrap_or(uint!(0)), + ), + invited_count: Some( + services() + .rooms + .state_cache + .room_invited_count(room_id)? + .map(UInt::new_saturating) + .unwrap_or(uint!(0)), + ), + // Count events in timeline greater than global sync counter + num_live: None, + timestamp: None, + // TODO + heroes: 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); + } + match tokio::time::timeout(duration, watcher).await { + Ok(x) => x.expect("watcher should succeed"), + Err(error) => debug!(%error, "Timed out"), + }; + } + + Ok(Ra(sync_events::v4::Response { + initial: globalsince == 0, + txn_id: body.txn_id.clone(), + pos: next_batch.to_string(), + lists, + rooms, + extensions: sync_events::v4::Extensions { + to_device: body + .extensions + .to_device + .enabled + .unwrap_or(false) + .then(|| { + services() + .users + .get_to_device_events(&sender_user, &sender_device) + .map(|events| sync_events::v4::ToDevice { + events, + next_batch: next_batch.to_string(), + }) + }) + .transpose()?, + e2ee: sync_events::v4::E2EE { + device_lists: DeviceLists { + changed: device_list_changes.into_iter().collect(), + left: device_list_left.into_iter().collect(), + }, + device_one_time_keys_count: services() + .users + .count_one_time_keys(&sender_user, &sender_device)?, + // Fallback keys are not yet supported + device_unused_fallback_key_types: None, + }, + account_data: sync_events::v4::AccountData { + global: if body.extensions.account_data.enabled.unwrap_or(false) + { + services() + .account_data + .changes_since(None, &sender_user, globalsince)? + .into_iter() + .filter_map(|(_, v)| { + serde_json::from_str(v.json().get()) + .map_err(|_| { + Error::bad_database( + "Invalid account event in database.", + ) + }) + .ok() + }) + .collect() + } else { + 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/sync/v3.rs b/src/api/client_server/sync/v3.rs new file mode 100644 index 00000000..bcdfb10f --- /dev/null +++ b/src/api/client_server/sync/v3.rs @@ -0,0 +1,1050 @@ +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + time::Duration, +}; + +use ruma::{ + api::client::{ + filter::{FilterDefinition, LazyLoadOptions}, + sync::sync_events::{ + self, + v3::{ + Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, + JoinedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, + Rooms, State, Timeline, ToDevice, + }, + DeviceLists, UnreadNotificationsCount, + }, + uiaa::UiaaResponse, + }, + events::{ + room::member::{MembershipState, RoomMemberEventContent}, + StateEventType, TimelineEventType, + }, + uint, DeviceId, EventId, OwnedUserId, RoomId, UInt, UserId, +}; +use tracing::{debug, error}; + +use super::{load_timeline, share_encrypted_room}; +use crate::{ + service::{pdu::EventHash, rooms::timeline::PduCount}, + services, utils, Ar, Error, PduEvent, Ra, Result, +}; + +/// # `GET /_matrix/client/r0/sync` +/// +/// Synchronize the client's state with the latest state on the server. +/// +/// - This endpoint takes a `since` parameter which should be the `next_batch` +/// value from a previous request for incremental syncs. +/// +/// Calling this endpoint without a `since` parameter returns: +/// - Some of the most recent events of each timeline +/// - Notification counts for each room +/// - Joined and invited member counts, heroes +/// - All state events +/// +/// Calling this endpoint with a `since` parameter from a previous `next_batch` +/// returns: For joined rooms: +/// - Some of the most recent events of each timeline that happened after +/// `since` +/// - If user joined the room after `since`: All state events (unless lazy +/// loading is activated) and all device list updates in that room +/// - If the user was already in the room: A list of all events that are in the +/// state now, but were not in the state at `since` +/// - If the state we send contains a member event: Joined and invited member +/// counts, heroes +/// - Device list updates that happened after `since` +/// - If there are events in the timeline we send or the user send updated their +/// read mark: Notification counts +/// - EDUs that are active now (read receipts, typing updates, presence) +/// - TODO: Allow multiple sync streams to support Pantalaimon +/// +/// For invited rooms: +/// - If the user was invited after `since`: A subset of the state of the room +/// at the point of the invite +/// +/// For left rooms: +/// - If the user left after `since`: `prev_batch` token, empty state (TODO: +/// subset of the state at the point of the leave) +#[allow(clippy::too_many_lines)] +pub(crate) async fn sync_events_route( + body: Ar, +) -> Result, Ra> { + let sender_user = body.sender_user.expect("user is authenticated"); + let sender_device = body.sender_device.expect("user is authenticated"); + let body = 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 next_batchcount = PduCount::Normal(next_batch); + let next_batch_string = next_batch.to_string(); + + // Load filter + let filter = match body.filter { + None => FilterDefinition::default(), + Some(Filter::FilterDefinition(filter)) => filter, + Some(Filter::FilterId(filter_id)) => services() + .users + .get_filter(&sender_user, &filter_id)? + .unwrap_or_default(), + }; + + let (lazy_load_enabled, lazy_load_send_redundant) = + match filter.room.state.lazy_load_options { + LazyLoadOptions::Enabled { + include_redundant_members: redundant, + } => (true, redundant), + LazyLoadOptions::Disabled => (false, false), + }; + + let full_state = body.full_state; + + let mut joined_rooms = BTreeMap::new(); + let since = + body.since.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); + let sincecount = PduCount::Normal(since); + + // Users that have left any encrypted rooms the sender was in + let mut left_encrypted_users = HashSet::new(); + let mut device_list_updates = HashSet::new(); + let mut device_list_left = HashSet::new(); + + // Look for device list updates of this account + device_list_updates.extend( + services() + .users + .keys_changed(sender_user.as_ref(), since, None) + .filter_map(Result::ok), + ); + + let all_joined_rooms = services() + .rooms + .state_cache + .rooms_joined(&sender_user) + .collect::>(); + for room_id in all_joined_rooms { + let room_id = room_id?; + if let Ok(joined_room) = load_joined_room( + &sender_user, + &sender_device, + &room_id, + since, + sincecount, + next_batch, + next_batchcount, + lazy_load_enabled, + lazy_load_send_redundant, + full_state, + &mut device_list_updates, + &mut left_encrypted_users, + ) + .await + { + if !joined_room.is_empty() { + joined_rooms.insert(room_id.clone(), joined_room); + } + } + } + + let mut left_rooms = BTreeMap::new(); + let all_left_rooms: Vec<_> = + services().rooms.state_cache.rooms_left(&sender_user).collect(); + for result in all_left_rooms { + let (room_id, _) = result?; + + { + // Get and drop the lock to wait for remaining operations to finish + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; + drop(room_token); + } + + let left_count = services() + .rooms + .state_cache + .get_left_count(&room_id, &sender_user)?; + + // Left before last sync + if Some(since) >= left_count { + continue; + } + + if !services().rooms.metadata.exists(&room_id)? { + // This is just a rejected invite, not a room we know + let event = PduEvent { + event_id: EventId::new(services().globals.server_name()).into(), + sender: sender_user.clone(), + origin_server_ts: utils::millis_since_unix_epoch() + .try_into() + .expect("Timestamp is valid js_int value"), + kind: TimelineEventType::RoomMember, + content: serde_json::from_str(r#"{ "membership": "leave"}"#) + .unwrap(), + state_key: Some(sender_user.to_string()), + unsigned: None, + // The following keys are dropped on conversion + room_id: room_id.clone(), + prev_events: vec![], + depth: uint!(1), + auth_events: vec![], + redacts: None, + hashes: EventHash { + sha256: String::new(), + }, + signatures: None, + }; + + left_rooms.insert( + room_id, + LeftRoom { + account_data: RoomAccountData { + events: Vec::new(), + }, + timeline: Timeline { + limited: false, + prev_batch: Some(next_batch_string.clone()), + events: Vec::new(), + }, + state: State { + events: vec![event.to_sync_state_event()], + }, + }, + ); + + continue; + } + + let mut left_state_events = Vec::new(); + + let since_shortstatehash = + services().rooms.user.get_token_shortstatehash(&room_id, since)?; + + let since_state_ids = match since_shortstatehash { + Some(s) => { + services().rooms.state_accessor.state_full_ids(s).await? + } + None => HashMap::new(), + }; + + let Some(left_event_id) = + services().rooms.state_accessor.room_state_get_id( + &room_id, + &StateEventType::RoomMember, + sender_user.as_str(), + )? + else { + error!("Left room but no left state event"); + continue; + }; + + let Some(left_shortstatehash) = services() + .rooms + .state_accessor + .pdu_shortstatehash(&left_event_id)? + else { + error!("Leave event has no state"); + continue; + }; + + let mut left_state_ids = services() + .rooms + .state_accessor + .state_full_ids(left_shortstatehash) + .await?; + + let leave_shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &StateEventType::RoomMember, + sender_user.as_str(), + )?; + + left_state_ids.insert(leave_shortstatekey, left_event_id); + + let mut i = 0; + for (key, event_id) in left_state_ids { + if full_state || since_state_ids.get(&key) != Some(&event_id) { + let (event_type, state_key) = + services().rooms.short.get_statekey_from_short(key)?; + + if !lazy_load_enabled + || event_type != StateEventType::RoomMember + || full_state + // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 + || *sender_user == state_key + { + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); + continue; + }; + + left_state_events.push(pdu.to_sync_state_event()); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } + } + } + + left_rooms.insert( + room_id.clone(), + LeftRoom { + account_data: RoomAccountData { + events: Vec::new(), + }, + timeline: Timeline { + limited: false, + prev_batch: Some(next_batch_string.clone()), + events: Vec::new(), + }, + state: State { + events: left_state_events, + }, + }, + ); + } + + let mut invited_rooms = BTreeMap::new(); + let all_invited_rooms: Vec<_> = + services().rooms.state_cache.rooms_invited(&sender_user).collect(); + for result in all_invited_rooms { + let (room_id, invite_state_events) = result?; + + { + // Get and drop the lock to wait for remaining operations to finish + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; + drop(room_token); + } + + let invite_count = services() + .rooms + .state_cache + .get_invite_count(&room_id, &sender_user)?; + + // Invited before last sync + if Some(since) >= invite_count { + continue; + } + + invited_rooms.insert( + room_id.clone(), + InvitedRoom { + invite_state: InviteState { + events: invite_state_events, + }, + }, + ); + } + + for user_id in left_encrypted_users { + let dont_share_encrypted_room = services() + .rooms + .user + .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? + .filter_map(Result::ok) + .filter_map(|other_room_id| { + Some( + services() + .rooms + .state_accessor + .room_state_get( + &other_room_id, + &StateEventType::RoomEncryption, + "", + ) + .ok()? + .is_some(), + ) + }) + .all(|encrypted| !encrypted); + // If the user doesn't share an encrypted room with the target anymore, + // we need to tell them + if dont_share_encrypted_room { + device_list_left.insert(user_id); + } + } + + // Remove all to-device events the device received *last time* + services().users.remove_to_device_events( + &sender_user, + &sender_device, + since, + )?; + + let response = sync_events::v3::Response { + next_batch: next_batch_string, + rooms: Rooms { + leave: left_rooms, + join: joined_rooms, + invite: invited_rooms, + // TODO + knock: BTreeMap::new(), + }, + presence: Presence::default(), + account_data: GlobalAccountData { + events: services() + .account_data + .changes_since(None, &sender_user, since)? + .into_iter() + .filter_map(|(_, v)| { + serde_json::from_str(v.json().get()) + .map_err(|_| { + Error::bad_database( + "Invalid account event in database.", + ) + }) + .ok() + }) + .collect(), + }, + device_lists: DeviceLists { + changed: device_list_updates.into_iter().collect(), + left: device_list_left.into_iter().collect(), + }, + device_one_time_keys_count: services() + .users + .count_one_time_keys(&sender_user, &sender_device)?, + to_device: ToDevice { + events: services() + .users + .get_to_device_events(&sender_user, &sender_device)?, + }, + // Fallback keys are not yet supported + device_unused_fallback_key_types: None, + }; + + // TODO: Retry the endpoint instead of returning (waiting for #118) + if !full_state + && response.rooms.is_empty() + && response.presence.is_empty() + && response.account_data.is_empty() + && response.device_lists.is_empty() + && response.to_device.is_empty() + { + // Hang a few seconds so requests are not spammed + // Stop hanging if new info arrives + let mut duration = body.timeout.unwrap_or_default(); + if duration.as_secs() > 30 { + duration = Duration::from_secs(30); + } + match tokio::time::timeout(duration, watcher).await { + Ok(x) => x.expect("watcher should succeed"), + Err(error) => debug!(%error, "Timed out"), + }; + } + Ok(Ra(response)) +} + +#[tracing::instrument(skip_all, fields(room_id = %room_id))] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +async fn load_joined_room( + sender_user: &UserId, + sender_device: &DeviceId, + room_id: &RoomId, + since: u64, + sincecount: PduCount, + next_batch: u64, + next_batchcount: PduCount, + lazy_load_enabled: bool, + lazy_load_send_redundant: bool, + full_state: bool, + device_list_updates: &mut HashSet, + left_encrypted_users: &mut HashSet, +) -> Result { + { + // Get and drop the lock to wait for remaining operations to finish + // This will make sure the we have all events until next_batch + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.to_owned()) + .await; + drop(room_token); + } + + let (timeline_pdus, limited) = + load_timeline(sender_user, room_id, sincecount, 10)?; + + let send_notification_counts = !timeline_pdus.is_empty() + || services() + .rooms + .user + .last_notification_read(sender_user, room_id)? + > since; + + let mut timeline_users = HashSet::new(); + for (_, event) in &timeline_pdus { + timeline_users.insert(event.sender.as_str().to_owned()); + } + + services() + .rooms + .lazy_loading + .lazy_load_confirm_delivery( + sender_user, + sender_device, + room_id, + sincecount, + ) + .await?; + + // Database queries: + + let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + else { + error!("Room has no state"); + return Err(Error::BadDatabase("Room has no state")); + }; + + let since_shortstatehash = + services().rooms.user.get_token_shortstatehash(room_id, since)?; + + let ( + heroes, + joined_member_count, + invited_member_count, + joined_since_last_sync, + state_events, + ) = if timeline_pdus.is_empty() + && since_shortstatehash == Some(current_shortstatehash) + { + // No state changes + (Vec::new(), None, None, false, Vec::new()) + } else { + // Calculates joined_member_count, invited_member_count and heroes + let calculate_counts = || { + let joined_member_count = services() + .rooms + .state_cache + .room_joined_count(room_id)? + .unwrap_or(0); + let invited_member_count = services() + .rooms + .state_cache + .room_invited_count(room_id)? + .unwrap_or(0); + + // Recalculate heroes (first 5 members) + let mut heroes = Vec::new(); + + if joined_member_count + invited_member_count <= 5 { + // Go through all PDUs and for each member event, check if the + // user is still joined or invited until we have + // 5 or we reach the end + + for hero in services() + .rooms + .timeline + .all_pdus(sender_user, room_id)? + .filter_map(Result::ok) + .filter(|(_, pdu)| { + pdu.kind == TimelineEventType::RoomMember + }) + .map(|(_, pdu)| { + let content: RoomMemberEventContent = + serde_json::from_str(pdu.content.get()).map_err( + |_| { + Error::bad_database( + "Invalid member event in database.", + ) + }, + )?; + + if let Some(state_key) = &pdu.state_key { + let user_id = UserId::parse(state_key.clone()) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in member PDU.", + ) + })?; + + // The membership was and still is invite or join + if matches!( + content.membership, + MembershipState::Join | MembershipState::Invite + ) && (services() + .rooms + .state_cache + .is_joined(&user_id, room_id)? + || services() + .rooms + .state_cache + .is_invited(&user_id, room_id)?) + { + Ok::<_, Error>(Some(state_key.parse().expect( + "`state_key` should be a valid user ID", + ))) + } else { + Ok(None) + } + } else { + Ok(None) + } + }) + .filter_map(Result::ok) + .flatten() + { + if heroes.contains(&hero) || hero == sender_user.as_str() { + continue; + } + + heroes.push(hero); + } + } + + Ok::<_, Error>(( + Some(joined_member_count), + Some(invited_member_count), + heroes, + )) + }; + + let since_sender_member: Option = + since_shortstatehash + .and_then(|shortstatehash| { + services() + .rooms + .state_accessor + .state_get( + shortstatehash, + &StateEventType::RoomMember, + sender_user.as_str(), + ) + .transpose() + }) + .transpose()? + .and_then(|pdu| { + serde_json::from_str(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid PDU in database.") + }) + .ok() + }); + + let joined_since_last_sync = since_sender_member + .map_or(true, |member| member.membership != MembershipState::Join); + + if since_shortstatehash.is_none() || joined_since_last_sync { + // Probably since = 0, we will do an initial sync + + let (joined_member_count, invited_member_count, heroes) = + calculate_counts()?; + + let current_state_ids = services() + .rooms + .state_accessor + .state_full_ids(current_shortstatehash) + .await?; + + let mut state_events = Vec::new(); + let mut lazy_loaded = HashSet::new(); + + let mut i = 0; + for (shortstatekey, event_id) in current_state_ids { + let (event_type, state_key) = services() + .rooms + .short + .get_statekey_from_short(shortstatekey)?; + + if event_type != StateEventType::RoomMember { + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); + continue; + }; + state_events.push(pdu); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } else if !lazy_load_enabled + || full_state + || timeline_users.contains(&state_key) + // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 + || *sender_user == state_key + { + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); + continue; + }; + + // This check is in case a bad user ID made it into the + // database + if let Ok(uid) = UserId::parse(&state_key) { + lazy_loaded.insert(uid); + } + state_events.push(pdu); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } + } + + // Reset lazy loading because this is an initial sync + services().rooms.lazy_loading.lazy_load_reset( + sender_user, + sender_device, + room_id, + )?; + + // The state_events above should contain all timeline_users, let's + // mark them as lazy loaded. + services() + .rooms + .lazy_loading + .lazy_load_mark_sent( + sender_user, + sender_device, + room_id, + lazy_loaded, + next_batchcount, + ) + .await; + + ( + heroes, + joined_member_count, + invited_member_count, + true, + state_events, + ) + } else { + // Incremental /sync + let since_shortstatehash = since_shortstatehash.unwrap(); + + let mut delta_state_events = Vec::new(); + + if since_shortstatehash != current_shortstatehash { + let current_state_ids = services() + .rooms + .state_accessor + .state_full_ids(current_shortstatehash) + .await?; + let since_state_ids = services() + .rooms + .state_accessor + .state_full_ids(since_shortstatehash) + .await?; + + for (key, event_id) in current_state_ids { + if full_state + || since_state_ids.get(&key) != Some(&event_id) + { + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); + continue; + }; + + delta_state_events.push(pdu); + tokio::task::yield_now().await; + } + } + } + + let encrypted_room = services() + .rooms + .state_accessor + .state_get( + current_shortstatehash, + &StateEventType::RoomEncryption, + "", + )? + .is_some(); + + let since_encryption = services().rooms.state_accessor.state_get( + since_shortstatehash, + &StateEventType::RoomEncryption, + "", + )?; + + // Calculations: + let new_encrypted_room = + encrypted_room && since_encryption.is_none(); + + let send_member_count = delta_state_events + .iter() + .any(|event| event.kind == TimelineEventType::RoomMember); + + if encrypted_room { + for state_event in &delta_state_events { + if state_event.kind != TimelineEventType::RoomMember { + continue; + } + + if let Some(state_key) = &state_event.state_key { + let user_id = UserId::parse(state_key.clone()) + .map_err(|_| { + Error::bad_database( + "Invalid UserId in member PDU.", + ) + })?; + + if user_id == sender_user { + continue; + } + + let new_membership = + serde_json::from_str::( + state_event.content.get(), + ) + .map_err(|_| { + Error::bad_database("Invalid PDU in database.") + })? + .membership; + + match new_membership { + MembershipState::Join => { + // A new user joined an encrypted room + if !share_encrypted_room( + sender_user, + &user_id, + room_id, + )? { + device_list_updates.insert(user_id); + } + } + MembershipState::Leave => { + // Write down users that have left encrypted + // rooms we are in + left_encrypted_users.insert(user_id); + } + _ => {} + } + } + } + } + + if joined_since_last_sync && encrypted_room || new_encrypted_room { + // If the user is in a new encrypted room, give them all joined + // users + device_list_updates.extend( + services() + .rooms + .state_cache + .room_members(room_id) + .flatten() + .filter(|user_id| { + // Don't send key updates from the sender to the + // sender + sender_user != user_id + }) + .filter(|user_id| { + // Only send keys if the sender doesn't share an + // encrypted room with the target already + !share_encrypted_room(sender_user, user_id, room_id) + .unwrap_or(false) + }), + ); + } + + let (joined_member_count, invited_member_count, heroes) = + if send_member_count { + calculate_counts()? + } else { + (None, None, Vec::new()) + }; + + let mut state_events = delta_state_events; + let mut lazy_loaded = HashSet::new(); + + // Mark all member events we're returning as lazy-loaded + for pdu in &state_events { + if pdu.kind == TimelineEventType::RoomMember { + match UserId::parse( + pdu.state_key + .as_ref() + .expect("State event has state key") + .clone(), + ) { + Ok(state_key_userid) => { + lazy_loaded.insert(state_key_userid); + } + Err(error) => { + error!( + event_id = %pdu.event_id, + %error, + "Invalid state key for member event", + ); + } + } + } + } + + // Fetch contextual member state events for events from the + // timeline, and mark them as lazy-loaded as well. + for (_, event) in &timeline_pdus { + if lazy_loaded.contains(&event.sender) { + continue; + } + + if !services().rooms.lazy_loading.lazy_load_was_sent_before( + sender_user, + sender_device, + room_id, + &event.sender, + )? || lazy_load_send_redundant + { + if let Some(member_event) = + services().rooms.state_accessor.room_state_get( + room_id, + &StateEventType::RoomMember, + event.sender.as_str(), + )? + { + lazy_loaded.insert(event.sender.clone()); + state_events.push(member_event); + } + } + } + + services() + .rooms + .lazy_loading + .lazy_load_mark_sent( + sender_user, + sender_device, + room_id, + lazy_loaded, + next_batchcount, + ) + .await; + + ( + heroes, + joined_member_count, + invited_member_count, + joined_since_last_sync, + state_events, + ) + } + }; + + // Look for device list updates in this room + device_list_updates.extend( + services() + .users + .keys_changed(room_id.as_ref(), since, None) + .filter_map(Result::ok), + ); + + let notification_count = send_notification_counts + .then(|| services().rooms.user.notification_count(sender_user, room_id)) + .transpose()? + .map(|x| x.try_into().expect("notification count can't go that high")); + + let highlight_count = send_notification_counts + .then(|| services().rooms.user.highlight_count(sender_user, room_id)) + .transpose()? + .map(|x| x.try_into().expect("highlight count can't go that high")); + + 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 mut edus: Vec<_> = services() + .rooms + .edus + .read_receipt + .readreceipts_since(room_id, since) + .filter_map(Result::ok) + .map(|(_, _, v)| v) + .collect(); + + if services().rooms.edus.typing.last_typing_update(room_id).await? > since { + edus.push( + serde_json::from_str( + &serde_json::to_string( + &services().rooms.edus.typing.typings_all(room_id).await?, + ) + .expect("event is valid, we just created it"), + ) + .expect("event is valid, we just created it"), + ); + } + + // Save the state after this sync so we can send the correct state diff next + // sync + services().rooms.user.associate_token_shortstatehash( + room_id, + next_batch, + current_shortstatehash, + )?; + + Ok(JoinedRoom { + account_data: RoomAccountData { + events: services() + .account_data + .changes_since(Some(room_id), sender_user, since)? + .into_iter() + .filter_map(|(_, v)| { + serde_json::from_str(v.json().get()) + .map_err(|_| { + Error::bad_database( + "Invalid account event in database.", + ) + }) + .ok() + }) + .collect(), + }, + summary: RoomSummary { + heroes, + joined_member_count: joined_member_count.map(UInt::new_saturating), + invited_member_count: invited_member_count + .map(UInt::new_saturating), + }, + unread_notifications: UnreadNotificationsCount { + highlight_count, + notification_count, + }, + timeline: Timeline { + limited: limited || joined_since_last_sync, + prev_batch, + events: room_events, + }, + state: State { + events: state_events + .iter() + .map(|pdu| pdu.to_sync_state_event()) + .collect(), + }, + ephemeral: Ephemeral { + events: edus, + }, + unread_thread_notifications: BTreeMap::new(), + }) +} diff --git a/src/cli/serve.rs b/src/cli/serve.rs index d8521bcb..a4771641 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -548,8 +548,8 @@ fn client_routes() -> Router { .ruma_route(c2s::send_state_event_for_key_route) .ruma_route(c2s::get_state_events_route) .ruma_route(c2s::get_state_events_for_key_route) - .ruma_route(c2s::sync_events_route) - .ruma_route(c2s::sync_events_v4_route) + .ruma_route(c2s::v3::sync_events_route) + .ruma_route(c2s::msc3575::sync_events_v4_route) .ruma_route(c2s::get_context_route) .ruma_route(c2s::get_message_events_route) .ruma_route(c2s::search_events_route) From 55a04f77c67b7af66e82243cf8ae390dd53b6304 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 30 May 2024 19:16:42 +0000 Subject: [PATCH 490/617] sync/v3: record relevant span fields --- src/api/client_server/sync/v3.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index bcdfb10f..662400a5 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -23,7 +23,7 @@ use ruma::{ }, uint, DeviceId, EventId, OwnedUserId, RoomId, UInt, UserId, }; -use tracing::{debug, error}; +use tracing::{debug, error, field}; use super::{load_timeline, share_encrypted_room}; use crate::{ @@ -68,17 +68,33 @@ use crate::{ /// - If the user left after `since`: `prev_batch` token, empty state (TODO: /// subset of the state at the point of the leave) #[allow(clippy::too_many_lines)] +#[tracing::instrument( + skip_all, + fields( + sender_user, + sender_device, + next_batch, + since, + lazy_load_enabled, + lazy_load_send_redundant, + ) +)] pub(crate) async fn sync_events_route( body: Ar, ) -> Result, Ra> { + let current_span = tracing::Span::current(); + let sender_user = body.sender_user.expect("user is authenticated"); + current_span.record("sender_user", field::display(&sender_user)); let sender_device = body.sender_device.expect("user is authenticated"); + current_span.record("sender_device", field::display(&sender_device)); let body = 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()?; + current_span.record("next_batch", next_batch); let next_batchcount = PduCount::Normal(next_batch); let next_batch_string = next_batch.to_string(); @@ -99,12 +115,15 @@ pub(crate) async fn sync_events_route( } => (true, redundant), LazyLoadOptions::Disabled => (false, false), }; + current_span.record("lazy_load_enabled", lazy_load_enabled); + current_span.record("lazy_load_send_redundant", lazy_load_send_redundant); let full_state = body.full_state; let mut joined_rooms = BTreeMap::new(); let since = body.since.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); + current_span.record("since", since); let sincecount = PduCount::Normal(since); // Users that have left any encrypted rooms the sender was in From 8a7f87e9b415dd03b82254ce83bf032e43fe606b Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 30 May 2024 22:07:00 +0000 Subject: [PATCH 491/617] sync/v3: move readonly data to context struct This makes it a lot easier to factor out parts of the big sync_events_route(). --- src/api/client_server/sync/v3.rs | 261 ++++++++++++++++++------------- 1 file changed, 149 insertions(+), 112 deletions(-) diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index 662400a5..d3c596d8 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -31,6 +31,23 @@ use crate::{ services, utils, Ar, Error, PduEvent, Ra, Result, }; +struct SyncContext<'a> { + sender_user: &'a UserId, + sender_device: &'a DeviceId, + + next_batch: u64, + next_batch_string: String, + next_batchcount: PduCount, + + since: u64, + sincecount: PduCount, + + lazy_load_enabled: bool, + lazy_load_send_redundant: bool, + + full_state: bool, +} + /// # `GET /_matrix/client/r0/sync` /// /// Synchronize the client's state with the latest state on the server. @@ -93,38 +110,56 @@ pub(crate) async fn sync_events_route( // 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()?; - current_span.record("next_batch", next_batch); - let next_batchcount = PduCount::Normal(next_batch); - let next_batch_string = next_batch.to_string(); + let ctx = { + let next_batch = services().globals.current_count()?; + current_span.record("next_batch", next_batch); + let next_batchcount = PduCount::Normal(next_batch); + let next_batch_string = next_batch.to_string(); - // Load filter - let filter = match body.filter { - None => FilterDefinition::default(), - Some(Filter::FilterDefinition(filter)) => filter, - Some(Filter::FilterId(filter_id)) => services() - .users - .get_filter(&sender_user, &filter_id)? - .unwrap_or_default(), - }; - - let (lazy_load_enabled, lazy_load_send_redundant) = - match filter.room.state.lazy_load_options { - LazyLoadOptions::Enabled { - include_redundant_members: redundant, - } => (true, redundant), - LazyLoadOptions::Disabled => (false, false), + // Load filter + let filter = match body.filter { + None => FilterDefinition::default(), + Some(Filter::FilterDefinition(filter)) => filter, + Some(Filter::FilterId(filter_id)) => services() + .users + .get_filter(&sender_user, &filter_id)? + .unwrap_or_default(), }; - current_span.record("lazy_load_enabled", lazy_load_enabled); - current_span.record("lazy_load_send_redundant", lazy_load_send_redundant); - let full_state = body.full_state; + let (lazy_load_enabled, lazy_load_send_redundant) = + match filter.room.state.lazy_load_options { + LazyLoadOptions::Enabled { + include_redundant_members: redundant, + } => (true, redundant), + LazyLoadOptions::Disabled => (false, false), + }; + current_span.record("lazy_load_enabled", lazy_load_enabled); + current_span + .record("lazy_load_send_redundant", lazy_load_send_redundant); - let mut joined_rooms = BTreeMap::new(); - let since = - body.since.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); - current_span.record("since", since); - let sincecount = PduCount::Normal(since); + let full_state = body.full_state; + + let since = body + .since + .as_ref() + .and_then(|string| string.parse().ok()) + .unwrap_or(0); + current_span.record("since", since); + let sincecount = PduCount::Normal(since); + + SyncContext { + sender_user: &sender_user, + sender_device: &sender_device, + next_batch, + next_batch_string, + next_batchcount, + since, + sincecount, + lazy_load_enabled, + lazy_load_send_redundant, + full_state, + } + }; // Users that have left any encrypted rooms the sender was in let mut left_encrypted_users = HashSet::new(); @@ -135,28 +170,21 @@ pub(crate) async fn sync_events_route( device_list_updates.extend( services() .users - .keys_changed(sender_user.as_ref(), since, None) + .keys_changed(ctx.sender_user.as_ref(), ctx.since, None) .filter_map(Result::ok), ); + let mut joined_rooms = BTreeMap::new(); let all_joined_rooms = services() .rooms .state_cache - .rooms_joined(&sender_user) + .rooms_joined(ctx.sender_user) .collect::>(); for room_id in all_joined_rooms { let room_id = room_id?; if let Ok(joined_room) = load_joined_room( - &sender_user, - &sender_device, + &ctx, &room_id, - since, - sincecount, - next_batch, - next_batchcount, - lazy_load_enabled, - lazy_load_send_redundant, - full_state, &mut device_list_updates, &mut left_encrypted_users, ) @@ -170,7 +198,7 @@ pub(crate) async fn sync_events_route( let mut left_rooms = BTreeMap::new(); let all_left_rooms: Vec<_> = - services().rooms.state_cache.rooms_left(&sender_user).collect(); + services().rooms.state_cache.rooms_left(ctx.sender_user).collect(); for result in all_left_rooms { let (room_id, _) = result?; @@ -187,10 +215,10 @@ pub(crate) async fn sync_events_route( let left_count = services() .rooms .state_cache - .get_left_count(&room_id, &sender_user)?; + .get_left_count(&room_id, ctx.sender_user)?; // Left before last sync - if Some(since) >= left_count { + if Some(ctx.since) >= left_count { continue; } @@ -198,14 +226,14 @@ pub(crate) async fn sync_events_route( // This is just a rejected invite, not a room we know let event = PduEvent { event_id: EventId::new(services().globals.server_name()).into(), - sender: sender_user.clone(), + sender: ctx.sender_user.to_owned(), origin_server_ts: utils::millis_since_unix_epoch() .try_into() .expect("Timestamp is valid js_int value"), kind: TimelineEventType::RoomMember, content: serde_json::from_str(r#"{ "membership": "leave"}"#) .unwrap(), - state_key: Some(sender_user.to_string()), + state_key: Some(ctx.sender_user.to_string()), unsigned: None, // The following keys are dropped on conversion room_id: room_id.clone(), @@ -227,7 +255,7 @@ pub(crate) async fn sync_events_route( }, timeline: Timeline { limited: false, - prev_batch: Some(next_batch_string.clone()), + prev_batch: Some(ctx.next_batch_string.clone()), events: Vec::new(), }, state: State { @@ -241,8 +269,10 @@ pub(crate) async fn sync_events_route( let mut left_state_events = Vec::new(); - let since_shortstatehash = - services().rooms.user.get_token_shortstatehash(&room_id, since)?; + let since_shortstatehash = services() + .rooms + .user + .get_token_shortstatehash(&room_id, ctx.since)?; let since_state_ids = match since_shortstatehash { Some(s) => { @@ -255,7 +285,7 @@ pub(crate) async fn sync_events_route( services().rooms.state_accessor.room_state_get_id( &room_id, &StateEventType::RoomMember, - sender_user.as_str(), + ctx.sender_user.as_str(), )? else { error!("Left room but no left state event"); @@ -280,22 +310,22 @@ pub(crate) async fn sync_events_route( let leave_shortstatekey = services().rooms.short.get_or_create_shortstatekey( &StateEventType::RoomMember, - sender_user.as_str(), + ctx.sender_user.as_str(), )?; left_state_ids.insert(leave_shortstatekey, left_event_id); let mut i = 0; for (key, event_id) in left_state_ids { - if full_state || since_state_ids.get(&key) != Some(&event_id) { + if ctx.full_state || since_state_ids.get(&key) != Some(&event_id) { let (event_type, state_key) = services().rooms.short.get_statekey_from_short(key)?; - if !lazy_load_enabled + if !ctx.lazy_load_enabled || event_type != StateEventType::RoomMember - || full_state + || ctx.full_state // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 - || *sender_user == state_key + || *ctx.sender_user == state_key { let Some(pdu) = services().rooms.timeline.get_pdu(&event_id)? @@ -322,7 +352,7 @@ pub(crate) async fn sync_events_route( }, timeline: Timeline { limited: false, - prev_batch: Some(next_batch_string.clone()), + prev_batch: Some(ctx.next_batch_string.clone()), events: Vec::new(), }, state: State { @@ -334,7 +364,7 @@ pub(crate) async fn sync_events_route( let mut invited_rooms = BTreeMap::new(); let all_invited_rooms: Vec<_> = - services().rooms.state_cache.rooms_invited(&sender_user).collect(); + services().rooms.state_cache.rooms_invited(ctx.sender_user).collect(); for result in all_invited_rooms { let (room_id, invite_state_events) = result?; @@ -351,10 +381,10 @@ pub(crate) async fn sync_events_route( let invite_count = services() .rooms .state_cache - .get_invite_count(&room_id, &sender_user)?; + .get_invite_count(&room_id, ctx.sender_user)?; // Invited before last sync - if Some(since) >= invite_count { + if Some(ctx.since) >= invite_count { continue; } @@ -372,7 +402,10 @@ pub(crate) async fn sync_events_route( let dont_share_encrypted_room = services() .rooms .user - .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? + .get_shared_rooms(vec![ + ctx.sender_user.to_owned(), + user_id.clone(), + ])? .filter_map(Result::ok) .filter_map(|other_room_id| { Some( @@ -398,13 +431,13 @@ pub(crate) async fn sync_events_route( // Remove all to-device events the device received *last time* services().users.remove_to_device_events( - &sender_user, - &sender_device, - since, + ctx.sender_user, + ctx.sender_device, + ctx.since, )?; let response = sync_events::v3::Response { - next_batch: next_batch_string, + next_batch: ctx.next_batch_string, rooms: Rooms { leave: left_rooms, join: joined_rooms, @@ -416,7 +449,7 @@ pub(crate) async fn sync_events_route( account_data: GlobalAccountData { events: services() .account_data - .changes_since(None, &sender_user, since)? + .changes_since(None, ctx.sender_user, ctx.since)? .into_iter() .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) @@ -435,18 +468,18 @@ pub(crate) async fn sync_events_route( }, device_one_time_keys_count: services() .users - .count_one_time_keys(&sender_user, &sender_device)?, + .count_one_time_keys(ctx.sender_user, ctx.sender_device)?, to_device: ToDevice { events: services() .users - .get_to_device_events(&sender_user, &sender_device)?, + .get_to_device_events(ctx.sender_user, ctx.sender_device)?, }, // Fallback keys are not yet supported device_unused_fallback_key_types: None, }; // TODO: Retry the endpoint instead of returning (waiting for #118) - if !full_state + if !ctx.full_state && response.rooms.is_empty() && response.presence.is_empty() && response.account_data.is_empty() @@ -470,16 +503,8 @@ pub(crate) async fn sync_events_route( #[tracing::instrument(skip_all, fields(room_id = %room_id))] #[allow(clippy::too_many_arguments, clippy::too_many_lines)] async fn load_joined_room( - sender_user: &UserId, - sender_device: &DeviceId, + ctx: &SyncContext<'_>, room_id: &RoomId, - since: u64, - sincecount: PduCount, - next_batch: u64, - next_batchcount: PduCount, - lazy_load_enabled: bool, - lazy_load_send_redundant: bool, - full_state: bool, device_list_updates: &mut HashSet, left_encrypted_users: &mut HashSet, ) -> Result { @@ -495,14 +520,14 @@ async fn load_joined_room( } let (timeline_pdus, limited) = - load_timeline(sender_user, room_id, sincecount, 10)?; + load_timeline(ctx.sender_user, room_id, ctx.sincecount, 10)?; let send_notification_counts = !timeline_pdus.is_empty() || services() .rooms .user - .last_notification_read(sender_user, room_id)? - > since; + .last_notification_read(ctx.sender_user, room_id)? + > ctx.since; let mut timeline_users = HashSet::new(); for (_, event) in &timeline_pdus { @@ -513,10 +538,10 @@ async fn load_joined_room( .rooms .lazy_loading .lazy_load_confirm_delivery( - sender_user, - sender_device, + ctx.sender_user, + ctx.sender_device, room_id, - sincecount, + ctx.sincecount, ) .await?; @@ -530,7 +555,7 @@ async fn load_joined_room( }; let since_shortstatehash = - services().rooms.user.get_token_shortstatehash(room_id, since)?; + services().rooms.user.get_token_shortstatehash(room_id, ctx.since)?; let ( heroes, @@ -568,7 +593,7 @@ async fn load_joined_room( for hero in services() .rooms .timeline - .all_pdus(sender_user, room_id)? + .all_pdus(ctx.sender_user, room_id)? .filter_map(Result::ok) .filter(|(_, pdu)| { pdu.kind == TimelineEventType::RoomMember @@ -617,7 +642,9 @@ async fn load_joined_room( .filter_map(Result::ok) .flatten() { - if heroes.contains(&hero) || hero == sender_user.as_str() { + if heroes.contains(&hero) + || hero == ctx.sender_user.as_str() + { continue; } @@ -641,7 +668,7 @@ async fn load_joined_room( .state_get( shortstatehash, &StateEventType::RoomMember, - sender_user.as_str(), + ctx.sender_user.as_str(), ) .transpose() }) @@ -692,11 +719,11 @@ async fn load_joined_room( if i % 100 == 0 { tokio::task::yield_now().await; } - } else if !lazy_load_enabled - || full_state + } else if !ctx.lazy_load_enabled + || ctx.full_state || timeline_users.contains(&state_key) // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 - || *sender_user == state_key + || *ctx.sender_user == state_key { let Some(pdu) = services().rooms.timeline.get_pdu(&event_id)? @@ -721,8 +748,8 @@ async fn load_joined_room( // Reset lazy loading because this is an initial sync services().rooms.lazy_loading.lazy_load_reset( - sender_user, - sender_device, + ctx.sender_user, + ctx.sender_device, room_id, )?; @@ -732,11 +759,11 @@ async fn load_joined_room( .rooms .lazy_loading .lazy_load_mark_sent( - sender_user, - sender_device, + ctx.sender_user, + ctx.sender_device, room_id, lazy_loaded, - next_batchcount, + ctx.next_batchcount, ) .await; @@ -766,7 +793,7 @@ async fn load_joined_room( .await?; for (key, event_id) in current_state_ids { - if full_state + if ctx.full_state || since_state_ids.get(&key) != Some(&event_id) { let Some(pdu) = @@ -820,7 +847,7 @@ async fn load_joined_room( ) })?; - if user_id == sender_user { + if user_id == ctx.sender_user { continue; } @@ -837,7 +864,7 @@ async fn load_joined_room( MembershipState::Join => { // A new user joined an encrypted room if !share_encrypted_room( - sender_user, + ctx.sender_user, &user_id, room_id, )? { @@ -867,13 +894,17 @@ async fn load_joined_room( .filter(|user_id| { // Don't send key updates from the sender to the // sender - sender_user != user_id + ctx.sender_user != *user_id }) .filter(|user_id| { // Only send keys if the sender doesn't share an // encrypted room with the target already - !share_encrypted_room(sender_user, user_id, room_id) - .unwrap_or(false) + !share_encrypted_room( + ctx.sender_user, + user_id, + room_id, + ) + .unwrap_or(false) }), ); } @@ -919,11 +950,11 @@ async fn load_joined_room( } if !services().rooms.lazy_loading.lazy_load_was_sent_before( - sender_user, - sender_device, + ctx.sender_user, + ctx.sender_device, room_id, &event.sender, - )? || lazy_load_send_redundant + )? || ctx.lazy_load_send_redundant { if let Some(member_event) = services().rooms.state_accessor.room_state_get( @@ -942,11 +973,11 @@ async fn load_joined_room( .rooms .lazy_loading .lazy_load_mark_sent( - sender_user, - sender_device, + ctx.sender_user, + ctx.sender_device, room_id, lazy_loaded, - next_batchcount, + ctx.next_batchcount, ) .await; @@ -964,17 +995,21 @@ async fn load_joined_room( device_list_updates.extend( services() .users - .keys_changed(room_id.as_ref(), since, None) + .keys_changed(room_id.as_ref(), ctx.since, None) .filter_map(Result::ok), ); let notification_count = send_notification_counts - .then(|| services().rooms.user.notification_count(sender_user, room_id)) + .then(|| { + services().rooms.user.notification_count(ctx.sender_user, room_id) + }) .transpose()? .map(|x| x.try_into().expect("notification count can't go that high")); let highlight_count = send_notification_counts - .then(|| services().rooms.user.highlight_count(sender_user, room_id)) + .then(|| { + services().rooms.user.highlight_count(ctx.sender_user, room_id) + }) .transpose()? .map(|x| x.try_into().expect("highlight count can't go that high")); @@ -998,12 +1033,14 @@ async fn load_joined_room( .rooms .edus .read_receipt - .readreceipts_since(room_id, since) + .readreceipts_since(room_id, ctx.since) .filter_map(Result::ok) .map(|(_, _, v)| v) .collect(); - if services().rooms.edus.typing.last_typing_update(room_id).await? > since { + if services().rooms.edus.typing.last_typing_update(room_id).await? + > ctx.since + { edus.push( serde_json::from_str( &serde_json::to_string( @@ -1019,7 +1056,7 @@ async fn load_joined_room( // sync services().rooms.user.associate_token_shortstatehash( room_id, - next_batch, + ctx.next_batch, current_shortstatehash, )?; @@ -1027,7 +1064,7 @@ async fn load_joined_room( account_data: RoomAccountData { events: services() .account_data - .changes_since(Some(room_id), sender_user, since)? + .changes_since(Some(room_id), ctx.sender_user, ctx.since)? .into_iter() .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) From daceadb31095e07dc5e05443055e47cc7c61dea4 Mon Sep 17 00:00:00 2001 From: Lambda Date: Mon, 3 Jun 2024 19:36:23 +0000 Subject: [PATCH 492/617] sync/v3: factor out into separate functions This is both easier to read and produces much better tracing spans. --- src/api/client_server/sync/v3.rs | 480 ++++++++++++++++--------------- 1 file changed, 256 insertions(+), 224 deletions(-) diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index d3c596d8..65830053 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -21,7 +21,7 @@ use ruma::{ room::member::{MembershipState, RoomMemberEventContent}, StateEventType, TimelineEventType, }, - uint, DeviceId, EventId, OwnedUserId, RoomId, UInt, UserId, + uint, DeviceId, EventId, OwnedRoomId, OwnedUserId, RoomId, UInt, UserId, }; use tracing::{debug, error, field}; @@ -174,229 +174,14 @@ pub(crate) async fn sync_events_route( .filter_map(Result::ok), ); - let mut joined_rooms = BTreeMap::new(); - let all_joined_rooms = services() - .rooms - .state_cache - .rooms_joined(ctx.sender_user) - .collect::>(); - for room_id in all_joined_rooms { - let room_id = room_id?; - if let Ok(joined_room) = load_joined_room( - &ctx, - &room_id, - &mut device_list_updates, - &mut left_encrypted_users, - ) - .await - { - if !joined_room.is_empty() { - joined_rooms.insert(room_id.clone(), joined_room); - } - } - } - - let mut left_rooms = BTreeMap::new(); - let all_left_rooms: Vec<_> = - services().rooms.state_cache.rooms_left(ctx.sender_user).collect(); - for result in all_left_rooms { - let (room_id, _) = result?; - - { - // Get and drop the lock to wait for remaining operations to finish - let room_token = services() - .globals - .roomid_mutex_insert - .lock_key(room_id.clone()) - .await; - drop(room_token); - } - - let left_count = services() - .rooms - .state_cache - .get_left_count(&room_id, ctx.sender_user)?; - - // Left before last sync - if Some(ctx.since) >= left_count { - continue; - } - - if !services().rooms.metadata.exists(&room_id)? { - // This is just a rejected invite, not a room we know - let event = PduEvent { - event_id: EventId::new(services().globals.server_name()).into(), - sender: ctx.sender_user.to_owned(), - origin_server_ts: utils::millis_since_unix_epoch() - .try_into() - .expect("Timestamp is valid js_int value"), - kind: TimelineEventType::RoomMember, - content: serde_json::from_str(r#"{ "membership": "leave"}"#) - .unwrap(), - state_key: Some(ctx.sender_user.to_string()), - unsigned: None, - // The following keys are dropped on conversion - room_id: room_id.clone(), - prev_events: vec![], - depth: uint!(1), - auth_events: vec![], - redacts: None, - hashes: EventHash { - sha256: String::new(), - }, - signatures: None, - }; - - left_rooms.insert( - room_id, - LeftRoom { - account_data: RoomAccountData { - events: Vec::new(), - }, - timeline: Timeline { - limited: false, - prev_batch: Some(ctx.next_batch_string.clone()), - events: Vec::new(), - }, - state: State { - events: vec![event.to_sync_state_event()], - }, - }, - ); - - continue; - } - - let mut left_state_events = Vec::new(); - - let since_shortstatehash = services() - .rooms - .user - .get_token_shortstatehash(&room_id, ctx.since)?; - - let since_state_ids = match since_shortstatehash { - Some(s) => { - services().rooms.state_accessor.state_full_ids(s).await? - } - None => HashMap::new(), - }; - - let Some(left_event_id) = - services().rooms.state_accessor.room_state_get_id( - &room_id, - &StateEventType::RoomMember, - ctx.sender_user.as_str(), - )? - else { - error!("Left room but no left state event"); - continue; - }; - - let Some(left_shortstatehash) = services() - .rooms - .state_accessor - .pdu_shortstatehash(&left_event_id)? - else { - error!("Leave event has no state"); - continue; - }; - - let mut left_state_ids = services() - .rooms - .state_accessor - .state_full_ids(left_shortstatehash) - .await?; - - let leave_shortstatekey = - services().rooms.short.get_or_create_shortstatekey( - &StateEventType::RoomMember, - ctx.sender_user.as_str(), - )?; - - left_state_ids.insert(leave_shortstatekey, left_event_id); - - let mut i = 0; - for (key, event_id) in left_state_ids { - if ctx.full_state || since_state_ids.get(&key) != Some(&event_id) { - let (event_type, state_key) = - services().rooms.short.get_statekey_from_short(key)?; - - if !ctx.lazy_load_enabled - || event_type != StateEventType::RoomMember - || ctx.full_state - // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 - || *ctx.sender_user == state_key - { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - - left_state_events.push(pdu.to_sync_state_event()); - - i += 1; - if i % 100 == 0 { - tokio::task::yield_now().await; - } - } - } - } - - left_rooms.insert( - room_id.clone(), - LeftRoom { - account_data: RoomAccountData { - events: Vec::new(), - }, - timeline: Timeline { - limited: false, - prev_batch: Some(ctx.next_batch_string.clone()), - events: Vec::new(), - }, - state: State { - events: left_state_events, - }, - }, - ); - } - - let mut invited_rooms = BTreeMap::new(); - let all_invited_rooms: Vec<_> = - services().rooms.state_cache.rooms_invited(ctx.sender_user).collect(); - for result in all_invited_rooms { - let (room_id, invite_state_events) = result?; - - { - // Get and drop the lock to wait for remaining operations to finish - let room_token = services() - .globals - .roomid_mutex_insert - .lock_key(room_id.clone()) - .await; - drop(room_token); - } - - let invite_count = services() - .rooms - .state_cache - .get_invite_count(&room_id, ctx.sender_user)?; - - // Invited before last sync - if Some(ctx.since) >= invite_count { - continue; - } - - invited_rooms.insert( - room_id.clone(), - InvitedRoom { - invite_state: InviteState { - events: invite_state_events, - }, - }, - ); - } + let joined_rooms = collect_joined_rooms( + &ctx, + &mut device_list_updates, + &mut left_encrypted_users, + ) + .await?; + let left_rooms = collect_left_rooms(&ctx).await?; + let invited_rooms = collect_invited_rooms(&ctx).await?; for user_id in left_encrypted_users { let dont_share_encrypted_room = services() @@ -500,6 +285,37 @@ pub(crate) async fn sync_events_route( Ok(Ra(response)) } +#[tracing::instrument(skip_all)] +async fn collect_joined_rooms( + ctx: &SyncContext<'_>, + device_list_updates: &mut HashSet, + left_encrypted_users: &mut HashSet, +) -> Result> { + let mut joined_rooms = BTreeMap::new(); + let all_joined_rooms = services() + .rooms + .state_cache + .rooms_joined(ctx.sender_user) + .collect::>(); + for room_id in all_joined_rooms { + let room_id = room_id?; + if let Ok(joined_room) = load_joined_room( + ctx, + &room_id, + device_list_updates, + left_encrypted_users, + ) + .await + { + if !joined_room.is_empty() { + joined_rooms.insert(room_id.clone(), joined_room); + } + } + } + + Ok(joined_rooms) +} + #[tracing::instrument(skip_all, fields(room_id = %room_id))] #[allow(clippy::too_many_arguments, clippy::too_many_lines)] async fn load_joined_room( @@ -1104,3 +920,219 @@ async fn load_joined_room( unread_thread_notifications: BTreeMap::new(), }) } + +#[tracing::instrument(skip_all)] +async fn collect_left_rooms( + ctx: &SyncContext<'_>, +) -> Result> { + let mut left_rooms = BTreeMap::new(); + let all_left_rooms: Vec<_> = + services().rooms.state_cache.rooms_left(ctx.sender_user).collect(); + for result in all_left_rooms { + let (room_id, _) = result?; + + { + // Get and drop the lock to wait for remaining operations to finish + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; + drop(room_token); + } + + let left_count = services() + .rooms + .state_cache + .get_left_count(&room_id, ctx.sender_user)?; + + // Left before last sync + if Some(ctx.since) >= left_count { + continue; + } + + if !services().rooms.metadata.exists(&room_id)? { + // This is just a rejected invite, not a room we know + let event = PduEvent { + event_id: EventId::new(services().globals.server_name()).into(), + sender: ctx.sender_user.to_owned(), + origin_server_ts: utils::millis_since_unix_epoch() + .try_into() + .expect("Timestamp is valid js_int value"), + kind: TimelineEventType::RoomMember, + content: serde_json::from_str(r#"{ "membership": "leave"}"#) + .unwrap(), + state_key: Some(ctx.sender_user.to_string()), + unsigned: None, + // The following keys are dropped on conversion + room_id: room_id.clone(), + prev_events: vec![], + depth: uint!(1), + auth_events: vec![], + redacts: None, + hashes: EventHash { + sha256: String::new(), + }, + signatures: None, + }; + + left_rooms.insert( + room_id, + LeftRoom { + account_data: RoomAccountData { + events: Vec::new(), + }, + timeline: Timeline { + limited: false, + prev_batch: Some(ctx.next_batch_string.clone()), + events: Vec::new(), + }, + state: State { + events: vec![event.to_sync_state_event()], + }, + }, + ); + + continue; + } + + let mut left_state_events = Vec::new(); + + let since_shortstatehash = services() + .rooms + .user + .get_token_shortstatehash(&room_id, ctx.since)?; + + let since_state_ids = match since_shortstatehash { + Some(s) => { + services().rooms.state_accessor.state_full_ids(s).await? + } + None => HashMap::new(), + }; + + let Some(left_event_id) = + services().rooms.state_accessor.room_state_get_id( + &room_id, + &StateEventType::RoomMember, + ctx.sender_user.as_str(), + )? + else { + error!("Left room but no left state event"); + continue; + }; + + let Some(left_shortstatehash) = services() + .rooms + .state_accessor + .pdu_shortstatehash(&left_event_id)? + else { + error!("Leave event has no state"); + continue; + }; + + let mut left_state_ids = services() + .rooms + .state_accessor + .state_full_ids(left_shortstatehash) + .await?; + + let leave_shortstatekey = + services().rooms.short.get_or_create_shortstatekey( + &StateEventType::RoomMember, + ctx.sender_user.as_str(), + )?; + + left_state_ids.insert(leave_shortstatekey, left_event_id); + + let mut i = 0; + for (key, event_id) in left_state_ids { + if ctx.full_state || since_state_ids.get(&key) != Some(&event_id) { + let (event_type, state_key) = + services().rooms.short.get_statekey_from_short(key)?; + + if !ctx.lazy_load_enabled + || event_type != StateEventType::RoomMember + || ctx.full_state + // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 + || *ctx.sender_user == state_key + { + let Some(pdu) = + services().rooms.timeline.get_pdu(&event_id)? + else { + error!(%event_id, "Event in state not found"); + continue; + }; + + left_state_events.push(pdu.to_sync_state_event()); + + i += 1; + if i % 100 == 0 { + tokio::task::yield_now().await; + } + } + } + } + + left_rooms.insert( + room_id.clone(), + LeftRoom { + account_data: RoomAccountData { + events: Vec::new(), + }, + timeline: Timeline { + limited: false, + prev_batch: Some(ctx.next_batch_string.clone()), + events: Vec::new(), + }, + state: State { + events: left_state_events, + }, + }, + ); + } + + Ok(left_rooms) +} + +#[tracing::instrument(skip_all)] +async fn collect_invited_rooms( + ctx: &SyncContext<'_>, +) -> Result> { + let mut invited_rooms = BTreeMap::new(); + let all_invited_rooms: Vec<_> = + services().rooms.state_cache.rooms_invited(ctx.sender_user).collect(); + for result in all_invited_rooms { + let (room_id, invite_state_events) = result?; + + { + // Get and drop the lock to wait for remaining operations to finish + let room_token = services() + .globals + .roomid_mutex_insert + .lock_key(room_id.clone()) + .await; + drop(room_token); + } + + let invite_count = services() + .rooms + .state_cache + .get_invite_count(&room_id, ctx.sender_user)?; + + // Invited before last sync + if Some(ctx.since) >= invite_count { + continue; + } + + invited_rooms.insert( + room_id.clone(), + InvitedRoom { + invite_state: InviteState { + events: invite_state_events, + }, + }, + ); + } + + Ok(invited_rooms) +} From 554751917a5f2b5727f01fdc8856daa7969e81f4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 10:03:17 -0800 Subject: [PATCH 493/617] fix some minor documentation typos * Capitalize Conduit and Grapevine * Replace a "Not" with "Note" * Change a "Conduit 0.7.0" to "Conduit <0.8.0" * Expand "db" to "database" and "database schema" as appropriate --- book/SUMMARY.md | 2 +- book/migration.md | 35 ++++++++++++++++++----------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 289236ad..b6029127 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -2,6 +2,6 @@ * [Introduction](./introduction.md) * [Code of conduct](./code-of-conduct.md) -* [Migration to/from conduit](./migration.md) +* [Migration to/from Conduit](./migration.md) * [Contributing](./contributing.md) * [Changelog](./changelog.md) diff --git a/book/migration.md b/book/migration.md index e1651531..6ec2f833 100644 --- a/book/migration.md +++ b/book/migration.md @@ -1,12 +1,12 @@ -# Migration to/from conduit +# Migration to/from Conduit -Before migrating a conduit instance to grapevine, make sure to read through +Before migrating a Conduit instance to Grapevine, make sure to read through all of the breaking changes listed in [the changelog](./changelog.md). -In order to migrate an existing conduit instance to/from grapevine, the -grapevine config must include `conduit_compat = true`. This parameter cannot +In order to migrate an existing Conduit instance to/from Grapevine, the +Grapevine config must include `conduit_compat = true`. This parameter cannot currently be modified after creating the database for the first time, so make -sure to set it when creating a fresh grapevine instance that you may want to +sure to set it when creating a fresh Grapevine instance that you may want to migrate to a different implementation in the future. ## Config @@ -26,11 +26,11 @@ automatically migrating existing configs to the new schema. ## Database Grapevine is currently compatible with the Conduit 0.7.0 database format. It is -still possible to migrate to or from some newer conduit versions, but it may +still possible to migrate to or from some newer Conduit versions, but it may require manual intervention or break some functionality. We plan to add [a migration tool][db-compatibility-mr] to support cleanly -migrating to or from conduit versions we are not internally compatible with. +migrating to or from Conduit versions we are not internally compatible with. | Is migrating from | to | workable? | |-|-|-| @@ -46,11 +46,12 @@ migrating to or from conduit versions we are not internally compatible with. Conduit 0.9.0 includes [a database migration][conduit-db-16-migration] that modifies data that Grapevine doesn't read. Grapevine does not currently -recognize the new db version, and will fail to start against a conduit 0.9.0 -database. Grapevine can start and run without issues if the version recorded in -the db is rolled back from 16 to 13. It is possible to do this by editing the -db manually, or by modifying grapevine to change the version. [This -patch][conduit-db-16-patch] is an example of the latter approach. +recognize the new database schema version, and will fail to start against +a Conduit 0.9.0 database. Grapevine can start and run without issues if the +version recorded in the databse is rolled back from 16 to 13. It is possible to +do this by editing the database manually, or by modifying Grapevine to change +the version. [This patch][conduit-db-16-patch] is an example of the latter +approach. [conduit-db-16-migration]: https://gitlab.com/famedly/conduit/-/blob/f8d7ef04e664580e882bac852877b68e7bd3ab1e/src/database/mod.rs#L945 [conduit-db-16-patch]: https://gitlab.computer.surgery/matrix/grapevine/-/commit/fdaa30f0d670c6f04f4e6be5d193f9146d179d95 @@ -59,11 +60,11 @@ patch][conduit-db-16-patch] is an example of the latter approach. Conduit 0.8.0 added [a new database table][alias_userid-commit] to track which users created each room alias. Grapevine does not write to this table, so it is -not possible to delete aliases created in grapevine through the normal -client-server API after migrating to conduit 0.8.0. It is possible to delete -aliases with the `remove-alias` admin command. Not that this issue also applies -to migrations from conduit 0.7.0 to conduit 0.8.0. +not possible to delete aliases created in Grapevine through the normal +client-server API after migrating to Conduit 0.8.0. It is possible to delete +aliases with the `remove-alias` admin command. Note that this issue also applies +to migrations from Conduit <0.8.0 to Conduit 0.8.0. -There are no additional known issues when migrating to conduit 0.9.0. +There are no additional known issues when migrating to Conduit 0.9.0. [alias_userid-commit]: https://gitlab.com/famedly/conduit/-/commit/144d548ef739324ca97db12e8cada60ca3e43e09 From a520c4e032594e61583264cfcd781ad82b0272d0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 1 Nov 2024 12:08:37 -0700 Subject: [PATCH 494/617] fix sorting in shell.nix --- nix/shell.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/shell.nix b/nix/shell.nix index 6b4ff81a..1d72941c 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,6 +1,6 @@ # Keep sorted -{ default -, cargo-insta +{ cargo-insta +, default , engage , inputs , jq From d4ffa7897977be5c1763e45783c0e56a076c588f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 1 Nov 2024 12:21:43 -0700 Subject: [PATCH 495/617] make it possible to `direnv exec .` at top level This way we don't have to be so careful about where we call `direnv exec .` and can safely assume e.g. in scripts that we're always in the direnv environment. --- .gitlab-ci.yml | 4 ++-- nix/shell.nix | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ecc662d..e73cbc9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,7 +42,7 @@ ci: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - - ./bin/nix-build-and-cache ci + - direnv exec . nix-build-and-cache ci - direnv exec . engage cache: @@ -57,7 +57,7 @@ artifacts: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - - ./bin/nix-build-and-cache packages + - direnv exec . nix-build-and-cache packages pages: stage: deploy diff --git a/nix/shell.nix b/nix/shell.nix index 1d72941c..4373a0b5 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -43,4 +43,9 @@ mkShell { default.propagatedBuildInputs ++ default.buildInputs; + + shellHook = '' + # Workaround for + unset TMPDIR + ''; } From 540cc89c8382e2cd06db5bfead8b08e18969b6c5 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 1 Dec 2024 16:23:26 -0800 Subject: [PATCH 496/617] move ci job scipts into an actual script This will make it possible/easier to: * share code between jobs * run jobs locally --- .gitlab-ci.yml | 9 +++------ bin/job | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ nix/shell.nix | 2 ++ 3 files changed, 57 insertions(+), 6 deletions(-) create mode 100755 bin/job diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e73cbc9c..5336e0ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,9 +42,7 @@ ci: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - - direnv exec . nix-build-and-cache ci - - - direnv exec . engage + - direnv exec . job ci cache: paths: - target @@ -57,7 +55,7 @@ artifacts: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - - direnv exec . nix-build-and-cache packages + - direnv exec . job artifacts pages: stage: deploy @@ -65,8 +63,7 @@ pages: rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH script: - - nix build .#website-root - - cp --recursive --dereference result public + - direnv exec . job pages artifacts: paths: - public diff --git a/bin/job b/bin/job new file mode 100755 index 00000000..2d5166e5 --- /dev/null +++ b/bin/job @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +job_artifacts() ( + set -euo pipefail + + nix-build-and-cache packages +) + +job_ci() ( + set -euo pipefail + + nix-build-and-cache ci + direnv exec . engage +) + +job_pages() ( + set -euo pipefail + + nix build .#website-root + cp --recursive --dereference result public +) + +bail() ( + set -euo pipefail + + echo + echo "Job failed" + exit 1 +) + +run() ( + set -euo pipefail + + if [[ -z "${1+x}" ]]; then + echo "You must supply a job to run. Available jobs:" + declare -F | rg \ + --only-matching \ + --color never \ + --replace '* $1' \ + '^declare -f job_(.*)$' + + exit 1 + fi + + job="$1" + + cd "$(git rev-parse --show-toplevel)" + + job_"$job" || bail +) + +run "$@" diff --git a/nix/shell.nix b/nix/shell.nix index 4373a0b5..5e642001 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -8,6 +8,7 @@ , markdownlint-cli , mdbook , mkShell +, ripgrep , stdenv , toolchain }: @@ -35,6 +36,7 @@ mkShell { lychee markdownlint-cli mdbook + ripgrep toolchain ] ++ From a5eba4547295a14f4933dd0a89d1444debff4bf5 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 10:38:53 -0800 Subject: [PATCH 497/617] set owner write bit in pages job This makes deleting the copied files easier. --- bin/job | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/job b/bin/job index 2d5166e5..7f3ad091 100755 --- a/bin/job +++ b/bin/job @@ -18,6 +18,7 @@ job_pages() ( nix build .#website-root cp --recursive --dereference result public + chmod u+w -R public ) bail() ( From 2265b6615ee8bae7373a332e35e430e9e0755ed4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 1 Nov 2024 09:15:51 -0700 Subject: [PATCH 498/617] run ci for each commit GitLab doesn't seem to have built-in support for this because of course it doesn't. To do this, we move the job scripts to a different file to make it possible to share code between job scripts. --- bin/job | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/bin/job b/bin/job index 7f3ad091..23db0ad7 100755 --- a/bin/job +++ b/bin/job @@ -24,8 +24,9 @@ job_pages() ( bail() ( set -euo pipefail + git show --shortstat echo - echo "Job failed" + echo "Failure caused by the above commit" exit 1 ) @@ -47,7 +48,20 @@ run() ( cd "$(git rev-parse --show-toplevel)" - job_"$job" || bail + if [[ -z "${CI_MERGE_REQUEST_DIFF_BASE_SHA+x}" ]]; then + echo "Running against latest commit only..." + + job_"$job" || bail + else + echo "Running against all commits since this branch's base..." + + readarray -t commits < \ + <(git rev-list --reverse "$CI_MERGE_REQUEST_DIFF_BASE_SHA..HEAD") + for commit in "${commits[@]}"; do + git checkout "$commit" + job_"$job" || bail + done + fi ) run "$@" From 976aef690fef1bb6fd40b4c0bfffb097cfcc54f3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 1 Nov 2024 12:45:03 -0700 Subject: [PATCH 499/617] cache and skip commits that have passed already Commits marked as passed can be re-run by changing their hash or clearing the cache. --- bin/job | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bin/job b/bin/job index 23db0ad7..fd700081 100755 --- a/bin/job +++ b/bin/job @@ -30,6 +30,19 @@ bail() ( exit 1 ) +mark_commit_passed() ( + set -euo pipefail + + mkdir -p ".gitlab-ci.d/passed/$1" + touch ".gitlab-ci.d/passed/$1/$(git rev-parse HEAD)" +) + +commit_passed() ( + set -euo pipefail + + [[ -f ".gitlab-ci.d/passed/$1/$(git rev-parse HEAD)" ]] +) + run() ( set -euo pipefail @@ -59,7 +72,15 @@ run() ( <(git rev-list --reverse "$CI_MERGE_REQUEST_DIFF_BASE_SHA..HEAD") for commit in "${commits[@]}"; do git checkout "$commit" + + if commit_passed "$job"; then + echo "Skipping commit because it already passed: $commit" + continue + fi + job_"$job" || bail + + mark_commit_passed "$job" done fi ) From f4dfb496e168ca23960d4b40345dedf5441b121b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 1 Nov 2024 14:23:03 -0700 Subject: [PATCH 500/617] skip jobs that don't make artifacts on main branch This is okay because we only allow fast-forward merges, changes always go through MRs, and MRs are subject to, e.g., the code quality checks. This change is desirable because it should save some time and energy. --- .gitlab-ci.yml | 9 ++++++++- bin/job | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5336e0ea..9460a94e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,11 +35,18 @@ before_script: # Set CARGO_HOME to a cacheable path - export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo" -ci: + +cache-ci-deps: stage: ci image: nixos/nix:2.18.2 rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + script: direnv exec . job cache-ci-deps + +ci: + stage: ci + image: nixos/nix:2.18.2 + rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - direnv exec . job ci diff --git a/bin/job b/bin/job index fd700081..659e9b5b 100755 --- a/bin/job +++ b/bin/job @@ -21,6 +21,12 @@ job_pages() ( chmod u+w -R public ) +job_cache-ci-deps() ( + set -euo pipefail + + nix-build-and-cache ci +) + bail() ( set -euo pipefail From 9e3738d3301ada8361a67fa8526fa3b37028fe66 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 11:25:54 -0800 Subject: [PATCH 501/617] run some jobs on the final commit only These are too expensive to be worth running against all commits. --- bin/job | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/bin/job b/bin/job index 659e9b5b..ca4dccc5 100755 --- a/bin/job +++ b/bin/job @@ -49,6 +49,26 @@ commit_passed() ( [[ -f ".gitlab-ci.d/passed/$1/$(git rev-parse HEAD)" ]] ) +contains() ( + set -euo pipefail + + local -n xs=$1 + + for x in "${xs[@]}"; do + if [[ "$x" == "$2" ]]; then + return 0 + else + return 1 + fi + done +) + +# Jobs that should only run on the latest commit rather than since the branch's +# base. +last_commit_only=( + artifacts +) + run() ( set -euo pipefail @@ -67,7 +87,10 @@ run() ( cd "$(git rev-parse --show-toplevel)" - if [[ -z "${CI_MERGE_REQUEST_DIFF_BASE_SHA+x}" ]]; then + if \ + [[ -z "${CI_MERGE_REQUEST_DIFF_BASE_SHA+x}" ]] \ + || contains last_commit_only "$job" + then echo "Running against latest commit only..." job_"$job" || bail From 79cedccdb62d9b36d4434e5da53ed55f11330951 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 15:57:06 -0800 Subject: [PATCH 502/617] factor remote device key request logic into helper functions This is pure code-motion, with no behavior changes. The new structure will make it easier to fix the backoff behavior, and makes the code somewhat less of a nightmare to follow. --- src/api/client_server/keys.rs | 154 +++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 69 deletions(-) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index c4422079..40a2ccb7 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -17,7 +17,8 @@ use ruma::{ federation, }, serde::Raw, - OneTimeKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId, + OneTimeKeyAlgorithm, OwnedDeviceId, OwnedServerName, OwnedUserId, + ServerName, UserId, }; use serde_json::json; use tracing::debug; @@ -385,80 +386,17 @@ pub(crate) async fn get_keys_helper bool>( let mut failures = BTreeMap::new(); - let back_off = |id| async { - match services().globals.bad_query_ratelimiter.write().await.entry(id) { - hash_map::Entry::Vacant(e) => { - e.insert((Instant::now(), 1)); - } - hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1); - } - } - }; - let mut futures: FuturesUnordered<_> = get_over_federation .into_iter() - .map(|(server, vec)| async move { - if let Some((time, tries)) = services() - .globals - .bad_query_ratelimiter - .read() - .await - .get(server) - { - // Exponential backoff - let mut min_elapsed_duration = - Duration::from_secs(30) * (*tries) * (*tries); - if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { - min_elapsed_duration = Duration::from_secs(60 * 60 * 24); - } - - if let Some(remaining) = - min_elapsed_duration.checked_sub(time.elapsed()) - { - debug!(%server, %tries, ?remaining, "Backing off from server"); - return ( - server, - Err(Error::BadServerResponse( - "bad query, still backing off", - )), - ); - } - } - - let mut device_keys_input_fed = BTreeMap::new(); - for (user_id, keys) in vec { - device_keys_input_fed.insert(user_id.to_owned(), keys.clone()); - } - // TODO: switch .and_then(|result| result) to .flatten() when stable - // - ( - server, - tokio::time::timeout( - Duration::from_secs(25), - services().sending.send_federation_request( - server, - federation::keys::get_keys::v1::Request { - device_keys: device_keys_input_fed, - }, - ), - ) - .await - .map_err(|_e| Error::BadServerResponse("Query took too long")) - .and_then(|result| result), - ) + .map(|(server, keys)| async move { + (server, request_keys_from(server, keys).await) }) .collect(); while let Some((server, response)) = futures.next().await { - let response = match response { - Ok(response) => response, - Err(error) => { - back_off(server.to_owned()).await; - debug!(%server, %error, "remote device key query failed"); - failures.insert(server.to_string(), json!({})); - continue; - } + let Ok(response) = response else { + failures.insert(server.to_string(), json!({})); + continue; }; for (user, masterkey) in response.master_keys { @@ -511,6 +449,84 @@ pub(crate) async fn get_keys_helper bool>( }) } +/// Returns `Err` if key requests to the server are being backed off due to +/// previous errors. +async fn check_key_requests_back_off(server: &ServerName) -> Result<()> { + if let Some((time, tries)) = + services().globals.bad_query_ratelimiter.read().await.get(server) + { + // Exponential backoff + let mut min_elapsed_duration = + Duration::from_secs(30) * (*tries) * (*tries); + if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { + min_elapsed_duration = Duration::from_secs(60 * 60 * 24); + } + + if let Some(remaining) = + min_elapsed_duration.checked_sub(time.elapsed()) + { + debug!(%server, %tries, ?remaining, "Backing off from server"); + return Err(Error::BadServerResponse( + "bad query, still backing off", + )); + } + } + Ok(()) +} + +/// Backs off future remote device key requests to a server after a failure. +async fn back_off_key_requests(server: OwnedServerName) { + match services().globals.bad_query_ratelimiter.write().await.entry(server) { + hash_map::Entry::Vacant(e) => { + e.insert((Instant::now(), 1)); + } + hash_map::Entry::Occupied(mut e) => { + *e.get_mut() = (Instant::now(), e.get().1 + 1); + } + } +} + +/// Requests device keys from a remote server, unless the server is in backoff. +/// +/// Updates backoff state depending on the result of the request. +async fn request_keys_from( + server: &ServerName, + keys: Vec<(&UserId, &Vec)>, +) -> Result { + let result = request_keys_from_inner(server, keys).await; + if let Err(error) = &result { + debug!(%server, %error, "remote device key query failed"); + back_off_key_requests(server.to_owned()).await; + } + result +} + +async fn request_keys_from_inner( + server: &ServerName, + keys: Vec<(&UserId, &Vec)>, +) -> Result { + check_key_requests_back_off(server).await?; + + let mut device_keys_input_fed = BTreeMap::new(); + for (user_id, keys) in keys { + device_keys_input_fed.insert(user_id.to_owned(), keys.clone()); + } + // TODO: switch .and_then(|result| result) to .flatten() when stable + // + tokio::time::timeout( + Duration::from_secs(25), + services().sending.send_federation_request( + server, + federation::keys::get_keys::v1::Request { + device_keys: device_keys_input_fed, + }, + ), + ) + .await + .map_err(|_e| Error::BadServerResponse("Query took too long")) + .and_then(|result| result) +} + fn add_unsigned_device_display_name( keys: &mut Raw, metadata: ruma::api::client::device::Device, From 4ee83120682d9e366505cb26172f09143c7fa486 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 16:06:09 -0800 Subject: [PATCH 503/617] reset device key query backoff after a successful request Failing to reset the backoff state resulted in a monotonically increasing backoff delay. If a remote server was temporarily unavailable, we would have a persistently increased rate of key query failures until the backoff state was reset by a server restart. If enough key queries were attempted while the remote was unavailable, it can accumulate an arbitrarily long backoff delay and effectively block all future key queries to this server. --- book/changelog.md | 4 ++++ src/api/client_server/keys.rs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 3cd11429..369a850f 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -205,6 +205,10 @@ This will be the first release of Grapevine since it was forked from Conduit 21. Fix tiebreaking comparisons between events during state resolution. This will reduce the rate at which servers disagree about the state of rooms. ([!141](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/141)) +22. Fix bug where the backoff state for remote device key queries was not reset + after a successful request, causing an increasing rate of key query failures + over time until a server restart. + ([!149](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/149)) ### Added diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 40a2ccb7..3b5603c9 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -486,6 +486,11 @@ async fn back_off_key_requests(server: OwnedServerName) { } } +/// Stops backing off remote device key requests to a server after a success. +async fn reset_key_request_back_off(server: &ServerName) { + services().globals.bad_query_ratelimiter.write().await.remove(server); +} + /// Requests device keys from a remote server, unless the server is in backoff. /// /// Updates backoff state depending on the result of the request. @@ -494,9 +499,12 @@ async fn request_keys_from( keys: Vec<(&UserId, &Vec)>, ) -> Result { let result = request_keys_from_inner(server, keys).await; - if let Err(error) = &result { - debug!(%server, %error, "remote device key query failed"); - back_off_key_requests(server.to_owned()).await; + match &result { + Ok(_) => reset_key_request_back_off(server).await, + Err(error) => { + debug!(%server, %error, "remote device key query failed"); + back_off_key_requests(server.to_owned()).await; + } } result } From ba72616672c7a37ab6428de567b68064f853021c Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 1 Dec 2024 16:12:11 -0800 Subject: [PATCH 504/617] do not backoff remote device key queries when a request fails due to backoff The previous logic would increment the backoff counter both when a request actually fails and when we do not make a request because the server was already in backoff. This lead to a positive feedback loop where every request made while a server is in backoff increases the backoff delay, making it impossible to recover from backoff unless the entire backoff delay elapses with zero requests. --- book/changelog.md | 4 ++++ src/api/client_server/keys.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 369a850f..e78fcf96 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -209,6 +209,10 @@ This will be the first release of Grapevine since it was forked from Conduit after a successful request, causing an increasing rate of key query failures over time until a server restart. ([!149](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/149)) +23. Fix bug where remote key queries that were skipped because the target server + was in backoff would increment the backoff delay further, leading to a + positive feedback loop. + ([!149](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/149)) ### Added diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 3b5603c9..304be1d0 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -498,6 +498,8 @@ async fn request_keys_from( server: &ServerName, keys: Vec<(&UserId, &Vec)>, ) -> Result { + check_key_requests_back_off(server).await?; + let result = request_keys_from_inner(server, keys).await; match &result { Ok(_) => reset_key_request_back_off(server).await, @@ -513,8 +515,6 @@ async fn request_keys_from_inner( server: &ServerName, keys: Vec<(&UserId, &Vec)>, ) -> Result { - check_key_requests_back_off(server).await?; - let mut device_keys_input_fed = BTreeMap::new(); for (user_id, keys) in keys { device_keys_input_fed.insert(user_id.to_owned(), keys.clone()); From cf067dbeb1a99768ff560d8b564278a63ff7e280 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 3 Oct 2024 12:41:53 -0700 Subject: [PATCH 505/617] move changelog section above contributing --- book/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/SUMMARY.md b/book/SUMMARY.md index b6029127..de2bd079 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -3,5 +3,5 @@ * [Introduction](./introduction.md) * [Code of conduct](./code-of-conduct.md) * [Migration to/from Conduit](./migration.md) -* [Contributing](./contributing.md) * [Changelog](./changelog.md) +* [Contributing](./contributing.md) From 3a10e23d942c49d208a615762208b125c6fa4ce3 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 3 Oct 2024 12:35:10 -0700 Subject: [PATCH 506/617] move matrix link to introduction page --- book/contributing.md | 16 +--------------- book/introduction.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/book/contributing.md b/book/contributing.md index e332e56b..8eb5bdd4 100644 --- a/book/contributing.md +++ b/book/contributing.md @@ -1,20 +1,5 @@ # Contributing -## On Matrix - -Currently, the Matrix room at [#grapevine:computer.surgery][room] serves -multiple purposes: - -* General discussion about the project, such as answering questions about it -* Reporting issues with Grapevine, if getting GitLab access is too much trouble - for you -* Providing support to users running Grapevine -* Discussing the development of Grapevine - -If you'd like to engage in or observe any of those things, please join! - -[room]: https://matrix.to/#/#grapevine:computer.surgery - ## On GitLab Instructions for getting GitLab access can be found on the [sign-in][sign-in] @@ -28,6 +13,7 @@ like to report an issue, feel free to report it in the Matrix room at [#grapevine:computer.surgery][room]; someone with GitLab access can open an issue on your behalf. +[room]: https://matrix.to/#/#grapevine:computer.surgery [sign-in]: https://gitlab.computer.surgery/users/sign_in ## Information about a vulnerability diff --git a/book/introduction.md b/book/introduction.md index 055ee6b8..edebd33b 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -6,6 +6,21 @@ Grapevine is a [Matrix][matrix] homeserver that was originally forked from [matrix]: https://matrix.org/ [conduit]: https://gitlab.com/famedly/conduit/-/tree/v0.7.0?ref_type=tags +## Chat with us + +Currently, the Matrix room at [#grapevine:computer.surgery][room] serves +multiple purposes: + +* General discussion about the project, such as answering questions about it +* Reporting issues with Grapevine, if getting GitLab access is too much trouble + for you +* Providing support to users running Grapevine +* Discussing the development of Grapevine + +If you'd like to engage in or observe any of those things, please join! + +[room]: https://matrix.to/#/#grapevine:computer.surgery + ## Goals Our goal is to provide a robust and reliable Matrix homeserver implementation. From 42adad330e6e05b229ffbe8288ead8b67d1fd24a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 3 Oct 2024 12:40:50 -0700 Subject: [PATCH 507/617] move security info to its own page This makes it easier to find. Also sort the maintainers list while I'm here. --- book/SUMMARY.md | 1 + book/contributing.md | 18 ------------------ book/contributing/security.md | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 18 deletions(-) create mode 100644 book/contributing/security.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index de2bd079..089a9ad7 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -5,3 +5,4 @@ * [Migration to/from Conduit](./migration.md) * [Changelog](./changelog.md) * [Contributing](./contributing.md) + * [Coordinated vulnerability disclosure](./contributing/security.md) diff --git a/book/contributing.md b/book/contributing.md index 8eb5bdd4..286cc3c3 100644 --- a/book/contributing.md +++ b/book/contributing.md @@ -1,7 +1,5 @@ # Contributing -## On GitLab - Instructions for getting GitLab access can be found on the [sign-in][sign-in] page. @@ -15,19 +13,3 @@ issue on your behalf. [room]: https://matrix.to/#/#grapevine:computer.surgery [sign-in]: https://gitlab.computer.surgery/users/sign_in - -## Information about a vulnerability - -If you find a security vulnerability in Grapevine, please privately report it to -the Grapevine maintainers in one of the following ways: - -* Open a GitLab issue that's marked as confidential -* Create a private, invite-only, E2EE Matrix room and invite the following - users: - * `@olivia:computer.surgery` - * `@charles:computer.surgery` - * `@xiretza:xiretza.xyz` - -If the maintainers determine that the vulnerability is shared with Conduit or -other forks, we'll work with their teams to ensure that all affected projects -can release a fix at the same time. diff --git a/book/contributing/security.md b/book/contributing/security.md new file mode 100644 index 00000000..f54e0567 --- /dev/null +++ b/book/contributing/security.md @@ -0,0 +1,15 @@ +# Coordinated vulnerability disclosure + +If you find a security vulnerability in Grapevine, please privately report it to +the Grapevine maintainers in one of the following ways: + +* Open a GitLab issue that's marked as confidential +* Create a private, invite-only, E2EE Matrix room and invite the following + users: + * `@charles:computer.surgery` + * `@olivia:computer.surgery` + * `@xiretza:xiretza.xyz` + +If the maintainers determine that the vulnerability is shared with Conduit or +other forks, we'll work with their teams to ensure that all affected projects +can release a fix at the same time. From 94c1a39437764a69bcf4dd5fd392b5748400b9a0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 3 Oct 2024 12:54:23 -0700 Subject: [PATCH 508/617] add a style guide --- book/SUMMARY.md | 1 + book/contributing/style-guide.md | 148 +++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 book/contributing/style-guide.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 089a9ad7..cc933b8f 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -6,3 +6,4 @@ * [Changelog](./changelog.md) * [Contributing](./contributing.md) * [Coordinated vulnerability disclosure](./contributing/security.md) + * [Style guide](./contributing/style-guide.md) diff --git a/book/contributing/style-guide.md b/book/contributing/style-guide.md new file mode 100644 index 00000000..957a13a1 --- /dev/null +++ b/book/contributing/style-guide.md @@ -0,0 +1,148 @@ +# Style guide + +It is recommended that contributors follow this guide to minimize nitpicking on +their merge requests. + +However, this guide is not set in stone. It is open to changes as new patterns +emerge, requirements change, compelling arguments are made, and so on. The goal +is to document the existing style so it can be applied consistently, not to +ensure the style never changes. + +## Merge requests + +When updating a merge request branch, use `git rebase`; do not create merge +commits to merge other branches into a merge request branch. + +**Why?** This keeps the history simpler, and lacking merge commits makes it +easier to revert any individual commit. + +## Commit messages + +[Here's a good article][git-commit] on how to write good commit messages in +general. + +Specifically for this project: + +* Capitalizing the first letter is not required. +* It is recommended to avoid "conventional commits", as they take away from the + very limited subject line length, and will not be processed in an automated + fashion anyway. + +**Why?** The linked article explains why this is good practice. + +[git-commit]: https://cbea.ms/git-commit/ + +## Structuring commits + +Try to structure each commit so that it falls into one of the following +categories: + +* Refactoring, with no behavior change. +* Changing behavior, with no refactoring. +* Removing behavior, with no refactoring. +* Adding behavior, with no refactoring. +* Rewriting something completely. It is rare that these kinds of commits are + warranted. + +If you find yourself wanting to use the word "and" in the commit's subject line, +it should probably be broken into multiple commits. + +During code review, it's common to get feedback requesting changes to your +commits. To apply this feedback, do not make and push a new commit containing +the requested change. Instead, include the requested change in the commit of +yours that gave rise to the suggestion. If you are unfamiliar with rewriting +history in git, [this website][git-rebase] is a great tutorial. + +**Why?** Small, targeted, and well-explained commits make it easier for +reviewers to verify that a change has its intended effect. Or, for someone +running `git bisect` to find a more granular answer to why their test began +failing. + +[git-rebase]: https://git-rebase.io/ + +## `mod`/`use` order + +`mod` and `use` statements should appear in the following order, separated by +a blank line: + +1. `use` statements referring to `std`, `alloc`, or `core`, if any. +2. `use` statements referring to other crates, if any. +3. `use` statements referring to `super` or `crate`, if any. +4. Macro definitions that need to be accessible from child modules, if any. +5. `mod` statements, if any. +6. `use` statements referring to modules declared by the above `mod` statements, + if any. + +`rust-analyzer` and `rustfmt` automate most of this except points 4 and 5. + +**Why?** Consistency is good. + +## Testing + +When writing tests, be sure to keep the contents of [this article][cargo-test] +in mind. Especially, keeping Cargo unit tests in a dedicated tests file +(mentioned towards the end of the article). + +**Why?** The linked article explains why this is good practice. + +[cargo-test]: https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html + +## Tracing + +`tracing` events should: + +1. Start with a capital letter (when applicable). + +`tracing` events should not: + +1. End with punctuation. +2. Interpolate values into the event's message. + * Instead, add those values as structured fields. + +**Why?** Consistency is good. Also, interpolating values into the event message +essentially defeats the point of structured logging. + +### Examples + +#### 1 + +```rust,ignore +// This does not conform because it does not start with a capital letter. +info!("started pentametric fan"); + +// Do this instead: +info!("Started pentametric fan"); +``` + +#### 2 + +```rust,ignore +// This does not conform because it ends with punctuation. +info!("Started pentametric fan."); + +// Do this instead: +info!("Started pentametric fan"); +``` + +#### 3 + +```rust,ignore +// This does not conform because it interpolates values into the event's +// message. +warn!("Noticed {} discombobulated waneshafts", count); + +// Do this instead: +warn!(count, "Noticed discombobulated waneshafts"); +``` + +## Services + +Services are abstraction units that live inside the `src/service` directory. + +Calling service constructors must not cause side effects, with a few exceptions: + +* Database reads. +* Local filesystem reads. + +**Why?** This restriction makes it possible to implement subcommands that run +"offline" that reuse service code. From 1e050c898367d273262b99f026291655c426871d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 14:35:53 -0800 Subject: [PATCH 509/617] expose static binaries in gitlab artifacts again --- .gitignore | 4 +++- .gitlab-ci.yml | 4 ++++ bin/job | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4fba1aea..24b746e7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,10 @@ target # Nix artifacts result* -# GitLab CI cache +# GitLab CI artifacts /.gitlab-ci.d +/grapevine-static-aarch64-unknown-linux-musl +/grapevine-static-x86_64-unknown-linux-musl # mdbook artifacts /public diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9460a94e..7e271a82 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,6 +63,10 @@ artifacts: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: - direnv exec . job artifacts + artifacts: + paths: + - grapevine-static-aarch64-unknown-linux-musl + - grapevine-static-x86_64-unknown-linux-musl pages: stage: deploy diff --git a/bin/job b/bin/job index ca4dccc5..3726f92d 100755 --- a/bin/job +++ b/bin/job @@ -4,6 +4,20 @@ job_artifacts() ( set -euo pipefail nix-build-and-cache packages + + # Subsequent `nix build` calls should be fast because the above line ensures + # the packages have been built already. + + packages=( + static-aarch64-unknown-linux-musl + static-x86_64-unknown-linux-musl + ) + + for x in "${packages[@]}"; do + nix build ".#$x" + cp result/bin/grapevine grapevine-"$x" + chmod u+w grapevine-"$x" + done ) job_ci() ( From 67f0689d738f00c8dcfb19a6c9e9e5cf0488fbfc Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Wed, 11 Dec 2024 14:50:04 -0800 Subject: [PATCH 510/617] move media file read/write logic to a helper function --- src/service/media.rs | 64 ++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 0d231c67..4129649d 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -63,9 +63,7 @@ impl Service { // Width, Height = 0 if it's not a thumbnail let key = self.db.create_file_metadata(mxc, 0, 0, &meta)?; - let path = services().globals.get_media_file(&key); - let mut f = File::create(path).await?; - f.write_all(file).await?; + self.write_content(&key, file).await?; Ok(meta) } @@ -86,10 +84,7 @@ impl Service { }; let key = self.db.create_file_metadata(mxc, width, height, &meta)?; - let path = services().globals.get_media_file(&key); - let mut f = File::create(path).await?; - f.write_all(file).await?; - + self.write_content(&key, file).await?; Ok(meta) } @@ -100,15 +95,7 @@ impl Service { mxc: OwnedMxcUri, ) -> Result)>> { if let Some((meta, key)) = self.db.search_file_metadata(mxc, 0, 0)? { - let path = services().globals.get_media_file(&key); - let mut file_data = Vec::new(); - let Ok(mut file) = File::open(path).await else { - return Ok(None); - }; - - file.read_to_end(&mut file_data).await?; - - Ok(Some((meta, file_data))) + Ok(self.read_content(&key).await?.map(|data| (meta, data))) } else { Ok(None) } @@ -370,11 +357,48 @@ impl Service { // again next time let thumbnail_key = self.db.create_file_metadata(mxc, width, height, &meta)?; - - let path = services().globals.get_media_file(&thumbnail_key); - let mut f = File::create(path).await?; - f.write_all(&thumbnail_bytes).await?; + self.write_content(&thumbnail_key, &thumbnail_bytes).await?; Ok(Some((meta, thumbnail_bytes.clone()))) } + + /// Writes contents for a media file to the fs. + /// + /// If a file already existed with the specified key, it is replaced. + async fn write_content( + &self, + key: &MediaFileKey, + data: &[u8], + ) -> Result<()> { + let path = services().globals.get_media_file(key); + let mut file = File::create(path).await?; + + file.write_all(data).await?; + + Ok(()) + } + + /// Returns the contents of a media file, read from the fs. + /// + /// If the file cannot be opened, returns `Ok(None)`. This is needed because + /// before media deletion admin commands were implemented, the only way to + /// delete abuse media was to remove the associated files from the fs. This + /// leaves the db in an inconsistent state, where media keys exist in the db + /// but their content files do not. We want to return `M_NOT_YET_UPLOADED` + /// in this case rather than the `M_UNKNOWN` we would normally use for db + /// consistency problems. + async fn read_content( + &self, + key: &MediaFileKey, + ) -> Result>> { + let path = services().globals.get_media_file(key); + let Ok(mut file) = File::open(path).await else { + return Ok(None); + }; + + let mut data = Vec::new(); + file.read_to_end(&mut data).await?; + + Ok(Some(data)) + } } From f216112455cb297c59cc4964839005f6c94716bd Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Fri, 13 Dec 2024 01:18:09 -0800 Subject: [PATCH 511/617] don't treat media file open errors other than NotFound as missing media For example, we want to return M_UNKNOWN and propagate the error if somebody set up their database directory permissions wrong. --- src/service/media.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index 4129649d..f892271c 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -392,8 +392,12 @@ impl Service { key: &MediaFileKey, ) -> Result>> { let path = services().globals.get_media_file(key); - let Ok(mut file) = File::open(path).await else { - return Ok(None); + let mut file = match File::open(path).await { + Ok(file) => file, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(None) + } + Err(e) => return Err(e.into()), }; let mut data = Vec::new(); From 795ce4251843df95401084b50281a93eac353b8f Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Wed, 11 Dec 2024 15:07:06 -0800 Subject: [PATCH 512/617] return M_NOT_YET_UPLOADED when backing files are missing for a thumbnail This was done for fetching original media files in c70cfd3d254924d42b2c3b4a7c271526bd5c29ba, but the change for thumbnails was missed. --- src/service/media.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/service/media.rs b/src/service/media.rs index f892271c..9b965caa 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -314,11 +314,7 @@ impl Service { self.db.search_file_metadata(mxc.clone(), width, height)? { debug!("Using saved thumbnail"); - let path = services().globals.get_media_file(&key); - let mut file = Vec::new(); - File::open(path).await?.read_to_end(&mut file).await?; - - return Ok(Some((meta, file.clone()))); + return Ok(self.read_content(&key).await?.map(|file| (meta, file))); } let Some((meta, key)) = @@ -328,9 +324,10 @@ impl Service { return Ok(None); }; - let path = services().globals.get_media_file(&key); - let mut file = Vec::new(); - File::open(path).await?.read_to_end(&mut file).await?; + let Some(file) = self.read_content(&key).await? else { + debug!("Original image not found, can't generate thumbnail"); + return Ok(None); + }; debug!("Generating thumbnail"); let thumbnail_result = { From 491518d2f37f477bf55cf9944f73c3de1c5d5016 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Wed, 11 Dec 2024 17:35:47 -0800 Subject: [PATCH 513/617] add changelog entry for M_NOT_YET_UPLOADED when media is missing in fs The first half of this change happened in !55, but we never added a changelog entry until now. --- book/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index e78fcf96..70af61f4 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -213,6 +213,13 @@ This will be the first release of Grapevine since it was forked from Conduit was in backoff would increment the backoff delay further, leading to a positive feedback loop. ([!149](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/149)) +24. Return 504 M_NOT_YET_UPLOADED instead of 500 M_UNKNOWN when a media file is + present in the database but the contents are missing in the filesystem. + Removing media from the filesystem was the only way to delete media before + [!99](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/99), + so this situation is common. + ([!55](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/55)) + ([!153](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/153)) ### Added From f831dfd1f1e3e986813b176d0eba720582ba053d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 19:51:51 -0800 Subject: [PATCH 514/617] add "Installing" chapter to the book --- book/SUMMARY.md | 1 + book/installing.md | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 book/installing.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index cc933b8f..a696c9f4 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -4,6 +4,7 @@ * [Code of conduct](./code-of-conduct.md) * [Migration to/from Conduit](./migration.md) * [Changelog](./changelog.md) +* [Installing](./installing.md) * [Contributing](./contributing.md) * [Coordinated vulnerability disclosure](./contributing/security.md) * [Style guide](./contributing/style-guide.md) diff --git a/book/installing.md b/book/installing.md new file mode 100644 index 00000000..7de21af9 --- /dev/null +++ b/book/installing.md @@ -0,0 +1,4 @@ +# Installing + +This chapter will explain how to start running a Grapevine instance for the +first time. From 28a4483c6e631c96df0b5ae0fb551a1e0e7b3674 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 17:15:34 -0800 Subject: [PATCH 515/617] move conduit migration to installing section --- book/SUMMARY.md | 2 +- book/installing/migrating-conduit.md | 70 ++++++++++++++++++++++++++++ book/migration.md | 69 --------------------------- 3 files changed, 71 insertions(+), 70 deletions(-) create mode 100644 book/installing/migrating-conduit.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index a696c9f4..136c331d 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -2,9 +2,9 @@ * [Introduction](./introduction.md) * [Code of conduct](./code-of-conduct.md) -* [Migration to/from Conduit](./migration.md) * [Changelog](./changelog.md) * [Installing](./installing.md) + * [Migrating to/from Conduit](./installing/migrating-conduit.md) * [Contributing](./contributing.md) * [Coordinated vulnerability disclosure](./contributing/security.md) * [Style guide](./contributing/style-guide.md) diff --git a/book/installing/migrating-conduit.md b/book/installing/migrating-conduit.md new file mode 100644 index 00000000..f847c7af --- /dev/null +++ b/book/installing/migrating-conduit.md @@ -0,0 +1,70 @@ +# Migrating to/from Conduit + +Before migrating a Conduit instance to Grapevine, make sure to read through +all of the breaking changes listed in [the changelog](../changelog.md). + +In order to migrate an existing Conduit instance to/from Grapevine, the +Grapevine config must include `conduit_compat = true`. This parameter cannot +currently be modified after creating the database for the first time, so make +sure to set it when creating a fresh Grapevine instance that you may want to +migrate to a different implementation in the future. + +## Config + +Grapevine includes several breaking changes to the config schema. We don't +currently have docs on how to migrate an existing config. All breaking config +changes are mentioned in [the changelog](../changelog.md), so the best current +option is to read through those. Feel free to ask for config migration help in +[#grapevine:computer.surgery][room] if something is unclear. + +We plan to add [a config migration tool][config-migration-issue] to support +automatically migrating existing configs to the new schema. + +[room]: https://matrix.to/#/#grapevine:computer.surgery +[config-migration-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/38 + +## Database + +Grapevine is currently compatible with the Conduit 0.7.0 database format. It is +still possible to migrate to or from some newer Conduit versions, but it may +require manual intervention or break some functionality. + +We plan to add [a migration tool][db-compatibility-mr] to support cleanly +migrating to or from Conduit versions we are not internally compatible with. + +| Is migrating from | to | workable? | +|-|-|-| +| Conduit <=0.8.0 | Grapevine | Yes | +| Conduit 0.9.0 | Grapevine | [Yes, with caveats](#conduit-090-to-grapevine) | +| Grapevine | Conduit 0.7.0 | Yes | +| Grapevine | Conduit 0.8.0 | [Yes, with caveats](#grapevine-to-conduit-080-or-090) | +| Grapevine | Conduit 0.9.0 | [Yes, with caveats](#grapevine-to-conduit-080-or-090) | + +[db-compatibility-mr]: https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/85 + +### Conduit 0.9.0 to Grapevine + +Conduit 0.9.0 includes [a database migration][conduit-db-16-migration] that +modifies data that Grapevine doesn't read. Grapevine does not currently +recognize the new database schema version, and will fail to start against +a Conduit 0.9.0 database. Grapevine can start and run without issues if the +version recorded in the databse is rolled back from 16 to 13. It is possible to +do this by editing the database manually, or by modifying Grapevine to change +the version. [This patch][conduit-db-16-patch] is an example of the latter +approach. + +[conduit-db-16-migration]: https://gitlab.com/famedly/conduit/-/blob/f8d7ef04e664580e882bac852877b68e7bd3ab1e/src/database/mod.rs#L945 +[conduit-db-16-patch]: https://gitlab.computer.surgery/matrix/grapevine/-/commit/fdaa30f0d670c6f04f4e6be5d193f9146d179d95 + +### Grapevine to Conduit 0.8.0 or 0.9.0 + +Conduit 0.8.0 added [a new database table][alias_userid-commit] to track which +users created each room alias. Grapevine does not write to this table, so it is +not possible to delete aliases created in Grapevine through the normal +client-server API after migrating to Conduit 0.8.0. It is possible to delete +aliases with the `remove-alias` admin command. Note that this issue also applies +to migrations from Conduit <0.8.0 to Conduit 0.8.0. + +There are no additional known issues when migrating to Conduit 0.9.0. + +[alias_userid-commit]: https://gitlab.com/famedly/conduit/-/commit/144d548ef739324ca97db12e8cada60ca3e43e09 diff --git a/book/migration.md b/book/migration.md index 6ec2f833..432f4e47 100644 --- a/book/migration.md +++ b/book/migration.md @@ -1,70 +1 @@ # Migration to/from Conduit - -Before migrating a Conduit instance to Grapevine, make sure to read through -all of the breaking changes listed in [the changelog](./changelog.md). - -In order to migrate an existing Conduit instance to/from Grapevine, the -Grapevine config must include `conduit_compat = true`. This parameter cannot -currently be modified after creating the database for the first time, so make -sure to set it when creating a fresh Grapevine instance that you may want to -migrate to a different implementation in the future. - -## Config - -Grapevine includes several breaking changes to the config schema. We don't -currently have docs on how to migrate an existing config. All breaking config -changes are mentioned in [the changelog](./changelog.md), so the best current -option is to read through those. Feel free to ask for config migration help in -[#grapevine:computer.surgery][room] if something is unclear. - -We plan to add [a config migration tool][config-migration-issue] to support -automatically migrating existing configs to the new schema. - -[room]: https://matrix.to/#/#grapevine:computer.surgery -[config-migration-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/38 - -## Database - -Grapevine is currently compatible with the Conduit 0.7.0 database format. It is -still possible to migrate to or from some newer Conduit versions, but it may -require manual intervention or break some functionality. - -We plan to add [a migration tool][db-compatibility-mr] to support cleanly -migrating to or from Conduit versions we are not internally compatible with. - -| Is migrating from | to | workable? | -|-|-|-| -| Conduit <=0.8.0 | Grapevine | Yes | -| Conduit 0.9.0 | Grapevine | [Yes, with caveats](#conduit-090-to-grapevine) | -| Grapevine | Conduit 0.7.0 | Yes | -| Grapevine | Conduit 0.8.0 | [Yes, with caveats](#grapevine-to-conduit-080-or-090) | -| Grapevine | Conduit 0.9.0 | [Yes, with caveats](#grapevine-to-conduit-080-or-090) | - -[db-compatibility-mr]: https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/85 - -### Conduit 0.9.0 to Grapevine - -Conduit 0.9.0 includes [a database migration][conduit-db-16-migration] that -modifies data that Grapevine doesn't read. Grapevine does not currently -recognize the new database schema version, and will fail to start against -a Conduit 0.9.0 database. Grapevine can start and run without issues if the -version recorded in the databse is rolled back from 16 to 13. It is possible to -do this by editing the database manually, or by modifying Grapevine to change -the version. [This patch][conduit-db-16-patch] is an example of the latter -approach. - -[conduit-db-16-migration]: https://gitlab.com/famedly/conduit/-/blob/f8d7ef04e664580e882bac852877b68e7bd3ab1e/src/database/mod.rs#L945 -[conduit-db-16-patch]: https://gitlab.computer.surgery/matrix/grapevine/-/commit/fdaa30f0d670c6f04f4e6be5d193f9146d179d95 - -### Grapevine to Conduit 0.8.0 or 0.9.0 - -Conduit 0.8.0 added [a new database table][alias_userid-commit] to track which -users created each room alias. Grapevine does not write to this table, so it is -not possible to delete aliases created in Grapevine through the normal -client-server API after migrating to Conduit 0.8.0. It is possible to delete -aliases with the `remove-alias` admin command. Note that this issue also applies -to migrations from Conduit <0.8.0 to Conduit 0.8.0. - -There are no additional known issues when migrating to Conduit 0.9.0. - -[alias_userid-commit]: https://gitlab.com/famedly/conduit/-/commit/144d548ef739324ca97db12e8cada60ca3e43e09 From 1808ad66f4db2d8eff506a952837e90ab94d1559 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 17:25:54 -0800 Subject: [PATCH 516/617] document supported targets This is also a good spot to link to the pre-built binaries. And since I did that, I can also remove the bit about not publishing binary builds from the introduction section. --- book/SUMMARY.md | 1 + book/installing.md | 3 +++ book/installing/supported-targets.md | 37 ++++++++++++++++++++++++++++ book/introduction.md | 10 +++----- 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 book/installing/supported-targets.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 136c331d..34bdc3d3 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -4,6 +4,7 @@ * [Code of conduct](./code-of-conduct.md) * [Changelog](./changelog.md) * [Installing](./installing.md) + * [Supported targets](./installing/supported-targets.md) * [Migrating to/from Conduit](./installing/migrating-conduit.md) * [Contributing](./contributing.md) * [Coordinated vulnerability disclosure](./contributing/security.md) diff --git a/book/installing.md b/book/installing.md index 7de21af9..a784ed3e 100644 --- a/book/installing.md +++ b/book/installing.md @@ -2,3 +2,6 @@ This chapter will explain how to start running a Grapevine instance for the first time. + +**Note:** Pre-built binaries can be found in the [**Supported +targets**](./installing/supported-targets.md) subchapter. diff --git a/book/installing/supported-targets.md b/book/installing/supported-targets.md new file mode 100644 index 00000000..b099dd1d --- /dev/null +++ b/book/installing/supported-targets.md @@ -0,0 +1,37 @@ +# Supported targets + + + +| Architecture | Vendor | OS | libc | Linkage | Tier | Availability[^1], [^2] | +|-|-|-|-|-|-|-| +| aarch64 | unknown | linux | musl | static | 2 | Nix, [Download](https://gitlab.computer.surgery/api/v4/projects/matrix%2Fgrapevine/jobs/artifacts/main/raw/grapevine-static-aarch64-unknown-linux-musl?job=artifacts) | +| x86_64 | unknown | linux | glibc | dynamic | 1 | Nix | +| x86_64 | unknown | linux | musl | static | 2 | Nix, [Download](https://gitlab.computer.surgery/api/v4/projects/matrix%2Fgrapevine/jobs/artifacts/main/raw/grapevine-static-x86_64-unknown-linux-musl?job=artifacts) | + +[^1]: All download links refer to the latest build of the `main` branch. +[^2]: All targets can theoretically also be built from source without Nix. + However, this may require spending several hours debugging build systems. + +## Target tiers + +The "Tier" column for each target indicates the level of support that target +has. Below is an explanation of what each tier means. + +### Tier 1 + +Tier 1 targets are guaranteed to: + +* Build +* Pass the test suite + +### Tier 2 + +Tier 2 targets are guaranteed to: + +* Build + +## Unsupported targets + +Targets that don't appear in the table at the top of this page are unsupported. +At any given time, such targets may or may not build, and may or may not pass +the test suite. diff --git a/book/introduction.md b/book/introduction.md index edebd33b..4f02e389 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -91,12 +91,10 @@ the importance of the reliability of a project like this. ## Can I use it? Theoretically yes, but it's not in a good state for general users yet. There -[isn't very much user-facing docs yet][docs-issue], we -[don't have releases yet][releases-issue], we don't currently publish any binary -builds, and there are several breaking changes to the conduit config that -haven't been documented in detail yet. If building from source and figuring out -the config by reading source code/commit messages doesn't scare you away, go -for it! +[isn't very much user-facing docs yet][docs-issue], we [don't have releases +yet][releases-issue], and there are several breaking changes to the conduit +config that haven't been documented in detail yet. If these issues don't scare +you away, go for it! If you use nixos, [here's an example][nixos-example]. From 5c4d7bbe372d5978c0791e24a6aac6fe8ea15261 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 20:10:04 -0800 Subject: [PATCH 517/617] rewrite "Can I use it?" section --- book/introduction.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/book/introduction.md b/book/introduction.md index 4f02e389..df2fbddf 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -90,13 +90,16 @@ the importance of the reliability of a project like this. ## Can I use it? -Theoretically yes, but it's not in a good state for general users yet. There -[isn't very much user-facing docs yet][docs-issue], we [don't have releases -yet][releases-issue], and there are several breaking changes to the conduit -config that haven't been documented in detail yet. If these issues don't scare -you away, go for it! +Theoretically yes, but it's not ready for general use yet, because: -If you use nixos, [here's an example][nixos-example]. +* [There aren't any releases][releases-issue]. +* [There isn't very much user-facing documentation][docs-issue]. +* There have been several additions and breaking changes to the configuration + file format that haven't been documented in detail. This means you'll need to + read the source code to figure out what all the options are and what they do. + +If these issues don't scare you away, go for it! (And if you use NixOS, [here's +an example][nixos-example].) [docs-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/21 [releases-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/18 From 8537c0e8ac3eb388500587b035008e5f98204a4b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 11 Dec 2024 20:12:19 -0800 Subject: [PATCH 518/617] rearrange introduction I think it's most important for people to read the "Chat with us", "Can I use it?" and "Expectations management" sections, though I'm not sure what the best ordering is. This is probably fine. --- book/introduction.md | 50 +++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/book/introduction.md b/book/introduction.md index df2fbddf..e03e4e9e 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -21,6 +21,30 @@ If you'd like to engage in or observe any of those things, please join! [room]: https://matrix.to/#/#grapevine:computer.surgery +## Can I use it? + +Theoretically yes, but it's not ready for general use yet, because: + +* [There aren't any releases][releases-issue]. +* [There isn't very much user-facing documentation][docs-issue]. +* There have been several additions and breaking changes to the configuration + file format that haven't been documented in detail. This means you'll need to + read the source code to figure out what all the options are and what they do. + +If these issues don't scare you away, go for it! (And if you use NixOS, [here's +an example][nixos-example].) + +[docs-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/21 +[releases-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/18 +[nixos-example]: https://gitlab.computer.surgery/charles/servy-fleet/-/blob/main/config/grapevine/default.nix + +## Expectations management + +This project is run and maintained entirely by volunteers who are doing their +best. Additionally, due to our goals, the development of new features may be +slower than alternatives. We find this to be an acceptable tradeoff considering +the importance of the reliability of a project like this. + ## Goals Our goal is to provide a robust and reliable Matrix homeserver implementation. @@ -81,32 +105,6 @@ We require at least 1 approving code review from a maintainer[^2] before changes can be merged. This number may increase in the future as the list of maintainers grows. -## Expectations management - -This project is run and maintained entirely by volunteers who are doing their -best. Additionally, due to our goals, the development of new features may be -slower than alternatives. We find this to be an acceptable tradeoff considering -the importance of the reliability of a project like this. - -## Can I use it? - -Theoretically yes, but it's not ready for general use yet, because: - -* [There aren't any releases][releases-issue]. -* [There isn't very much user-facing documentation][docs-issue]. -* There have been several additions and breaking changes to the configuration - file format that haven't been documented in detail. This means you'll need to - read the source code to figure out what all the options are and what they do. - -If these issues don't scare you away, go for it! (And if you use NixOS, [here's -an example][nixos-example].) - -[docs-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/21 -[releases-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/18 -[nixos-example]: https://gitlab.computer.surgery/charles/servy-fleet/-/blob/main/config/grapevine/default.nix - ---- - [^1]: A "maintainer" is someone who has the ability to close issues opened by someone else and merge changes. [^2]: A maintainer approving their own change doesn't count. From c748c7c188c0c6343685b32e2807ce1e612aac86 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 20 Jan 2025 12:04:14 -0800 Subject: [PATCH 519/617] return M_BAD_ALIAS when trying to set non-existent canonical aliases This is the error code named in the spec. --- book/changelog.md | 5 +++++ src/api/client_server/state.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index 70af61f4..6dfe833b 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -220,6 +220,11 @@ This will be the first release of Grapevine since it was forked from Conduit so this situation is common. ([!55](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/55)) ([!153](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/153)) +25. Return 400 M_BAD_ALIAS from + [PUT /_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}](https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey) + instead of 400 M_FORBIDDEN when trying to set a canonical alias that does + not exist. + ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) ### Added diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index b0e5809a..a9a6c0fc 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -232,7 +232,7 @@ async fn send_state_event_for_key_helper( .is_none() { return Err(Error::BadRequest( - ErrorKind::forbidden(), + ErrorKind::BadAlias, "You are only allowed to send canonical_alias events when \ it's aliases already exists", )); From 051221c0ab05830f7f93daa0060ad4c573918059 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 20 Jan 2025 12:11:41 -0800 Subject: [PATCH 520/617] validate schema of new canonical alias events sent by clients Previously, we would only attempt to validate the aliases in the event content if we were able to parse the event, and would silently allow it otherwise. --- book/changelog.md | 3 ++ src/api/client_server/state.rs | 56 +++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 6dfe833b..0ee07aa1 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -225,6 +225,9 @@ This will be the first release of Grapevine since it was forked from Conduit instead of 400 M_FORBIDDEN when trying to set a canonical alias that does not exist. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) +26. Validate schema of new `m.room.canonical_alias` event sent by clients, + rather than silently allowing any contents if the event can't be parsed. + ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) ### Added diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index a9a6c0fc..54621603 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -210,33 +210,39 @@ async fn send_state_event_for_key_helper( ) -> Result> { let sender_user = sender; - // TODO: Review this check, error if event is unparsable, use event type, - // allow alias if it previously existed - if let Ok(canonical_alias) = serde_json::from_str::< - RoomCanonicalAliasEventContent, - >(json.json().get()) - { - let mut aliases = canonical_alias.alt_aliases.clone(); + // TODO: allow alias if it previously existed + if event_type == &StateEventType::RoomCanonicalAlias { + if let Ok(canonical_alias) = serde_json::from_str::< + RoomCanonicalAliasEventContent, + >(json.json().get()) + { + let mut aliases = canonical_alias.alt_aliases.clone(); - if let Some(alias) = canonical_alias.alias { - aliases.push(alias); - } - - for alias in aliases { - if alias.server_name() != services().globals.server_name() - || services() - .rooms - .alias - .resolve_local_alias(&alias)? - .filter(|room| room == room_id) - .is_none() - { - return Err(Error::BadRequest( - ErrorKind::BadAlias, - "You are only allowed to send canonical_alias events when \ - it's aliases already exists", - )); + if let Some(alias) = canonical_alias.alias { + aliases.push(alias); } + + for alias in aliases { + if alias.server_name() != services().globals.server_name() + || services() + .rooms + .alias + .resolve_local_alias(&alias)? + .filter(|room| room == room_id) + .is_none() + { + return Err(Error::BadRequest( + ErrorKind::BadAlias, + "You are only allowed to send canonical_alias events \ + when it's aliases already exists", + )); + } + } + } else { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "m.room.canonical_alias event did not match expected schema", + )); } } From 50c1e77cd6e187016566da566b6e39f79a503ae2 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 20 Jan 2025 13:43:11 -0800 Subject: [PATCH 521/617] factor canonical alias event validation into a helper function Not all the semantics from the spec quote in the doc comment are implemented yet. --- src/api/client_server/state.rs | 87 +++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 54621603..85cd2a61 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -210,40 +210,8 @@ async fn send_state_event_for_key_helper( ) -> Result> { let sender_user = sender; - // TODO: allow alias if it previously existed if event_type == &StateEventType::RoomCanonicalAlias { - if let Ok(canonical_alias) = serde_json::from_str::< - RoomCanonicalAliasEventContent, - >(json.json().get()) - { - let mut aliases = canonical_alias.alt_aliases.clone(); - - if let Some(alias) = canonical_alias.alias { - aliases.push(alias); - } - - for alias in aliases { - if alias.server_name() != services().globals.server_name() - || services() - .rooms - .alias - .resolve_local_alias(&alias)? - .filter(|room| room == room_id) - .is_none() - { - return Err(Error::BadRequest( - ErrorKind::BadAlias, - "You are only allowed to send canonical_alias events \ - when it's aliases already exists", - )); - } - } - } else { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "m.room.canonical_alias event did not match expected schema", - )); - } + validate_canonical_alias_event(room_id, json.cast_ref())?; } let room_token = services() @@ -271,3 +239,56 @@ async fn send_state_event_for_key_helper( Ok(event_id) } + +/// Checks that a new `m.room.canonical_alias` event is valid, by the spec's +/// requirements. +/// +/// From the [spec]: +/// +/// > If the event type being sent is m.room.canonical_alias servers SHOULD +/// > ensure that any new aliases being listed in the event are valid per their +/// > grammar/syntax and that they point to the room ID where the state event +/// > is to be sent. Servers do not validate aliases which are being removed or +/// > are already present in the state event. +/// +/// [spec]: https://spec.matrix.org/v1.13/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey +fn validate_canonical_alias_event( + room_id: &RoomId, + json: &Raw, +) -> Result<()> { + // TODO: allow alias if it previously existed + if let Ok(canonical_alias) = serde_json::from_str::< + RoomCanonicalAliasEventContent, + >(json.json().get()) + { + let mut aliases = canonical_alias.alt_aliases.clone(); + + if let Some(alias) = canonical_alias.alias { + aliases.push(alias); + } + + for alias in aliases { + if alias.server_name() != services().globals.server_name() + || services() + .rooms + .alias + .resolve_local_alias(&alias)? + .filter(|room| room == room_id) + .is_none() + { + return Err(Error::BadRequest( + ErrorKind::BadAlias, + "You are only allowed to send canonical_alias events when \ + it's aliases already exists", + )); + } + } + } else { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "m.room.canonical_alias event did not match expected schema", + )); + } + + Ok(()) +} From 29d8fbaefa29d33ee341854fc6c1c0c5ccd970ab Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 20 Jan 2025 15:46:36 -0800 Subject: [PATCH 522/617] only validate canonical aliases that are new Previously we required every alias in a canonical alias event sent by a client to be valid, and would only validate local aliases. This prevented clients from adding/removing canonical aliases if there were existing remote or invalid aliases. --- book/changelog.md | 4 ++ src/api/client_server/state.rs | 67 ++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 0ee07aa1..5850e9ed 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -228,6 +228,10 @@ This will be the first release of Grapevine since it was forked from Conduit 26. Validate schema of new `m.room.canonical_alias` event sent by clients, rather than silently allowing any contents if the event can't be parsed. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) +27. Only validate canonical aliases that are new, rather than rather than + revalidating every alias. This makes it possible to add/remove aliases when + some of the existing aliases cannot be validated. + ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) ### Added diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index 85cd2a61..cfa48ef0 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; use ruma::{ api::client::{ @@ -10,9 +10,10 @@ use ruma::{ AnyStateEventContent, StateEventType, }, serde::Raw, - EventId, RoomId, UserId, + EventId, RoomAliasId, RoomId, UserId, }; -use tracing::log::warn; +use serde::Deserialize; +use tracing::warn; use crate::{service::pdu::PduBuilder, services, Ar, Error, Ra, Result}; @@ -256,18 +257,60 @@ fn validate_canonical_alias_event( room_id: &RoomId, json: &Raw, ) -> Result<()> { - // TODO: allow alias if it previously existed - if let Ok(canonical_alias) = serde_json::from_str::< - RoomCanonicalAliasEventContent, - >(json.json().get()) + // Use a custom struct instead of RoomCanonicalAliasEventContent because we + // only want to validate the syntax of new aliases, so can't deserialize + // everything to OwnedRoomAliasId. + #[derive(Deserialize)] + struct Extract { + alias: Option, + #[serde(default)] + alt_aliases: Vec, + } + + // If the existing canonical alias event is invalid, treat it as if there + // are no existing aliases instead of erroring out. This allows users to + // fix a bad canonical alias event by sending a new one, but means that + // every alias in the new event will be revalidated. + let old_event = services() + .rooms + .state_accessor + .room_state_get(room_id, &StateEventType::RoomCanonicalAlias, "")? + .and_then(|old_event| { + serde_json::from_str::(old_event.content.get()) + .inspect_err(|error| { + warn!( + %error, + event_id=%old_event.event_id, + "Invalid canonical alias event in database" + ); + }) + .ok() + }); + + let old_aliases = if let Some(old_event) = &old_event { + old_event.alias.iter().chain(old_event.alt_aliases.iter()).collect() + } else { + HashSet::new() + }; + + if let Ok(canonical_alias) = + serde_json::from_str::(json.json().get()) { - let mut aliases = canonical_alias.alt_aliases.clone(); + let aliases = canonical_alias + .alias + .iter() + .chain(canonical_alias.alt_aliases.iter()); + let new_aliases = aliases.filter(|alias| !old_aliases.contains(alias)); - if let Some(alias) = canonical_alias.alias { - aliases.push(alias); - } + for alias in new_aliases { + let alias = RoomAliasId::parse(alias).map_err(|_| { + Error::BadRequest( + ErrorKind::InvalidParam, + "One or more aliases in m.room.canonical_alias event have \ + invalid syntax", + ) + })?; - for alias in aliases { if alias.server_name() != services().globals.server_name() || services() .rooms From 472f51c35094c5adf4bf734bc9d87557cab0b3fc Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 20 Jan 2025 16:53:08 -0800 Subject: [PATCH 523/617] allow adding canonical aliases from remote servers Like is mentioned in the comment, this isn't explicitly required by the spec, but it's reasonable and what synapse does. --- book/changelog.md | 2 ++ src/api/client_server/alias.rs | 2 ++ src/api/client_server/state.rs | 16 +++++------ src/service/rooms/alias.rs | 49 +++++++++++++++++++++++++++++++--- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 5850e9ed..eeb9fdf6 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -311,3 +311,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!121](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/121)) 25. Add configuration options to tune the value of each cache individually. ([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124)) +26. Allow adding canonical aliases from remote servers. + ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index c885d5e2..b2fbcf65 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -107,6 +107,8 @@ pub(crate) async fn get_alias_route( get_alias_helper(body.body.room_alias).await.map(Ra) } +// Can't use `services().rooms.alias.resolve_alias` because we also need the set +// of servers from the remote get_room_information request. pub(crate) async fn get_alias_helper( room_alias: OwnedRoomAliasId, ) -> Result { diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index cfa48ef0..2e6f3b13 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -212,7 +212,7 @@ async fn send_state_event_for_key_helper( let sender_user = sender; if event_type == &StateEventType::RoomCanonicalAlias { - validate_canonical_alias_event(room_id, json.cast_ref())?; + validate_canonical_alias_event(room_id, json.cast_ref()).await?; } let room_token = services() @@ -253,7 +253,7 @@ async fn send_state_event_for_key_helper( /// > are already present in the state event. /// /// [spec]: https://spec.matrix.org/v1.13/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey -fn validate_canonical_alias_event( +async fn validate_canonical_alias_event( room_id: &RoomId, json: &Raw, ) -> Result<()> { @@ -311,13 +311,11 @@ fn validate_canonical_alias_event( ) })?; - if alias.server_name() != services().globals.server_name() - || services() - .rooms - .alias - .resolve_local_alias(&alias)? - .filter(|room| room == room_id) - .is_none() + // The spec doesn't say explicitly that we should allow adding new + // remote canonical aliases, but it's reasonable behavior and what + // synapse does. + if services().rooms.alias.resolve_alias(&alias).await?.as_deref() + != Some(room_id) { return Err(Error::BadRequest( ErrorKind::BadAlias, diff --git a/src/service/rooms/alias.rs b/src/service/rooms/alias.rs index 0b934fe7..638d4d92 100644 --- a/src/service/rooms/alias.rs +++ b/src/service/rooms/alias.rs @@ -1,6 +1,7 @@ +use http::StatusCode; use ruma::{ - api::client::error::ErrorKind, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, - RoomId, UserId, + api::{client::error::ErrorKind, federation}, + OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, UserId, }; use crate::{services, Error, Result}; @@ -60,7 +61,7 @@ impl Service { self.db.remove_alias(alias) } - /// Looks up the roomid for the given alias. + /// Looks up the roomid for the given local alias. pub(crate) fn resolve_local_alias( &self, alias: &RoomAliasId, @@ -75,4 +76,46 @@ impl Service { ) -> Box> + 'a> { self.db.local_aliases_for_room(room_id) } + + /// Looks up the roomid for the given alias, fetching over federation if + /// remote. + pub(crate) async fn resolve_alias( + &self, + alias: &RoomAliasId, + ) -> Result> { + if alias.server_name() == services().globals.server_name() { + self.resolve_local_alias(alias) + } else { + self.resolve_remote_alias(alias).await + } + } + + /// Look up an alias on a remote server over federation. + async fn resolve_remote_alias( + &self, + alias: &RoomAliasId, + ) -> Result> { + let result = services() + .sending + .send_federation_request( + alias.server_name(), + federation::query::get_room_information::v1::Request { + room_alias: alias.to_owned(), + }, + ) + .await; + + match result { + Ok(response) => Ok(Some(response.room_id)), + // The spec only names the 404 status code explicitly, but matching + // on M_NOT_FOUND as well seems reasonable. + Err(Error::Federation(_, error)) + if error.status_code == StatusCode::NOT_FOUND + || error.error_kind() == Some(&ErrorKind::NotFound) => + { + Ok(None) + } + Err(error) => Err(error), + } + } } From 4cc390345a90db0f18f34815e6c5f196134cb8a8 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 16 Dec 2024 02:07:42 -0800 Subject: [PATCH 524/617] don't markdownlint ignored files I like to put a bunch of untracked stuff in a /scratch directory for each project, and then puth /scratch in my global gitignore. There are usually some markdown notes files in here that I don't care about style for. The previous markdownlint invokation didn't respect the global gitignore, making local 'engage' runs kinda useless due to false positives from the scratch dir. --- engage.toml | 6 +++++- nix/shell.nix | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/engage.toml b/engage.toml index 3e5d4417..94c2bd5e 100644 --- a/engage.toml +++ b/engage.toml @@ -48,7 +48,11 @@ script = "lychee --offline ." [[task]] name = "markdownlint" group = "lints" -script = "markdownlint ." +# don't just use 'markdownlint .' because it will lint files that are ignored by +# git +script = """ +git ls-files --cached --others --exclude-standard '*.md' | xargs markdownlint +""" [[task]] name = "cargo-fmt" diff --git a/nix/shell.nix b/nix/shell.nix index 5e642001..34e9d5c2 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -2,6 +2,7 @@ { cargo-insta , default , engage +, findutils , inputs , jq , lychee @@ -32,6 +33,7 @@ mkShell { # Keep sorted cargo-insta engage + findutils jq lychee markdownlint-cli From 5eab758bd29c5234faa0df69bbf0cff03eb91101 Mon Sep 17 00:00:00 2001 From: Lambda Date: Fri, 14 Feb 2025 23:59:41 +0000 Subject: [PATCH 525/617] Make tracing filter reload handles mutable Makes the following diff nicer. --- src/observability.rs | 3 ++- src/service/admin.rs | 9 +++++---- src/service/globals.rs | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/observability.rs b/src/observability.rs index f2503f61..0dfb3b55 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -79,7 +79,8 @@ impl ReloadHandle for reload::Handle { } /// A type-erased [reload handle][reload::Handle] for an [`EnvFilter`]. -pub(crate) type FilterReloadHandle = Box + Sync>; +pub(crate) type FilterReloadHandle = + Box + Send + Sync>; /// Collection of [`FilterReloadHandle`]s, allowing the filters for tracing /// backends to be changed dynamically. Handles may be [`None`] if the backend diff --git a/src/service/admin.rs b/src/service/admin.rs index 7fc67118..eb453239 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1176,12 +1176,13 @@ impl Service { "Reloading filters is disabled", )); }; + let mut handles = handles.write().await; let handle = match backend { - TracingBackend::Log => &handles.log, - TracingBackend::Flame => &handles.flame, - TracingBackend::Traces => &handles.traces, + TracingBackend::Log => &mut handles.log, + TracingBackend::Flame => &mut handles.flame, + TracingBackend::Traces => &mut handles.traces, }; - let Some(handle) = handle else { + let Some(handle) = handle.as_mut() else { return Ok(RoomMessageEventContent::text_plain( "Backend is disabled", )); diff --git a/src/service/globals.rs b/src/service/globals.rs index 0881ce13..65d8b7bc 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -66,7 +66,7 @@ pub(crate) mod marker { pub(crate) struct Service { pub(crate) db: &'static dyn Data, - pub(crate) reload_handles: Option, + pub(crate) reload_handles: Option>>, // actual_destination, host pub(crate) actual_destination_cache: Arc>, @@ -252,7 +252,7 @@ impl Service { let mut s = Self { db, config, - reload_handles, + reload_handles: reload_handles.map(|h| Arc::new(RwLock::new(h))), keypair: Arc::new(keypair), dns_resolver: TokioAsyncResolver::tokio_from_system_conf() .map_err(|e| { From 99924e577920bb32acad5184d48cb0f34f395732 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sat, 15 Feb 2025 00:12:38 +0000 Subject: [PATCH 526/617] Add admin commands to get and reset tracing filters --- book/changelog.md | 5 ++- src/config/env_filter_clone.rs | 2 +- src/observability.rs | 69 +++++++++++++++++++++++++++++++--- src/service/admin.rs | 64 +++++++++++++++++++++++++------ 4 files changed, 121 insertions(+), 19 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index eeb9fdf6..79801297 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -261,9 +261,10 @@ This will be the first release of Grapevine since it was forked from Conduit ([!46](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/46)) 10. Recognize the `!admin` prefix to invoke admin commands. ([!45](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/45)) -11. Add the `set-tracing-filter` admin command to change log/metrics/flame +11. Add the `tracing-filter` admin command to view and change log/metrics/flame filters dynamically at runtime. - ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) + ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49), + [!164](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/164)) 12. Add more configuration options. ([!49](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/49)) * `observability.traces.filter`: The `tracing` filter to use for diff --git a/src/config/env_filter_clone.rs b/src/config/env_filter_clone.rs index 9e8983b7..c4325fc2 100644 --- a/src/config/env_filter_clone.rs +++ b/src/config/env_filter_clone.rs @@ -15,7 +15,7 @@ use tracing_subscriber::EnvFilter; /// Use [`FromStr`] or [`Deserialize`] to construct this type, then [`From`] or /// [`Into`] to convert it into an [`EnvFilter`] when needed. #[derive(Debug, Clone)] -pub(crate) struct EnvFilterClone(String); +pub(crate) struct EnvFilterClone(pub(crate) String); impl FromStr for EnvFilterClone { type Err = ::Err; diff --git a/src/observability.rs b/src/observability.rs index 0dfb3b55..44e06c91 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -21,6 +21,7 @@ use opentelemetry_sdk::{ Resource, }; use strum::{AsRefStr, IntoStaticStr}; +use thiserror::Error; use tokio::time::Instant; use tracing::{subscriber::SetGlobalDefaultError, Span}; use tracing_flame::{FlameLayer, FlushGuard}; @@ -78,9 +79,67 @@ impl ReloadHandle for reload::Handle { } } -/// A type-erased [reload handle][reload::Handle] for an [`EnvFilter`]. -pub(crate) type FilterReloadHandle = - Box + Send + Sync>; +/// Error returned from [`FilterReloadHandle::set_filter()`] +#[allow(clippy::missing_docs_in_private_items)] +#[derive(Debug, Error)] +pub(crate) enum SetFilterError { + #[error("invalid filter string")] + InvalidFilter(#[from] tracing_subscriber::filter::ParseError), + #[error("failed to reload filter layer")] + Reload(#[from] reload::Error), +} + +/// A wrapper around a tracing filter [reload handle][reload::Handle] that +/// remembers the filter string that was last set. +pub(crate) struct FilterReloadHandle { + /// The actual [`reload::Handle`] that can be used to modify the filter + /// [`Layer`] + inner: Box + Send + Sync>, + /// Filter string that was last applied to `inner` + current_filter: String, + /// Filter string that was initially loaded from the configuration + initial_filter: String, +} + +impl FilterReloadHandle { + /// Creates a new [`FilterReloadHandle`] from a filter string, returning the + /// filter layer itself and the handle that can be used to modify it. + pub(crate) fn new( + filter: EnvFilterClone, + ) -> (impl tracing_subscriber::layer::Filter, Self) { + let (layer, handle) = reload::Layer::new(EnvFilter::from(&filter)); + let handle = Self { + inner: Box::new(handle), + current_filter: filter.0.clone(), + initial_filter: filter.0, + }; + (layer, handle) + } + + /// Sets the filter string for the linked filter layer. Can fail if the + /// filter string is invalid or when the link to the layer has been + /// broken. + pub(crate) fn set_filter( + &mut self, + filter: String, + ) -> Result<(), SetFilterError> { + self.inner.reload(filter.parse()?)?; + self.current_filter = filter; + Ok(()) + } + + /// Returns the filter string that the underlying filter layer is currently + /// configured for. + pub(crate) fn get_filter(&self) -> &str { + &self.current_filter + } + + /// Returns the filter string that the underlying filter layer was + /// initialized with. + pub(crate) fn get_initial_filter(&self) -> &str { + &self.initial_filter + } +} /// Collection of [`FilterReloadHandle`]s, allowing the filters for tracing /// backends to be changed dynamically. Handles may be [`None`] if the backend @@ -153,9 +212,9 @@ where return Ok((None, None, None)); } - let (filter, handle) = reload::Layer::new(EnvFilter::from(filter)); + let (filter, handle) = FilterReloadHandle::new(filter.clone()); let (layer, data) = init()?; - Ok((Some(layer.with_filter(filter)), Some(Box::new(handle)), Some(data))) + Ok((Some(layer.with_filter(filter)), Some(handle), Some(data))) } /// Initialize observability diff --git a/src/service/admin.rs b/src/service/admin.rs index eb453239..5e3ee770 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, fmt::Write, sync::Arc, time::Instant}; -use clap::{Parser, ValueEnum}; +use clap::{Parser, Subcommand, ValueEnum}; use regex::Regex; use ruma::{ api::appservice::Registration, @@ -205,10 +205,41 @@ enum AdminCommand { VerifyJson, /// Dynamically change a tracing backend's filter string - SetTracingFilter { + TracingFilter { + #[command(subcommand)] + cmd: TracingFilterCommand, + }, +} + +#[derive(Debug, Subcommand)] +enum TracingFilterCommand { + Get { + backend: TracingBackend, + }, + Set { backend: TracingBackend, filter: String, }, + Reset { + backend: TracingBackend, + }, +} + +impl TracingFilterCommand { + fn backend(&self) -> &TracingBackend { + match self { + TracingFilterCommand::Get { + backend, + } + | TracingFilterCommand::Set { + backend, + .. + } + | TracingFilterCommand::Reset { + backend, + } => backend, + } + } } #[derive(Debug)] @@ -1167,9 +1198,8 @@ impl Service { ) } } - AdminCommand::SetTracingFilter { - backend, - filter, + AdminCommand::TracingFilter { + cmd, } => { let Some(handles) = &services().globals.reload_handles else { return Ok(RoomMessageEventContent::text_plain( @@ -1177,7 +1207,7 @@ impl Service { )); }; let mut handles = handles.write().await; - let handle = match backend { + let handle = match cmd.backend() { TracingBackend::Log => &mut handles.log, TracingBackend::Flame => &mut handles.flame, TracingBackend::Traces => &mut handles.traces, @@ -1187,15 +1217,27 @@ impl Service { "Backend is disabled", )); }; - let filter = match filter.parse() { - Ok(filter) => filter, - Err(e) => { + + let filter = match cmd { + TracingFilterCommand::Set { + filter, + .. + } => filter, + TracingFilterCommand::Reset { + .. + } => handle.get_initial_filter().to_owned(), + TracingFilterCommand::Get { + .. + } => { return Ok(RoomMessageEventContent::text_plain( - format!("Invalid filter string: {e}"), + format!( + "Current filter string: {}", + handle.get_filter() + ), )); } }; - if let Err(e) = handle.reload(filter) { + if let Err(e) = handle.set_filter(filter) { return Ok(RoomMessageEventContent::text_plain(format!( "Failed to reload filter: {e}" ))); From 5616510727b8bb6ce672e36199162b5ddd5c1f0e Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 9 Feb 2025 13:39:33 +0000 Subject: [PATCH 527/617] Use UInt instead of u32 for max request size Sometimes you just really want to upload a full disk image as media. --- src/api/client_server/media.rs | 4 ++-- src/config.rs | 8 ++++---- src/service/globals.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index e0928cf2..ca9dda86 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -129,7 +129,7 @@ pub(crate) async fn get_media_config_legacy_route( _body: Ar, ) -> Result> { Ok(Ra(legacy_media::get_media_config::v3::Response { - upload_size: services().globals.max_request_size().into(), + upload_size: services().globals.max_request_size(), })) } @@ -140,7 +140,7 @@ pub(crate) async fn get_media_config_route( _body: Ar, ) -> Result> { Ok(Ra(authenticated_media_client::get_media_config::v1::Response { - upload_size: services().globals.max_request_size().into(), + upload_size: services().globals.max_request_size(), })) } diff --git a/src/config.rs b/src/config.rs index a440a455..d3100f41 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,7 +10,7 @@ use std::{ use reqwest::Url; use ruma::{ api::federation::discovery::OldVerifyKey, OwnedServerName, - OwnedServerSigningKeyId, RoomVersionId, + OwnedServerSigningKeyId, RoomVersionId, UInt, }; use serde::Deserialize; use strum::{Display, EnumIter, IntoEnumIterator}; @@ -51,7 +51,7 @@ pub(crate) struct Config { #[serde(default = "default_cleanup_second_interval")] pub(crate) cleanup_second_interval: u32, #[serde(default = "default_max_request_size")] - pub(crate) max_request_size: u32, + pub(crate) max_request_size: UInt, #[serde(default = "false_fn")] pub(crate) allow_registration: bool, pub(crate) registration_token: Option, @@ -466,9 +466,9 @@ fn default_cleanup_second_interval() -> u32 { 60 } -fn default_max_request_size() -> u32 { +fn default_max_request_size() -> UInt { // Default to 20 MB - 20 * 1024 * 1024 + (20_u32 * 1024 * 1024).into() } pub(crate) fn default_tracing_filter() -> EnvFilterClone { diff --git a/src/service/globals.rs b/src/service/globals.rs index 65d8b7bc..afbd9cb2 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -31,7 +31,7 @@ use ruma::{ serde::Base64, DeviceId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomAliasId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId, - ServerName, UserId, + ServerName, UInt, UserId, }; use tokio::sync::{broadcast, Mutex, RwLock, Semaphore}; use tracing::{error, warn, Instrument}; @@ -381,7 +381,7 @@ impl Service { self.config.server_name.as_ref() } - pub(crate) fn max_request_size(&self) -> u32 { + pub(crate) fn max_request_size(&self) -> UInt { self.config.max_request_size } From 175a62007d806c5c0f147147da3ce16cb1baeb87 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Feb 2025 11:30:07 +0000 Subject: [PATCH 528/617] Update MSRV to 1.84.0 And appease clippy (`__CARGO_FIX_YOLO=1 cargo clippy --fix` plus some manual type shuffling). --- Cargo.toml | 2 +- flake.lock | 6 +- flake.nix | 2 +- rust-toolchain.toml | 2 +- src/api/client_server/keys.rs | 6 +- src/api/client_server/room.rs | 83 ++++++++++----------- src/api/client_server/session.rs | 4 +- src/api/client_server/user_directory.rs | 7 +- src/api/server_server.rs | 4 +- src/cli/serve.rs | 4 +- src/database.rs | 4 +- src/database/key_value/rooms/state_cache.rs | 4 +- src/database/key_value/sending.rs | 10 +-- src/database/key_value/users.rs | 4 +- src/service/globals.rs | 11 ++- src/service/rooms/spaces.rs | 2 +- src/service/rooms/state_cache.rs | 2 +- src/service/rooms/timeline.rs | 7 +- src/service/users.rs | 4 +- src/service/users/data.rs | 4 +- 20 files changed, 83 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5f30f91..605f1be4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ version = "0.1.0" edition = "2021" # See also `rust-toolchain.toml` -rust-version = "1.81.0" +rust-version = "1.84.0" [lints] workspace = true diff --git a/flake.lock b/flake.lock index abebfbde..57242b6f 100644 --- a/flake.lock +++ b/flake.lock @@ -291,13 +291,13 @@ "rust-manifest": { "flake": false, "locked": { - "narHash": "sha256-tB9BZB6nRHDk5ELIVlGYlIjViLKBjQl52nC1avhcCwA=", + "narHash": "sha256-dm4qOlaZ/uZ+O2TmOrWEjjMAzZNWAGE8S0ne79Fo8OA=", "type": "file", - "url": "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml" + "url": "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml" }, "original": { "type": "file", - "url": "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml" + "url": "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml" } }, "systems": { diff --git a/flake.nix b/flake.nix index 0d6f516e..62084c91 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ rust-manifest = { # Keep version in sync with rust-toolchain.toml - url = "https://static.rust-lang.org/dist/channel-rust-1.81.0.toml"; + url = "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml"; flake = false; }; }; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 42c0fad3..12390a29 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,7 +9,7 @@ # If you're having trouble making the relevant changes, bug a maintainer. [toolchain] -channel = "1.81.0" +channel = "1.84.0" components = [ # For rust-analyzer "rust-src", diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 304be1d0..d2e3006f 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -150,8 +150,8 @@ pub(crate) async fn upload_signing_keys_route( services().users.add_cross_signing_keys( sender_user, master_key, - &body.self_signing_key, - &body.user_signing_key, + body.self_signing_key.as_ref(), + body.user_signing_key.as_ref(), // notify so that other users see the new keys true, )?; @@ -428,7 +428,7 @@ pub(crate) async fn get_keys_helper bool>( let raw = serde_json::from_value(json) .expect("Raw::from_value always works"); services().users.add_cross_signing_keys( - &user, &raw, &None, &None, + &user, &raw, None, None, // Dont notify. A notification would trigger another key // request resulting in an endless loop false, diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index c2488464..ebb5a26b 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -133,62 +133,59 @@ pub(crate) async fn create_room_route( }; let room_version = RoomVersion::try_from(&room_version_id)?; - let content = match &body.creation_content { - Some(content) => { - let mut content = content - .deserialize_as::() - .expect("Invalid creation content"); + let content = if let Some(content) = &body.creation_content { + let mut content = content + .deserialize_as::() + .expect("Invalid creation content"); - if room_version.create_event_creator_prop { - content.insert( - "creator".into(), - json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest( - ErrorKind::BadJson, - "Invalid creation content", - ) - })?, - ); - } + if room_version.create_event_creator_prop { content.insert( - "room_version".into(), - json!(room_version_id.as_str()).try_into().map_err(|_| { + "creator".into(), + json!(&sender_user).try_into().map_err(|_| { Error::BadRequest( ErrorKind::BadJson, "Invalid creation content", ) })?, ); - content } - None => { - let content = if room_version.create_event_creator_prop { - RoomCreateEventContent::new_v1(sender_user.to_owned()) - } else { - RoomCreateEventContent::new_v11() - }; - let mut content = serde_json::from_str::( - to_raw_value(&content) - .map_err(|_| { - Error::BadRequest( - ErrorKind::BadJson, - "Invalid creation content", - ) - })? - .get(), - ) - .unwrap(); - content.insert( - "room_version".into(), - json!(room_version_id.as_str()).try_into().map_err(|_| { + content.insert( + "room_version".into(), + json!(room_version_id.as_str()).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) + })?, + ); + content + } else { + let content = if room_version.create_event_creator_prop { + RoomCreateEventContent::new_v1(sender_user.to_owned()) + } else { + RoomCreateEventContent::new_v11() + }; + let mut content = serde_json::from_str::( + to_raw_value(&content) + .map_err(|_| { Error::BadRequest( ErrorKind::BadJson, "Invalid creation content", ) - })?, - ); - content - } + })? + .get(), + ) + .unwrap(); + content.insert( + "room_version".into(), + json!(room_version_id.as_str()).try_into().map_err(|_| { + Error::BadRequest( + ErrorKind::BadJson, + "Invalid creation content", + ) + })?, + ); + content }; // Validate creation content diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 188397cf..228ab4d3 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -232,11 +232,11 @@ pub(crate) async fn login_route( let token = utils::random_string(TOKEN_LENGTH); // Determine if device_id was provided and exists in the db for this user - let device_exists = body.device_id.as_ref().map_or(false, |device_id| { + let device_exists = body.device_id.as_ref().is_some_and(|device_id| { services() .users .all_device_ids(&user_id) - .any(|x| x.as_ref().map_or(false, |v| v == device_id)) + .any(|x| x.as_ref().is_ok_and(|v| v == device_id)) }); if device_exists { diff --git a/src/api/client_server/user_directory.rs b/src/api/client_server/user_directory.rs index 5b866dd8..0d8912ce 100644 --- a/src/api/client_server/user_directory.rs +++ b/src/api/client_server/user_directory.rs @@ -60,10 +60,9 @@ pub(crate) async fn search_users_route( .rooms .state_accessor .room_state_get(&room, &StateEventType::RoomJoinRules, "") - .map_or(false, |event| { - event.map_or(false, |event| { - serde_json::from_str(event.content.get()).map_or( - false, + .is_ok_and(|event| { + event.is_some_and(|event| { + serde_json::from_str(event.content.get()).is_ok_and( |r: RoomJoinRulesEventContent| { r.join_rule == JoinRule::Public }, diff --git a/src/api/server_server.rs b/src/api/server_server.rs index edba5291..9c29b60b 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1030,8 +1030,8 @@ pub(crate) async fn send_transaction_message_route( services().users.add_cross_signing_keys( &user_id, &master_key, - &self_signing_key, - &None, + self_signing_key.as_ref(), + None, true, )?; } diff --git a/src/cli/serve.rs b/src/cli/serve.rs index a4771641..71776801 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -114,10 +114,10 @@ async fn federation_self_test() -> Result<()> { ) .await?; - if !response + if response .server .as_ref() - .is_some_and(|s| s.name.as_deref() == Some(env!("CARGO_PKG_NAME"))) + .is_none_or(|s| s.name.as_deref() != Some(env!("CARGO_PKG_NAME"))) { error!(?response, "unexpected server version"); return Err(Error::BadConfig( diff --git a/src/database.rs b/src/database.rs index 5205b9ca..acfcda2b 100644 --- a/src/database.rs +++ b/src/database.rs @@ -488,8 +488,8 @@ impl KeyValueDatabase { for (userid, password) in self.userid_password.iter() { let password = utils::string_from_bytes(&password); - let empty_hashed_password = password - .map_or(false, |password| { + let empty_hashed_password = + password.is_ok_and(|password| { utils::verify_password("", password) }); diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index 2c2e215f..c90a18b1 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -182,9 +182,9 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { .ok(); let in_room = bridge_user_id - .map_or(false, |id| self.is_joined(&id, room_id).unwrap_or(false)) + .is_some_and(|id| self.is_joined(&id, room_id).unwrap_or(false)) || self.room_members(room_id).any(|userid| { - userid.map_or(false, |userid| { + userid.is_ok_and(|userid| { appservice.users.is_match(userid.as_str()) }) }); diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 10d437dd..1f7db867 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -86,12 +86,10 @@ impl service::sending::Data for KeyValueDatabase { ) -> Box> + 'a> { let prefix = destination.get_prefix(); - return Box::new(self.servernameevent_data.scan_prefix(prefix).map( - |(k, v)| { - let k = RequestKey::new(k); - parse_servercurrentevent(&k, v).map(|(_, ev)| (ev, k)) - }, - )); + Box::new(self.servernameevent_data.scan_prefix(prefix).map(|(k, v)| { + let k = RequestKey::new(k); + parse_servercurrentevent(&k, v).map(|(_, ev)| (ev, k)) + })) } fn mark_as_active( diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 97558f10..e3fdb95e 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -522,8 +522,8 @@ impl service::users::Data for KeyValueDatabase { &self, user_id: &UserId, master_key: &Raw, - self_signing_key: &Option>, - user_signing_key: &Option>, + self_signing_key: Option<&Raw>, + user_signing_key: Option<&Raw>, notify: bool, ) -> Result<()> { // TODO: Check signatures diff --git a/src/service/globals.rs b/src/service/globals.rs index afbd9cb2..7ba34e0d 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -443,8 +443,8 @@ impl Service { &self.config.turn.secret } - pub(crate) fn emergency_password(&self) -> &Option { - &self.config.emergency_password + pub(crate) fn emergency_password(&self) -> Option<&str> { + self.config.emergency_password.as_deref() } /// If the emergency password option is set, attempts to set the emergency @@ -455,10 +455,9 @@ impl Service { let inner = || -> Result { let admin_bot = self.admin_bot_user_id.as_ref(); - services().users.set_password( - admin_bot, - self.emergency_password().as_deref(), - )?; + services() + .users + .set_password(admin_bot, self.emergency_password())?; let (ruleset, res) = match self.emergency_password() { Some(_) => (Ruleset::server_default(admin_bot), Ok(true)), diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index 88b0c4a9..bfcbb072 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -76,7 +76,7 @@ impl Service { let mut results = Vec::new(); while let Some(current_room) = { - while stack.last().map_or(false, Vec::is_empty) { + while stack.last().is_some_and(Vec::is_empty) { stack.pop(); } if stack.is_empty() { diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index e3783d74..ea6721d9 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -120,7 +120,7 @@ impl Service { }) }) .transpose()? - .map_or(false, |ignored| { + .is_some_and(|ignored| { ignored .content .ignored_users diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 382c3aca..d8cfa368 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -726,9 +726,10 @@ impl Service { let matching_users = |users: &NamespaceRegex| { appservice.users.is_match(pdu.sender.as_str()) || pdu.kind == TimelineEventType::RoomMember - && pdu.state_key.as_ref().map_or(false, |state_key| { - users.is_match(state_key) - }) + && pdu + .state_key + .as_ref() + .is_some_and(|state_key| users.is_match(state_key)) }; let matching_aliases = |aliases: &NamespaceRegex| { services() diff --git a/src/service/users.rs b/src/service/users.rs index 614514ac..3ee47386 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -464,8 +464,8 @@ impl Service { &self, user_id: &UserId, master_key: &Raw, - self_signing_key: &Option>, - user_signing_key: &Option>, + self_signing_key: Option<&Raw>, + user_signing_key: Option<&Raw>, notify: bool, ) -> Result<()> { self.db.add_cross_signing_keys( diff --git a/src/service/users/data.rs b/src/service/users/data.rs index 0ff9a162..630ded7d 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -140,8 +140,8 @@ pub(crate) trait Data: Send + Sync { &self, user_id: &UserId, master_key: &Raw, - self_signing_key: &Option>, - user_signing_key: &Option>, + self_signing_key: Option<&Raw>, + user_signing_key: Option<&Raw>, notify: bool, ) -> Result<()>; From 65ec500d75a669a17058de5ccfd0a67e6d6bdeb9 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 2 Feb 2025 11:31:10 +0000 Subject: [PATCH 529/617] Enable useful new clippy lints Picked from this filtered list: https://rust-lang.github.io/rust-clippy/master/index.html?versions=gte%3A82%2Clte%3A84&levels=allow&groups=cargo%2Ccomplexity%2Ccorrectness%2Cnursery%2Cperf%2Crestriction%2Cstyle%2Csuspicious --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 605f1be4..c9754bb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ mod_module_files = "warn" multiple_inherent_impl = "warn" mutex_atomic = "warn" negative_feature_names = "warn" +non_zero_suggestions = "warn" pub_without_shorthand = "warn" rc_buffer = "warn" rc_mutex = "warn" @@ -64,6 +65,7 @@ unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" unneeded_field_pattern = "warn" unseparated_literal_suffix = "warn" +unused_result_ok = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" From 57c79b1999821e94751651d60767aa9c7e9ace6e Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 2 Mar 2025 01:08:31 -0800 Subject: [PATCH 530/617] Fix native compilation on aarch64 Turns out fenix.packages.aarch64-linux.targets.x86_64-*.minimal.rustc is an x86_64 compiler. The only component that you actually need to pull from 'targets' for cross compilation is rust-std. Because x86_64-unknown-linux-gnu is first in the target list, we were getting x86 rustc and cargo binaries, making it impossible to compile grapevine on an aarch64 host. We were also missing aarch64-unknown-linux-gnu in the targets list, which is used by the default package on a aarch64 host. --- flake.nix | 12 ++++++++---- rust-toolchain.toml | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index 62084c91..d704a0fd 100644 --- a/flake.nix +++ b/flake.nix @@ -59,12 +59,16 @@ toolchainFile.toolchain.components; targets = toolchainFile.toolchain.targets; fenix = inputs.fenix.packages.${pkgs.stdenv.buildPlatform.system}; - in - fenix.combine (builtins.map + + nativeToolchain = (fenix.fromManifestFile inputs.rust-manifest) + .withComponents components; + crossComponents = builtins.map (target: (fenix.targets.${target}.fromManifestFile inputs.rust-manifest) - .withComponents components) - targets); + .rust-std) + targets; + in + fenix.combine ([nativeToolchain] ++ crossComponents); website-root = self.callPackage ./nix/pkgs/website-root {}; }); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 12390a29..244b9085 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -17,5 +17,6 @@ components = [ targets = [ "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", ] From e11e27bc0c4cf10f653cdb4610372d236724eef7 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 2 Mar 2025 10:54:56 -0800 Subject: [PATCH 531/617] don't try to set version extra outside flakes The two attributes being accessed here don't exist when calling though flake-compat, so we need to handle that case by making the environment variable unset if neither attribute exists. --- nix/pkgs/default/default.nix | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index cb7be038..fc9ef5a7 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -57,9 +57,16 @@ let stdenv; }); - buildPackageEnv = { - GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev; - } // buildDepsOnlyEnv; + buildPackageEnv = + let + GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev + or inputs.self.dirtyShortRev + or null; + in + (lib.optionalAttrs (GRAPEVINE_VERSION_EXTRA != null) { + inherit GRAPEVINE_VERSION_EXTRA; + }) + // buildDepsOnlyEnv; commonAttrs = { # Reading from cargoManifest directly instead of using From 114e7ba577bff9fd1629a18b0aec279dcd696d0c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 2 Mar 2025 10:57:13 -0800 Subject: [PATCH 532/617] allow supplying custom version extra info in nix --- nix/pkgs/default/default.nix | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index fc9ef5a7..543537ff 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -15,6 +15,9 @@ , all-features ? false , features ? [] , profile ? "release" +, version-extra ? inputs.self.shortRev + or inputs.self.dirtyShortRev + or null, }: let @@ -58,13 +61,8 @@ let }); buildPackageEnv = - let - GRAPEVINE_VERSION_EXTRA = inputs.self.shortRev - or inputs.self.dirtyShortRev - or null; - in - (lib.optionalAttrs (GRAPEVINE_VERSION_EXTRA != null) { - inherit GRAPEVINE_VERSION_EXTRA; + (lib.optionalAttrs (version-extra != null) { + GRAPEVINE_VERSION_EXTRA = version-extra; }) // buildDepsOnlyEnv; From adb174d98530391856ea99a308e9c725fcebb343 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 2 Mar 2025 11:12:38 -0800 Subject: [PATCH 533/617] don't use `file` type flake inputs Because they aren't supported by flake-compat and can't be without IFD. --- flake.lock | 15 +-------------- flake.nix | 16 ++++++++-------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/flake.lock b/flake.lock index 57242b6f..329c78a4 100644 --- a/flake.lock +++ b/flake.lock @@ -267,8 +267,7 @@ "flake-utils": "flake-utils", "nix-filter": "nix-filter", "nixpkgs": "nixpkgs_2", - "rocksdb": "rocksdb", - "rust-manifest": "rust-manifest" + "rocksdb": "rocksdb" } }, "rust-analyzer-src": { @@ -288,18 +287,6 @@ "type": "github" } }, - "rust-manifest": { - "flake": false, - "locked": { - "narHash": "sha256-dm4qOlaZ/uZ+O2TmOrWEjjMAzZNWAGE8S0ne79Fo8OA=", - "type": "file", - "url": "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml" - }, - "original": { - "type": "file", - "url": "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml" - } - }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index d704a0fd..c997779c 100644 --- a/flake.nix +++ b/flake.nix @@ -9,16 +9,16 @@ nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; rocksdb = { url = "github:facebook/rocksdb?ref=v9.7.4"; flake = false; }; - - rust-manifest = { - # Keep version in sync with rust-toolchain.toml - url = "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml"; - flake = false; - }; }; outputs = inputs: let + rust-manifest = builtins.fetchurl { + # Keep version in sync with rust-toolchain.toml + url = "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml"; + sha256 = "sha256-lMLAupxng4Fd9F1oDw8gx+qA0RuF7ou7xhNU8wgs0PU="; + }; + # Keep sorted mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: { craneLib = @@ -60,11 +60,11 @@ targets = toolchainFile.toolchain.targets; fenix = inputs.fenix.packages.${pkgs.stdenv.buildPlatform.system}; - nativeToolchain = (fenix.fromManifestFile inputs.rust-manifest) + nativeToolchain = (fenix.fromManifestFile rust-manifest) .withComponents components; crossComponents = builtins.map (target: - (fenix.targets.${target}.fromManifestFile inputs.rust-manifest) + (fenix.targets.${target}.fromManifestFile rust-manifest) .rust-std) targets; in From ed67ae6418a729fa66f5687c4cbfc4b8dbcaa516 Mon Sep 17 00:00:00 2001 From: K900 Date: Tue, 18 Mar 2025 13:12:19 +0000 Subject: [PATCH 534/617] nixos: fix systemd service type --- nix/modules/default/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index d29a7f1f..40c03def 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -107,6 +107,7 @@ in StateDirectoryMode = "0700"; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" ]; + Type = "notify"; UMask = "077"; User = if cfg.settings.conduit_compat then "conduit" From fd2a0ac2abac59cbabb6e39f17fffc9358f6a7ad Mon Sep 17 00:00:00 2001 From: K900 Date: Tue, 18 Mar 2025 18:18:09 +0300 Subject: [PATCH 535/617] nixos: allow Unix sockets Because OF COURSE. --- nix/modules/default/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 40c03def..0402609e 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -97,7 +97,7 @@ in ProtectKernelTunables = true; Restart = "on-failure"; RestartSec = 10; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; RestrictNamespaces = true; RestrictRealtime = true; StartLimitBurst = 5; From f6b0a10e6e876f8a1a85a0c5aae1def07786f232 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 26 Sep 2024 20:02:02 -0700 Subject: [PATCH 536/617] implement _file in terms of _folder what the heck lol --- src/service/globals.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/service/globals.rs b/src/service/globals.rs index 7ba34e0d..c572fb1f 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -609,9 +609,7 @@ impl Service { } pub(crate) fn get_media_file(&self, key: &MediaFileKey) -> PathBuf { - let mut r = PathBuf::new(); - r.push(self.config.database.path.clone()); - r.push("media"); + let mut r = self.get_media_folder(); r.push(general_purpose::URL_SAFE_NO_PAD.encode(key.as_bytes())); r } From 0a6d2b273127d697a7ddbf9c35bd70bea1bbb01f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 28 Feb 2025 10:40:26 -0800 Subject: [PATCH 537/617] make a media section in the config file --- book/changelog.md | 7 ++++--- src/cli/serve.rs | 2 +- src/config.rs | 11 +++++++++-- ...tegrations__check_config__invalid_keys@stderr.snap | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 79801297..e303dc56 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -138,9 +138,10 @@ This will be the first release of Grapevine since it was forked from Conduit 10. Try to generate thumbnails for remote media ourselves if the federation thumbnail request fails. ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58)) -11. **BREAKING:** Disable unauthenticated access to media by default, set the - `serve_media_unauthenticated` config option to `true` to enable it. - ([!103](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/103)) +11. **BREAKING:** Disable unauthenticated access to media by default. Use + `media.serve_media_unauthenticated` to configure this behavior. + ([!103](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/103), + [!140](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/140)) 12. **BREAKING:** Split CLI into multiple subcommands. The CLI invocation to run the server is now behind the `serve` command, so `grapevine --config ...` becomes `grapevine serve --config ...`. diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 71776801..4660dc5c 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -443,7 +443,7 @@ fn legacy_media_routes(config: &Config) -> Router { // deprecated, but unproblematic let router = router.ruma_route(c2s::get_media_config_legacy_route); - if config.serve_media_unauthenticated { + if config.media.serve_media_unauthenticated { router .ruma_route(c2s::get_content_legacy_route) .ruma_route(c2s::get_content_as_filename_legacy_route) diff --git a/src/config.rs b/src/config.rs index d3100f41..b0f1aec7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -45,6 +45,8 @@ pub(crate) struct Config { pub(crate) server_discovery: ServerDiscovery, pub(crate) database: DatabaseConfig, #[serde(default)] + pub(crate) media: MediaConfig, + #[serde(default)] pub(crate) federation: FederationConfig, #[serde(default)] pub(crate) cache: CacheConfig, @@ -59,8 +61,6 @@ pub(crate) struct Config { pub(crate) allow_encryption: bool, #[serde(default = "true_fn")] pub(crate) allow_room_creation: bool, - #[serde(default = "false_fn")] - pub(crate) serve_media_unauthenticated: bool, #[serde(default = "default_default_room_version")] pub(crate) default_room_version: RoomVersionId, #[serde(default)] @@ -74,6 +74,13 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, } +#[derive(Debug, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub(crate) struct MediaConfig { + #[serde(default)] + pub(crate) serve_media_unauthenticated: bool, +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields, default)] pub(crate) struct CacheConfig { diff --git a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap index 2b7ce043..83cd650b 100644 --- a/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap +++ b/tests/integrations/snapshots/integrations__check_config__invalid_keys@stderr.snap @@ -8,4 +8,4 @@ Error: failed to validate configuration | 1 | some_name = "example.com" | ^^^^^^^^^ -unknown field `some_name`, expected one of `conduit_compat`, `listen`, `tls`, `server_name`, `server_discovery`, `database`, `federation`, `cache`, `cleanup_second_interval`, `max_request_size`, `allow_registration`, `registration_token`, `allow_encryption`, `allow_room_creation`, `serve_media_unauthenticated`, `default_room_version`, `proxy`, `jwt_secret`, `observability`, `turn`, `emergency_password` +unknown field `some_name`, expected one of `conduit_compat`, `listen`, `tls`, `server_name`, `server_discovery`, `database`, `media`, `federation`, `cache`, `cleanup_second_interval`, `max_request_size`, `allow_registration`, `registration_token`, `allow_encryption`, `allow_room_creation`, `default_room_version`, `proxy`, `jwt_secret`, `observability`, `turn`, `emergency_password` From ae920fdbe8493f5f95a36a7425fa54ea1a501089 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 28 Feb 2025 10:46:21 -0800 Subject: [PATCH 538/617] make option name less redundant --- book/changelog.md | 2 +- src/cli/serve.rs | 2 +- src/config.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index e303dc56..43928b1b 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -139,7 +139,7 @@ This will be the first release of Grapevine since it was forked from Conduit thumbnail request fails. ([!58](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/58)) 11. **BREAKING:** Disable unauthenticated access to media by default. Use - `media.serve_media_unauthenticated` to configure this behavior. + `media.allow_unauthenticated_access` to configure this behavior. ([!103](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/103), [!140](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/140)) 12. **BREAKING:** Split CLI into multiple subcommands. The CLI invocation to run diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 4660dc5c..bac85e48 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -443,7 +443,7 @@ fn legacy_media_routes(config: &Config) -> Router { // deprecated, but unproblematic let router = router.ruma_route(c2s::get_media_config_legacy_route); - if config.media.serve_media_unauthenticated { + if config.media.allow_unauthenticated_access { router .ruma_route(c2s::get_content_legacy_route) .ruma_route(c2s::get_content_as_filename_legacy_route) diff --git a/src/config.rs b/src/config.rs index b0f1aec7..b904a903 100644 --- a/src/config.rs +++ b/src/config.rs @@ -78,7 +78,7 @@ pub(crate) struct Config { #[serde(deny_unknown_fields)] pub(crate) struct MediaConfig { #[serde(default)] - pub(crate) serve_media_unauthenticated: bool, + pub(crate) allow_unauthenticated_access: bool, } #[derive(Debug, Deserialize)] From 5a5608e0884656c50c170bf8dce01c8d253d055b Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 28 Feb 2025 10:56:08 -0800 Subject: [PATCH 539/617] separate media and database paths The primary motivation for this change is to support databases that don't take a path, e.g. out of process databases. This configuration structure leaves the door open for other media storage mechanisms in the future, such as S3. It's also structured to avoid `#[serde(flatten)]` so that we can use `#[serde(deny_unknown_fields)]`. --- book/changelog.md | 3 ++ book/installing/migrating-conduit.md | 48 +++++++++++++++++++ src/config.rs | 17 ++++++- src/service/globals.rs | 10 ++-- .../fixtures/check_config/minimal-valid.toml | 6 ++- .../fixtures/check_config/valid.toml | 6 ++- 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 43928b1b..4ca6dcf4 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -146,6 +146,9 @@ This will be the first release of Grapevine since it was forked from Conduit the server is now behind the `serve` command, so `grapevine --config ...` becomes `grapevine serve --config ...`. ([!108](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/108)) +13. **BREAKING:** The path to media files is now specified separately from the + database path. + ([!40](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/140)) ### Fixed diff --git a/book/installing/migrating-conduit.md b/book/installing/migrating-conduit.md index f847c7af..42cde3aa 100644 --- a/book/installing/migrating-conduit.md +++ b/book/installing/migrating-conduit.md @@ -23,6 +23,54 @@ automatically migrating existing configs to the new schema. [room]: https://matrix.to/#/#grapevine:computer.surgery [config-migration-issue]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/38 +## Filesystem + +Grapevine requires database data and media data to live in **separate**, +**non-nested** directories, which are configurable. Here is a typical example, +starting with the filesystem structure: + +```text +/var/lib/grapevine ++ database/ +| + database-file-1 +| + ... +| + database-file-n ++ media/ + + media-file-1 + + ... + + media-file-n +``` + +And here is the matching configuration: + +```toml +[database] +path = "/var/lib/grapevine/database" + +[media.backend] +type = "filesystem" +path = "/var/lib/grapevine/media" +``` + +On the other hand, Conduit's filesystem layout looks like this: + +```text +/var/lib/conduit ++ media/ +| + media-file-1 +| + ... +| + media-file-n ++ database-file-1 ++ ... ++ database-file-n +``` + +Which **nests** the media directory inside the database directory. Grapevine +will reject this layout, so the filesystem layout must be changed before +starting Grapevine. It is important to migrate the filesystem layout before +starting Grapevine, because otherwise it will create a fresh database instead of +using the existing one. + ## Database Grapevine is currently compatible with the Conduit 0.7.0 database format. It is diff --git a/src/config.rs b/src/config.rs index b904a903..aed1244b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -44,7 +44,6 @@ pub(crate) struct Config { pub(crate) server_discovery: ServerDiscovery, pub(crate) database: DatabaseConfig, - #[serde(default)] pub(crate) media: MediaConfig, #[serde(default)] pub(crate) federation: FederationConfig, @@ -74,13 +73,27 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, } -#[derive(Debug, Deserialize, Default)] +#[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub(crate) struct MediaConfig { + pub(crate) backend: MediaBackendConfig, + #[serde(default)] pub(crate) allow_unauthenticated_access: bool, } +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields, tag = "type", rename_all = "snake_case")] +pub(crate) enum MediaBackendConfig { + Filesystem(MediaFilesystemConfig), +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct MediaFilesystemConfig { + pub(crate) path: PathBuf, +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields, default)] pub(crate) struct CacheConfig { diff --git a/src/service/globals.rs b/src/service/globals.rs index c572fb1f..ea1dce4f 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -39,6 +39,7 @@ use trust_dns_resolver::TokioAsyncResolver; use crate::{ api::server_server::FedDest, + config::{MediaBackendConfig, MediaFilesystemConfig}, observability::FilterReloadHandles, service::media::MediaFileKey, services, @@ -602,10 +603,11 @@ impl Service { } pub(crate) fn get_media_folder(&self) -> PathBuf { - let mut r = PathBuf::new(); - r.push(self.config.database.path.clone()); - r.push("media"); - r + let MediaBackendConfig::Filesystem(MediaFilesystemConfig { + path, + }) = &self.config.media.backend; + + path.clone() } pub(crate) fn get_media_file(&self, key: &MediaFileKey) -> PathBuf { diff --git a/tests/integrations/fixtures/check_config/minimal-valid.toml b/tests/integrations/fixtures/check_config/minimal-valid.toml index e8a93d0e..3c114f2e 100644 --- a/tests/integrations/fixtures/check_config/minimal-valid.toml +++ b/tests/integrations/fixtures/check_config/minimal-valid.toml @@ -5,4 +5,8 @@ client.base_url = "https://matrix.example.com" [database] backend = "rocksdb" -path = "/var/lib/grapevine" +path = "/var/lib/grapevine/database" + +[media.backend] +type = "filesystem" +path = "/var/lib/grapevine/media" diff --git a/tests/integrations/fixtures/check_config/valid.toml b/tests/integrations/fixtures/check_config/valid.toml index 8d8a0bae..6b6e1a3d 100644 --- a/tests/integrations/fixtures/check_config/valid.toml +++ b/tests/integrations/fixtures/check_config/valid.toml @@ -10,7 +10,11 @@ client.base_url = "https://matrix.example.com" [database] backend = "rocksdb" -path = "/var/lib/grapevine" +path = "/var/lib/grapevine/database" + +[media.backend] +type = "filesystem" +path = "/var/lib/grapevine/media" [federation] enable = true From 81a449d1d2ecc4c0404cc234f1d62dafda46e920 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 28 Feb 2025 11:03:25 -0800 Subject: [PATCH 540/617] make database path a pathbuf I'm guessing it wasn't like this already because of one of the admin commands I deleted. --- src/config.rs | 2 +- src/database.rs | 11 +++++------ src/database/abstraction/sqlite.rs | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/config.rs b/src/config.rs index aed1244b..951fd41a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -327,7 +327,7 @@ impl Display for DatabaseBackend { #[serde(deny_unknown_fields)] pub(crate) struct DatabaseConfig { pub(crate) backend: DatabaseBackend, - pub(crate) path: String, + pub(crate) path: PathBuf, #[serde(default = "default_db_cache_capacity_mb")] pub(crate) cache_capacity_mb: f64, #[cfg(feature = "rocksdb")] diff --git a/src/database.rs b/src/database.rs index acfcda2b..f7cb1352 100644 --- a/src/database.rs +++ b/src/database.rs @@ -3,7 +3,6 @@ use std::{ fs, io::Write, mem::size_of, - path::Path, sync::Arc, }; @@ -234,9 +233,9 @@ pub(crate) struct KeyValueDatabase { impl KeyValueDatabase { fn check_db_setup(config: &Config) -> Result<()> { - let path = Path::new(&config.database.path); - - let sqlite_exists = path + let sqlite_exists = config + .database + .path .join(format!( "{}.db", if config.conduit_compat { @@ -246,7 +245,7 @@ impl KeyValueDatabase { } )) .exists(); - let rocksdb_exists = path.join("IDENTITY").exists(); + let rocksdb_exists = config.database.path.join("IDENTITY").exists(); let mut count = 0; @@ -298,7 +297,7 @@ impl KeyValueDatabase { pub(crate) fn load_or_create(config: &Config) -> Result { Self::check_db_setup(config)?; - if !Path::new(&config.database.path).exists() { + if !config.database.path.exists() { fs::create_dir_all(&config.database.path).map_err(|_| { Error::BadConfig( "Database folder doesn't exists and couldn't be created \ diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index 7b41fc79..402e8621 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -110,7 +110,7 @@ impl Engine { impl KeyValueDatabaseEngine for Arc { fn open(config: &Config) -> Result { - let path = Path::new(&config.database.path).join(format!( + let path = config.database.path.join(format!( "{}.db", if config.conduit_compat { "conduit" From b01b70fc20f334d5060f14799bca7687eaeafd77 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 28 Feb 2025 11:14:52 -0800 Subject: [PATCH 541/617] reject overlapping media and database paths --- src/config.rs | 22 +++++++++++++++++++ src/error.rs | 6 +++++ tests/integrations/check_config.rs | 18 +++++++++++++++ .../check_config/database-in-media.toml | 12 ++++++++++ .../fixtures/check_config/dirs/a/.gitignore | 2 ++ .../fixtures/check_config/dirs/b/.gitignore | 2 ++ .../fixtures/check_config/dirs/c/a/.gitignore | 2 ++ .../fixtures/check_config/equal-paths.toml | 12 ++++++++++ .../check_config/media-in-database.toml | 12 ++++++++++ .../fixtures/check_config/minimal-valid.toml | 4 ++-- .../fixtures/check_config/valid.toml | 4 ++-- ...verlapping_paths_database@status_code.snap | 7 ++++++ ...ig__overlapping_paths_database@stderr.snap | 6 +++++ ...ig__overlapping_paths_database@stdout.snap | 5 +++++ ...__overlapping_paths_equal@status_code.snap | 7 ++++++ ...onfig__overlapping_paths_equal@stderr.snap | 6 +++++ ...onfig__overlapping_paths_equal@stdout.snap | 5 +++++ ...__overlapping_paths_media@status_code.snap | 7 ++++++ ...onfig__overlapping_paths_media@stderr.snap | 6 +++++ ...onfig__overlapping_paths_media@stdout.snap | 5 +++++ 20 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 tests/integrations/fixtures/check_config/database-in-media.toml create mode 100644 tests/integrations/fixtures/check_config/dirs/a/.gitignore create mode 100644 tests/integrations/fixtures/check_config/dirs/b/.gitignore create mode 100644 tests/integrations/fixtures/check_config/dirs/c/a/.gitignore create mode 100644 tests/integrations/fixtures/check_config/equal-paths.toml create mode 100644 tests/integrations/fixtures/check_config/media-in-database.toml create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stdout.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stdout.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stdout.snap diff --git a/src/config.rs b/src/config.rs index 951fd41a..03487ace 100644 --- a/src/config.rs +++ b/src/config.rs @@ -538,5 +538,27 @@ where return Err(Error::RegistrationTokenEmpty); } + match &config.media.backend { + MediaBackendConfig::Filesystem(x) => { + let media_path = x + .path + .canonicalize() + .map_err(|e| Error::Canonicalize(e, x.path.clone()))?; + + let database_path = + config.database.path.canonicalize().map_err(|e| { + Error::Canonicalize(e, config.database.path.clone()) + })?; + + let overlap = media_path == database_path + || media_path.starts_with(&database_path) + || database_path.starts_with(&media_path); + + if overlap { + return Err(Error::DatabaseMediaOverlap); + } + } + } + Ok(config) } diff --git a/src/error.rs b/src/error.rs index ce8ebb37..49cf7307 100644 --- a/src/error.rs +++ b/src/error.rs @@ -134,8 +134,14 @@ pub(crate) enum Config { #[error("failed to parse configuration file {1:?}")] Parse(#[source] toml::de::Error, PathBuf), + #[error("failed to canonicalize path {}", .1.display())] + Canonicalize(#[source] std::io::Error, PathBuf), + #[error("registration token must not be empty")] RegistrationTokenEmpty, + + #[error("database and media paths overlap")] + DatabaseMediaOverlap, } /// Errors that can occur while searching for a config file diff --git a/tests/integrations/check_config.rs b/tests/integrations/check_config.rs index 03474266..7db366e8 100644 --- a/tests/integrations/check_config.rs +++ b/tests/integrations/check_config.rs @@ -93,3 +93,21 @@ make_snapshot_test!( "A config with invalid values fails", "invalid-values.toml", ); + +make_snapshot_test!( + overlapping_paths_equal, + "A config with equal paths fails", + "equal-paths.toml", +); + +make_snapshot_test!( + overlapping_paths_media, + "A config with the media path inside the database path fails", + "media-in-database.toml", +); + +make_snapshot_test!( + overlapping_paths_database, + "A config with the database path inside the media path fails", + "database-in-media.toml", +); diff --git a/tests/integrations/fixtures/check_config/database-in-media.toml b/tests/integrations/fixtures/check_config/database-in-media.toml new file mode 100644 index 00000000..db50a5b8 --- /dev/null +++ b/tests/integrations/fixtures/check_config/database-in-media.toml @@ -0,0 +1,12 @@ +server_name = "example.com" + +[server_discovery] +client.base_url = "https://matrix.example.com" + +[database] +backend = "rocksdb" +path = "tests/integrations/fixtures/check_config/dirs/c/a" + +[media.backend] +type = "filesystem" +path = "tests/integrations/fixtures/check_config/dirs/c" diff --git a/tests/integrations/fixtures/check_config/dirs/a/.gitignore b/tests/integrations/fixtures/check_config/dirs/a/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/integrations/fixtures/check_config/dirs/a/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/integrations/fixtures/check_config/dirs/b/.gitignore b/tests/integrations/fixtures/check_config/dirs/b/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/integrations/fixtures/check_config/dirs/b/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/integrations/fixtures/check_config/dirs/c/a/.gitignore b/tests/integrations/fixtures/check_config/dirs/c/a/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/integrations/fixtures/check_config/dirs/c/a/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/integrations/fixtures/check_config/equal-paths.toml b/tests/integrations/fixtures/check_config/equal-paths.toml new file mode 100644 index 00000000..1271b2d3 --- /dev/null +++ b/tests/integrations/fixtures/check_config/equal-paths.toml @@ -0,0 +1,12 @@ +server_name = "example.com" + +[server_discovery] +client.base_url = "https://matrix.example.com" + +[database] +backend = "rocksdb" +path = "tests/integrations/fixtures/check_config/dirs/a" + +[media.backend] +type = "filesystem" +path = "tests/integrations/fixtures/check_config/dirs/a" diff --git a/tests/integrations/fixtures/check_config/media-in-database.toml b/tests/integrations/fixtures/check_config/media-in-database.toml new file mode 100644 index 00000000..66e94257 --- /dev/null +++ b/tests/integrations/fixtures/check_config/media-in-database.toml @@ -0,0 +1,12 @@ +server_name = "example.com" + +[server_discovery] +client.base_url = "https://matrix.example.com" + +[database] +backend = "rocksdb" +path = "tests/integrations/fixtures/check_config/dirs/c" + +[media.backend] +type = "filesystem" +path = "tests/integrations/fixtures/check_config/dirs/c/a" diff --git a/tests/integrations/fixtures/check_config/minimal-valid.toml b/tests/integrations/fixtures/check_config/minimal-valid.toml index 3c114f2e..330e0d57 100644 --- a/tests/integrations/fixtures/check_config/minimal-valid.toml +++ b/tests/integrations/fixtures/check_config/minimal-valid.toml @@ -5,8 +5,8 @@ client.base_url = "https://matrix.example.com" [database] backend = "rocksdb" -path = "/var/lib/grapevine/database" +path = "tests/integrations/fixtures/check_config/dirs/a" [media.backend] type = "filesystem" -path = "/var/lib/grapevine/media" +path = "tests/integrations/fixtures/check_config/dirs/b" diff --git a/tests/integrations/fixtures/check_config/valid.toml b/tests/integrations/fixtures/check_config/valid.toml index 6b6e1a3d..cb0fda11 100644 --- a/tests/integrations/fixtures/check_config/valid.toml +++ b/tests/integrations/fixtures/check_config/valid.toml @@ -10,11 +10,11 @@ client.base_url = "https://matrix.example.com" [database] backend = "rocksdb" -path = "/var/lib/grapevine/database" +path = "tests/integrations/fixtures/check_config/dirs/a" [media.backend] type = "filesystem" -path = "/var/lib/grapevine/media" +path = "tests/integrations/fixtures/check_config/dirs/b" [federation] enable = true diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@status_code.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@status_code.snap new file mode 100644 index 00000000..d2d33fe4 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@status_code.snap @@ -0,0 +1,7 @@ +--- +source: tests/integrations/check_config.rs +description: A config with the database path inside the media path fails +--- +Some( + 1, +) diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stderr.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stderr.snap new file mode 100644 index 00000000..8b550414 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stderr.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A config with the database path inside the media path fails +--- +Error: failed to validate configuration + Caused by: database and media paths overlap diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stdout.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stdout.snap new file mode 100644 index 00000000..065d47d1 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_database@stdout.snap @@ -0,0 +1,5 @@ +--- +source: tests/integrations/check_config.rs +description: A config with the database path inside the media path fails +--- + diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@status_code.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@status_code.snap new file mode 100644 index 00000000..ac822b67 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@status_code.snap @@ -0,0 +1,7 @@ +--- +source: tests/integrations/check_config.rs +description: A config with equal paths fails +--- +Some( + 1, +) diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stderr.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stderr.snap new file mode 100644 index 00000000..c1e68376 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stderr.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A config with equal paths fails +--- +Error: failed to validate configuration + Caused by: database and media paths overlap diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stdout.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stdout.snap new file mode 100644 index 00000000..acbd1296 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_equal@stdout.snap @@ -0,0 +1,5 @@ +--- +source: tests/integrations/check_config.rs +description: A config with equal paths fails +--- + diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@status_code.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@status_code.snap new file mode 100644 index 00000000..5240e180 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@status_code.snap @@ -0,0 +1,7 @@ +--- +source: tests/integrations/check_config.rs +description: A config with the media path inside the database path fails +--- +Some( + 1, +) diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stderr.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stderr.snap new file mode 100644 index 00000000..2402b8b4 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stderr.snap @@ -0,0 +1,6 @@ +--- +source: tests/integrations/check_config.rs +description: A config with the media path inside the database path fails +--- +Error: failed to validate configuration + Caused by: database and media paths overlap diff --git a/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stdout.snap b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stdout.snap new file mode 100644 index 00000000..8d4c65a1 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__overlapping_paths_media@stdout.snap @@ -0,0 +1,5 @@ +--- +source: tests/integrations/check_config.rs +description: A config with the media path inside the database path fails +--- + From 50583bc93ef948e450112c36dc159151a0c06c41 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 21 Mar 2025 16:18:40 -0700 Subject: [PATCH 542/617] reject overlapping non-canonical paths too For example, if the database path is `/foo` and the media path is `/foo/bar`, but `/foo/bar` is a symlink or hardlink to `/baz`, the previous check would pass. The whole point of this check is to ensure that the database and media data can't step on each other, so this check is needed to deny that kind of situation as well. It would probably be good to add a test for this behavior. --- src/config.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/config.rs b/src/config.rs index 03487ace..0c1f2e55 100644 --- a/src/config.rs +++ b/src/config.rs @@ -540,6 +540,10 @@ where match &config.media.backend { MediaBackendConfig::Filesystem(x) => { + if overlap(&x.path, &config.database.path) { + return Err(Error::DatabaseMediaOverlap); + } + let media_path = x .path .canonicalize() @@ -550,11 +554,7 @@ where Error::Canonicalize(e, config.database.path.clone()) })?; - let overlap = media_path == database_path - || media_path.starts_with(&database_path) - || database_path.starts_with(&media_path); - - if overlap { + if overlap(&media_path, &database_path) { return Err(Error::DatabaseMediaOverlap); } } @@ -562,3 +562,8 @@ where Ok(config) } + +/// Returns `true` if two paths overlap. +fn overlap(a: &Path, b: &Path) -> bool { + a == b || a.starts_with(b) || b.starts_with(a) +} From a04951541a4d844ed6a758bd25e8243f0bee7af9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 21 Mar 2025 15:22:14 -0700 Subject: [PATCH 543/617] don't check canonicalized paths while sandboxed Because the configured paths won't exist in the sandbox, so canonicalization would fail. --- nix/modules/default/default.nix | 2 +- src/cli.rs | 11 ++++++++--- src/cli/check_config.rs | 7 ++++--- src/cli/serve.rs | 2 +- src/config.rs | 27 ++++++++++++++++----------- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 0402609e..92873a71 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -12,7 +12,7 @@ let cfg = config.services.grapevine; configFile = format.generate "config.toml" cfg.settings; validateConfig = file: pkgs.runCommand "grapevine-checked-config" {} '' - ${lib.getExe cfg.package} check-config -c ${lib.escapeShellArg file} + ${lib.getExe cfg.package} check-config -sc ${lib.escapeShellArg file} ln -s ${lib.escapeShellArg file} "$out" ''; format = pkgs.formats.toml {}; diff --git a/src/cli.rs b/src/cli.rs index 99641790..4f4c85cc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -42,6 +42,13 @@ pub(crate) struct CheckConfigArgs { #[clap(flatten)] observability: ObservabilityArgs, + + /// Supply this option if the check is being performed in a sandbox. + /// + /// This causes certain checks that can only be run against the actual + /// installation to be skipped. + #[arg(long, short)] + sandboxed: bool, } /// Wrapper for the `--config` arg. @@ -96,9 +103,7 @@ impl Args { match self.command { Command::Serve(args) => serve::run(args).await?, - Command::CheckConfig(args) => { - check_config::run(args.config).await?; - } + Command::CheckConfig(args) => check_config::run(args).await?, } Ok(()) } diff --git a/src/cli/check_config.rs b/src/cli/check_config.rs index 9dd0dba3..943491bb 100644 --- a/src/cli/check_config.rs +++ b/src/cli/check_config.rs @@ -1,11 +1,12 @@ use tracing::info; -use crate::{cli::ConfigArg, config, error}; +use crate::{cli::CheckConfigArgs, config, error}; pub(crate) async fn run( - args: ConfigArg, + args: CheckConfigArgs, ) -> Result<(), error::CheckConfigCommand> { - let _config = config::load(args.config.as_ref()).await?; + let _config = + config::load(args.config.config.as_ref(), args.sandboxed).await?; info!("Configuration looks good"); Ok(()) } diff --git a/src/cli/serve.rs b/src/cli/serve.rs index bac85e48..d08807b6 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -70,7 +70,7 @@ use crate::{ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { use error::ServeCommand as Error; - let config = config::load(args.config.config.as_ref()).await?; + let config = config::load(args.config.config.as_ref(), false).await?; rustls::crypto::ring::default_provider() .install_default() diff --git a/src/config.rs b/src/config.rs index 0c1f2e55..8f3b2116 100644 --- a/src/config.rs +++ b/src/config.rs @@ -514,7 +514,10 @@ fn search() -> Result { } /// Load the configuration from the given path or XDG Base Directories -pub(crate) async fn load

(path: Option

) -> Result +pub(crate) async fn load

( + path: Option

, + sandboxed: bool, +) -> Result where P: AsRef, { @@ -544,18 +547,20 @@ where return Err(Error::DatabaseMediaOverlap); } - let media_path = x - .path - .canonicalize() - .map_err(|e| Error::Canonicalize(e, x.path.clone()))?; + if !sandboxed { + let media_path = x + .path + .canonicalize() + .map_err(|e| Error::Canonicalize(e, x.path.clone()))?; - let database_path = - config.database.path.canonicalize().map_err(|e| { - Error::Canonicalize(e, config.database.path.clone()) - })?; + let database_path = + config.database.path.canonicalize().map_err(|e| { + Error::Canonicalize(e, config.database.path.clone()) + })?; - if overlap(&media_path, &database_path) { - return Err(Error::DatabaseMediaOverlap); + if overlap(&media_path, &database_path) { + return Err(Error::DatabaseMediaOverlap); + } } } } From 218d75d57358429d3ecba371c8d5e0f3441d76cb Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 21 Mar 2025 15:32:59 -0700 Subject: [PATCH 544/617] update nixos module Can't believe I forgot to do this... --- nix/modules/default/default.nix | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 92873a71..37912a83 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -44,14 +44,40 @@ in type = types.nonEmptyStr; readOnly = true; description = '' - The path to store persistent data in. + The path to store database files in. Note that this is read-only because this module makes use of systemd's `StateDirectory` option. ''; default = if cfg.settings.conduit_compat - then "/var/lib/matrix-conduit" - else "/var/lib/grapevine"; + then "/var/lib/matrix-conduit/database" + else "/var/lib/grapevine/database"; + }; + media.backend = { + type = lib.mkOption { + type = types.nonEmptyStr; + readOnly = true; + description = '' + The media backend to use. + + Note that this is read-only because `filesystem` is currently + the only valid option. + ''; + default = "filesystem"; + }; + path = lib.mkOption { + type = types.nonEmptyStr; + readOnly = true; + description = '' + The path to store database files in. + + Note that this is read-only because this module makes use of + systemd's `StateDirectory` option. + ''; + default = if cfg.settings.conduit_compat + then "/var/lib/matrix-conduit/media" + else "/var/lib/grapevine/media"; + }; }; listen = lib.mkOption { type = types.listOf format.type; From 799594cd1f2c809de6d8e497b07a4b09db48db0d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 21 Mar 2025 16:28:11 -0700 Subject: [PATCH 545/617] fix typo and update changelog The MR number for the original MR was missing the leading `1`. --- book/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index 4ca6dcf4..0a233597 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -148,7 +148,8 @@ This will be the first release of Grapevine since it was forked from Conduit ([!108](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/108)) 13. **BREAKING:** The path to media files is now specified separately from the database path. - ([!40](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/140)) + ([!140](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/140), + [!170](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/170)) ### Fixed From d6475eee6df6de00d3fd631705a13b36c7ce3d42 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 21 Mar 2025 16:48:56 -0700 Subject: [PATCH 546/617] remove redundant condition --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 8f3b2116..4a174c35 100644 --- a/src/config.rs +++ b/src/config.rs @@ -570,5 +570,5 @@ where /// Returns `true` if two paths overlap. fn overlap(a: &Path, b: &Path) -> bool { - a == b || a.starts_with(b) || b.starts_with(a) + a.starts_with(b) || b.starts_with(a) } From 7ee117d36ac66fda4fde3b3f2f7a41f96c5d8114 Mon Sep 17 00:00:00 2001 From: K900 Date: Thu, 27 Mar 2025 21:38:49 +0300 Subject: [PATCH 547/617] fix: update nix inputs, fix build with latest crane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes references to rustc leaking into the binary. Flake lock file updates: • Updated input 'attic': 'github:zhaofengli/attic/47752427561f1c34debb16728a210d378f0ece36' (2024-11-10) → 'github:zhaofengli/attic/ff8a897d1f4408ebbf4d45fa9049c06b3e1e3f4e' (2025-02-02) • Updated input 'crane': 'github:ipetkov/crane/ef80ead953c1b28316cc3f8613904edc2eb90c28' (2024-11-08) → 'github:ipetkov/crane/70947c1908108c0c551ddfd73d4f750ff2ea67cd' (2025-03-19) • Updated input 'fenix': 'github:nix-community/fenix/e10ba121773f754a30d31b6163919a3e404a434f' (2024-11-16) → 'github:nix-community/fenix/7d9ba794daf5e8cc7ee728859bc688d8e26d5f06' (2025-03-20) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/1b90e979aeee8d1db7fe14603a00834052505497' (2024-11-15) → 'github:rust-lang/rust-analyzer/15d87419f1a123d8f888d608129c3ce3ff8f13d4' (2025-03-18) • Updated input 'flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) → 'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec' (2024-12-04) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/5e4fbfb6b3de1aa2872b76d49fafc942626e2add' (2024-11-15) → 'github:NixOS/nixpkgs/698214a32beb4f4c8e3942372c694f40848b360d' (2025-03-25) --- flake.lock | 36 ++++++++++++++++++------------------ nix/pkgs/default/default.nix | 3 +++ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/flake.lock b/flake.lock index 329c78a4..edf99e09 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1731270564, - "narHash": "sha256-6KMC/NH/VWP5Eb+hA56hz0urel3jP6Y6cF2PX6xaTkk=", + "lastModified": 1738524606, + "narHash": "sha256-hPYEJ4juK3ph7kbjbvv7PlU1D9pAkkhl+pwx8fZY53U=", "owner": "zhaofengli", "repo": "attic", - "rev": "47752427561f1c34debb16728a210d378f0ece36", + "rev": "ff8a897d1f4408ebbf4d45fa9049c06b3e1e3f4e", "type": "github" }, "original": { @@ -47,11 +47,11 @@ }, "crane_2": { "locked": { - "lastModified": 1731098351, - "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", + "lastModified": 1742394900, + "narHash": "sha256-vVOAp9ahvnU+fQoKd4SEXB2JG2wbENkpqcwlkIXgUC0=", "owner": "ipetkov", "repo": "crane", - "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", + "rev": "70947c1908108c0c551ddfd73d4f750ff2ea67cd", "type": "github" }, "original": { @@ -69,11 +69,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1731738660, - "narHash": "sha256-tIXhc9lX1b030v812yVJanSR37OnpTb/OY5rU3TbShA=", + "lastModified": 1742452566, + "narHash": "sha256-sVuLDQ2UIWfXUBbctzrZrXM2X05YjX08K7XHMztt36E=", "owner": "nix-community", "repo": "fenix", - "rev": "e10ba121773f754a30d31b6163919a3e404a434f", + "rev": "7d9ba794daf5e8cc7ee728859bc688d8e26d5f06", "type": "github" }, "original": { @@ -102,11 +102,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -227,11 +227,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1731676054, - "narHash": "sha256-OZiZ3m8SCMfh3B6bfGC/Bm4x3qc1m2SVEAlkV6iY7Yg=", + "lastModified": 1742889210, + "narHash": "sha256-hw63HnwnqU3ZQfsMclLhMvOezpM7RSB0dMAtD5/sOiw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5e4fbfb6b3de1aa2872b76d49fafc942626e2add", + "rev": "698214a32beb4f4c8e3942372c694f40848b360d", "type": "github" }, "original": { @@ -273,11 +273,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1731693936, - "narHash": "sha256-uHUUS1WPyW6ohp5Bt3dAZczUlQ22vOn7YZF8vaPKIEw=", + "lastModified": 1742296961, + "narHash": "sha256-gCpvEQOrugHWLimD1wTFOJHagnSEP6VYBDspq96Idu0=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "1b90e979aeee8d1db7fe14603a00834052505497", + "rev": "15d87419f1a123d8f888d608129c3ce3ff8f13d4", "type": "github" }, "original": { diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 543537ff..67d7c2e7 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -99,6 +99,9 @@ let # rebuilds of bindgen and its depedents. jq ]; + + # Opt out of crane's automagic cross support + doIncludeCrossToolchainEnv = false; }; in From 8da6c5d4b34791efd5501c12f55ca2071a75cf4d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 15 Mar 2025 14:42:05 -0700 Subject: [PATCH 548/617] bump lockfile format version This was actually introduced in version 1.83 of the rust toolchain. --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index d7ccc059..8ef5bf50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" From 8b7cbb5f25368f98975a001ec1e6249cea32db6e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 15 Mar 2025 14:42:48 -0700 Subject: [PATCH 549/617] update rocksdb This is done separately since it requires more involved changes than just cargo's files. --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- flake.lock | 8 ++++---- flake.nix | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ef5bf50..6543812b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2653,9 +2653,9 @@ dependencies = [ [[package]] name = "rust-librocksdb-sys" -version = "0.29.0+9.7.4" +version = "0.32.0+9.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431f14c28485bd13140e5b27298c22a3b96a0cc9f60a4f5318ae782b7288e9c" +checksum = "50146b7fadd68926e9dcb902bf0515783aaaf5f73af26949f188aead5ede8cd0" dependencies = [ "bindgen", "bzip2-sys", @@ -2669,9 +2669,9 @@ dependencies = [ [[package]] name = "rust-rocksdb" -version = "0.33.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4412bfff73ff8f0c458041934bee4f0bbf92488271e8e5d767679f4a670df44" +checksum = "3bf088a714aa3fad699f7dbe06047ca732c09629a2f9b28aa16ca6d3897a4c2f" dependencies = [ "libc", "rust-librocksdb-sys", diff --git a/Cargo.toml b/Cargo.toml index c9754bb9..888be77f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ rand = "0.8.5" regex = "1.11.1" reqwest = { version = "0.12.9", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.8" -rocksdb = { package = "rust-rocksdb", version = "0.33.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } rustls = { version = "0.23.19", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } diff --git a/flake.lock b/flake.lock index edf99e09..00b27744 100644 --- a/flake.lock +++ b/flake.lock @@ -244,16 +244,16 @@ "rocksdb": { "flake": false, "locked": { - "lastModified": 1730475155, - "narHash": "sha256-u5uuShM2SxHc9/zL4UU56IhCcR/ZQbzde0LgOYS44bM=", + "lastModified": 1734381914, + "narHash": "sha256-G+DlQwEUyd7JOCjS1Hg1cKWmA/qAiK8UpUIKcP+riGQ=", "owner": "facebook", "repo": "rocksdb", - "rev": "3c27a3dde0993210c5cc30d99717093f7537916f", + "rev": "ae8fb3e5000e46d8d4c9dbf3a36019c0aaceebff", "type": "github" }, "original": { "owner": "facebook", - "ref": "v9.7.4", + "ref": "v9.10.0", "repo": "rocksdb", "type": "github" } diff --git a/flake.nix b/flake.nix index c997779c..f390239a 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,7 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - rocksdb = { url = "github:facebook/rocksdb?ref=v9.7.4"; flake = false; }; + rocksdb = { url = "github:facebook/rocksdb?ref=v9.10.0"; flake = false; }; }; outputs = inputs: From a901d625750ccc3128e612ac06c90e6ff473a3d8 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 15 Mar 2025 15:12:01 -0700 Subject: [PATCH 550/617] skip checks for deps-only derivation This is probably a crane bug that it's doing this in the first place. --- nix/pkgs/default/default.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 67d7c2e7..3fcca36b 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -102,6 +102,9 @@ let # Opt out of crane's automagic cross support doIncludeCrossToolchainEnv = false; + + # This is redundant with CI + doCheck = false; }; in @@ -115,9 +118,6 @@ craneLib.buildPackage (commonAttrs // { (features' != []) "--features " + (builtins.concatStringsSep "," features'); - # This is redundant with CI - doCheck = false; - env = buildPackageEnv; passthru = { From bb80f1cf2e7a05af829bd44e5d38f68dc70eec98 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sat, 15 Mar 2025 15:24:57 -0700 Subject: [PATCH 551/617] update rust deps that don't require code changes I also had to pin ruma because otherwise cargo complained that features were removed from ruma so it couldn't resolve the dependency by itself. --- Cargo.lock | 1069 ++++++++++++++++++++++++++++++---------------------- Cargo.toml | 56 +-- 2 files changed, 643 insertions(+), 482 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6543812b..2641dca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,18 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -46,9 +34,9 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arc-swap" @@ -70,9 +58,9 @@ dependencies = [ [[package]] name = "as_variant" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38fa22307249f86fb7fad906fcae77f2564caeb56d7209103c551cd1cf4798f" +checksum = "9dbc3a507a82b17ba0d98f6ce8fd6954ea0c8152e98009d36a40d8dcc8ce078a" [[package]] name = "assert_cmd" @@ -120,9 +108,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -167,9 +155,9 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -190,7 +178,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -215,23 +203,22 @@ dependencies = [ "multer", "pin-project-lite", "serde", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", ] [[package]] name = "axum-server" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8" +checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" dependencies = [ "arc-swap", "bytes", - "futures-util", + "fs-err", "http", "http-body", - "http-body-util", "hyper", "hyper-util", "pin-project-lite", @@ -240,7 +227,6 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tower 0.4.13", "tower-service", ] @@ -273,9 +259,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bindgen" @@ -283,7 +269,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -297,6 +283,24 @@ dependencies = [ "syn", ] +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -305,9 +309,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake2" @@ -329,9 +333,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -340,21 +344,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" [[package]] name = "byteorder-lite" @@ -364,26 +362,25 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] [[package]] name = "cc" -version = "1.2.2" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "jobserver", "libc", @@ -424,9 +421,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" dependencies = [ "clap_builder", "clap_derive", @@ -434,9 +431,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" dependencies = [ "anstyle", "clap_lex", @@ -445,9 +442,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -457,9 +454,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "color_quant" @@ -469,14 +466,14 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "console" -version = "0.15.8" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] @@ -487,9 +484,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "013b6c2c3a14d678f38cd23994b02da3a1a1b6a5d1eedddfe63a5a5f11b13a81" +checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" [[package]] name = "core-foundation" @@ -509,9 +506,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -564,9 +561,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "date_header" @@ -586,9 +583,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -645,7 +642,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.6.4", "serde", "sha2", "subtle", @@ -654,15 +651,15 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -687,9 +684,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -715,15 +712,15 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -736,9 +733,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", @@ -746,9 +743,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ "num-traits", ] @@ -759,6 +756,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -768,6 +771,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" +dependencies = [ + "autocfg", + "tokio", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -857,7 +870,21 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] @@ -879,9 +906,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "grapevine" @@ -920,7 +947,7 @@ dependencies = [ "predicates", "prometheus", "proxy-header", - "rand", + "rand 0.8.5", "regex", "reqwest", "ring", @@ -935,12 +962,12 @@ dependencies = [ "serde_yaml", "sha-1", "strum", - "thiserror 2.0.3", + "thiserror 2.0.12", "thread_local", "tikv-jemallocator", "tokio", "toml", - "tower 0.5.1", + "tower 0.5.2", "tower-http", "tracing", "tracing-flame", @@ -952,9 +979,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -962,7 +989,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.6.0", + "indexmap 2.8.0", "slab", "tokio", "tokio-util", @@ -975,28 +1002,22 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -1046,13 +1067,13 @@ dependencies = [ [[package]] name = "hostname" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ + "cfg-if", "libc", - "match_cfg", - "winapi", + "windows", ] [[package]] @@ -1066,9 +1087,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1096,12 +1117,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "pin-project-lite", @@ -1109,9 +1130,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1121,9 +1142,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -1142,9 +1163,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http", @@ -1231,9 +1252,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -1255,9 +1276,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -1276,9 +1297,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -1341,9 +1362,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", @@ -1367,9 +1388,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1378,15 +1399,16 @@ dependencies = [ [[package]] name = "insta" -version = "1.41.1" +version = "1.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" +checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084" dependencies = [ "console", - "lazy_static", "linked-hash-map", + "once_cell", "pest", "pest_derive", + "pin-project", "regex", "serde", "similar", @@ -1406,9 +1428,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itertools" @@ -1429,10 +1451,19 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.14" +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" @@ -1445,10 +1476,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.73" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb15147158e79fd8b8afd0252522769c4f48725460b37338544d8379d94fc8f9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1472,11 +1504,11 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "9.3.0" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "js-sys", "pem", "ring", @@ -1487,9 +1519,9 @@ dependencies = [ [[package]] name = "konst" -version = "0.3.14" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65f00fb3910881e52bf0850ae2a82aea411488a557e1c02820ceaa60963dce3" +checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" dependencies = [ "const_panic", "konst_kernel", @@ -1498,9 +1530,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599c1232f55c72c7fc378335a3efe1c878c92720838c8e6a4fd87784ef7764de" +checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" dependencies = [ "typewit", ] @@ -1519,9 +1551,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" @@ -1535,9 +1567,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7" dependencies = [ "cc", "pkg-config", @@ -1546,9 +1578,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "pkg-config", @@ -1563,15 +1595,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -1585,9 +1617,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lru-cache" @@ -1614,12 +1646,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -1655,9 +1681,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", "simd-adler32", @@ -1670,7 +1696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1697,7 +1723,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases", "libc", @@ -1775,24 +1801,24 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b" [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "opentelemetry" @@ -1874,7 +1900,7 @@ dependencies = [ "once_cell", "opentelemetry", "percent-encoding", - "rand", + "rand 0.8.5", "serde_json", "thiserror 1.0.69", "tokio", @@ -1917,15 +1943,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] [[package]] name = "pem" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", @@ -1939,20 +1965,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", - "thiserror 1.0.69", + "thiserror 2.0.12", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -1960,9 +1986,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", @@ -1973,9 +1999,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", @@ -1984,9 +2010,9 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", "phf_shared", @@ -1994,19 +2020,19 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", @@ -2017,27 +2043,27 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -2046,9 +2072,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -2068,15 +2094,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" -version = "0.17.14" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2093,18 +2119,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -2116,15 +2142,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -2132,18 +2158,18 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -2165,9 +2191,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -2175,12 +2201,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", "syn", @@ -2202,45 +2228,41 @@ dependencies = [ "tokio", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quinn" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" dependencies = [ "bytes", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustls", "socket2", - "thiserror 2.0.3", + "thiserror 2.0.12", "tokio", "tracing", + "web-time", ] [[package]] name = "quinn-proto" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" dependencies = [ "bytes", - "getrandom", - "rand", + "getrandom 0.3.2", + "rand 0.9.0", "ring", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -2248,9 +2270,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" dependencies = [ "cfg_aliases", "libc", @@ -2262,13 +2284,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -2276,8 +2304,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "zerocopy", ] [[package]] @@ -2287,7 +2326,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -2296,16 +2345,25 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", ] [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -2354,9 +2412,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -2384,10 +2442,11 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tokio-rustls", "tokio-socks", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -2398,25 +2457,23 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" dependencies = [ "hostname", - "quick-error", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2424,7 +2481,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.11.1" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "assign", "js_int", @@ -2445,7 +2502,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", @@ -2457,7 +2514,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.19.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "as_variant", "assign", @@ -2472,7 +2529,7 @@ dependencies = [ "serde", "serde_html_form", "serde_json", - "thiserror 2.0.3", + "thiserror 2.0.12", "url", "web-time", ] @@ -2480,25 +2537,25 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.14.1" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "as_variant", "base64 0.22.1", "bytes", "form_urlencoded", "http", - "indexmap 2.6.0", + "indexmap 2.8.0", "js_int", "konst", "percent-encoding", - "rand", + "rand 0.8.5", "regex", "ruma-identifiers-validation", "ruma-macros", "serde", "serde_html_form", "serde_json", - "thiserror 2.0.3", + "thiserror 2.0.12", "time", "tracing", "url", @@ -2510,10 +2567,10 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.29.1" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "as_variant", - "indexmap 2.6.0", + "indexmap 2.8.0", "js_int", "js_option", "percent-encoding", @@ -2523,7 +2580,7 @@ dependencies = [ "ruma-macros", "serde", "serde_json", - "thiserror 2.0.3", + "thiserror 2.0.12", "tracing", "url", "web-time", @@ -2533,7 +2590,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "bytes", "http", @@ -2541,7 +2598,7 @@ dependencies = [ "js_int", "memchr", "mime", - "rand", + "rand 0.8.5", "ruma-common", "ruma-events", "serde", @@ -2551,16 +2608,16 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", - "thiserror 2.0.3", + "thiserror 2.0.12", ] [[package]] name = "ruma-identity-service-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", @@ -2570,7 +2627,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.14.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2585,7 +2642,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.10.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", @@ -2597,53 +2654,53 @@ dependencies = [ [[package]] name = "ruma-server-util" version = "0.4.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "headers", "http", "http-auth", "ruma-common", - "thiserror 2.0.3", + "thiserror 2.0.12", "tracing", ] [[package]] name = "ruma-signatures" version = "0.16.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "base64 0.22.1", "ed25519-dalek", "pkcs8", - "rand", + "rand 0.8.5", "ruma-common", "serde_json", "sha2", "subslice", - "thiserror 2.0.3", + "thiserror 2.0.12", ] [[package]] name = "ruma-state-res" version = "0.12.0" -source = "git+https://github.com/ruma/ruma?branch=main#bd33b9a38fbf4aea0413f947469be916f120032e" +source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" dependencies = [ "js_int", "ruma-common", "ruma-events", "serde", "serde_json", - "thiserror 2.0.3", + "thiserror 2.0.12", "tracing", ] [[package]] name = "rusqlite" -version = "0.32.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +checksum = "37e34486da88d8e051c7c0e23c3f15fd806ea8546260aa2fec247e97242ec143" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2657,7 +2714,7 @@ version = "0.32.0+9.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50146b7fadd68926e9dcb902bf0515783aaaf5f73af26949f188aead5ede8cd0" dependencies = [ - "bindgen", + "bindgen 0.69.5", "bzip2-sys", "cc", "glob", @@ -2691,9 +2748,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -2706,22 +2763,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" dependencies = [ "log", "once_cell", @@ -2755,18 +2812,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" dependencies = [ "web-time", ] [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", "rustls-pki-types", @@ -2775,15 +2832,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" @@ -2802,17 +2859,20 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sd-notify" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be20c5f7f393ee700f8b2f28ea35812e4e212f40774b550cd2a93ea91684451" +checksum = "b943eadf71d8b69e661330cb0e2656e31040acf21ee7708e2c238a0ec6af2bf4" +dependencies = [ + "libc", +] [[package]] name = "security-framework" -version = "3.0.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -2821,9 +2881,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2831,24 +2891,24 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -2857,12 +2917,12 @@ dependencies = [ [[package]] name = "serde_html_form" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" +checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4" dependencies = [ "form_urlencoded", - "indexmap 2.6.0", + "indexmap 2.8.0", "itoa", "ryu", "serde", @@ -2870,9 +2930,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -2882,9 +2942,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", @@ -2917,7 +2977,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.8.0", "itoa", "ryu", "serde", @@ -2987,7 +3047,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2998,27 +3058,27 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 1.0.69", + "thiserror 2.0.12", "time", ] [[package]] name = "siphasher" -version = "0.3.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" @@ -3031,9 +3091,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -3069,18 +3129,18 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ "heck", "proc-macro2", @@ -3106,21 +3166,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.89" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3143,9 +3197,9 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ "rustix", "windows-sys 0.59.0", @@ -3153,9 +3207,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "thiserror" @@ -3168,11 +3222,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.12", ] [[package]] @@ -3188,9 +3242,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -3229,9 +3283,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -3244,15 +3298,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -3270,9 +3324,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -3285,9 +3339,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", @@ -3302,9 +3356,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -3313,12 +3367,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] @@ -3336,9 +3389,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -3347,9 +3400,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -3360,9 +3413,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -3381,11 +3434,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", @@ -3433,7 +3486,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -3444,14 +3497,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -3463,12 +3516,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "bytes", "http", "http-body", "pin-project-lite", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -3492,7 +3545,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3606,7 +3658,7 @@ dependencies = [ "idna 0.4.0", "ipnet", "once_cell", - "rand", + "rand 0.8.5", "smallvec", "thiserror 1.0.69", "tinyvec", @@ -3627,7 +3679,7 @@ dependencies = [ "lru-cache", "once_cell", "parking_lot", - "rand", + "rand 0.8.5", "resolv-conf", "smallvec", "thiserror 1.0.69", @@ -3644,15 +3696,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typewit" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51dbd25812f740f45e2a9769f84711982e000483b13b73a8a1852e092abac8c" +checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" dependencies = [ "typewit_proc_macros", ] @@ -3671,15 +3723,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" @@ -3734,18 +3786,18 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom", + "getrandom 0.3.2", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -3761,9 +3813,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -3784,25 +3836,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.96" +name = "wasi" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.96" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -3811,9 +3872,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.46" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951fe82312ed48443ac78b66fa43eded9999f738f6022e67aead7b708659e49a" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -3824,9 +3885,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.96" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3834,9 +3895,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.96" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -3847,15 +3908,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.96" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.73" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476364ff87d0ae6bfb661053a9104ab312542658c3d8f963b7ace80b6f9b26b9" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -3879,9 +3943,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "widestring" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "wildmatch" @@ -3912,33 +3976,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -3992,13 +4080,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4011,6 +4115,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4023,6 +4133,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4035,12 +4151,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4053,6 +4181,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4065,6 +4199,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4077,6 +4217,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4090,10 +4236,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.6.20" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -4108,6 +4260,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -4152,19 +4313,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", @@ -4173,18 +4333,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", @@ -4222,10 +4382,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ + "bindgen 0.71.1", "cc", "pkg-config", ] @@ -4238,9 +4399,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-jpeg" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 888be77f..f0913909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,22 +89,22 @@ workspace = true # Keep sorted [dependencies] argon2 = "0.5.3" -async-trait = "0.1.83" +async-trait = "0.1.88" axum = { version = "0.7.9", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } -axum-extra = { version = "0.9.6", features = ["typed-header"] } -axum-server = { version = "0.7.1", features = ["tls-rustls-no-provider"] } +axum-extra = { version = "0.9.5", features = ["typed-header"] } +axum-server = { version = "0.7.2", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" -bytes = "1.9.0" -clap = { version = "4.5.21", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } +bytes = "1.10.1" +clap = { version = "4.5.34", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } futures-util = { version = "0.3.31", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" -http = "1.1.0" -http-body-util = "0.1.2" -hyper = "1.5.1" +http = "1.3.1" +http-body-util = "0.1.3" +hyper = "1.6.0" hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "service"] } -image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "gif"] } -jsonwebtoken = "9.3.0" +image = { version = "0.25.6", default-features = false, features = ["jpeg", "png", "gif"] } +jsonwebtoken = "9.3.1" lru-cache = "0.1.2" num_cpus = "1.16.0" opentelemetry = "0.24.0" @@ -113,31 +113,31 @@ opentelemetry-otlp = "0.17.0" opentelemetry-prometheus = "0.17.0" opentelemetry_sdk = { version = "0.24.1", features = ["rt-tokio"] } parking_lot = { version = "0.12.3", optional = true } -phf = { version = "0.11.2", features = ["macros"] } -pin-project-lite = "0.2.15" +phf = { version = "0.11.3", features = ["macros"] } +pin-project-lite = "0.2.16" prometheus = "0.13.4" proxy-header = { version = "0.1.2", features = ["tokio"] } rand = "0.8.5" regex = "1.11.1" -reqwest = { version = "0.12.9", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } -ring = "0.17.8" +reqwest = { version = "0.12.15", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } +ring = "0.17.14" rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } -ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } -rusqlite = { version = "0.32.1", optional = true, features = ["bundled"] } -rustls = { version = "0.23.19", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } -sd-notify = { version = "0.4.3", optional = true } -serde = { version = "1.0.215", features = ["rc"] } -serde_html_form = "0.2.6" -serde_json = { version = "1.0.133", features = ["raw_value"] } +ruma = { git = "https://github.com/ruma/ruma", rev = "bd33b9a38fbf4aea0413f947469be916f120032e", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } +rusqlite = { version = "0.34.0", optional = true, features = ["bundled"] } +rustls = { version = "0.23.25", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } +sd-notify = { version = "0.4.5", optional = true } +serde = { version = "1.0.219", features = ["rc"] } +serde_html_form = "0.2.7" +serde_json = { version = "1.0.140", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" -strum = { version = "0.26.3", features = ["derive"] } -thiserror = "2.0.3" +strum = { version = "0.27.1", features = ["derive"] } +thiserror = "2.0.12" thread_local = "1.1.8" tikv-jemallocator = { version = "0.6.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } -tokio = { version = "1.41.1", features = ["fs", "macros", "signal", "sync"] } -toml = "0.8.19" -tower = { version = "0.5.1", features = ["util"] } +tokio = { version = "1.44.1", features = ["fs", "macros", "signal", "sync"] } +toml = "0.8.20" +tower = { version = "0.5.2", features = ["util"] } tower-http = { version = "0.6.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.41", features = [] } tracing-flame = "0.2.0" @@ -151,8 +151,8 @@ nix = { version = "0.29", features = ["resource", "time"] } [dev-dependencies] assert_cmd = "2.0.16" -insta = { version = "1.41.1", features = ["filters", "json", "redactions"] } -predicates = "3.1.2" +insta = { version = "1.42.2", features = ["filters", "json", "redactions"] } +predicates = "3.1.3" [profile.dev.package.insta] opt-level = 3 From ed12b0bf67d402ce1d9ab059270bf6141f84926c Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 9 Feb 2025 17:35:23 +0000 Subject: [PATCH 552/617] services/sending: guard against federation requests to ourselves These would always fail and get stuck/retried forever. --- src/service/sending.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/service/sending.rs b/src/service/sending.rs index 1c3b6ecb..06a2da83 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -869,6 +869,11 @@ async fn handle_federation_event( let mut edu_jsons = Vec::new(); let mut pdu_jsons = Vec::new(); + if server == services().globals.server_name() { + warn!("Dropping outbound federation request to ourselves"); + return Ok(()); + } + for event in &events { match event { SendingEventType::Pdu(pdu_id) => { From d9c7fbbd82b14c99adaf7629bd05e47912d2365f Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 9 Feb 2025 17:51:12 +0000 Subject: [PATCH 553/617] services/sending: avoid unnecessary clone --- src/database/key_value/sending.rs | 2 +- src/service/sending.rs | 11 ++++------- src/service/sending/data.rs | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 1f7db867..70f235b7 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -55,7 +55,7 @@ impl service::sending::Data for KeyValueDatabase { fn queue_requests( &self, - requests: &[(&Destination, SendingEventType)], + requests: &[(&Destination, &SendingEventType)], ) -> Result> { let mut batch = Vec::new(); let mut keys = Vec::new(); diff --git a/src/service/sending.rs b/src/service/sending.rs index 06a2da83..803c9c1c 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -576,8 +576,7 @@ impl Service { ) -> Result<()> { let destination = Destination::Push(user.to_owned(), pushkey); let event_type = SendingEventType::Pdu(pdu_id.to_owned()); - let keys = - self.db.queue_requests(&[(&destination, event_type.clone())])?; + let keys = self.db.queue_requests(&[(&destination, &event_type)])?; self.sender .send(RequestData { destination, @@ -606,7 +605,7 @@ impl Service { }) .collect::>(); let keys = self.db.queue_requests( - &requests.iter().map(|(o, e)| (o, e.clone())).collect::>(), + &requests.iter().map(|(o, e)| (o, e)).collect::>(), )?; for ((destination, event_type), key) in requests.into_iter().zip(keys) { self.sender @@ -631,8 +630,7 @@ impl Service { ) -> Result<()> { let destination = Destination::Normal(server.to_owned()); let event_type = SendingEventType::Edu(serialized); - let keys = - self.db.queue_requests(&[(&destination, event_type.clone())])?; + let keys = self.db.queue_requests(&[(&destination, &event_type)])?; self.sender .send(RequestData { destination, @@ -653,8 +651,7 @@ impl Service { ) -> Result<()> { let destination = Destination::Appservice(appservice_id); let event_type = SendingEventType::Pdu(pdu_id); - let keys = - self.db.queue_requests(&[(&destination, event_type.clone())])?; + let keys = self.db.queue_requests(&[(&destination, &event_type)])?; self.sender .send(RequestData { destination, diff --git a/src/service/sending/data.rs b/src/service/sending/data.rs index 00795faf..39ea51d0 100644 --- a/src/service/sending/data.rs +++ b/src/service/sending/data.rs @@ -22,7 +22,7 @@ pub(crate) trait Data: Send + Sync { ) -> Result<()>; fn queue_requests( &self, - requests: &[(&Destination, SendingEventType)], + requests: &[(&Destination, &SendingEventType)], ) -> Result>; fn queued_requests<'a>( &'a self, From 81c5f099196dee3c2d561b2827a583e3180bcd30 Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 9 Feb 2025 17:55:17 +0000 Subject: [PATCH 554/617] service/sending: tiny refactors, NFC --- src/service/sending.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/service/sending.rs b/src/service/sending.rs index 803c9c1c..d940f16b 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -576,12 +576,17 @@ impl Service { ) -> Result<()> { let destination = Destination::Push(user.to_owned(), pushkey); let event_type = SendingEventType::Pdu(pdu_id.to_owned()); - let keys = self.db.queue_requests(&[(&destination, &event_type)])?; + let key = self + .db + .queue_requests(&[(&destination, &event_type)])? + .into_iter() + .next() + .unwrap(); self.sender .send(RequestData { destination, event_type, - key: keys.into_iter().next().unwrap(), + key, requester_span: Span::current(), }) .unwrap(); @@ -630,12 +635,17 @@ impl Service { ) -> Result<()> { let destination = Destination::Normal(server.to_owned()); let event_type = SendingEventType::Edu(serialized); - let keys = self.db.queue_requests(&[(&destination, &event_type)])?; + let key = self + .db + .queue_requests(&[(&destination, &event_type)])? + .into_iter() + .next() + .unwrap(); self.sender .send(RequestData { destination, event_type, - key: keys.into_iter().next().unwrap(), + key, requester_span: Span::current(), }) .unwrap(); @@ -651,12 +661,17 @@ impl Service { ) -> Result<()> { let destination = Destination::Appservice(appservice_id); let event_type = SendingEventType::Pdu(pdu_id); - let keys = self.db.queue_requests(&[(&destination, &event_type)])?; + let key = self + .db + .queue_requests(&[(&destination, &event_type)])? + .into_iter() + .next() + .unwrap(); self.sender .send(RequestData { destination, event_type, - key: keys.into_iter().next().unwrap(), + key, requester_span: Span::current(), }) .unwrap(); From 6bcc4e310e26f742dd2e8508271b93bb9b61edce Mon Sep 17 00:00:00 2001 From: Lambda Date: Sun, 9 Feb 2025 17:57:23 +0000 Subject: [PATCH 555/617] Immediately trigger EDU sending after client read receipt Previously, read receipts would only be forwarded via federation incidentally when some PDU was later sent to the destination server. Trigger a send without any event to collect EDUs and get read receipts out directly. --- book/changelog.md | 2 ++ src/api/client_server/read_marker.rs | 8 ++++++ src/service/sending.rs | 43 +++++++++++++++++++--------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 0a233597..7ed662b9 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -237,6 +237,8 @@ This will be the first release of Grapevine since it was forked from Conduit revalidating every alias. This makes it possible to add/remove aliases when some of the existing aliases cannot be validated. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) +28. Fix read receipts not being sent over federation (or only arbitrarily late) + ([!162](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/162)) ### Added diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index c30a4cb8..2daabf73 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -95,6 +95,9 @@ pub(crate) async fn set_read_marker_route( room_id: body.room_id.clone(), }, )?; + for server in services().rooms.state_cache.room_servers(&body.room_id) { + services().sending.trigger_edu_send(&server?)?; + } } Ok(Ra(set_read_marker::v3::Response {})) @@ -159,6 +162,11 @@ pub(crate) async fn create_receipt_route( room_id: body.room_id.clone(), }, )?; + for server in + services().rooms.state_cache.room_servers(&body.room_id) + { + services().sending.trigger_edu_send(&server?)?; + } } create_receipt::v3::ReceiptType::ReadPrivate => { let count = services() diff --git a/src/service/sending.rs b/src/service/sending.rs index d940f16b..148d3a74 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -110,8 +110,10 @@ impl RequestKey { pub(crate) struct RequestData { destination: Destination, - event_type: SendingEventType, - key: RequestKey, + /// The PDU or reliable EDU and its associated key, or `None` if this is + /// only a trigger to collect and send EDUs to the destination (e.g. + /// read receipts). + event: Option<(SendingEventType, RequestKey)>, /// Span of the original `send_*()` method call requester_span: Span, } @@ -309,7 +311,7 @@ impl Service { } #[tracing::instrument( - skip(self, event_type, key, requester_span, current_transaction_status), + skip(self, event, requester_span, current_transaction_status), fields( current_status = ?current_transaction_status.get(&destination), ), @@ -318,8 +320,7 @@ impl Service { &self, RequestData { destination, - event_type, - key, + event, requester_span, }: RequestData, current_transaction_status: &mut TransactionStatusMap, @@ -329,7 +330,7 @@ impl Service { match self.select_events( &destination, - vec![(event_type, key)], + event.into_iter().collect(), current_transaction_status, ) { Ok(SelectedEvents::Retries(events)) => { @@ -585,8 +586,7 @@ impl Service { self.sender .send(RequestData { destination, - event_type, - key, + event: Some((event_type, key)), requester_span: Span::current(), }) .unwrap(); @@ -616,8 +616,7 @@ impl Service { self.sender .send(RequestData { destination: destination.clone(), - event_type, - key, + event: Some((event_type, key)), requester_span: Span::current(), }) .unwrap(); @@ -644,8 +643,25 @@ impl Service { self.sender .send(RequestData { destination, - event_type, - key, + event: Some((event_type, key)), + requester_span: Span::current(), + }) + .unwrap(); + + Ok(()) + } + + #[tracing::instrument(skip(self))] + pub(crate) fn trigger_edu_send(&self, server: &ServerName) -> Result<()> { + if server == services().globals.server_name() { + debug!("Ignoring EDU send request to ourselves"); + return Ok(()); + } + let destination = Destination::Normal(server.to_owned()); + self.sender + .send(RequestData { + destination, + event: None, requester_span: Span::current(), }) .unwrap(); @@ -670,8 +686,7 @@ impl Service { self.sender .send(RequestData { destination, - event_type, - key, + event: Some((event_type, key)), requester_span: Span::current(), }) .unwrap(); From 1cd12460d7e7244de2725e3386dc48b9e6977670 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 30 Mar 2025 16:42:22 -0700 Subject: [PATCH 556/617] make systemd patient for startup RocksDB compaction can take quite a while if it doesn't happen regularly, which it doesn't, currently. --- nix/modules/default/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 37912a83..1bd8f8d6 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -133,6 +133,7 @@ in StateDirectoryMode = "0700"; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" ]; + TimeoutStartSec = "infinity"; Type = "notify"; UMask = "077"; User = if cfg.settings.conduit_compat From 1529d60a6a0428e87abcf35cca29ee6b03b3f4d8 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 6 Apr 2025 20:03:09 -0700 Subject: [PATCH 557/617] set default database backend in nixos module to rocksdb The media backend already has a default value. --- nix/modules/default/default.nix | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 1bd8f8d6..38468e44 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -40,18 +40,27 @@ in ''; default = false; }; - database.path = lib.mkOption { - type = types.nonEmptyStr; - readOnly = true; - description = '' - The path to store database files in. + database = { + backend = lib.mkOption { + type = types.nonEmptyStr; + description = '' + The database backend to use. + ''; + default = "rocksdb"; + }; + path = lib.mkOption { + type = types.nonEmptyStr; + readOnly = true; + description = '' + The path to store database files in. - Note that this is read-only because this module makes use of - systemd's `StateDirectory` option. - ''; - default = if cfg.settings.conduit_compat - then "/var/lib/matrix-conduit/database" - else "/var/lib/grapevine/database"; + Note that this is read-only because this module makes use of + systemd's `StateDirectory` option. + ''; + default = if cfg.settings.conduit_compat + then "/var/lib/matrix-conduit/database" + else "/var/lib/grapevine/database"; + }; }; media.backend = { type = lib.mkOption { From c03103a14208e39e64548e36f3953e9d654b21fd Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Fri, 11 Apr 2025 13:06:02 -0700 Subject: [PATCH 558/617] document that conduwuit migration is unlikely to work says that they broke db compatibility explicitly. I'm not sure when this happened and which versions are compatible. Until we evaluate that we should warn people not to try. --- book/SUMMARY.md | 1 + book/installing/migrating-conduwuit.md | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 book/installing/migrating-conduwuit.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 34bdc3d3..b1317642 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -6,6 +6,7 @@ * [Installing](./installing.md) * [Supported targets](./installing/supported-targets.md) * [Migrating to/from Conduit](./installing/migrating-conduit.md) + * [Migrating to/from Conduwuit](./installing/migrating-conduwuit.md) * [Contributing](./contributing.md) * [Coordinated vulnerability disclosure](./contributing/security.md) * [Style guide](./contributing/style-guide.md) diff --git a/book/installing/migrating-conduwuit.md b/book/installing/migrating-conduwuit.md new file mode 100644 index 00000000..7530f532 --- /dev/null +++ b/book/installing/migrating-conduwuit.md @@ -0,0 +1,6 @@ +# Migrating to/from Conduwuit + +Current Conduwuit is explicitly incompatible with the Conduit/Grapevine +database format. Some older versions have been migrated to Grapevine +successfully, but we haven't evaluated which versions it is safe to migrate +from yet. Try this at your own risk, and *definitely* take a backup first. From abb1b5681ebd08f78d62420ce175ea852dcafa45 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 6 Apr 2025 17:43:12 -0700 Subject: [PATCH 559/617] add partial_canonicalize helper function This is useful for checking for potential overlap between paths that have not been fully created yet. --- Cargo.lock | 14 +++++++ Cargo.toml | 1 + src/utils.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2641dca3..59f2ff41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -962,6 +962,7 @@ dependencies = [ "serde_yaml", "sha-1", "strum", + "tempfile", "thiserror 2.0.12", "thread_local", "tikv-jemallocator", @@ -3195,6 +3196,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "terminal_size" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index f0913909..00971197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,6 +153,7 @@ nix = { version = "0.29", features = ["resource", "time"] } assert_cmd = "2.0.16" insta = { version = "1.42.2", features = ["filters", "json", "redactions"] } predicates = "3.1.3" +tempfile = "3.19.1" [profile.dev.package.insta] opt-level = 3 diff --git a/src/utils.rs b/src/utils.rs index 45211399..568bc5be 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,6 +2,8 @@ use std::{ borrow::Cow, cmp, fmt, fmt::Write, + io, + path::{Component, Path, PathBuf}, str::FromStr, time::{SystemTime, UNIX_EPOCH}, }; @@ -14,6 +16,7 @@ use ruma::{ api::client::error::ErrorKind, canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri, }; +use tokio::fs; use crate::{Error, Result}; @@ -380,9 +383,67 @@ pub(crate) fn u8_slice_to_hex(slice: &[u8]) -> String { }) } +/// Canonicalize a path where some components may not exist yet. +/// +/// It's assumed that non-existent components will be created as +/// directories. This should match the result of [`fs::canonicalize`] +/// _after_ calling [`fs::create_dir_all`] on `path`. +#[allow(dead_code)] +pub(crate) async fn partial_canonicalize(path: &Path) -> io::Result { + let mut ret = std::env::current_dir()?; + + let mut base_path = Cow::Borrowed(path); + let mut components = base_path.components(); + + while let Some(component) = components.next() { + match component { + Component::Prefix(_) | Component::RootDir => { + let component_path: &Path = component.as_ref(); + component_path.clone_into(&mut ret); + } + Component::CurDir => (), + Component::ParentDir => { + ret.pop(); + } + Component::Normal(p) => { + let component_path = ret.join(p); + match fs::symlink_metadata(&component_path).await { + // path is a symlink + Ok(metadata) if metadata.is_symlink() => { + let destination = + fs::read_link(&component_path).await?; + // iterate over the symlink destination components + // before continuing with the original path + base_path = + Cow::Owned(destination.join(components.as_path())); + components = base_path.components(); + } + // path exists, not a symlink + Ok(_) => { + ret.push(p); + } + // path does not exist + Err(error) if error.kind() == io::ErrorKind::NotFound => { + // assume a directory will be created here + ret.push(p); + } + Err(error) => return Err(error), + } + } + } + } + + Ok(ret) +} + #[cfg(test)] mod tests { - use crate::utils::{dbg_truncate_str, u8_slice_to_hex}; + use tempfile::TempDir; + use tokio::fs; + + use crate::utils::{ + dbg_truncate_str, partial_canonicalize, u8_slice_to_hex, + }; #[test] fn test_truncate_str() { @@ -412,4 +473,52 @@ mod tests { 4242424242424242424242424242424242424242" ); } + + #[tokio::test] + async fn test_partial_canonicalize() { + let tmp_dir = + TempDir::with_prefix("test_partial_canonicalize").unwrap(); + let path = tmp_dir.path(); + + fs::create_dir(&path.join("dir")).await.unwrap(); + fs::symlink(path.join("dir"), path.join("absolute-link-to-dir")) + .await + .unwrap(); + fs::symlink("./dir", path.join("relative-link-to-dir")).await.unwrap(); + + assert_eq!(partial_canonicalize(path).await.unwrap(), path); + assert_eq!(partial_canonicalize(&path.join("./")).await.unwrap(), path); + assert_eq!( + partial_canonicalize(&path.join("dir/..")).await.unwrap(), + path + ); + assert_eq!( + partial_canonicalize(&path.join("absolute-link-to-dir")) + .await + .unwrap(), + path.join("dir") + ); + assert_eq!( + partial_canonicalize(&path.join("relative-link-to-dir")) + .await + .unwrap(), + path.join("dir") + ); + assert_eq!( + partial_canonicalize(&path.join("absolute-link-to-dir/new-dir")) + .await + .unwrap(), + path.join("dir/new-dir") + ); + assert_eq!( + partial_canonicalize( + &path.join("absolute-link-to-dir/new-dir/../..") + ) + .await + .unwrap(), + path, + ); + + tmp_dir.close().unwrap(); + } } From 33f35926121d910e906b33c9d7ee7b77d1963fdf Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 6 Apr 2025 19:38:55 -0700 Subject: [PATCH 560/617] fix starting the server when db/media dirs do not exist yet This used to be supported, as we explicitly call std::fs::create_dir_all when initializing these, but it was broken in b01b70fc20f334d5060f14799bca7687eaeafd77, which attempts to canonicalize the paths to check for overlap before creating them. --- src/config.rs | 12 ++++++------ src/utils.rs | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4a174c35..9ad6e1ec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,7 +15,7 @@ use ruma::{ use serde::Deserialize; use strum::{Display, EnumIter, IntoEnumIterator}; -use crate::error; +use crate::{error, utils::partial_canonicalize}; mod env_filter_clone; mod proxy; @@ -548,13 +548,13 @@ where } if !sandboxed { - let media_path = x - .path - .canonicalize() + let media_path = partial_canonicalize(&x.path) + .await .map_err(|e| Error::Canonicalize(e, x.path.clone()))?; - let database_path = - config.database.path.canonicalize().map_err(|e| { + let database_path = partial_canonicalize(&config.database.path) + .await + .map_err(|e| { Error::Canonicalize(e, config.database.path.clone()) })?; diff --git a/src/utils.rs b/src/utils.rs index 568bc5be..48803d82 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -388,7 +388,6 @@ pub(crate) fn u8_slice_to_hex(slice: &[u8]) -> String { /// It's assumed that non-existent components will be created as /// directories. This should match the result of [`fs::canonicalize`] /// _after_ calling [`fs::create_dir_all`] on `path`. -#[allow(dead_code)] pub(crate) async fn partial_canonicalize(path: &Path) -> io::Result { let mut ret = std::env::current_dir()?; From e98dd5b9a3679dad5f1b75b0b0b97e11f0e4ab37 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 15 Dec 2024 21:43:29 -0800 Subject: [PATCH 561/617] put global trust-dns resolver in an Arc This allows us to reference it in reqwest clients configuration. --- src/service/globals.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/service/globals.rs b/src/service/globals.rs index ea1dce4f..96c62697 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -74,7 +74,7 @@ pub(crate) struct Service { pub(crate) tls_name_override: Arc>, pub(crate) config: Config, keypair: Arc, - dns_resolver: TokioAsyncResolver, + dns_resolver: Arc, jwt_decoding_key: Option, federation_client: reqwest::Client, default_client: reqwest::Client, @@ -215,6 +215,18 @@ impl Service { }; let tls_name_override = Arc::new(StdRwLock::new(TlsNameMap::new())); + let dns_resolver = Arc::new( + TokioAsyncResolver::tokio_from_system_conf().map_err(|e| { + error!( + "Failed to set up trust dns resolver with system config: \ + {}", + e + ); + Error::bad_config( + "Failed to set up trust dns resolver with system config.", + ) + })?, + ); let jwt_decoding_key = config.jwt_secret.as_ref().map(|secret| { jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()) @@ -255,18 +267,7 @@ impl Service { config, reload_handles: reload_handles.map(|h| Arc::new(RwLock::new(h))), keypair: Arc::new(keypair), - dns_resolver: TokioAsyncResolver::tokio_from_system_conf() - .map_err(|e| { - error!( - "Failed to set up trust dns resolver with system \ - config: {}", - e - ); - Error::bad_config( - "Failed to set up trust dns resolver with system \ - config.", - ) - })?, + dns_resolver, actual_destination_cache: Arc::new( RwLock::new(WellKnownMap::new()), ), From e249aed1cb4ce67b93f7932b8b748e20da8a27ca Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 15 Dec 2024 22:02:13 -0800 Subject: [PATCH 562/617] rename Resolver -> FederationResolver and document --- src/service/globals.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/service/globals.rs b/src/service/globals.rs index 96c62697..0f42d9fc 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -141,21 +141,29 @@ impl Default for RotationHandler { } } -pub(crate) struct Resolver { +/// Resolver used for outgoing requests to the federation API. +/// +/// Hostnames that have been mapped to a different domain by SRV records in +/// [server discovery][1] are resolved to the SRV record target. This is done to +/// get reqwest to check the TLS certificate against the correct hostname +/// required in steps 3.3, 3.4, and 4 of the server discovery spec. +/// +/// [1]: https://spec.matrix.org/v1.12/server-server-api/#server-discovery +pub(crate) struct FederationResolver { inner: GaiResolver, overrides: Arc>, } -impl Resolver { +impl FederationResolver { pub(crate) fn new(overrides: Arc>) -> Self { - Resolver { + FederationResolver { inner: GaiResolver::new(), overrides, } } } -impl Resolve for Resolver { +impl Resolve for FederationResolver { #[tracing::instrument(skip(self))] fn resolve(&self, name: Name) -> Resolving { self.overrides @@ -234,7 +242,9 @@ impl Service { let default_client = reqwest_client_builder(&config)?.build()?; let federation_client = reqwest_client_builder(&config)? - .dns_resolver(Arc::new(Resolver::new(tls_name_override.clone()))) + .dns_resolver(Arc::new(FederationResolver::new( + tls_name_override.clone(), + ))) .build()?; // Supported and stable room versions From 6cb7896e1740c6fdf0ce2ad9e35563a70a43f579 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Mon, 16 Dec 2024 01:12:06 -0800 Subject: [PATCH 563/617] use trust-dns for all DNS queries Previously we were only using trust-dns for resolving SRV records in server discovery, and then for resolving the hostname from the SRV record target if one exists. With the previous behavior, admins need to ensure that both their system resolver and trust-dns are working correctly in order for outgoing traffic to work reliably. This can be confusing to debug, because it's not obvious to the admin if or when each resolver are being used. Now, everything goes through trust-dns and outgoing federation DNS should fail/succeed more predictably. I also expect some performance improvement from having an in-process DNS cache, but haven't taken measurements yet. --- Cargo.lock | 1 - Cargo.toml | 1 - book/changelog.md | 3 ++ src/service/globals.rs | 82 +++++++++++++++++++++++++----------------- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59f2ff41..d257d117 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -929,7 +929,6 @@ dependencies = [ "http", "http-body-util", "hyper", - "hyper-util", "image", "insta", "jsonwebtoken", diff --git a/Cargo.toml b/Cargo.toml index 00971197..0cf41846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,6 @@ html-escape = "0.2.13" http = "1.3.1" http-body-util = "0.1.3" hyper = "1.6.0" -hyper-util = { version = "0.1.10", features = ["client", "client-legacy", "service"] } image = { version = "0.25.6", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.3.1" lru-cache = "0.1.2" diff --git a/book/changelog.md b/book/changelog.md index 7ed662b9..b0baf25f 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -150,6 +150,9 @@ This will be the first release of Grapevine since it was forked from Conduit database path. ([!140](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/140), [!170](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/170)) +14. Use trust-dns for all DNS queries, instead of only for SRV records and SRV + record targets in server discovery. + ([!156](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/156)) ### Fixed diff --git a/src/service/globals.rs b/src/service/globals.rs index 0f42d9fc..ede9f5c6 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -1,6 +1,5 @@ use std::{ collections::{BTreeMap, HashMap}, - error::Error as StdError, fs, future::{self, Future}, iter, @@ -14,11 +13,6 @@ use std::{ }; use base64::{engine::general_purpose, Engine as _}; -use futures_util::FutureExt; -use hyper::service::Service as _; -use hyper_util::{ - client::legacy::connect::dns::GaiResolver, service::TowerToHyperService, -}; use reqwest::dns::{Addrs, Name, Resolve, Resolving}; use ruma::{ api::federation::discovery::ServerSigningKeys, @@ -141,6 +135,38 @@ impl Default for RotationHandler { } } +/// Wrapper around [`trust_dns_resolver`]'s [`TokioAsyncResolver`] that can be +/// used with reqwest. +pub(crate) struct DefaultResolver { + inner: Arc, +} + +impl DefaultResolver { + fn new(inner: Arc) -> Self { + DefaultResolver { + inner, + } + } + + fn resolve_inner(&self, name: Name) -> Resolving { + let inner = Arc::clone(&self.inner); + let future = async move { + let lookup = inner.lookup_ip(name.as_str()).await?; + let addrs: Addrs = + Box::new(lookup.into_iter().map(|ip| SocketAddr::new(ip, 0))); + Ok(addrs) + }; + Box::pin(future.in_current_span()) + } +} + +impl Resolve for DefaultResolver { + #[tracing::instrument(skip(self))] + fn resolve(&self, name: Name) -> Resolving { + self.resolve_inner(name) + } +} + /// Resolver used for outgoing requests to the federation API. /// /// Hostnames that have been mapped to a different domain by SRV records in @@ -150,14 +176,17 @@ impl Default for RotationHandler { /// /// [1]: https://spec.matrix.org/v1.12/server-server-api/#server-discovery pub(crate) struct FederationResolver { - inner: GaiResolver, + inner: Arc, overrides: Arc>, } impl FederationResolver { - pub(crate) fn new(overrides: Arc>) -> Self { + pub(crate) fn new( + inner: Arc, + overrides: Arc>, + ) -> Self { FederationResolver { - inner: GaiResolver::new(), + inner, overrides, } } @@ -181,26 +210,7 @@ impl Resolve for FederationResolver { x }) }) - .unwrap_or_else(|| { - // This should never fail because reqwest's type is a wrapper - // around hyper-utils' type - let name = name.as_str().parse().expect("name should be valid"); - - Box::pin( - TowerToHyperService::new(self.inner.clone()) - .call(name) - .map(|result| { - result - .map(|addrs| -> Addrs { Box::new(addrs) }) - .map_err( - |err| -> Box { - Box::new(err) - }, - ) - }) - .in_current_span(), - ) - }) + .unwrap_or_else(|| self.inner.resolve_inner(name)) } } @@ -235,16 +245,22 @@ impl Service { ) })?, ); + let default_resolver = + Arc::new(DefaultResolver::new(Arc::clone(&dns_resolver))); + let federation_resolver = Arc::new(FederationResolver::new( + Arc::clone(&default_resolver), + Arc::clone(&tls_name_override), + )); let jwt_decoding_key = config.jwt_secret.as_ref().map(|secret| { jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()) }); - let default_client = reqwest_client_builder(&config)?.build()?; + let default_client = reqwest_client_builder(&config)? + .dns_resolver(default_resolver) + .build()?; let federation_client = reqwest_client_builder(&config)? - .dns_resolver(Arc::new(FederationResolver::new( - tls_name_override.clone(), - ))) + .dns_resolver(federation_resolver) .build()?; // Supported and stable room versions From d1370f9834a5fd8d9af4502cbbeb81f82c029ed2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 10 Oct 2024 16:23:03 -0700 Subject: [PATCH 564/617] refactor fetch_unknown_prev_events Early returns (or continues, in this case) good. --- src/service/rooms/event_handler.rs | 87 +++++++++++++++--------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 1543e423..cbe8079d 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1442,7 +1442,7 @@ impl Service { let mut amount = 0; while let Some(prev_event_id) = todo_outlier_stack.pop() { - if let Some((pdu, json_opt)) = self + let Some((pdu, json_opt)) = self .fetch_and_handle_outliers( origin, &[prev_event_id.clone()], @@ -1452,51 +1452,50 @@ impl Service { ) .await .pop() - { - Self::check_room_id(room_id, &pdu)?; - - if amount > services().globals.max_fetch_prev_events() { - // Max limit reached - warn!("Max prev event limit reached!"); - graph.insert(prev_event_id.clone(), HashSet::new()); - continue; - } - - if let Some(json) = json_opt.or_else(|| { - services() - .rooms - .outlier - .get_outlier_pdu_json(&prev_event_id) - .ok() - .flatten() - }) { - if pdu.origin_server_ts > first_pdu_in_room.origin_server_ts - { - amount += 1; - for prev_prev in &pdu.prev_events { - if !graph.contains_key(prev_prev) { - todo_outlier_stack.push(prev_prev.clone()); - } - } - - graph.insert( - prev_event_id.clone(), - pdu.prev_events.iter().cloned().collect(), - ); - } else { - // Time based check failed - graph.insert(prev_event_id.clone(), HashSet::new()); - } - - eventid_info.insert(prev_event_id.clone(), (pdu, json)); - } else { - // Get json failed, so this was not fetched over federation - graph.insert(prev_event_id.clone(), HashSet::new()); - } - } else { - // Fetch and handle failed + else { graph.insert(prev_event_id.clone(), HashSet::new()); + continue; + }; + + Self::check_room_id(room_id, &pdu)?; + + if amount > services().globals.max_fetch_prev_events() { + warn!("Max prev event limit reached"); + graph.insert(prev_event_id.clone(), HashSet::new()); + continue; } + + let Some(json) = json_opt.or_else(|| { + services() + .rooms + .outlier + .get_outlier_pdu_json(&prev_event_id) + .ok() + .flatten() + }) else { + graph.insert(prev_event_id.clone(), HashSet::new()); + continue; + }; + + if pdu.origin_server_ts <= first_pdu_in_room.origin_server_ts { + graph.insert(prev_event_id.clone(), HashSet::new()); + continue; + } + + amount += 1; + + for prev_prev in &pdu.prev_events { + if !graph.contains_key(prev_prev) { + todo_outlier_stack.push(prev_prev.clone()); + } + } + + graph.insert( + prev_event_id.clone(), + pdu.prev_events.iter().cloned().collect(), + ); + + eventid_info.insert(prev_event_id.clone(), (pdu, json)); } let sorted = From 48ecf50973b759eb6ca1940650e074f2b2a925f3 Mon Sep 17 00:00:00 2001 From: avdb13 Date: Thu, 13 Feb 2025 21:23:55 +0000 Subject: [PATCH 565/617] update documentation of `service::users::Data::iter` --- src/database/key_value/users.rs | 4 ++-- src/service/users.rs | 4 ++-- src/service/users/data.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index e3fdb95e..4c2295de 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -35,7 +35,7 @@ impl service::users::Data for KeyValueDatabase { .is_empty()) } - /// Returns the number of users registered on this server. + /// Returns the number of local and remote users known by this server. fn count(&self) -> Result { Ok(self.userid_password.iter().count()) } @@ -84,7 +84,7 @@ impl service::users::Data for KeyValueDatabase { ) } - /// Returns an iterator over all users on this homeserver. + /// Returns an iterator over all local and remote users on this homeserver. fn iter<'a>( &'a self, ) -> Box> + 'a> { diff --git a/src/service/users.rs b/src/service/users.rs index 3ee47386..b05a2ccb 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -282,7 +282,7 @@ impl Service { Ok(()) } - /// Returns the number of users registered on this server. + /// Returns the number of local and remote users known by this server. pub(crate) fn count(&self) -> Result { self.db.count() } @@ -295,7 +295,7 @@ impl Service { self.db.find_from_token(token) } - /// Returns an iterator over all users on this homeserver. + /// Returns an iterator over all local and remote users on this homeserver. pub(crate) fn iter( &self, ) -> impl Iterator> + '_ { diff --git a/src/service/users/data.rs b/src/service/users/data.rs index 630ded7d..579bdfb0 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -18,7 +18,7 @@ pub(crate) trait Data: Send + Sync { /// Check if account is deactivated fn is_deactivated(&self, user_id: &UserId) -> Result; - /// Returns the number of users registered on this server. + /// Returns the number of local and remote users known by this server. fn count(&self) -> Result; /// Find out which user an access token belongs to. @@ -27,7 +27,7 @@ pub(crate) trait Data: Send + Sync { token: &str, ) -> Result>; - /// Returns an iterator over all users on this homeserver. + /// Returns an iterator over all local and remote users on this homeserver. fn iter<'a>(&'a self) -> Box> + 'a>; From d425ba72f879854e10de5f8f2e4b6bc18257eb89 Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 17 Apr 2025 17:34:49 +0000 Subject: [PATCH 566/617] Update ruma to 0.12.2 --- .cargo/config.toml | 2 ++ Cargo.lock | 60 +++++++++++++++--------------------- Cargo.toml | 2 +- nix/pkgs/default/default.nix | 1 + src/api/server_server.rs | 1 + src/service/pusher.rs | 3 +- src/service/sending.rs | 5 ++- src/utils/error.rs | 2 +- 8 files changed, 36 insertions(+), 40 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..4c64d1c8 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +RUMA_UNSTABLE_EXHAUSTIVE_TYPES = "1" diff --git a/Cargo.lock b/Cargo.lock index d257d117..199a5971 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2480,8 +2480,8 @@ dependencies = [ [[package]] name = "ruma" -version = "0.11.1" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.12.2" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "assign", "js_int", @@ -2491,7 +2491,6 @@ dependencies = [ "ruma-common", "ruma-events", "ruma-federation-api", - "ruma-identity-service-api", "ruma-push-gateway-api", "ruma-server-util", "ruma-signatures", @@ -2501,8 +2500,8 @@ dependencies = [ [[package]] name = "ruma-appservice-api" -version = "0.11.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.12.1" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "js_int", "ruma-common", @@ -2513,8 +2512,8 @@ dependencies = [ [[package]] name = "ruma-client-api" -version = "0.19.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.20.2" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "as_variant", "assign", @@ -2536,13 +2535,14 @@ dependencies = [ [[package]] name = "ruma-common" -version = "0.14.1" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.15.2" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "as_variant", "base64 0.22.1", "bytes", "form_urlencoded", + "getrandom 0.2.15", "http", "indexmap 2.8.0", "js_int", @@ -2566,8 +2566,8 @@ dependencies = [ [[package]] name = "ruma-events" -version = "0.29.1" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.30.2" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "as_variant", "indexmap 2.8.0", @@ -2589,8 +2589,8 @@ dependencies = [ [[package]] name = "ruma-federation-api" -version = "0.10.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.11.1" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "bytes", "http", @@ -2607,27 +2607,17 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" -version = "0.10.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.10.1" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "js_int", "thiserror 2.0.12", ] -[[package]] -name = "ruma-identity-service-api" -version = "0.10.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" -dependencies = [ - "js_int", - "ruma-common", - "serde", -] - [[package]] name = "ruma-macros" -version = "0.14.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.15.1" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2641,8 +2631,8 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" -version = "0.10.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.11.0" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "js_int", "ruma-common", @@ -2653,8 +2643,8 @@ dependencies = [ [[package]] name = "ruma-server-util" -version = "0.4.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.5.0" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "headers", "http", @@ -2666,8 +2656,8 @@ dependencies = [ [[package]] name = "ruma-signatures" -version = "0.16.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.17.1" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2682,8 +2672,8 @@ dependencies = [ [[package]] name = "ruma-state-res" -version = "0.12.0" -source = "git+https://github.com/ruma/ruma?rev=bd33b9a38fbf4aea0413f947469be916f120032e#bd33b9a38fbf4aea0413f947469be916f120032e" +version = "0.13.0" +source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index 0cf41846..65761015 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ regex = "1.11.1" reqwest = { version = "0.12.15", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.14" rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } -ruma = { git = "https://github.com/ruma/ruma", rev = "bd33b9a38fbf4aea0413f947469be916f120032e", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } +ruma = { git = "https://gitlab.computer.surgery/matrix/ruma.git", rev = "ruma-0.12.2+grapevine-1", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.34.0", optional = true, features = ["bundled"] } rustls = { version = "0.23.25", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.5", optional = true } diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index 3fcca36b..a9abdc5a 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -77,6 +77,7 @@ let # Keep sorted include = [ + ".cargo/config.toml" "Cargo.lock" "Cargo.toml" "src" diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 9c29b60b..5bb7b554 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1901,6 +1901,7 @@ pub(crate) async fn create_invite_route( .event_id() .as_bytes()])) .into(), + ephemeral: Vec::new(), }, ) .await?; diff --git a/src/service/pusher.rs b/src/service/pusher.rs index c2ae7c46..1b5d375d 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -263,8 +263,7 @@ impl Service { pusher.ids.app_id.clone(), pusher.ids.pushkey.clone(), ); - device.data.default_payload = http.default_payload.clone(); - device.data.format.clone_from(&http.format); + device.data = http.clone().into(); // Tweaks are only added if the format is NOT event_id_only if !event_id_only { diff --git a/src/service/sending.rs b/src/service/sending.rs index 148d3a74..1b7e8d01 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -775,7 +775,9 @@ async fn handle_appservice_event( ); } SendingEventType::Edu(_) => { - // Appservices don't need EDUs (?) + // TODO: send EDUs in + // `appservice::event::push_events::v1::Request::ephemeral` if + // enabled in registration } } } @@ -796,6 +798,7 @@ async fn handle_appservice_event( SendingEventType::Pdu(b) => b.as_bytes(), }))) .into(), + ephemeral: Vec::new(), }, ) .await?; diff --git a/src/utils/error.rs b/src/utils/error.rs index 49b5ab15..0b5420b3 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -67,7 +67,7 @@ pub(crate) enum Error { BadDatabase(&'static str), #[error("uiaa")] Uiaa(UiaaInfo), - #[error("{0}: {1}")] + #[error("{}: {}", .0.errcode(), .1)] BadRequest(ErrorKind, &'static str), // This is only needed for when a room alias already exists #[error("{0}")] From c965c9747b70252976fce59c1da1b61ebe94195d Mon Sep 17 00:00:00 2001 From: Lambda Date: Thu, 29 May 2025 19:49:44 +0000 Subject: [PATCH 567/617] Send User-Agent header in outbound requests --- book/changelog.md | 2 ++ src/service/globals.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/book/changelog.md b/book/changelog.md index b0baf25f..978f1a7e 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -324,3 +324,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124)) 26. Allow adding canonical aliases from remote servers. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) +27. Grapevine now sends a User-Agent header on outbound requests + ([!189](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/189)) diff --git a/src/service/globals.rs b/src/service/globals.rs index ede9f5c6..5f8e4c27 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -653,7 +653,8 @@ fn reqwest_client_builder(config: &Config) -> Result { let mut reqwest_client_builder = reqwest::Client::builder() .pool_max_idle_per_host(0) .connect_timeout(Duration::from_secs(30)) - .timeout(Duration::from_secs(60 * 3)); + .timeout(Duration::from_secs(60 * 3)) + .user_agent(format!("{}/{}", env!("CARGO_PKG_NAME"), crate::version())); if let Some(proxy) = config.proxy.to_proxy()? { reqwest_client_builder = reqwest_client_builder.proxy(proxy); From 188eac5cfdc524204e79362fd2a24c3fa4a4411f Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 4 May 2025 14:15:27 -0700 Subject: [PATCH 568/617] include ban reasons when banning a user that already had a member event The case where the user never had a member event in the room is already handled correctly. --- book/changelog.md | 3 +++ src/api/client_server/membership.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 978f1a7e..ae9c8a6a 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -242,6 +242,9 @@ This will be the first release of Grapevine since it was forked from Conduit ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) 28. Fix read receipts not being sent over federation (or only arbitrarily late) ([!162](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/162)) +29. Fix bug where ban reasons would be ignored when the banned user already had + a member event in the room. + ([!185](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/185)) ### Added diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 7d6c9cdb..21a75be9 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -288,6 +288,7 @@ pub(crate) async fn ban_user_route( RoomMemberEventContent { membership: MembershipState::Ban, join_authorized_via_users_server: None, + reason: body.reason.clone(), ..event } }) From 868bb44adfb296d57fa7b933aeedeaa9733e55eb Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Sat, 24 May 2025 15:18:42 +0200 Subject: [PATCH 569/617] support listening on Unix sockets --- Cargo.lock | 5 +- Cargo.toml | 2 +- book/changelog.md | 2 + src/cli/serve.rs | 155 +++++++++++++++--- src/config.rs | 26 ++- tests/integrations/check_config.rs | 6 + .../fixtures/check_config/unix-socket.toml | 14 ++ ...check_config__unix_socket@status_code.snap | 7 + ...ons__check_config__unix_socket@stderr.snap | 14 ++ ...ons__check_config__unix_socket@stdout.snap | 5 + 10 files changed, 202 insertions(+), 34 deletions(-) create mode 100644 tests/integrations/fixtures/check_config/unix-socket.toml create mode 100644 tests/integrations/snapshots/integrations__check_config__unix_socket@status_code.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__unix_socket@stderr.snap create mode 100644 tests/integrations/snapshots/integrations__check_config__unix_socket@stdout.snap diff --git a/Cargo.lock b/Cargo.lock index 199a5971..6e0714f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,9 +210,8 @@ dependencies = [ [[package]] name = "axum-server" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" +version = "0.7.2+grapevine-1" +source = "git+https://gitlab.computer.surgery/matrix/thirdparty/axum-server.git?rev=v0.7.2%2Bgrapevine-1#1f9b20296494792a1f09ab14689f3b2954b4f782" dependencies = [ "arc-swap", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 65761015..d8fb1488 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ argon2 = "0.5.3" async-trait = "0.1.88" axum = { version = "0.7.9", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } axum-extra = { version = "0.9.5", features = ["typed-header"] } -axum-server = { version = "0.7.2", features = ["tls-rustls-no-provider"] } +axum-server = { git = "https://gitlab.computer.surgery/matrix/thirdparty/axum-server.git", rev = "v0.7.2+grapevine-1", version = "0.7.2", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" bytes = "1.10.1" clap = { version = "4.5.34", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } diff --git a/book/changelog.md b/book/changelog.md index ae9c8a6a..23ae40d6 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -329,3 +329,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) 27. Grapevine now sends a User-Agent header on outbound requests ([!189](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/189)) +28. Added the ability to listen on Unix sockets + ([!187](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/187)) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index d08807b6..e1bc79c7 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -1,12 +1,13 @@ use std::{ - collections::HashSet, convert::Infallible, future::Future, net::SocketAddr, - sync::atomic, time::Duration, + collections::HashSet, convert::Infallible, future::Future, + net::SocketAddr as IpSocketAddr, + os::unix::net::SocketAddr as UnixSocketAddr, sync::atomic, time::Duration, }; use axum::{ extract::{ - connect_info::IntoMakeServiceWithConnectInfo, ConnectInfo, - DefaultBodyLimit, FromRequestParts, MatchedPath, + connect_info::{Connected, IntoMakeServiceWithConnectInfo}, + ConnectInfo, DefaultBodyLimit, FromRequestParts, MatchedPath, }, middleware::AddExtension, response::IntoResponse, @@ -18,7 +19,7 @@ use axum_server::{ bind, service::SendService, tls_rustls::{RustlsAcceptor, RustlsConfig}, - Handle as ServerHandle, Server, + Address, Server, }; use http::{ header::{self, HeaderName}, @@ -34,9 +35,9 @@ use ruma::api::{ federation::discovery::get_server_version, IncomingRequest, }; +use strum::Display; use tokio::{ io::{AsyncRead, AsyncWrite}, - net::TcpStream, signal, task::JoinSet, }; @@ -84,6 +85,9 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { .map_err(Error::DatabaseError)?, )); + // This struct will remove old Unix sockets once it's dropped. + let _clean_up_socks = CleanUpUnixSockets(config.listen.clone()); + Services::new(db, config, Some(reload_handles)) .map_err(Error::InitializeServices)? .install(); @@ -104,6 +108,29 @@ pub(crate) async fn run(args: ServeArgs) -> Result<(), error::ServeCommand> { Ok(()) } +struct CleanUpUnixSockets(Vec); + +impl Drop for CleanUpUnixSockets { + fn drop(&mut self) { + // Remove old Unix sockets + for listen in &self.0 { + if let ListenTransport::Unix { + path, + .. + } = &listen.transport + { + info!( + path = path.display().to_string(), + "Removing Unix socket" + ); + if let Err(error) = std::fs::remove_file(path) { + warn!(%error, "Couldn't remove Unix socket"); + } + } + } + } +} + #[tracing::instrument] async fn federation_self_test() -> Result<()> { let response = server_server::send_request( @@ -128,6 +155,48 @@ async fn federation_self_test() -> Result<()> { Ok(()) } +// A trait we'll implement on `axum_server::Handle` in order to be able to +// shutdown handles regardless of their generics. +trait ServerHandle: Send { + fn shutdown(&self, timeout: Option); +} + +impl ServerHandle for axum_server::Handle { + fn shutdown(&self, timeout: Option) { + self.graceful_shutdown(timeout); + } +} + +/// This type is needed to allow us to find out where incoming connections came +/// from. Before Unix socket support, we could simply use `IpSocketAddr` here, +/// but this is no longer possible. +#[derive(Clone, Display)] +enum AddrConnectInfo { + #[strum(to_string = "{0}")] + Ip(IpSocketAddr), + + #[strum(to_string = "[unix socket]")] + UnixSocket, + + #[strum(to_string = "[unknown]")] + Unknown, +} + +impl Connected for AddrConnectInfo { + fn connect_info(target: IpSocketAddr) -> Self { + Self::Ip(target) + } +} + +impl Connected for AddrConnectInfo { + fn connect_info(_target: UnixSocketAddr) -> Self { + // The `UnixSocketAddr` we get here is one that we can't recover the + // path from (`as_pathname` returns `None`), so there's no point + // in saving it (we only use all this for logging). + Self::UnixSocket + } +} + struct ServerSpawner<'cfg, M> { config: &'cfg Config, middlewares: M, @@ -135,7 +204,7 @@ struct ServerSpawner<'cfg, M> { tls_config: Option, proxy_config: ProxyAcceptorConfig, servers: JoinSet<(ListenConfig, std::io::Result<()>)>, - handles: Vec, + handles: Vec>, } impl<'cfg, M> ServerSpawner<'cfg, M> @@ -202,14 +271,20 @@ where |inner| ProxyAcceptor::new(inner, config) } - fn spawn_server_inner( + fn spawn_server_inner( &mut self, listen: ListenConfig, - server: Server, - app: IntoMakeServiceWithConnectInfo, + server: Server, + app: IntoMakeServiceWithConnectInfo, ) where - A: Accept>> - + Clone + AddrConnectInfo: Connected, + Addr: Address + Send + 'static, + Addr::Stream: Send, + Addr::Listener: Send, + A: Accept< + Addr::Stream, + AddExtension>, + > + Clone + Send + Sync + 'static, @@ -217,14 +292,14 @@ where A::Service: SendService> + Send, A::Future: Send, { - let handle = ServerHandle::new(); + let handle = axum_server::Handle::new(); let server = server.handle(handle.clone()).serve(app); self.servers.spawn(async move { let result = server.await; (listen, result) }); - self.handles.push(handle); + self.handles.push(Box::new(handle)); } fn spawn_server( @@ -233,16 +308,16 @@ where ) -> Result<(), error::Serve> { let app = routes(self.config, &listen.components) .layer(self.middlewares.clone()) - .into_make_service_with_connect_info::(); + .into_make_service_with_connect_info::(); - match listen.transport { + match &listen.transport { ListenTransport::Tcp { address, port, tls, proxy_protocol, } => { - let addr = SocketAddr::from((address, port)); + let addr = IpSocketAddr::from((*address, *port)); let server = bind(addr); match (tls, proxy_protocol) { @@ -266,6 +341,30 @@ where } } + Ok(()) + } + ListenTransport::Unix { + path, + proxy_protocol, + } => { + let addr = match UnixSocketAddr::from_pathname(path) { + Ok(addr) => addr, + Err(e) => { + // We can't use `map_err` here, as that would move + // `listen` into a closure, preventing us from using it + // later. + return Err(error::Serve::Listen(e, listen)); + } + }; + let server = bind(addr); + + if *proxy_protocol { + let server = server.map(self.proxy_acceptor_factory()); + self.spawn_server_inner(listen, server, app); + } else { + self.spawn_server_inner(listen, server, app); + } + Ok(()) } } @@ -303,21 +402,23 @@ async fn run_server() -> Result<(), error::Serve> { || { request .extensions() - .get::>() - .map(|&ConnectInfo(addr)| addr) + .get::>() + .map(|ConnectInfo(addr)| addr.clone()) }, - |h| h.proxied_address().map(|addr| addr.source), - ); + |h| { + h.proxied_address().map(|addr| { + AddrConnectInfo::Ip(addr.source) + }) + }, + ) + .unwrap_or(AddrConnectInfo::Unknown); tracing::info_span!( "http_request", otel.name = format!("{method} {endpoint}"), %method, %endpoint, - source_address = source_address.map_or( - "[unknown]".to_owned(), - |a| a.to_string() - ), + %source_address, ) }) .on_request( @@ -720,7 +821,7 @@ async fn reload_tls_config( async fn handle_signals( tls_config: Option, - handles: Vec, + handles: Vec>, ) { #[cfg(unix)] async fn wait_signal(sig: signal::unix::SignalKind) { @@ -769,7 +870,7 @@ async fn handle_signals( services().globals.shutdown(); for handle in handles { - handle.graceful_shutdown(Some(Duration::from_secs(30))); + handle.shutdown(Some(Duration::from_secs(30))); } set_application_state(ApplicationState::Stopping); diff --git a/src/config.rs b/src/config.rs index 9ad6e1ec..b597e4a9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -196,11 +196,16 @@ pub(crate) enum ListenTransport { #[serde(default = "false_fn")] proxy_protocol: bool, }, + Unix { + path: PathBuf, + #[serde(default = "false_fn")] + proxy_protocol: bool, + }, } impl Display for ListenTransport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { + match self { ListenTransport::Tcp { address, port, @@ -209,12 +214,12 @@ impl Display for ListenTransport { } => { let scheme = format!( "{}{}", - if proxy_protocol { + if *proxy_protocol { "proxy+" } else { "" }, - if tls { + if *tls { "https" } else { "http" @@ -222,6 +227,21 @@ impl Display for ListenTransport { ); write!(f, "{scheme}://{address}:{port}") } + ListenTransport::Unix { + path, + proxy_protocol, + } => { + write!( + f, + "{}http+unix://{}", + if *proxy_protocol { + "proxy+" + } else { + "" + }, + path.display() + ) + } } } } diff --git a/tests/integrations/check_config.rs b/tests/integrations/check_config.rs index 7db366e8..119acd03 100644 --- a/tests/integrations/check_config.rs +++ b/tests/integrations/check_config.rs @@ -111,3 +111,9 @@ make_snapshot_test!( "A config with the database path inside the media path fails", "database-in-media.toml", ); + +make_snapshot_test!( + unix_socket, + "A config listening to a Unix socket is valid", + "unix-socket.toml", +); diff --git a/tests/integrations/fixtures/check_config/unix-socket.toml b/tests/integrations/fixtures/check_config/unix-socket.toml new file mode 100644 index 00000000..fe088d5d --- /dev/null +++ b/tests/integrations/fixtures/check_config/unix-socket.toml @@ -0,0 +1,14 @@ +server_name = "example.com" +listen = [{ type = "unix", path = "/tmp/grapevine.sock" }] + +[server_discovery] +client.base_url = "https://matrix.example.com" + +[database] +backend = "rocksdb" +path = "tests/integrations/fixtures/check_config/dirs/a" + +[media.backend] +type = "filesystem" +path = "tests/integrations/fixtures/check_config/dirs/b" + diff --git a/tests/integrations/snapshots/integrations__check_config__unix_socket@status_code.snap b/tests/integrations/snapshots/integrations__check_config__unix_socket@status_code.snap new file mode 100644 index 00000000..2866558c --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__unix_socket@status_code.snap @@ -0,0 +1,7 @@ +--- +source: tests/integrations/check_config.rs +description: A config listening to a Unix socket is valid +--- +Some( + 0, +) diff --git a/tests/integrations/snapshots/integrations__check_config__unix_socket@stderr.snap b/tests/integrations/snapshots/integrations__check_config__unix_socket@stderr.snap new file mode 100644 index 00000000..93e08914 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__unix_socket@stderr.snap @@ -0,0 +1,14 @@ +--- +source: tests/integrations/check_config.rs +description: A config listening to a Unix socket is valid +--- +[ + { + "fields": { + "message": "Configuration looks good" + }, + "level": "INFO", + "target": "grapevine::cli::check_config", + "timestamp": "[timestamp]" + } +] diff --git a/tests/integrations/snapshots/integrations__check_config__unix_socket@stdout.snap b/tests/integrations/snapshots/integrations__check_config__unix_socket@stdout.snap new file mode 100644 index 00000000..0b9549b9 --- /dev/null +++ b/tests/integrations/snapshots/integrations__check_config__unix_socket@stdout.snap @@ -0,0 +1,5 @@ +--- +source: tests/integrations/check_config.rs +description: A config listening to a Unix socket is valid +--- + From fe14300d918cb4ac4114c31a374be4cb6082631d Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 22 Mar 2025 17:53:16 -0700 Subject: [PATCH 570/617] reintroduce account_data::Service struct In preparation for adding some additional methods at the service level. Dropping the tracing spans for the data methods, because two duplicate spans here seems kinda excessive. --- src/database/key_value/account_data.rs | 7 ---- src/service.rs | 2 +- src/service/account_data.rs | 58 +++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/database/key_value/account_data.rs b/src/database/key_value/account_data.rs index 9033ac90..dfabb322 100644 --- a/src/database/key_value/account_data.rs +++ b/src/database/key_value/account_data.rs @@ -12,9 +12,6 @@ use crate::{ }; impl service::account_data::Data for KeyValueDatabase { - /// Places one event in the account data of the user and removes the - /// previous entry. - #[tracing::instrument(skip(self, room_id, user_id, event_type, data))] fn update( &self, room_id: Option<&RoomId>, @@ -65,8 +62,6 @@ impl service::account_data::Data for KeyValueDatabase { Ok(()) } - /// Searches the account data for a specific kind. - #[tracing::instrument(skip(self, room_id, user_id, kind))] fn get( &self, room_id: Option<&RoomId>, @@ -96,8 +91,6 @@ impl service::account_data::Data for KeyValueDatabase { .transpose() } - /// Returns all changes to the account data that happened after `since`. - #[tracing::instrument(skip(self, room_id, user_id, since))] fn changes_since( &self, room_id: Option<&RoomId>, diff --git a/src/service.rs b/src/service.rs index 85e7a302..38d44941 100644 --- a/src/service.rs +++ b/src/service.rs @@ -114,7 +114,7 @@ impl Services { transaction_ids: db, uiaa: uiaa::Service::new(db), users: users::Service::new(db), - account_data: db, + account_data: account_data::Service::new(db), admin: admin::Service::new(), key_backups: db, media: media::Service { diff --git a/src/service/account_data.rs b/src/service/account_data.rs index 4f4b4344..c755ece1 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -1,5 +1,61 @@ +use std::collections::HashMap; + +use ruma::{ + events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, + serde::Raw, + RoomId, UserId, +}; + +use crate::Result; + mod data; pub(crate) use data::Data; -pub(crate) type Service = &'static dyn Data; +pub(crate) struct Service { + pub(crate) db: &'static dyn Data, +} + +impl Service { + pub(crate) fn new(db: &'static dyn Data) -> Self { + Self { + db, + } + } + + /// Places one event in the account data of the user and removes the + /// previous entry. + #[tracing::instrument(skip(self, room_id, user_id, event_type, data))] + pub(crate) fn update( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + event_type: RoomAccountDataEventType, + data: &serde_json::Value, + ) -> Result<()> { + self.db.update(room_id, user_id, event_type, data) + } + + /// Searches the account data for a specific kind. + #[tracing::instrument(skip(self, room_id, user_id, event_type))] + pub(crate) fn get( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + event_type: RoomAccountDataEventType, + ) -> Result>> { + self.db.get(room_id, user_id, event_type) + } + + /// Returns all changes to the account data that happened after `since`. + #[tracing::instrument(skip(self, room_id, user_id, since))] + pub(crate) fn changes_since( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + since: u64, + ) -> Result>> + { + self.db.changes_since(room_id, user_id, since) + } +} From 6897f0ba348b3e91ffae6cd7255d7bd997b02de2 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 23 Mar 2025 01:42:51 -0700 Subject: [PATCH 571/617] clarify behavior in service::account_data::changes_since docs --- src/service/account_data.rs | 3 +++ src/service/account_data/data.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/service/account_data.rs b/src/service/account_data.rs index c755ece1..7b51b421 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -48,6 +48,9 @@ impl Service { } /// Returns all changes to the account data that happened after `since`. + /// + /// When there have been multiple changes to the same event type, returned + /// map contains the most recent value. #[tracing::instrument(skip(self, room_id, user_id, since))] pub(crate) fn changes_since( &self, diff --git a/src/service/account_data/data.rs b/src/service/account_data/data.rs index 04d4bf76..868881c8 100644 --- a/src/service/account_data/data.rs +++ b/src/service/account_data/data.rs @@ -28,6 +28,9 @@ pub(crate) trait Data: Send + Sync { ) -> Result>>; /// Returns all changes to the account data that happened after `since`. + /// + /// When there have been multiple changes to the same event type, returned + /// map contains the most recent value. fn changes_since( &self, room_id: Option<&RoomId>, From 66210bc32d100812d958e5981ff978a53046e290 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 23 Mar 2025 02:08:38 -0700 Subject: [PATCH 572/617] separate account_data service methods for room vs global events Previously we were mashing everything together as RoomAccountDataEvent, even the global events. This technically worked, because of the hidden custom fields on the ruma event types, but it's confusing and easy to mess up. Separate methods with appropriate types are preferable. --- src/api/client_server/account.rs | 17 ++-- src/api/client_server/config.rs | 78 +++++++--------- src/api/client_server/push.rs | 123 ++++++++++--------------- src/api/client_server/read_marker.rs | 25 ++--- src/api/client_server/sync/msc3575.rs | 13 +-- src/api/client_server/sync/v3.rs | 26 +----- src/api/client_server/tag.rs | 47 +++++----- src/database.rs | 56 +++++------ src/database/key_value/account_data.rs | 70 +++++++------- src/service/account_data.rs | 105 +++++++++++++++++---- src/service/account_data/data.rs | 35 ++++--- src/service/admin.rs | 15 ++- src/service/globals.rs | 16 ++-- src/service/rooms/state_cache.rs | 58 +++++------- src/service/rooms/timeline.rs | 16 +--- src/service/sending.rs | 10 +- 16 files changed, 349 insertions(+), 361 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index d660c6ba..035b1788 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -12,9 +12,12 @@ use ruma::{ uiaa::{AuthFlow, AuthType, UiaaInfo}, }, events::{ - room::message::RoomMessageEventContent, GlobalAccountDataEventType, + room::message::RoomMessageEventContent, AnyGlobalAccountDataEvent, + GlobalAccountDataEventType, }, - push, UserId, + push, + serde::Raw, + UserId, }; use tracing::{info, warn}; @@ -234,16 +237,16 @@ pub(crate) async fn register_route( services().users.set_displayname(&user_id, Some(displayname.clone()))?; // Initial account data - services().account_data.update( - None, + services().account_data.update_global( &user_id, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(ruma::events::push_rules::PushRulesEvent { + &GlobalAccountDataEventType::PushRules, + &Raw::new(&ruma::events::push_rules::PushRulesEvent { content: ruma::events::push_rules::PushRulesEventContent { global: push::Ruleset::server_default(&user_id), }, }) - .expect("to json always works"), + .expect("constructed event should be valid") + .cast::(), )?; // Inhibit login does not work for guests diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index 8d9a0a84..76bf3395 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -7,12 +7,13 @@ use ruma::{ error::ErrorKind, }, events::{ - AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent, + AnyGlobalAccountDataEvent, AnyGlobalAccountDataEventContent, + AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, }, serde::Raw, }; use serde::Deserialize; -use serde_json::{json, value::RawValue as RawJsonValue}; +use serde_json::json; use crate::{services, Ar, Error, Ra, Result}; @@ -24,21 +25,17 @@ pub(crate) async fn set_global_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let data: serde_json::Value = serde_json::from_str(body.data.json().get()) - .map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Data is invalid.") - })?; + let event = Raw::new(&json!({ + "type": &body.event_type, + "content": &body.data, + })) + .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))? + .cast::(); - let event_type = body.event_type.to_string(); - - services().account_data.update( - None, + services().account_data.update_global( sender_user, - event_type.clone().into(), - &json!({ - "type": event_type, - "content": data, - }), + &body.event_type, + &event, )?; Ok(Ra(set_global_account_data::v3::Response {})) @@ -52,21 +49,18 @@ pub(crate) async fn set_room_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let data: serde_json::Value = serde_json::from_str(body.data.json().get()) - .map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Data is invalid.") - })?; + let event = Raw::new(&json!({ + "type": &body.event_type, + "content": &body.data, + })) + .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))? + .cast::(); - let event_type = body.event_type.to_string(); - - services().account_data.update( - Some(&body.room_id), + services().account_data.update_room( + &body.room_id, sender_user, - event_type.clone().into(), - &json!({ - "type": event_type, - "content": data, - }), + &body.event_type, + &event, )?; Ok(Ra(set_room_account_data::v3::Response {})) @@ -80,17 +74,15 @@ pub(crate) async fn get_global_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event: Box = services() + let event = services() .account_data - .get(None, sender_user, body.event_type.to_string().into())? + .get_global(sender_user, &body.event_type)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - let account_data = - serde_json::from_str::(event.get()) - .map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })? - .content; + let account_data = event + .deserialize_as::() + .map_err(|_| Error::bad_database("Invalid account data event in db."))? + .content; Ok(Ra(get_global_account_data::v3::Response { account_data, @@ -105,17 +97,15 @@ pub(crate) async fn get_room_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event: Box = services() + let event = services() .account_data - .get(Some(&body.room_id), sender_user, body.event_type.clone())? + .get_room(&body.room_id, sender_user, &body.event_type)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - let account_data = - serde_json::from_str::(event.get()) - .map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })? - .content; + let account_data = event + .deserialize_as::() + .map_err(|_| Error::bad_database("Invalid account data event in db."))? + .content; Ok(Ra(get_room_account_data::v3::Response { account_data, diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 9d2b5956..b0e12cec 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -7,8 +7,12 @@ use ruma::{ set_pushrule_actions, set_pushrule_enabled, }, }, - events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, + events::{ + push_rules::PushRulesEvent, AnyGlobalAccountDataEvent, + GlobalAccountDataEventType, + }, push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, + serde::Raw, }; use crate::{services, Ar, Error, Ra, Result}; @@ -23,17 +27,14 @@ pub(crate) async fn get_pushrules_all_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = serde_json::from_str::(event.get()) + let account_data = event + .deserialize_as::() .map_err(|_| Error::bad_database("Invalid account data event in db."))? .content; @@ -52,17 +53,14 @@ pub(crate) async fn get_pushrule_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = serde_json::from_str::(event.get()) + let account_data = event + .deserialize_as::() .map_err(|_| Error::bad_database("Invalid account data event in db."))? .content; @@ -91,18 +89,14 @@ pub(crate) async fn set_pushrule_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| { + let mut account_data = + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; @@ -142,12 +136,12 @@ pub(crate) async fn set_pushrule_route( return Err(err); } - services().account_data.update( - None, + services().account_data.update_global( sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data) - .expect("to json value always works"), + &GlobalAccountDataEventType::PushRules, + &Raw::new(&account_data) + .expect("json event serialization should always succeed") + .cast::(), )?; Ok(Ra(set_pushrule::v3::Response {})) @@ -163,17 +157,14 @@ pub(crate) async fn get_pushrule_actions_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = serde_json::from_str::(event.get()) + let account_data = event + .deserialize_as::() .map_err(|_| Error::bad_database("Invalid account data event in db."))? .content; @@ -201,18 +192,14 @@ pub(crate) async fn set_pushrule_actions_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| { + let mut account_data = + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; @@ -228,12 +215,12 @@ pub(crate) async fn set_pushrule_actions_route( )); } - services().account_data.update( - None, + services().account_data.update_global( sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data) - .expect("to json value always works"), + &GlobalAccountDataEventType::PushRules, + &Raw::new(&account_data) + .expect("json event serialization should always suceed") + .cast::(), )?; Ok(Ra(set_pushrule_actions::v3::Response {})) @@ -249,18 +236,14 @@ pub(crate) async fn get_pushrule_enabled_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = serde_json::from_str::(event.get()) - .map_err(|_| { + let account_data = + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; @@ -288,18 +271,14 @@ pub(crate) async fn set_pushrule_enabled_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| { + let mut account_data = + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; @@ -315,12 +294,12 @@ pub(crate) async fn set_pushrule_enabled_route( )); } - services().account_data.update( - None, + services().account_data.update_global( sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data) - .expect("to json value always works"), + &GlobalAccountDataEventType::PushRules, + &Raw::new(&account_data) + .expect("json event serialization should always succeed") + .cast::(), )?; Ok(Ra(set_pushrule_enabled::v3::Response {})) @@ -336,18 +315,14 @@ pub(crate) async fn delete_pushrule_route( let event = services() .account_data - .get( - None, - sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = serde_json::from_str::(event.get()) - .map_err(|_| { + let mut account_data = + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; @@ -368,12 +343,12 @@ pub(crate) async fn delete_pushrule_route( return Err(err); } - services().account_data.update( - None, + services().account_data.update_global( sender_user, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(account_data) - .expect("to json value always works"), + &GlobalAccountDataEventType::PushRules, + &Raw::new(&account_data) + .expect("json event serialization should always suceed") + .cast::(), )?; Ok(Ra(delete_pushrule::v3::Response {})) diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index 2daabf73..0f6655a4 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -6,8 +6,9 @@ use ruma::{ }, events::{ receipt::{ReceiptThread, ReceiptType}, - RoomAccountDataEventType, + AnyRoomAccountDataEvent, RoomAccountDataEventType, }, + serde::Raw, MilliSecondsSinceUnixEpoch, }; @@ -33,12 +34,13 @@ pub(crate) async fn set_read_marker_route( event_id: fully_read.clone(), }, }; - services().account_data.update( - Some(&body.room_id), + services().account_data.update_room( + &body.room_id, sender_user, - RoomAccountDataEventType::FullyRead, - &serde_json::to_value(fully_read_event) - .expect("to json value always works"), + &RoomAccountDataEventType::FullyRead, + &Raw::new(&fully_read_event) + .expect("json event serialization should always suceed") + .cast::(), )?; } @@ -129,12 +131,13 @@ pub(crate) async fn create_receipt_route( event_id: body.event_id.clone(), }, }; - services().account_data.update( - Some(&body.room_id), + services().account_data.update_room( + &body.room_id, sender_user, - RoomAccountDataEventType::FullyRead, - &serde_json::to_value(fully_read_event) - .expect("to json value always works"), + &RoomAccountDataEventType::FullyRead, + &Raw::new(&fully_read_event) + .expect("json event serialization should always succeed") + .cast::(), )?; } create_receipt::v3::ReceiptType::Read => { diff --git a/src/api/client_server/sync/msc3575.rs b/src/api/client_server/sync/msc3575.rs index 1de1c7d1..20ee6379 100644 --- a/src/api/client_server/sync/msc3575.rs +++ b/src/api/client_server/sync/msc3575.rs @@ -644,17 +644,8 @@ pub(crate) async fn sync_events_v4_route( { services() .account_data - .changes_since(None, &sender_user, globalsince)? - .into_iter() - .filter_map(|(_, v)| { - serde_json::from_str(v.json().get()) - .map_err(|_| { - Error::bad_database( - "Invalid account event in database.", - ) - }) - .ok() - }) + .global_changes_since(&sender_user, globalsince)? + .into_values() .collect() } else { Vec::new() diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index 65830053..21bb0004 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -234,17 +234,8 @@ pub(crate) async fn sync_events_route( account_data: GlobalAccountData { events: services() .account_data - .changes_since(None, ctx.sender_user, ctx.since)? - .into_iter() - .filter_map(|(_, v)| { - serde_json::from_str(v.json().get()) - .map_err(|_| { - Error::bad_database( - "Invalid account event in database.", - ) - }) - .ok() - }) + .global_changes_since(ctx.sender_user, ctx.since)? + .into_values() .collect(), }, device_lists: DeviceLists { @@ -880,17 +871,8 @@ async fn load_joined_room( account_data: RoomAccountData { events: services() .account_data - .changes_since(Some(room_id), ctx.sender_user, ctx.since)? - .into_iter() - .filter_map(|(_, v)| { - serde_json::from_str(v.json().get()) - .map_err(|_| { - Error::bad_database( - "Invalid account event in database.", - ) - }) - .ok() - }) + .room_changes_since(ctx.sender_user, room_id, ctx.since)? + .into_values() .collect(), }, summary: RoomSummary { diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 17367300..12a3e37d 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -4,8 +4,9 @@ use ruma::{ api::client::tag::{create_tag, delete_tag, get_tags}, events::{ tag::{TagEvent, TagEventContent}, - RoomAccountDataEventType, + AnyRoomAccountDataEvent, RoomAccountDataEventType, }, + serde::Raw, }; use crate::{services, Ar, Error, Ra, Result}; @@ -20,10 +21,10 @@ pub(crate) async fn update_tag_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services().account_data.get( - Some(&body.room_id), + let event = services().account_data.get_room( + &body.room_id, sender_user, - RoomAccountDataEventType::Tag, + &RoomAccountDataEventType::Tag, )?; let mut tags_event = event.map_or_else( @@ -35,7 +36,7 @@ pub(crate) async fn update_tag_route( }) }, |e| { - serde_json::from_str(e.get()).map_err(|_| { + e.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, @@ -46,11 +47,13 @@ pub(crate) async fn update_tag_route( .tags .insert(body.tag.clone().into(), body.tag_info.clone()); - services().account_data.update( - Some(&body.room_id), + services().account_data.update_room( + &body.room_id, sender_user, - RoomAccountDataEventType::Tag, - &serde_json::to_value(tags_event).expect("to json value always works"), + &RoomAccountDataEventType::Tag, + &Raw::new(&tags_event) + .expect("json event serialization should always suceed") + .cast::(), )?; Ok(Ra(create_tag::v3::Response {})) @@ -66,10 +69,10 @@ pub(crate) async fn delete_tag_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services().account_data.get( - Some(&body.room_id), + let event = services().account_data.get_room( + &body.room_id, sender_user, - RoomAccountDataEventType::Tag, + &RoomAccountDataEventType::Tag, )?; let mut tags_event = event.map_or_else( @@ -81,7 +84,7 @@ pub(crate) async fn delete_tag_route( }) }, |e| { - serde_json::from_str(e.get()).map_err(|_| { + e.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, @@ -89,11 +92,13 @@ pub(crate) async fn delete_tag_route( tags_event.content.tags.remove(&body.tag.clone().into()); - services().account_data.update( - Some(&body.room_id), + services().account_data.update_room( + &body.room_id, sender_user, - RoomAccountDataEventType::Tag, - &serde_json::to_value(tags_event).expect("to json value always works"), + &RoomAccountDataEventType::Tag, + &Raw::new(&tags_event) + .expect("json value serialization should always succeed") + .cast::(), )?; Ok(Ra(delete_tag::v3::Response {})) @@ -109,10 +114,10 @@ pub(crate) async fn get_tags_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services().account_data.get( - Some(&body.room_id), + let event = services().account_data.get_room( + &body.room_id, sender_user, - RoomAccountDataEventType::Tag, + &RoomAccountDataEventType::Tag, )?; let tags_event = event.map_or_else( @@ -124,7 +129,7 @@ pub(crate) async fn get_tags_route( }) }, |e| { - serde_json::from_str(e.get()).map_err(|_| { + e.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, diff --git a/src/database.rs b/src/database.rs index f7cb1352..b374f361 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,8 +7,12 @@ use std::{ }; use ruma::{ - events::{push_rules::PushRulesEvent, GlobalAccountDataEventType}, + events::{ + push_rules::PushRulesEvent, AnyGlobalAccountDataEvent, + GlobalAccountDataEventType, + }, push::Ruleset, + serde::Raw, EventId, OwnedRoomId, RoomId, UserId, }; use tracing::{debug, error, info, info_span, warn, Instrument}; @@ -859,20 +863,15 @@ impl KeyValueDatabase { let raw_rules_list = services() .account_data - .get( - None, + .get_global( &user, - GlobalAccountDataEventType::PushRules - .to_string() - .into(), + &GlobalAccountDataEventType::PushRules, ) .unwrap() .expect("Username is invalid"); - let mut account_data = - serde_json::from_str::( - raw_rules_list.get(), - ) + let mut account_data = raw_rules_list + .deserialize_as::() .unwrap(); let rules_list = &mut account_data.content.global; @@ -927,14 +926,12 @@ impl KeyValueDatabase { } } - services().account_data.update( - None, + services().account_data.update_global( &user, - GlobalAccountDataEventType::PushRules - .to_string() - .into(), - &serde_json::to_value(account_data) - .expect("to json value always works"), + &GlobalAccountDataEventType::PushRules, + &Raw::new(&account_data) + .expect("json serialization should always succeed") + .cast::(), )?; } Ok(()) @@ -961,20 +958,15 @@ impl KeyValueDatabase { let raw_rules_list = services() .account_data - .get( - None, + .get_global( &user, - GlobalAccountDataEventType::PushRules - .to_string() - .into(), + &GlobalAccountDataEventType::PushRules, ) .unwrap() .expect("Username is invalid"); - let mut account_data = - serde_json::from_str::( - raw_rules_list.get(), - ) + let mut account_data = raw_rules_list + .deserialize_as::() .unwrap(); let user_default_rules = Ruleset::server_default(&user); @@ -983,14 +975,12 @@ impl KeyValueDatabase { .global .update_with_server_default(user_default_rules); - services().account_data.update( - None, + services().account_data.update_global( &user, - GlobalAccountDataEventType::PushRules - .to_string() - .into(), - &serde_json::to_value(account_data) - .expect("to json value always works"), + &GlobalAccountDataEventType::PushRules, + &Raw::new(&account_data) + .expect("json serialization should always succeed") + .cast::(), )?; } Ok(()) diff --git a/src/database/key_value/account_data.rs b/src/database/key_value/account_data.rs index dfabb322..49231a2b 100644 --- a/src/database/key_value/account_data.rs +++ b/src/database/key_value/account_data.rs @@ -1,11 +1,8 @@ use std::collections::HashMap; -use ruma::{ - api::client::error::ErrorKind, - events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, - serde::Raw, - RoomId, UserId, -}; +use ruma::{api::client::error::ErrorKind, RoomId, UserId}; +use serde::Deserialize; +use serde_json::value::RawValue; use crate::{ database::KeyValueDatabase, service, services, utils, Error, Result, @@ -16,9 +13,19 @@ impl service::account_data::Data for KeyValueDatabase { &self, room_id: Option<&RoomId>, user_id: &UserId, - event_type: RoomAccountDataEventType, - data: &serde_json::Value, + event_type: &str, + data: &RawValue, ) -> Result<()> { + // Allowed because we just use this type to validate the schema, and + // don't read the fields. + #[allow(dead_code)] + #[derive(Deserialize)] + struct ExtractEventFields<'a> { + #[serde(rename = "type")] + event_type: &'a str, + content: &'a RawValue, + } + let mut prefix = room_id .map(ToString::to_string) .unwrap_or_default() @@ -32,23 +39,20 @@ impl service::account_data::Data for KeyValueDatabase { roomuserdataid .extend_from_slice(&services().globals.next_count()?.to_be_bytes()); roomuserdataid.push(0xFF); - roomuserdataid.extend_from_slice(event_type.to_string().as_bytes()); + roomuserdataid.extend_from_slice(event_type.as_bytes()); let mut key = prefix; - key.extend_from_slice(event_type.to_string().as_bytes()); + key.extend_from_slice(event_type.as_bytes()); - if data.get("type").is_none() || data.get("content").is_none() { + if serde_json::from_str::>(data.get()).is_err() { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Account data doesn't have all required fields.", )); } - self.roomuserdataid_accountdata.insert( - &roomuserdataid, - &serde_json::to_vec(&data) - .expect("to_vec always works on json values"), - )?; + self.roomuserdataid_accountdata + .insert(&roomuserdataid, data.get().as_bytes())?; let prev = self.roomusertype_roomuserdataid.get(&key)?; @@ -66,8 +70,8 @@ impl service::account_data::Data for KeyValueDatabase { &self, room_id: Option<&RoomId>, user_id: &UserId, - kind: RoomAccountDataEventType, - ) -> Result>> { + kind: &str, + ) -> Result>> { let mut key = room_id .map(ToString::to_string) .unwrap_or_default() @@ -76,7 +80,7 @@ impl service::account_data::Data for KeyValueDatabase { key.push(0xFF); key.extend_from_slice(user_id.as_bytes()); key.push(0xFF); - key.extend_from_slice(kind.to_string().as_bytes()); + key.extend_from_slice(kind.as_bytes()); self.roomusertype_roomuserdataid .get(&key)? @@ -96,8 +100,7 @@ impl service::account_data::Data for KeyValueDatabase { room_id: Option<&RoomId>, user_id: &UserId, since: u64, - ) -> Result>> - { + ) -> Result>> { let mut userdata = HashMap::new(); let mut prefix = room_id @@ -119,28 +122,23 @@ impl service::account_data::Data for KeyValueDatabase { .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(k, v)| { Ok::<_, Error>(( - RoomAccountDataEventType::from( - utils::string_from_bytes( - k.rsplit(|&b| b == 0xFF).next().ok_or_else( - || { - Error::bad_database( - "RoomUserData ID in db is invalid.", - ) - }, - )?, - ) - .map_err(|_| { + utils::string_from_bytes( + k.rsplit(|&b| b == 0xFF).next().ok_or_else(|| { Error::bad_database( "RoomUserData ID in db is invalid.", ) })?, - ), - serde_json::from_slice::>(&v) - .map_err(|_| { + ) + .map_err(|_| { + Error::bad_database("RoomUserData ID in db is invalid.") + })?, + serde_json::from_slice::>(&v).map_err( + |_| { Error::bad_database( "Database contains invalid account data.", ) - })?, + }, + )?, )) }) { diff --git a/src/service/account_data.rs b/src/service/account_data.rs index 7b51b421..014afcfd 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; use ruma::{ - events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, + events::{ + AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, + GlobalAccountDataEventType, RoomAccountDataEventType, + }, serde::Raw, RoomId, UserId, }; @@ -23,42 +26,104 @@ impl Service { } } - /// Places one event in the account data of the user and removes the + /// Places one event in the global account data of the user and removes the /// previous entry. - #[tracing::instrument(skip(self, room_id, user_id, event_type, data))] - pub(crate) fn update( + #[tracing::instrument(skip(self, user_id, event))] + pub(crate) fn update_global( &self, - room_id: Option<&RoomId>, user_id: &UserId, - event_type: RoomAccountDataEventType, - data: &serde_json::Value, + event_type: &GlobalAccountDataEventType, + event: &Raw, ) -> Result<()> { - self.db.update(room_id, user_id, event_type, data) + self.db.update(None, user_id, &event_type.to_string(), event.json()) } - /// Searches the account data for a specific kind. - #[tracing::instrument(skip(self, room_id, user_id, event_type))] - pub(crate) fn get( + /// Places one event in the room account data of the user and removes the + /// previous entry for that room. + #[tracing::instrument(skip(self, room_id, user_id, event))] + pub(crate) fn update_room( &self, - room_id: Option<&RoomId>, + room_id: &RoomId, user_id: &UserId, - event_type: RoomAccountDataEventType, - ) -> Result>> { - self.db.get(room_id, user_id, event_type) + event_type: &RoomAccountDataEventType, + event: &Raw, + ) -> Result<()> { + self.db.update( + Some(room_id), + user_id, + &event_type.to_string(), + event.json(), + ) } - /// Returns all changes to the account data that happened after `since`. + /// Searches the global account data for a specific kind. + #[tracing::instrument(skip(self, user_id, event_type))] + pub(crate) fn get_global( + &self, + user_id: &UserId, + event_type: &GlobalAccountDataEventType, + ) -> Result>> { + Ok(self + .db + .get(None, user_id, &event_type.to_string())? + .map(Raw::from_json)) + } + + /// Searches the room account data for a specific kind. + #[tracing::instrument(skip(self, room_id, user_id, event_type))] + pub(crate) fn get_room( + &self, + room_id: &RoomId, + user_id: &UserId, + event_type: &RoomAccountDataEventType, + ) -> Result>> { + Ok(self + .db + .get(Some(room_id), user_id, &event_type.to_string())? + .map(Raw::from_json)) + } + + /// Returns all changes to global account data that happened after `since`. + /// + /// When there have been multiple changes to the same event type, returned + /// map contains the most recent value. + #[tracing::instrument(skip(self, user_id, since))] + pub(crate) fn global_changes_since( + &self, + user_id: &UserId, + since: u64, + ) -> Result< + HashMap>, + > { + Ok(self + .db + .changes_since(None, user_id, since)? + .into_iter() + .map(|(event_type, event)| { + (event_type.into(), Raw::from_json(event)) + }) + .collect()) + } + + /// Returns all changes to room account data that happened after `since`. /// /// When there have been multiple changes to the same event type, returned /// map contains the most recent value. #[tracing::instrument(skip(self, room_id, user_id, since))] - pub(crate) fn changes_since( + pub(crate) fn room_changes_since( &self, - room_id: Option<&RoomId>, user_id: &UserId, + room_id: &RoomId, since: u64, - ) -> Result>> + ) -> Result>> { - self.db.changes_since(room_id, user_id, since) + Ok(self + .db + .changes_since(Some(room_id), user_id, since)? + .into_iter() + .map(|(event_type, event)| { + (event_type.into(), Raw::from_json(event)) + }) + .collect()) } } diff --git a/src/service/account_data/data.rs b/src/service/account_data/data.rs index 868881c8..9db322ad 100644 --- a/src/service/account_data/data.rs +++ b/src/service/account_data/data.rs @@ -1,40 +1,51 @@ use std::collections::HashMap; -use ruma::{ - events::{AnyEphemeralRoomEvent, RoomAccountDataEventType}, - serde::Raw, - RoomId, UserId, -}; +use ruma::{RoomId, UserId}; +use serde_json::value::RawValue; use crate::Result; +/// Unlike the service-level API, the database API for account data does not +/// distinguish between global and room events. Because there are no ruma types +/// that cover both, we use strings for the event types and raw json values for +/// the contents. pub(crate) trait Data: Send + Sync { /// Places one event in the account data of the user and removes the /// previous entry. + /// + /// If `room_id` is `None`, set a global event, otherwise set a room event + /// in the specified room. fn update( &self, room_id: Option<&RoomId>, user_id: &UserId, - event_type: RoomAccountDataEventType, - data: &serde_json::Value, + event_type: &str, + data: &RawValue, ) -> Result<()>; /// Searches the account data for a specific kind. + /// + /// If `room_id` is `None`, search global events, otherwise search room + /// events in the specified room. fn get( &self, room_id: Option<&RoomId>, user_id: &UserId, - kind: RoomAccountDataEventType, - ) -> Result>>; + kind: &str, + ) -> Result>>; /// Returns all changes to the account data that happened after `since`. /// - /// When there have been multiple changes to the same event type, returned - /// map contains the most recent value. + /// If `room_id` is `None`, read global events, otherwise read room events + /// in the specified room. + /// + /// Returned as a map from event type to event objects (containing both a + /// `type` and a `content` key). When there have been multiple changes to + /// the same event type, returned map contains the most recent value. fn changes_since( &self, room_id: Option<&RoomId>, user_id: &UserId, since: u64, - ) -> Result>>; + ) -> Result>>; } diff --git a/src/service/admin.rs b/src/service/admin.rs index 5e3ee770..f5fb39aa 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -20,8 +20,9 @@ use ruma::{ power_levels::RoomPowerLevelsEventContent, topic::RoomTopicEventContent, }, - TimelineEventType, + AnyGlobalAccountDataEvent, TimelineEventType, }, + serde::Raw, signatures::verify_json, EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, @@ -770,20 +771,18 @@ impl Service { .set_displayname(&user_id, Some(displayname))?; // Initial account data - services().account_data.update( - None, + services().account_data.update_global( &user_id, - ruma::events::GlobalAccountDataEventType::PushRules - .to_string() - .into(), - &serde_json::to_value(PushRulesEvent { + &ruma::events::GlobalAccountDataEventType::PushRules, + &Raw::new(&PushRulesEvent { content: PushRulesEventContent { global: ruma::push::Ruleset::server_default( &user_id, ), }, }) - .expect("to json value always works"), + .expect("json serialization should always succeed") + .cast::(), )?; // we dont add a device since we're not the user, just the diff --git a/src/service/globals.rs b/src/service/globals.rs index 5f8e4c27..c1922fef 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -18,11 +18,11 @@ use ruma::{ api::federation::discovery::ServerSigningKeys, events::{ push_rules::PushRulesEventContent, - room::message::RoomMessageEventContent, GlobalAccountDataEvent, - GlobalAccountDataEventType, + room::message::RoomMessageEventContent, AnyGlobalAccountDataEvent, + GlobalAccountDataEvent, GlobalAccountDataEventType, }, push::Ruleset, - serde::Base64, + serde::{Base64, Raw}, DeviceId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomAliasId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId, ServerName, UInt, UserId, @@ -492,16 +492,16 @@ impl Service { None => (Ruleset::new(), Ok(false)), }; - services().account_data.update( - None, + services().account_data.update_global( admin_bot, - GlobalAccountDataEventType::PushRules.to_string().into(), - &serde_json::to_value(&GlobalAccountDataEvent { + &GlobalAccountDataEventType::PushRules, + &Raw::new(&GlobalAccountDataEvent { content: PushRulesEventContent { global: ruleset, }, }) - .expect("to json value always works"), + .expect("json serialization should always succeed") + .cast::(), )?; res diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index ea6721d9..6fe100cb 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -6,8 +6,8 @@ use std::{ use ruma::{ events::{ ignored_user_list::IgnoredUserListEvent, room::member::MembershipState, - AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, - RoomAccountDataEventType, + AnyGlobalAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, + GlobalAccountDataEventType, RoomAccountDataEventType, }, serde::Raw, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, @@ -92,29 +92,21 @@ impl Service { self.db.mark_as_joined(user_id, room_id)?; } MembershipState::Invite => { - let event_kind = RoomAccountDataEventType::from( - GlobalAccountDataEventType::IgnoredUserList.to_string(), - ); - // We want to know if the sender is ignored by the receiver let is_ignored = services() .account_data - .get( - // Ignored users are in global account data - None, + // Ignored users are in global account data + .get_global( // Receiver user_id, - event_kind.clone(), + &GlobalAccountDataEventType::IgnoredUserList, )? .map(|event| { - serde_json::from_str::( - event.get(), - ) + event.deserialize_as::() .map_err(|error| { warn!( %error, - %event_kind, - "Invalid account data event", + "Invalid m.ignored_user_list account data event", ); Error::BadDatabase("Invalid account data event.") }) @@ -200,20 +192,18 @@ impl Service { from_room_id: &RoomId, to_room_id: &RoomId, ) -> Result<()> { - let Some(event) = services().account_data.get( - Some(from_room_id), + let Some(event) = services().account_data.get_room( + from_room_id, user_id, - RoomAccountDataEventType::Tag, + &RoomAccountDataEventType::Tag, )? else { return Ok(()); }; - let event = serde_json::from_str::(event.get()) - .expect("RawValue -> Value should always succeed"); - if let Err(error) = services().account_data.update( - Some(to_room_id), + if let Err(error) = services().account_data.update_room( + to_room_id, user_id, - RoomAccountDataEventType::Tag, + &RoomAccountDataEventType::Tag, &event, ) { warn!(%error, "error writing m.tag account data to upgraded room"); @@ -231,16 +221,15 @@ impl Service { from_room_id: &RoomId, to_room_id: &RoomId, ) -> Result<()> { - let event_kind = RoomAccountDataEventType::from( - GlobalAccountDataEventType::Direct.to_string(), - ); - let Some(event) = - services().account_data.get(None, user_id, event_kind.clone())? + let Some(event) = services() + .account_data + .get_global(user_id, &GlobalAccountDataEventType::Direct)? else { return Ok(()); }; - let mut event = serde_json::from_str::(event.get()) + let mut event = event + .deserialize_as::() .expect("RawValue -> Value should always succeed"); // As a server, we should try not to assume anything about the schema @@ -285,13 +274,14 @@ impl Service { } if event_updated { - if let Err(error) = services().account_data.update( - None, + if let Err(error) = services().account_data.update_global( user_id, - event_kind.clone(), - &event, + &GlobalAccountDataEventType::Direct, + &Raw::new(&event) + .expect("json serialization should always succeed") + .cast::(), ) { - warn!(%event_kind, %error, "error writing account data event after upgrading room"); + warn!(%error, "error writing m.direct account data event after upgrading room"); } } Ok(()) diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index d8cfa368..3a422830 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -417,19 +417,11 @@ impl Service { let rules_for_user = services() .account_data - .get( - None, - user, - GlobalAccountDataEventType::PushRules.to_string().into(), - )? + .get_global(user, &GlobalAccountDataEventType::PushRules)? .map(|event| { - serde_json::from_str::(event.get()).map_err( - |_| { - Error::bad_database( - "Invalid push rules event in db.", - ) - }, - ) + event.deserialize_as::().map_err(|_| { + Error::bad_database("Invalid push rules event in db.") + }) }) .transpose()? .map_or_else( diff --git a/src/service/sending.rs b/src/service/sending.rs index 1b7e8d01..0a5ff541 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -857,15 +857,9 @@ async fn handle_push_event( let rules_for_user = services() .account_data - .get( - None, - userid, - GlobalAccountDataEventType::PushRules.to_string().into(), - ) + .get_global(userid, &GlobalAccountDataEventType::PushRules) .unwrap_or_default() - .and_then(|event| { - serde_json::from_str::(event.get()).ok() - }) + .and_then(|event| event.deserialize_as::().ok()) .map_or_else( || push::Ruleset::server_default(userid), |ev: PushRulesEvent| ev.content.global, From b82458a460e5da89e78341d0d09b219c121a7870 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 23 Mar 2025 14:45:33 -0700 Subject: [PATCH 573/617] use event content in account_data service api instead of full events This eliminates the possibility of passing an event that has a mismatching type, reducing the space of possible invalid states. --- src/api/client_server/account.rs | 15 ++- src/api/client_server/config.rs | 61 ++-------- src/api/client_server/push.rs | 67 +++++------ src/api/client_server/read_marker.rs | 26 ++--- src/api/client_server/sync/msc3575.rs | 11 +- src/api/client_server/sync/v3.rs | 18 ++- src/api/client_server/tag.rs | 48 +++----- src/database.rs | 22 ++-- src/service/account_data.rs | 158 +++++++++++++++++++++----- src/service/account_data/data.rs | 3 + src/service/admin.rs | 16 +-- src/service/globals.rs | 15 ++- src/service/rooms/state_cache.rs | 32 +++--- src/service/rooms/timeline.rs | 17 +-- src/service/sending.rs | 8 +- 15 files changed, 277 insertions(+), 240 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 035b1788..c616f62b 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -12,8 +12,7 @@ use ruma::{ uiaa::{AuthFlow, AuthType, UiaaInfo}, }, events::{ - room::message::RoomMessageEventContent, AnyGlobalAccountDataEvent, - GlobalAccountDataEventType, + room::message::RoomMessageEventContent, GlobalAccountDataEventType, }, push, serde::Raw, @@ -240,13 +239,13 @@ pub(crate) async fn register_route( services().account_data.update_global( &user_id, &GlobalAccountDataEventType::PushRules, - &Raw::new(&ruma::events::push_rules::PushRulesEvent { - content: ruma::events::push_rules::PushRulesEventContent { + &Raw::new( + &ruma::events::push_rules::PushRulesEventContent { global: push::Ruleset::server_default(&user_id), - }, - }) - .expect("constructed event should be valid") - .cast::(), + } + .into(), + ) + .expect("constructed event should be valid"), )?; // Inhibit login does not work for guests diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index 76bf3395..c136b118 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -1,19 +1,10 @@ -use ruma::{ - api::client::{ - config::{ - get_global_account_data, get_room_account_data, - set_global_account_data, set_room_account_data, - }, - error::ErrorKind, +use ruma::api::client::{ + config::{ + get_global_account_data, get_room_account_data, + set_global_account_data, set_room_account_data, }, - events::{ - AnyGlobalAccountDataEvent, AnyGlobalAccountDataEventContent, - AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, - }, - serde::Raw, + error::ErrorKind, }; -use serde::Deserialize; -use serde_json::json; use crate::{services, Ar, Error, Ra, Result}; @@ -25,17 +16,10 @@ pub(crate) async fn set_global_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = Raw::new(&json!({ - "type": &body.event_type, - "content": &body.data, - })) - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))? - .cast::(); - services().account_data.update_global( sender_user, &body.event_type, - &event, + &body.data, )?; Ok(Ra(set_global_account_data::v3::Response {})) @@ -49,18 +33,11 @@ pub(crate) async fn set_room_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = Raw::new(&json!({ - "type": &body.event_type, - "content": &body.data, - })) - .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))? - .cast::(); - services().account_data.update_room( &body.room_id, sender_user, &body.event_type, - &event, + &body.data, )?; Ok(Ra(set_room_account_data::v3::Response {})) @@ -74,16 +51,11 @@ pub(crate) async fn get_global_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services() + let account_data = services() .account_data .get_global(sender_user, &body.event_type)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - let account_data = event - .deserialize_as::() - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; - Ok(Ra(get_global_account_data::v3::Response { account_data, })) @@ -97,27 +69,12 @@ pub(crate) async fn get_room_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services() + let account_data = services() .account_data .get_room(&body.room_id, sender_user, &body.event_type)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - let account_data = event - .deserialize_as::() - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; - Ok(Ra(get_room_account_data::v3::Response { account_data, })) } - -#[derive(Deserialize)] -struct ExtractRoomEventContent { - content: Raw, -} - -#[derive(Deserialize)] -struct ExtractGlobalEventContent { - content: Raw, -} diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index b0e12cec..737cb27b 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -7,10 +7,7 @@ use ruma::{ set_pushrule_actions, set_pushrule_enabled, }, }, - events::{ - push_rules::PushRulesEvent, AnyGlobalAccountDataEvent, - GlobalAccountDataEventType, - }, + events::{push_rules::PushRulesEventContent, GlobalAccountDataEventType}, push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, serde::Raw, }; @@ -33,10 +30,10 @@ pub(crate) async fn get_pushrules_all_route( "PushRules event not found.", ))?; - let account_data = event - .deserialize_as::() - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; + let account_data = + event.deserialize_as::().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; Ok(Ra(get_pushrules_all::v3::Response { global: account_data.global, @@ -59,10 +56,10 @@ pub(crate) async fn get_pushrule_route( "PushRules event not found.", ))?; - let account_data = event - .deserialize_as::() - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; + let account_data = + event.deserialize_as::().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; let rule = account_data .global @@ -96,11 +93,11 @@ pub(crate) async fn set_pushrule_route( ))?; let mut account_data = - event.deserialize_as::().map_err(|_| { + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; - if let Err(error) = account_data.content.global.insert( + if let Err(error) = account_data.global.insert( body.rule.clone(), body.after.as_deref(), body.before.as_deref(), @@ -139,9 +136,8 @@ pub(crate) async fn set_pushrule_route( services().account_data.update_global( sender_user, &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data) - .expect("json event serialization should always succeed") - .cast::(), + &Raw::new(&account_data.into()) + .expect("json event serialization should always succeed"), )?; Ok(Ra(set_pushrule::v3::Response {})) @@ -163,10 +159,10 @@ pub(crate) async fn get_pushrule_actions_route( "PushRules event not found.", ))?; - let account_data = event - .deserialize_as::() - .map_err(|_| Error::bad_database("Invalid account data event in db."))? - .content; + let account_data = + event.deserialize_as::().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; let global = account_data.global; let actions = global @@ -199,12 +195,11 @@ pub(crate) async fn set_pushrule_actions_route( ))?; let mut account_data = - event.deserialize_as::().map_err(|_| { + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; if account_data - .content .global .set_actions(body.kind.clone(), &body.rule_id, body.actions.clone()) .is_err() @@ -218,9 +213,8 @@ pub(crate) async fn set_pushrule_actions_route( services().account_data.update_global( sender_user, &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data) - .expect("json event serialization should always suceed") - .cast::(), + &Raw::new(&account_data.into()) + .expect("json event serialization should always suceed"), )?; Ok(Ra(set_pushrule_actions::v3::Response {})) @@ -243,11 +237,11 @@ pub(crate) async fn get_pushrule_enabled_route( ))?; let account_data = - event.deserialize_as::().map_err(|_| { + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; - let global = account_data.content.global; + let global = account_data.global; let enabled = global .get(body.kind.clone(), &body.rule_id) .map(AnyPushRuleRef::enabled) @@ -278,12 +272,11 @@ pub(crate) async fn set_pushrule_enabled_route( ))?; let mut account_data = - event.deserialize_as::().map_err(|_| { + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; if account_data - .content .global .set_enabled(body.kind.clone(), &body.rule_id, body.enabled) .is_err() @@ -297,9 +290,8 @@ pub(crate) async fn set_pushrule_enabled_route( services().account_data.update_global( sender_user, &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data) - .expect("json event serialization should always succeed") - .cast::(), + &Raw::new(&account_data.into()) + .expect("json event serialization should always succeed"), )?; Ok(Ra(set_pushrule_enabled::v3::Response {})) @@ -322,12 +314,12 @@ pub(crate) async fn delete_pushrule_route( ))?; let mut account_data = - event.deserialize_as::().map_err(|_| { + event.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") })?; if let Err(error) = - account_data.content.global.remove(body.kind.clone(), &body.rule_id) + account_data.global.remove(body.kind.clone(), &body.rule_id) { let err = match error { RemovePushRuleError::ServerDefault => Error::BadRequest( @@ -346,9 +338,8 @@ pub(crate) async fn delete_pushrule_route( services().account_data.update_global( sender_user, &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data) - .expect("json event serialization should always suceed") - .cast::(), + &Raw::new(&account_data.into()) + .expect("json event serialization should always suceed"), )?; Ok(Ra(delete_pushrule::v3::Response {})) diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index 0f6655a4..cf253dab 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -6,7 +6,7 @@ use ruma::{ }, events::{ receipt::{ReceiptThread, ReceiptType}, - AnyRoomAccountDataEvent, RoomAccountDataEventType, + RoomAccountDataEventType, }, serde::Raw, MilliSecondsSinceUnixEpoch, @@ -29,18 +29,16 @@ pub(crate) async fn set_read_marker_route( let sender_user = body.sender_user.as_ref().expect("user is authenticated"); if let Some(fully_read) = &body.fully_read { - let fully_read_event = ruma::events::fully_read::FullyReadEvent { - content: ruma::events::fully_read::FullyReadEventContent { + let fully_read_event = + ruma::events::fully_read::FullyReadEventContent { event_id: fully_read.clone(), - }, - }; + }; services().account_data.update_room( &body.room_id, sender_user, &RoomAccountDataEventType::FullyRead, - &Raw::new(&fully_read_event) - .expect("json event serialization should always suceed") - .cast::(), + &Raw::new(&fully_read_event.into()) + .expect("json event serialization should always suceed"), )?; } @@ -126,18 +124,16 @@ pub(crate) async fn create_receipt_route( match body.receipt_type { create_receipt::v3::ReceiptType::FullyRead => { - let fully_read_event = ruma::events::fully_read::FullyReadEvent { - content: ruma::events::fully_read::FullyReadEventContent { + let fully_read_event = + ruma::events::fully_read::FullyReadEventContent { event_id: body.event_id.clone(), - }, - }; + }; services().account_data.update_room( &body.room_id, sender_user, &RoomAccountDataEventType::FullyRead, - &Raw::new(&fully_read_event) - .expect("json event serialization should always succeed") - .cast::(), + &Raw::new(&fully_read_event.into()) + .expect("json event serialization should always succeed"), )?; } create_receipt::v3::ReceiptType::Read => { diff --git a/src/api/client_server/sync/msc3575.rs b/src/api/client_server/sync/msc3575.rs index 20ee6379..52ce877d 100644 --- a/src/api/client_server/sync/msc3575.rs +++ b/src/api/client_server/sync/msc3575.rs @@ -25,7 +25,8 @@ use tracing::{debug, error}; use super::{load_timeline, share_encrypted_room}; use crate::{ - service::rooms::timeline::PduCount, services, Ar, Error, Ra, Result, + service::{account_data, rooms::timeline::PduCount}, + services, Ar, Error, Ra, Result, }; #[allow(clippy::too_many_lines)] @@ -645,7 +646,13 @@ pub(crate) async fn sync_events_v4_route( services() .account_data .global_changes_since(&sender_user, globalsince)? - .into_values() + .into_iter() + .map(|(event_type, content)| { + account_data::raw_global_event_from_parts( + &event_type, + &content, + ) + }) .collect() } else { Vec::new() diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index 21bb0004..8046bc0f 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -27,7 +27,7 @@ use tracing::{debug, error, field}; use super::{load_timeline, share_encrypted_room}; use crate::{ - service::{pdu::EventHash, rooms::timeline::PduCount}, + service::{account_data, pdu::EventHash, rooms::timeline::PduCount}, services, utils, Ar, Error, PduEvent, Ra, Result, }; @@ -235,7 +235,13 @@ pub(crate) async fn sync_events_route( events: services() .account_data .global_changes_since(ctx.sender_user, ctx.since)? - .into_values() + .into_iter() + .map(|(event_type, content)| { + account_data::raw_global_event_from_parts( + &event_type, + &content, + ) + }) .collect(), }, device_lists: DeviceLists { @@ -872,7 +878,13 @@ async fn load_joined_room( events: services() .account_data .room_changes_since(ctx.sender_user, room_id, ctx.since)? - .into_values() + .into_iter() + .map(|(event_type, content)| { + account_data::raw_room_event_from_parts( + &event_type, + &content, + ) + }) .collect(), }, summary: RoomSummary { diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 12a3e37d..fdfd4dfc 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -2,10 +2,7 @@ use std::collections::BTreeMap; use ruma::{ api::client::tag::{create_tag, delete_tag, get_tags}, - events::{ - tag::{TagEvent, TagEventContent}, - AnyRoomAccountDataEvent, RoomAccountDataEventType, - }, + events::{tag::TagEventContent, RoomAccountDataEventType}, serde::Raw, }; @@ -29,31 +26,25 @@ pub(crate) async fn update_tag_route( let mut tags_event = event.map_or_else( || { - Ok(TagEvent { - content: TagEventContent { - tags: BTreeMap::new(), - }, + Ok(TagEventContent { + tags: BTreeMap::new(), }) }, |e| { - e.deserialize_as::().map_err(|_| { + e.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, )?; - tags_event - .content - .tags - .insert(body.tag.clone().into(), body.tag_info.clone()); + tags_event.tags.insert(body.tag.clone().into(), body.tag_info.clone()); services().account_data.update_room( &body.room_id, sender_user, &RoomAccountDataEventType::Tag, - &Raw::new(&tags_event) - .expect("json event serialization should always suceed") - .cast::(), + &Raw::new(&tags_event.into()) + .expect("json event serialization should always suceed"), )?; Ok(Ra(create_tag::v3::Response {})) @@ -77,28 +68,25 @@ pub(crate) async fn delete_tag_route( let mut tags_event = event.map_or_else( || { - Ok(TagEvent { - content: TagEventContent { - tags: BTreeMap::new(), - }, + Ok(TagEventContent { + tags: BTreeMap::new(), }) }, |e| { - e.deserialize_as::().map_err(|_| { + e.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, )?; - tags_event.content.tags.remove(&body.tag.clone().into()); + tags_event.tags.remove(&body.tag.clone().into()); services().account_data.update_room( &body.room_id, sender_user, &RoomAccountDataEventType::Tag, - &Raw::new(&tags_event) - .expect("json value serialization should always succeed") - .cast::(), + &Raw::new(&tags_event.into()) + .expect("json value serialization should always succeed"), )?; Ok(Ra(delete_tag::v3::Response {})) @@ -122,20 +110,18 @@ pub(crate) async fn get_tags_route( let tags_event = event.map_or_else( || { - Ok(TagEvent { - content: TagEventContent { - tags: BTreeMap::new(), - }, + Ok(TagEventContent { + tags: BTreeMap::new(), }) }, |e| { - e.deserialize_as::().map_err(|_| { + e.deserialize_as::().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, )?; Ok(Ra(get_tags::v3::Response { - tags: tags_event.content.tags, + tags: tags_event.tags, })) } diff --git a/src/database.rs b/src/database.rs index b374f361..367dba0d 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,10 +7,7 @@ use std::{ }; use ruma::{ - events::{ - push_rules::PushRulesEvent, AnyGlobalAccountDataEvent, - GlobalAccountDataEventType, - }, + events::{push_rules::PushRulesEventContent, GlobalAccountDataEventType}, push::Ruleset, serde::Raw, EventId, OwnedRoomId, RoomId, UserId, @@ -871,9 +868,9 @@ impl KeyValueDatabase { .expect("Username is invalid"); let mut account_data = raw_rules_list - .deserialize_as::() + .deserialize_as::() .unwrap(); - let rules_list = &mut account_data.content.global; + let rules_list = &mut account_data.global; //content rule { @@ -929,9 +926,8 @@ impl KeyValueDatabase { services().account_data.update_global( &user, &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data) - .expect("json serialization should always succeed") - .cast::(), + &Raw::new(&account_data.into()) + .expect("json serialization should always succeed"), )?; } Ok(()) @@ -966,21 +962,19 @@ impl KeyValueDatabase { .expect("Username is invalid"); let mut account_data = raw_rules_list - .deserialize_as::() + .deserialize_as::() .unwrap(); let user_default_rules = Ruleset::server_default(&user); account_data - .content .global .update_with_server_default(user_default_rules); services().account_data.update_global( &user, &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data) - .expect("json serialization should always succeed") - .cast::(), + &Raw::new(&account_data.into()) + .expect("json serialization should always succeed"), )?; } Ok(()) diff --git a/src/service/account_data.rs b/src/service/account_data.rs index 014afcfd..314a78a2 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -2,14 +2,16 @@ use std::collections::HashMap; use ruma::{ events::{ - AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, + AnyGlobalAccountDataEvent, AnyGlobalAccountDataEventContent, + AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, GlobalAccountDataEventType, RoomAccountDataEventType, }, serde::Raw, RoomId, UserId, }; +use serde::{Deserialize, Serialize}; -use crate::Result; +use crate::{Error, Result}; mod data; @@ -19,6 +21,78 @@ pub(crate) struct Service { pub(crate) db: &'static dyn Data, } +pub(crate) fn raw_global_event_to_parts( + event: &Raw, +) -> serde_json::Result<( + GlobalAccountDataEventType, + Raw, +)> { + #[derive(Deserialize)] + struct Parts { + #[serde(rename = "type")] + event_type: GlobalAccountDataEventType, + content: Raw, + } + + let parts = event.deserialize_as::()?; + Ok((parts.event_type, parts.content)) +} + +pub(crate) fn raw_global_event_from_parts( + event_type: &GlobalAccountDataEventType, + content: &Raw, +) -> Raw { + #[derive(Serialize)] + struct Parts<'a> { + #[serde(rename = "type")] + event_type: &'a GlobalAccountDataEventType, + content: &'a Raw, + } + + Raw::new(&Parts { + event_type, + content, + }) + .expect("json serialization should always succeed") + .cast::() +} + +pub(crate) fn raw_room_event_to_parts( + event: &Raw, +) -> serde_json::Result<( + RoomAccountDataEventType, + Raw, +)> { + #[derive(Deserialize)] + struct Parts { + #[serde(rename = "type")] + event_type: RoomAccountDataEventType, + content: Raw, + } + + let parts = event.deserialize_as::()?; + Ok((parts.event_type, parts.content)) +} + +pub(crate) fn raw_room_event_from_parts( + event_type: &RoomAccountDataEventType, + content: &Raw, +) -> Raw { + #[derive(Serialize)] + struct Parts<'a> { + #[serde(rename = "type")] + event_type: &'a RoomAccountDataEventType, + content: &'a Raw, + } + + Raw::new(&Parts { + event_type, + content, + }) + .expect("json serialization should always succeed") + .cast::() +} + impl Service { pub(crate) fn new(db: &'static dyn Data) -> Self { Self { @@ -28,26 +102,28 @@ impl Service { /// Places one event in the global account data of the user and removes the /// previous entry. - #[tracing::instrument(skip(self, user_id, event))] + #[tracing::instrument(skip(self, user_id, content))] pub(crate) fn update_global( &self, user_id: &UserId, event_type: &GlobalAccountDataEventType, - event: &Raw, + content: &Raw, ) -> Result<()> { + let event = raw_global_event_from_parts(event_type, content); self.db.update(None, user_id, &event_type.to_string(), event.json()) } /// Places one event in the room account data of the user and removes the /// previous entry for that room. - #[tracing::instrument(skip(self, room_id, user_id, event))] + #[tracing::instrument(skip(self, room_id, user_id, content))] pub(crate) fn update_room( &self, room_id: &RoomId, user_id: &UserId, event_type: &RoomAccountDataEventType, - event: &Raw, + content: &Raw, ) -> Result<()> { + let event = raw_room_event_from_parts(event_type, content); self.db.update( Some(room_id), user_id, @@ -62,11 +138,17 @@ impl Service { &self, user_id: &UserId, event_type: &GlobalAccountDataEventType, - ) -> Result>> { - Ok(self - .db - .get(None, user_id, &event_type.to_string())? - .map(Raw::from_json)) + ) -> Result>> { + let Some(event) = + self.db.get(None, user_id, &event_type.to_string())? + else { + return Ok(None); + }; + let event = Raw::::from_json(event); + let (_, content) = raw_global_event_to_parts(&event).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; + Ok(Some(content)) } /// Searches the room account data for a specific kind. @@ -76,11 +158,17 @@ impl Service { room_id: &RoomId, user_id: &UserId, event_type: &RoomAccountDataEventType, - ) -> Result>> { - Ok(self - .db - .get(Some(room_id), user_id, &event_type.to_string())? - .map(Raw::from_json)) + ) -> Result>> { + let Some(event) = + self.db.get(Some(room_id), user_id, &event_type.to_string())? + else { + return Ok(None); + }; + let event = Raw::::from_json(event); + let (_, content) = raw_room_event_to_parts(&event).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; + Ok(Some(content)) } /// Returns all changes to global account data that happened after `since`. @@ -93,16 +181,21 @@ impl Service { user_id: &UserId, since: u64, ) -> Result< - HashMap>, + HashMap< + GlobalAccountDataEventType, + Raw, + >, > { - Ok(self - .db + self.db .changes_since(None, user_id, since)? - .into_iter() - .map(|(event_type, event)| { - (event_type.into(), Raw::from_json(event)) + .into_values() + .map(|event| { + let event = Raw::::from_json(event); + raw_global_event_to_parts(&event).map_err(|_| { + Error::bad_database("Invalid account data event in db") + }) }) - .collect()) + .collect() } /// Returns all changes to room account data that happened after `since`. @@ -115,15 +208,18 @@ impl Service { user_id: &UserId, room_id: &RoomId, since: u64, - ) -> Result>> - { - Ok(self - .db + ) -> Result< + HashMap>, + > { + self.db .changes_since(Some(room_id), user_id, since)? - .into_iter() - .map(|(event_type, event)| { - (event_type.into(), Raw::from_json(event)) + .into_values() + .map(|event| { + let event = Raw::::from_json(event); + raw_room_event_to_parts(&event).map_err(|_| { + Error::bad_database("Invalid account data event in db") + }) }) - .collect()) + .collect() } } diff --git a/src/service/account_data/data.rs b/src/service/account_data/data.rs index 9db322ad..3616c322 100644 --- a/src/service/account_data/data.rs +++ b/src/service/account_data/data.rs @@ -9,6 +9,9 @@ use crate::Result; /// distinguish between global and room events. Because there are no ruma types /// that cover both, we use strings for the event types and raw json values for /// the contents. +// +// TODO: once we have the ability to make db schema changes, we should consider +// storing only the content in the db, rather than the whole event object. pub(crate) trait Data: Send + Sync { /// Places one event in the account data of the user and removes the /// previous entry. diff --git a/src/service/admin.rs b/src/service/admin.rs index f5fb39aa..688f35d3 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -5,7 +5,7 @@ use regex::Regex; use ruma::{ api::appservice::Registration, events::{ - push_rules::{PushRulesEvent, PushRulesEventContent}, + push_rules::PushRulesEventContent, room::{ canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, @@ -20,7 +20,7 @@ use ruma::{ power_levels::RoomPowerLevelsEventContent, topic::RoomTopicEventContent, }, - AnyGlobalAccountDataEvent, TimelineEventType, + TimelineEventType, }, serde::Raw, signatures::verify_json, @@ -774,15 +774,15 @@ impl Service { services().account_data.update_global( &user_id, &ruma::events::GlobalAccountDataEventType::PushRules, - &Raw::new(&PushRulesEvent { - content: PushRulesEventContent { + &Raw::new( + &PushRulesEventContent { global: ruma::push::Ruleset::server_default( &user_id, ), - }, - }) - .expect("json serialization should always succeed") - .cast::(), + } + .into(), + ) + .expect("json serialization should always succeed"), )?; // we dont add a device since we're not the user, just the diff --git a/src/service/globals.rs b/src/service/globals.rs index c1922fef..d6df561d 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -18,8 +18,7 @@ use ruma::{ api::federation::discovery::ServerSigningKeys, events::{ push_rules::PushRulesEventContent, - room::message::RoomMessageEventContent, AnyGlobalAccountDataEvent, - GlobalAccountDataEvent, GlobalAccountDataEventType, + room::message::RoomMessageEventContent, GlobalAccountDataEventType, }, push::Ruleset, serde::{Base64, Raw}, @@ -495,13 +494,13 @@ impl Service { services().account_data.update_global( admin_bot, &GlobalAccountDataEventType::PushRules, - &Raw::new(&GlobalAccountDataEvent { - content: PushRulesEventContent { + &Raw::new( + &PushRulesEventContent { global: ruleset, - }, - }) - .expect("json serialization should always succeed") - .cast::(), + } + .into(), + ) + .expect("json serialization should always succeed"), )?; res diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 6fe100cb..961cd043 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -5,9 +5,10 @@ use std::{ use ruma::{ events::{ - ignored_user_list::IgnoredUserListEvent, room::member::MembershipState, - AnyGlobalAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, - GlobalAccountDataEventType, RoomAccountDataEventType, + ignored_user_list::IgnoredUserListEventContent, + room::member::MembershipState, AnyGlobalAccountDataEventContent, + AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, + RoomAccountDataEventType, }, serde::Raw, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, @@ -102,7 +103,7 @@ impl Service { &GlobalAccountDataEventType::IgnoredUserList, )? .map(|event| { - event.deserialize_as::() + event.deserialize_as::() .map_err(|error| { warn!( %error, @@ -114,7 +115,6 @@ impl Service { .transpose()? .is_some_and(|ignored| { ignored - .content .ignored_users .iter() .any(|(user, _details)| user == sender) @@ -221,14 +221,14 @@ impl Service { from_room_id: &RoomId, to_room_id: &RoomId, ) -> Result<()> { - let Some(event) = services() + let Some(event_content) = services() .account_data .get_global(user_id, &GlobalAccountDataEventType::Direct)? else { return Ok(()); }; - let mut event = event + let mut event_content = event_content .deserialize_as::() .expect("RawValue -> Value should always succeed"); @@ -240,14 +240,11 @@ impl Service { // // [1]: https://github.com/element-hq/element-web/issues/27630 // - // A valid m.direct event looks like this: + // Valid m.direct event content looks like this: // // { - // "type": "m.account_data", - // "content": { - // "@userid1": [ "!roomid1", "!roomid2" ], - // "@userid2": [ "!roomid3" ], - // } + // "@userid1": [ "!roomid1", "!roomid2" ], + // "@userid2": [ "!roomid3" ], // } // // We want to find userid keys where the value contains from_room_id, @@ -257,10 +254,7 @@ impl Service { // parts. let mut event_updated = false; - let Some(direct_user_ids) = event.get_mut("content") else { - return Ok(()); - }; - let Some(direct_user_ids) = direct_user_ids.as_object_mut() else { + let Some(direct_user_ids) = event_content.as_object_mut() else { return Ok(()); }; for room_ids in direct_user_ids.values_mut() { @@ -277,9 +271,9 @@ impl Service { if let Err(error) = services().account_data.update_global( user_id, &GlobalAccountDataEventType::Direct, - &Raw::new(&event) + &Raw::new(&event_content) .expect("json serialization should always succeed") - .cast::(), + .cast::(), ) { warn!(%error, "error writing m.direct account data event after upgrading room"); } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 3a422830..f255c985 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -9,7 +9,7 @@ use ruma::{ api::{client::error::ErrorKind, federation}, canonical_json::to_canonical_value, events::{ - push_rules::PushRulesEvent, + push_rules::PushRulesEventContent, room::{ create::RoomCreateEventContent, encrypted::Relation, member::MembershipState, message::RoomMessageEventContent, @@ -419,15 +419,16 @@ impl Service { .account_data .get_global(user, &GlobalAccountDataEventType::PushRules)? .map(|event| { - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid push rules event in db.") - }) + event.deserialize_as::().map_err( + |_| { + Error::bad_database( + "Invalid push rules event in db.", + ) + }, + ) }) .transpose()? - .map_or_else( - || Ruleset::server_default(user), - |ev: PushRulesEvent| ev.content.global, - ); + .map_or_else(|| Ruleset::server_default(user), |ev| ev.global); let mut highlight = false; let mut notify = false; diff --git a/src/service/sending.rs b/src/service/sending.rs index 0a5ff541..20d6eb17 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -22,7 +22,7 @@ use ruma::{ }, device_id, events::{ - push_rules::PushRulesEvent, receipt::ReceiptType, + push_rules::PushRulesEventContent, receipt::ReceiptType, AnySyncEphemeralRoomEvent, GlobalAccountDataEventType, }, push, @@ -859,10 +859,12 @@ async fn handle_push_event( .account_data .get_global(userid, &GlobalAccountDataEventType::PushRules) .unwrap_or_default() - .and_then(|event| event.deserialize_as::().ok()) + .and_then(|event| { + event.deserialize_as::().ok() + }) .map_or_else( || push::Ruleset::server_default(userid), - |ev: PushRulesEvent| ev.content.global, + |ev| ev.global, ); let unread: UInt = services() From 88ad596e8dafd31732e15aad938e575d3a28cd53 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 23 Mar 2025 15:17:19 -0700 Subject: [PATCH 574/617] add type-safe accessors to account_data service --- src/api/client_server/account.rs | 14 +--- src/api/client_server/config.rs | 8 +- src/api/client_server/push.rs | 86 +++++++++------------ src/api/client_server/read_marker.rs | 11 +-- src/api/client_server/tag.rs | 38 ++++----- src/database.rs | 30 +++----- src/service/account_data.rs | 111 ++++++++++++++++++++++++--- src/service/admin.rs | 12 +-- src/service/globals.rs | 12 +-- src/service/rooms/state_cache.rs | 37 ++++----- src/service/rooms/timeline.rs | 14 ++-- src/service/sending.rs | 8 +- 12 files changed, 202 insertions(+), 179 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index c616f62b..1a569b41 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -11,9 +11,7 @@ use ruma::{ error::ErrorKind, uiaa::{AuthFlow, AuthType, UiaaInfo}, }, - events::{ - room::message::RoomMessageEventContent, GlobalAccountDataEventType, - }, + events::room::message::RoomMessageEventContent, push, serde::Raw, UserId, @@ -238,13 +236,9 @@ pub(crate) async fn register_route( // Initial account data services().account_data.update_global( &user_id, - &GlobalAccountDataEventType::PushRules, - &Raw::new( - &ruma::events::push_rules::PushRulesEventContent { - global: push::Ruleset::server_default(&user_id), - } - .into(), - ) + &Raw::new(&ruma::events::push_rules::PushRulesEventContent { + global: push::Ruleset::server_default(&user_id), + }) .expect("constructed event should be valid"), )?; diff --git a/src/api/client_server/config.rs b/src/api/client_server/config.rs index c136b118..8abe8516 100644 --- a/src/api/client_server/config.rs +++ b/src/api/client_server/config.rs @@ -16,7 +16,7 @@ pub(crate) async fn set_global_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services().account_data.update_global( + services().account_data.update_global_any( sender_user, &body.event_type, &body.data, @@ -33,7 +33,7 @@ pub(crate) async fn set_room_account_data_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - services().account_data.update_room( + services().account_data.update_room_any( &body.room_id, sender_user, &body.event_type, @@ -53,7 +53,7 @@ pub(crate) async fn get_global_account_data_route( let account_data = services() .account_data - .get_global(sender_user, &body.event_type)? + .get_global_any(sender_user, &body.event_type)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; Ok(Ra(get_global_account_data::v3::Response { @@ -71,7 +71,7 @@ pub(crate) async fn get_room_account_data_route( let account_data = services() .account_data - .get_room(&body.room_id, sender_user, &body.event_type)? + .get_room_any(&body.room_id, sender_user, &body.event_type)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; Ok(Ra(get_room_account_data::v3::Response { diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 737cb27b..258766db 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -7,7 +7,7 @@ use ruma::{ set_pushrule_actions, set_pushrule_enabled, }, }, - events::{push_rules::PushRulesEventContent, GlobalAccountDataEventType}, + events::push_rules::PushRulesEventContent, push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError}, serde::Raw, }; @@ -24,16 +24,15 @@ pub(crate) async fn get_pushrules_all_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; Ok(Ra(get_pushrules_all::v3::Response { global: account_data.global, @@ -50,16 +49,15 @@ pub(crate) async fn get_pushrule_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; let rule = account_data .global @@ -86,16 +84,15 @@ pub(crate) async fn set_pushrule_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let mut account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if let Err(error) = account_data.global.insert( body.rule.clone(), @@ -135,8 +132,7 @@ pub(crate) async fn set_pushrule_route( services().account_data.update_global( sender_user, - &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data.into()) + &Raw::new(&account_data) .expect("json event serialization should always succeed"), )?; @@ -153,16 +149,15 @@ pub(crate) async fn get_pushrule_actions_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; let global = account_data.global; let actions = global @@ -188,16 +183,15 @@ pub(crate) async fn set_pushrule_actions_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let mut account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if account_data .global @@ -212,8 +206,7 @@ pub(crate) async fn set_pushrule_actions_route( services().account_data.update_global( sender_user, - &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data.into()) + &Raw::new(&account_data) .expect("json event serialization should always suceed"), )?; @@ -230,16 +223,15 @@ pub(crate) async fn get_pushrule_enabled_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; let global = account_data.global; let enabled = global @@ -265,16 +257,15 @@ pub(crate) async fn set_pushrule_enabled_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let mut account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if account_data .global @@ -289,8 +280,7 @@ pub(crate) async fn set_pushrule_enabled_route( services().account_data.update_global( sender_user, - &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data.into()) + &Raw::new(&account_data) .expect("json event serialization should always succeed"), )?; @@ -307,16 +297,15 @@ pub(crate) async fn delete_pushrule_route( let event = services() .account_data - .get_global(sender_user, &GlobalAccountDataEventType::PushRules)? + .get_global::(sender_user)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", ))?; - let mut account_data = - event.deserialize_as::().map_err(|_| { - Error::bad_database("Invalid account data event in db.") - })?; + let mut account_data = event.deserialize().map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; if let Err(error) = account_data.global.remove(body.kind.clone(), &body.rule_id) @@ -337,8 +326,7 @@ pub(crate) async fn delete_pushrule_route( services().account_data.update_global( sender_user, - &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data.into()) + &Raw::new(&account_data) .expect("json event serialization should always suceed"), )?; diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index cf253dab..dc83de60 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -4,10 +4,7 @@ use ruma::{ api::client::{ error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt, }, - events::{ - receipt::{ReceiptThread, ReceiptType}, - RoomAccountDataEventType, - }, + events::receipt::{ReceiptThread, ReceiptType}, serde::Raw, MilliSecondsSinceUnixEpoch, }; @@ -36,8 +33,7 @@ pub(crate) async fn set_read_marker_route( services().account_data.update_room( &body.room_id, sender_user, - &RoomAccountDataEventType::FullyRead, - &Raw::new(&fully_read_event.into()) + &Raw::new(&fully_read_event) .expect("json event serialization should always suceed"), )?; } @@ -131,8 +127,7 @@ pub(crate) async fn create_receipt_route( services().account_data.update_room( &body.room_id, sender_user, - &RoomAccountDataEventType::FullyRead, - &Raw::new(&fully_read_event.into()) + &Raw::new(&fully_read_event) .expect("json event serialization should always succeed"), )?; } diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index fdfd4dfc..aec1a2f0 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use ruma::{ api::client::tag::{create_tag, delete_tag, get_tags}, - events::{tag::TagEventContent, RoomAccountDataEventType}, + events::tag::TagEventContent, serde::Raw, }; @@ -18,11 +18,9 @@ pub(crate) async fn update_tag_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services().account_data.get_room( - &body.room_id, - sender_user, - &RoomAccountDataEventType::Tag, - )?; + let event = services() + .account_data + .get_room::(&body.room_id, sender_user)?; let mut tags_event = event.map_or_else( || { @@ -31,7 +29,7 @@ pub(crate) async fn update_tag_route( }) }, |e| { - e.deserialize_as::().map_err(|_| { + e.deserialize().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, @@ -42,8 +40,7 @@ pub(crate) async fn update_tag_route( services().account_data.update_room( &body.room_id, sender_user, - &RoomAccountDataEventType::Tag, - &Raw::new(&tags_event.into()) + &Raw::new(&tags_event) .expect("json event serialization should always suceed"), )?; @@ -60,11 +57,9 @@ pub(crate) async fn delete_tag_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services().account_data.get_room( - &body.room_id, - sender_user, - &RoomAccountDataEventType::Tag, - )?; + let event = services() + .account_data + .get_room::(&body.room_id, sender_user)?; let mut tags_event = event.map_or_else( || { @@ -73,7 +68,7 @@ pub(crate) async fn delete_tag_route( }) }, |e| { - e.deserialize_as::().map_err(|_| { + e.deserialize().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, @@ -84,8 +79,7 @@ pub(crate) async fn delete_tag_route( services().account_data.update_room( &body.room_id, sender_user, - &RoomAccountDataEventType::Tag, - &Raw::new(&tags_event.into()) + &Raw::new(&tags_event) .expect("json value serialization should always succeed"), )?; @@ -102,11 +96,9 @@ pub(crate) async fn get_tags_route( ) -> Result> { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let event = services().account_data.get_room( - &body.room_id, - sender_user, - &RoomAccountDataEventType::Tag, - )?; + let event = services() + .account_data + .get_room::(&body.room_id, sender_user)?; let tags_event = event.map_or_else( || { @@ -115,7 +107,7 @@ pub(crate) async fn get_tags_route( }) }, |e| { - e.deserialize_as::().map_err(|_| { + e.deserialize().map_err(|_| { Error::bad_database("Invalid account data event in db.") }) }, diff --git a/src/database.rs b/src/database.rs index 367dba0d..16a109e7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,9 +7,7 @@ use std::{ }; use ruma::{ - events::{push_rules::PushRulesEventContent, GlobalAccountDataEventType}, - push::Ruleset, - serde::Raw, + events::push_rules::PushRulesEventContent, push::Ruleset, serde::Raw, EventId, OwnedRoomId, RoomId, UserId, }; use tracing::{debug, error, info, info_span, warn, Instrument}; @@ -860,16 +858,12 @@ impl KeyValueDatabase { let raw_rules_list = services() .account_data - .get_global( - &user, - &GlobalAccountDataEventType::PushRules, - ) + .get_global::(&user) .unwrap() .expect("Username is invalid"); - let mut account_data = raw_rules_list - .deserialize_as::() - .unwrap(); + let mut account_data = + raw_rules_list.deserialize().unwrap(); let rules_list = &mut account_data.global; //content rule @@ -925,8 +919,7 @@ impl KeyValueDatabase { services().account_data.update_global( &user, - &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data.into()) + &Raw::new(&account_data) .expect("json serialization should always succeed"), )?; } @@ -954,16 +947,12 @@ impl KeyValueDatabase { let raw_rules_list = services() .account_data - .get_global( - &user, - &GlobalAccountDataEventType::PushRules, - ) + .get_global::(&user) .unwrap() .expect("Username is invalid"); - let mut account_data = raw_rules_list - .deserialize_as::() - .unwrap(); + let mut account_data = + raw_rules_list.deserialize().unwrap(); let user_default_rules = Ruleset::server_default(&user); account_data @@ -972,8 +961,7 @@ impl KeyValueDatabase { services().account_data.update_global( &user, - &GlobalAccountDataEventType::PushRules, - &Raw::new(&account_data.into()) + &Raw::new(&account_data) .expect("json serialization should always succeed"), )?; } diff --git a/src/service/account_data.rs b/src/service/account_data.rs index 314a78a2..086d75b4 100644 --- a/src/service/account_data.rs +++ b/src/service/account_data.rs @@ -4,7 +4,9 @@ use ruma::{ events::{ AnyGlobalAccountDataEvent, AnyGlobalAccountDataEventContent, AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, - GlobalAccountDataEventType, RoomAccountDataEventType, + GlobalAccountDataEventContent, GlobalAccountDataEventType, + RoomAccountDataEventContent, RoomAccountDataEventType, + StaticEventContent, }, serde::Raw, RoomId, UserId, @@ -101,9 +103,29 @@ impl Service { } /// Places one event in the global account data of the user and removes the - /// previous entry. + /// previous entry, with a static event type. #[tracing::instrument(skip(self, user_id, content))] - pub(crate) fn update_global( + pub(crate) fn update_global( + &self, + user_id: &UserId, + content: &Raw, + ) -> Result<()> + where + T: GlobalAccountDataEventContent + StaticEventContent, + { + let event_type = T::TYPE.into(); + let content = content.cast_ref::(); + let event = raw_global_event_from_parts(&event_type, content); + self.db.update(None, user_id, &event_type.to_string(), event.json()) + } + + /// Places one event in the global account data of the user and removes the + /// previous entry, with a dynamic event type. + /// + /// If the event type is known statically, [`Service::update_global`] should + /// be perferred for better type-safety. + #[tracing::instrument(skip(self, user_id, content))] + pub(crate) fn update_global_any( &self, user_id: &UserId, event_type: &GlobalAccountDataEventType, @@ -114,9 +136,35 @@ impl Service { } /// Places one event in the room account data of the user and removes the - /// previous entry for that room. + /// previous entry for that room, with a static event type. #[tracing::instrument(skip(self, room_id, user_id, content))] - pub(crate) fn update_room( + pub(crate) fn update_room( + &self, + room_id: &RoomId, + user_id: &UserId, + content: &Raw, + ) -> Result<()> + where + T: RoomAccountDataEventContent + StaticEventContent, + { + let event_type = T::TYPE.into(); + let content = content.cast_ref::(); + let event = raw_room_event_from_parts(&event_type, content); + self.db.update( + Some(room_id), + user_id, + &event_type.to_string(), + event.json(), + ) + } + + /// Places one event in the room account data of the user and removes the + /// previous entry for that room, with a dynamic event type. + /// + /// If the event type is known statically, [`Service::update_room`] should + /// be perferred for better type-safety. + #[tracing::instrument(skip(self, room_id, user_id, content))] + pub(crate) fn update_room_any( &self, room_id: &RoomId, user_id: &UserId, @@ -132,9 +180,31 @@ impl Service { ) } - /// Searches the global account data for a specific kind. + /// Searches the global account data for a specific static event type. + #[tracing::instrument(skip(self, user_id))] + pub(crate) fn get_global( + &self, + user_id: &UserId, + ) -> Result>> + where + T: GlobalAccountDataEventContent + StaticEventContent, + { + let Some(event) = self.db.get(None, user_id, T::TYPE)? else { + return Ok(None); + }; + let event = Raw::::from_json(event); + let (_, content) = raw_global_event_to_parts(&event).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; + Ok(Some(content.cast::())) + } + + /// Searches the global account data for a specific dynamic event type. + /// + /// If the event type is known statically, [`Service::get_global`] should + /// be perferred for better type-safety. #[tracing::instrument(skip(self, user_id, event_type))] - pub(crate) fn get_global( + pub(crate) fn get_global_any( &self, user_id: &UserId, event_type: &GlobalAccountDataEventType, @@ -151,9 +221,32 @@ impl Service { Ok(Some(content)) } - /// Searches the room account data for a specific kind. + /// Searches the room account data for a specific static event type. + #[tracing::instrument(skip(self, room_id, user_id))] + pub(crate) fn get_room( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result>> + where + T: RoomAccountDataEventContent + StaticEventContent, + { + let Some(event) = self.db.get(Some(room_id), user_id, T::TYPE)? else { + return Ok(None); + }; + let event = Raw::::from_json(event); + let (_, content) = raw_room_event_to_parts(&event).map_err(|_| { + Error::bad_database("Invalid account data event in db.") + })?; + Ok(Some(content.cast::())) + } + + /// Searches the room account data for a specific dynamic event type. + /// + /// If the event type is known statically, [`Service::get_room`] should + /// be perferred for better type-safety. #[tracing::instrument(skip(self, room_id, user_id, event_type))] - pub(crate) fn get_room( + pub(crate) fn get_room_any( &self, room_id: &RoomId, user_id: &UserId, diff --git a/src/service/admin.rs b/src/service/admin.rs index 688f35d3..0f15d5bc 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -773,15 +773,9 @@ impl Service { // Initial account data services().account_data.update_global( &user_id, - &ruma::events::GlobalAccountDataEventType::PushRules, - &Raw::new( - &PushRulesEventContent { - global: ruma::push::Ruleset::server_default( - &user_id, - ), - } - .into(), - ) + &Raw::new(&PushRulesEventContent { + global: ruma::push::Ruleset::server_default(&user_id), + }) .expect("json serialization should always succeed"), )?; diff --git a/src/service/globals.rs b/src/service/globals.rs index d6df561d..524c18d3 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -18,7 +18,7 @@ use ruma::{ api::federation::discovery::ServerSigningKeys, events::{ push_rules::PushRulesEventContent, - room::message::RoomMessageEventContent, GlobalAccountDataEventType, + room::message::RoomMessageEventContent, }, push::Ruleset, serde::{Base64, Raw}, @@ -493,13 +493,9 @@ impl Service { services().account_data.update_global( admin_bot, - &GlobalAccountDataEventType::PushRules, - &Raw::new( - &PushRulesEventContent { - global: ruleset, - } - .into(), - ) + &Raw::new(&PushRulesEventContent { + global: ruleset, + }) .expect("json serialization should always succeed"), )?; diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 961cd043..3e9608a3 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -5,10 +5,10 @@ use std::{ use ruma::{ events::{ + direct::DirectEventContent, ignored_user_list::IgnoredUserListEventContent, - room::member::MembershipState, AnyGlobalAccountDataEventContent, - AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, - RoomAccountDataEventType, + room::member::MembershipState, tag::TagEventContent, + AnyStrippedStateEvent, AnySyncStateEvent, }, serde::Raw, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, @@ -96,14 +96,9 @@ impl Service { // We want to know if the sender is ignored by the receiver let is_ignored = services() .account_data - // Ignored users are in global account data - .get_global( - // Receiver - user_id, - &GlobalAccountDataEventType::IgnoredUserList, - )? + .get_global::(user_id)? .map(|event| { - event.deserialize_as::() + event.deserialize() .map_err(|error| { warn!( %error, @@ -192,20 +187,15 @@ impl Service { from_room_id: &RoomId, to_room_id: &RoomId, ) -> Result<()> { - let Some(event) = services().account_data.get_room( - from_room_id, - user_id, - &RoomAccountDataEventType::Tag, - )? + let Some(event) = services() + .account_data + .get_room::(from_room_id, user_id)? else { return Ok(()); }; - if let Err(error) = services().account_data.update_room( - to_room_id, - user_id, - &RoomAccountDataEventType::Tag, - &event, - ) { + if let Err(error) = + services().account_data.update_room(to_room_id, user_id, &event) + { warn!(%error, "error writing m.tag account data to upgraded room"); } @@ -223,7 +213,7 @@ impl Service { ) -> Result<()> { let Some(event_content) = services() .account_data - .get_global(user_id, &GlobalAccountDataEventType::Direct)? + .get_global::(user_id)? else { return Ok(()); }; @@ -270,10 +260,9 @@ impl Service { if event_updated { if let Err(error) = services().account_data.update_global( user_id, - &GlobalAccountDataEventType::Direct, &Raw::new(&event_content) .expect("json serialization should always succeed") - .cast::(), + .cast::(), ) { warn!(%error, "error writing m.direct account data event after upgrading room"); } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index f255c985..93bf6c53 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -16,7 +16,7 @@ use ruma::{ power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent, }, - GlobalAccountDataEventType, StateEventType, TimelineEventType, + StateEventType, TimelineEventType, }, push::{Action, Ruleset, Tweak}, state_res::{self, Event}, @@ -417,15 +417,11 @@ impl Service { let rules_for_user = services() .account_data - .get_global(user, &GlobalAccountDataEventType::PushRules)? + .get_global::(user)? .map(|event| { - event.deserialize_as::().map_err( - |_| { - Error::bad_database( - "Invalid push rules event in db.", - ) - }, - ) + event.deserialize().map_err(|_| { + Error::bad_database("Invalid push rules event in db.") + }) }) .transpose()? .map_or_else(|| Ruleset::server_default(user), |ev| ev.global); diff --git a/src/service/sending.rs b/src/service/sending.rs index 20d6eb17..c7723e45 100644 --- a/src/service/sending.rs +++ b/src/service/sending.rs @@ -23,7 +23,7 @@ use ruma::{ device_id, events::{ push_rules::PushRulesEventContent, receipt::ReceiptType, - AnySyncEphemeralRoomEvent, GlobalAccountDataEventType, + AnySyncEphemeralRoomEvent, }, push, serde::Raw, @@ -857,11 +857,9 @@ async fn handle_push_event( let rules_for_user = services() .account_data - .get_global(userid, &GlobalAccountDataEventType::PushRules) + .get_global::(userid) .unwrap_or_default() - .and_then(|event| { - event.deserialize_as::().ok() - }) + .and_then(|event| event.deserialize().ok()) .map_or_else( || push::Ruleset::server_default(userid), |ev| ev.global, From 9a142c7557a887c3b4879cb9fb6e8ae402978298 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 20 Jul 2025 12:24:41 -0700 Subject: [PATCH 575/617] refactor Pdu::copy_redacts to use early returns --- src/service/pdu.rs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/service/pdu.rs b/src/service/pdu.rs index b6052a4c..1131a5b8 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -161,26 +161,27 @@ impl PduEvent { pub(crate) fn copy_redacts( &self, ) -> (Option>, Box) { - if self.kind == TimelineEventType::RoomRedaction { - if let Ok(mut content) = serde_json::from_str::< - RoomRedactionEventContent, - >(self.content.get()) - { - if let Some(redacts) = content.redacts { - return (Some(redacts.into()), self.content.clone()); - } else if let Some(redacts) = self.redacts.clone() { - content.redacts = Some(redacts.into()); - return ( - self.redacts.clone(), - to_raw_value(&content).expect( - "Must be valid, we only added redacts field", - ), - ); - } - } + if self.kind != TimelineEventType::RoomRedaction { + return (self.redacts.clone(), self.content.clone()); } + let Ok(mut content) = serde_json::from_str::( + self.content.get(), + ) else { + return (self.redacts.clone(), self.content.clone()); + }; - (self.redacts.clone(), self.content.clone()) + if let Some(redacts) = content.redacts { + (Some(redacts.into()), self.content.clone()) + } else if let Some(redacts) = self.redacts.clone() { + content.redacts = Some(redacts.into()); + ( + self.redacts.clone(), + to_raw_value(&content) + .expect("Must be valid, we only added redacts field"), + ) + } else { + (self.redacts.clone(), self.content.clone()) + } } #[tracing::instrument(skip(self))] From 55a01e711320d49353af309c02beeb92d76c386e Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 19 Jul 2025 23:19:41 -0700 Subject: [PATCH 576/617] don't strip unknown keys when copying redacts property to/from content Servers are required to preserve unknown properties in event content, since they may be added by a future version of the spec. Round-tripping through RoomRedactionEventContent results in dropping all unknown properties. --- book/changelog.md | 3 +++ src/service/pdu.rs | 52 +++++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/book/changelog.md b/book/changelog.md index 23ae40d6..cadf4c25 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -245,6 +245,9 @@ This will be the first release of Grapevine since it was forked from Conduit 29. Fix bug where ban reasons would be ignored when the banned user already had a member event in the room. ([!185](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/185)) +30. Stop stripping unknown properties from redaction events before sending them + to clients. + ([!191](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/191)) ### Added diff --git a/src/service/pdu.rs b/src/service/pdu.rs index 1131a5b8..fe13f1d2 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -1,16 +1,13 @@ -use std::{cmp::Ordering, collections::BTreeMap, sync::Arc}; +use std::{borrow::Cow, cmp::Ordering, collections::BTreeMap, sync::Arc}; use ruma::{ canonical_json::redact_content_in_place, events::{ - room::{ - member::RoomMemberEventContent, - redaction::RoomRedactionEventContent, - }, - space::child::HierarchySpaceChildEvent, - AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyStateEvent, - AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent, - AnyTimelineEvent, StateEvent, TimelineEventType, + room::member::RoomMemberEventContent, + space::child::HierarchySpaceChildEvent, AnyEphemeralRoomEvent, + AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, + AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, + TimelineEventType, }, serde::Raw, state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, @@ -161,24 +158,41 @@ impl PduEvent { pub(crate) fn copy_redacts( &self, ) -> (Option>, Box) { + #[derive(Deserialize)] + struct ExtractRedacts<'a> { + #[serde(borrow)] + redacts: Option>, + } + if self.kind != TimelineEventType::RoomRedaction { return (self.redacts.clone(), self.content.clone()); } - let Ok(mut content) = serde_json::from_str::( - self.content.get(), - ) else { + let Ok(extract) = + serde_json::from_str::>(self.content.get()) + else { return (self.redacts.clone(), self.content.clone()); }; - if let Some(redacts) = content.redacts { + if let Some(redacts) = extract.redacts { (Some(redacts.into()), self.content.clone()) } else if let Some(redacts) = self.redacts.clone() { - content.redacts = Some(redacts.into()); - ( - self.redacts.clone(), - to_raw_value(&content) - .expect("Must be valid, we only added redacts field"), - ) + let content = serde_json::from_str::>( + self.content.get(), + ); + let mut content = match content { + Ok(content) => content, + Err(error) => { + warn!(%error, "PDU is not a valid json object"); + return (self.redacts.clone(), self.content.clone()); + } + }; + + let redacts_json = to_raw_value(&redacts) + .expect("all strings should be representable as json"); + content.insert("redacts", &redacts_json); + let content_json = to_raw_value(&content) + .expect("Must be valid, we only added redacts field"); + (self.redacts.clone(), content_json) } else { (self.redacts.clone(), self.content.clone()) } From e6dbc293f11c4453ed18b9cefc31580377203965 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 21 Jul 2025 21:36:42 -0700 Subject: [PATCH 577/617] remove jemalloc support It's no longer being developed and it is very very annoying to maintain support for. --- Cargo.lock | 21 ----------- Cargo.toml | 2 -- book/changelog.md | 50 +++++++++++++------------- flake.nix | 4 --- nix/pkgs/default/default.nix | 14 ++------ nix/pkgs/rocksdb/default.nix | 7 ---- nix/pkgs/rust-jemalloc-sys/default.nix | 12 ------- src/main.rs | 6 ---- 8 files changed, 27 insertions(+), 89 deletions(-) delete mode 100644 nix/pkgs/rust-jemalloc-sys/default.nix diff --git a/Cargo.lock b/Cargo.lock index 6e0714f3..a3dd7de4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -963,7 +963,6 @@ dependencies = [ "tempfile", "thiserror 2.0.12", "thread_local", - "tikv-jemallocator", "tokio", "toml", "tower 0.5.2", @@ -3263,26 +3262,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tikv-jemalloc-sys" -version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "tikv-jemallocator" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" -dependencies = [ - "libc", - "tikv-jemalloc-sys", -] - [[package]] name = "time" version = "0.3.41" diff --git a/Cargo.toml b/Cargo.toml index d8fb1488..6c61f19e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,7 +133,6 @@ sha-1 = "0.10.1" strum = { version = "0.27.1", features = ["derive"] } thiserror = "2.0.12" thread_local = "1.1.8" -tikv-jemallocator = { version = "0.6.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true } tokio = { version = "1.44.1", features = ["fs", "macros", "signal", "sync"] } toml = "0.8.20" tower = { version = "0.5.2", features = ["util"] } @@ -164,7 +163,6 @@ opt-level = 3 default = ["rocksdb", "sqlite", "systemd"] # Keep sorted -jemalloc = ["dep:tikv-jemallocator"] rocksdb = ["dep:rocksdb"] sqlite = ["dep:rusqlite", "dep:parking_lot", "tokio/signal"] systemd = ["dep:sd-notify"] diff --git a/book/changelog.md b/book/changelog.md index cadf4c25..3729bfbb 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -85,6 +85,8 @@ This will be the first release of Grapevine since it was forked from Conduit `global.pdu_cache_capacity` configuration options. ([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124)) * Instead, it is now possible to configure each cache capacity individually. +10. Remove jemalloc support. + ([!93](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/193)) ### Changed @@ -171,81 +173,79 @@ This will be the first release of Grapevine since it was forked from Conduit ([!20 (263edcc)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/20/diffs?commit_id=263edcc8a127ad2a541a3bb6ad35a8a459ea5616)) 6. Reduce the likelihood of locking up the async runtime. ([!19](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/19)) -7. Fix dynamically linked jemalloc builds. - ([!23](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/23)) -8. Fix search results not including subsequent pages in certain situations. +7. Fix search results not including subsequent pages in certain situations. ([!35 (0cdf032)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/35/diffs?commit_id=0cdf03288ab8fa363c313bd929c8b5183d14ab77)) -9. Fix search results missing events in subsequent pages in certain situations. +8. Fix search results missing events in subsequent pages in certain situations. ([!35 (3551a6e)](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/35/diffs?commit_id=3551a6ef7a29219b9b30f50a7e8c92b92debcdcf)) -10. Only process admin commands if the admin bot is in the admin room. +9. Only process admin commands if the admin bot is in the admin room. ([!43](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/43)) -11. Fix bug where invalid account data from a client could prevent a user from +10. Fix bug where invalid account data from a client could prevent a user from joining any upgraded rooms and brick rooms that affected users attempted to upgrade. ([!53](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/53)) -12. Fix bug where unexpected keys were deleted from `m.direct` account data +11. Fix bug where unexpected keys were deleted from `m.direct` account data events when joining an upgraded room. ([!53](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/53)) -13. Fixed appservice users not receiving federated invites if the local server +12. Fixed appservice users not receiving federated invites if the local server isn't already resident in the room ([!80](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/80)) -14. Fix bug where, if a server has multiple public keys, only one would be fetched. +13. Fix bug where, if a server has multiple public keys, only one would be fetched. ([!78](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/78)) -15. Fix bug where expired keys may not be re-fetched in some scenarios. +14. Fix bug where expired keys may not be re-fetched in some scenarios. ([!78](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/78)) -16. Fix bug where signing keys would not be fetched when joining a room if we +15. Fix bug where signing keys would not be fetched when joining a room if we hadn't previously seen any signing keys from that server. ([!87](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/87)) -17. Fixed bug +16. Fixed bug ([#48](https://gitlab.computer.surgery/matrix/grapevine/-/issues/48)) that caused us to attempt to fetch our own signing keys from ourselves over federation, and fail ("Won't send federation request to ourselves"). ([!96](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/96)) -18. Fixed incoming HTTP/2 requests failing federation signature check. +17. Fixed incoming HTTP/2 requests failing federation signature check. ([!104](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/104)) -19. Return 403 instead of 500 when joins to a local-only room are denied. +18. Return 403 instead of 500 when joins to a local-only room are denied. Consequently fixes Heisenbridge being unable to join puppeted users to its rooms ([#85](https://gitlab.computer.surgery/matrix/grapevine/-/issues/85)). ([!127](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/127)) -20. Fix handling of v11 rooms with `m.room.create` event content that passes +19. Fix handling of v11 rooms with `m.room.create` event content that passes the authorization rules but doesn't match other parts of the spec. ([!139](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/139)) -21. Fix tiebreaking comparisons between events during state resolution. This +20. Fix tiebreaking comparisons between events during state resolution. This will reduce the rate at which servers disagree about the state of rooms. ([!141](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/141)) -22. Fix bug where the backoff state for remote device key queries was not reset +21. Fix bug where the backoff state for remote device key queries was not reset after a successful request, causing an increasing rate of key query failures over time until a server restart. ([!149](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/149)) -23. Fix bug where remote key queries that were skipped because the target server +22. Fix bug where remote key queries that were skipped because the target server was in backoff would increment the backoff delay further, leading to a positive feedback loop. ([!149](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/149)) -24. Return 504 M_NOT_YET_UPLOADED instead of 500 M_UNKNOWN when a media file is +23. Return 504 M_NOT_YET_UPLOADED instead of 500 M_UNKNOWN when a media file is present in the database but the contents are missing in the filesystem. Removing media from the filesystem was the only way to delete media before [!99](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/99), so this situation is common. ([!55](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/55)) ([!153](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/153)) -25. Return 400 M_BAD_ALIAS from +24. Return 400 M_BAD_ALIAS from [PUT /_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}](https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey) instead of 400 M_FORBIDDEN when trying to set a canonical alias that does not exist. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) -26. Validate schema of new `m.room.canonical_alias` event sent by clients, +25. Validate schema of new `m.room.canonical_alias` event sent by clients, rather than silently allowing any contents if the event can't be parsed. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) -27. Only validate canonical aliases that are new, rather than rather than +26. Only validate canonical aliases that are new, rather than rather than revalidating every alias. This makes it possible to add/remove aliases when some of the existing aliases cannot be validated. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) -28. Fix read receipts not being sent over federation (or only arbitrarily late) +27. Fix read receipts not being sent over federation (or only arbitrarily late) ([!162](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/162)) -29. Fix bug where ban reasons would be ignored when the banned user already had +28. Fix bug where ban reasons would be ignored when the banned user already had a member event in the room. ([!185](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/185)) -30. Stop stripping unknown properties from redaction events before sending them +29. Stop stripping unknown properties from redaction events before sending them to clients. ([!191](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/191)) diff --git a/flake.nix b/flake.nix index f390239a..c9ca3630 100644 --- a/flake.nix +++ b/flake.nix @@ -37,10 +37,6 @@ inherit (pkgs) rocksdb; }; - rust-jemalloc-sys = self.callPackage ./nix/pkgs/rust-jemalloc-sys { - inherit (pkgs) rust-jemalloc-sys; - }; - shell = self.callPackage ./nix/shell.nix {}; # The Rust toolchain to use diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index a9abdc5a..c15b8f82 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -6,7 +6,6 @@ , pkgsBuildHost , rocksdb , rust -, rust-jemalloc-sys , snappy , stdenv @@ -35,19 +34,12 @@ let lib.optionals default-features allDefaultFeatures ++ lib.optionals all-features allFeatures); - featureEnabled = feature : builtins.elem feature features'; - buildDepsOnlyEnv = - let - rocksdb' = rocksdb.override { - enableJemalloc = featureEnabled "jemalloc"; - }; - in { NIX_OUTPATH_USED_AS_RANDOM_SEED = "randomseed"; CARGO_PROFILE = profile; - ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include"; - ROCKSDB_LIB_DIR = "${rocksdb'}/lib"; + ROCKSDB_INCLUDE_DIR = "${rocksdb}/include"; + ROCKSDB_LIB_DIR = "${rocksdb}/lib"; } // (import ./cross-compilation-env.nix { @@ -86,8 +78,6 @@ let dontStrip = profile != "release"; - buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys; - nativeBuildInputs = [ # bindgen needs the build platform's libclang. Apparently due to "splicing # weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the diff --git a/nix/pkgs/rocksdb/default.nix b/nix/pkgs/rocksdb/default.nix index 9aa62353..6292e977 100644 --- a/nix/pkgs/rocksdb/default.nix +++ b/nix/pkgs/rocksdb/default.nix @@ -2,10 +2,6 @@ { inputs , lib , rocksdb -, rust-jemalloc-sys - -# Options (keep sorted) -, enableJemalloc ? false }: let @@ -17,10 +13,7 @@ let (builtins.readFile ../../../flake.lock) ).nodes.rocksdb.original.ref; })).override { - jemalloc = rust-jemalloc-sys; - enableLiburing = false; - inherit enableJemalloc; }; cVersion = rocksdb'.version; diff --git a/nix/pkgs/rust-jemalloc-sys/default.nix b/nix/pkgs/rust-jemalloc-sys/default.nix deleted file mode 100644 index 7d1b178d..00000000 --- a/nix/pkgs/rust-jemalloc-sys/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -# This derivation will set the JEMALLOC_OVERRIDE variable, causing the -# tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's -# own. In order for this to work, we need to set flags on the build that match -# whatever flags tikv-jemalloc-sys was going to use. These are dependent on -# which features we enable in tikv-jemalloc-sys. - -{ rust-jemalloc-sys }: - -rust-jemalloc-sys.override { - # tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms feature - unprefixed = true; -} diff --git a/src/main.rs b/src/main.rs index 8f4268a1..fc6a3459 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,6 @@ use std::process::ExitCode; use clap::Parser; -#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] -use tikv_jemallocator::Jemalloc; use tracing::{error, info}; mod api; @@ -23,10 +21,6 @@ pub(crate) use config::Config; pub(crate) use service::{pdu::PduEvent, services, Services}; pub(crate) use utils::error::{Error, Result}; -#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; - /// Returns the current version of the crate with extra info if supplied /// /// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string From b5294f9aa0e64245382f34bc4503a858b8c5add0 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Jul 2025 14:56:18 -0700 Subject: [PATCH 578/617] remove all-features devshell It's no longer necessary since it's equivalent to the default-features one now. The `DIRENV_DEVSHELL` thing is left in place however in case that becomes useful again in the future. --- bin/nix-build-and-cache | 1 - engage.toml | 30 ------------------------------ flake.nix | 3 --- 3 files changed, 34 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index ff06bc96..4c2a8d71 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -51,7 +51,6 @@ ci() { # Keep sorted "$toplevel#devShells.x86_64-linux.default" - "$toplevel#devShells.x86_64-linux.all-features" attic#default nixpkgs#direnv nixpkgs#jq diff --git a/engage.toml b/engage.toml index 94c2bd5e..30e81d31 100644 --- a/engage.toml +++ b/engage.toml @@ -88,21 +88,6 @@ name = "cargo-clippy/default" group = "lints" script = "cargo clippy --workspace --all-targets --color=always -- -D warnings" -[[task]] -name = "cargo-clippy/all" -group = "lints" -script = """ -env DIRENV_DEVSHELL=all-features \ - direnv exec . \ - cargo clippy \ - --workspace \ - --all-targets \ - --all-features \ - --color=always \ - -- \ - -D warnings -""" - [[task]] name = "cargo/default" group = "tests" @@ -114,18 +99,3 @@ cargo test \ -- \ --color=always """ - -[[task]] -name = "cargo/all" -group = "tests" -script = """ -env DIRENV_DEVSHELL=all-features \ - direnv exec . \ - cargo test \ - --all-features \ - --workspace \ - --all-targets \ - --color=always \ - -- \ - --color=always -""" diff --git a/flake.nix b/flake.nix index c9ca3630..ba839631 100644 --- a/flake.nix +++ b/flake.nix @@ -121,9 +121,6 @@ ); devShells.default = (mkScope pkgs).shell; - devShells.all-features = ((mkScope pkgs).overrideDefaultPackage { - all-features = true; - }).shell; } ) // From 40da74f28c0298f964b1b3033625eece4ad10404 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 24 Jul 2025 15:05:43 -0700 Subject: [PATCH 579/617] Revert "do default-feature unification in nix" This reverts commit 8f24ac1f27d5e09cb7ba36cc4bf0d7835945d5ba. This is no longer necessary since the set of all features is equal to the set of default features. --- nix/pkgs/default/default.nix | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/nix/pkgs/default/default.nix b/nix/pkgs/default/default.nix index c15b8f82..60b85a38 100644 --- a/nix/pkgs/default/default.nix +++ b/nix/pkgs/default/default.nix @@ -11,7 +11,6 @@ # Options (keep sorted) , default-features ? true -, all-features ? false , features ? [] , profile ? "release" , version-extra ? inputs.self.shortRev @@ -20,19 +19,7 @@ }: let - # We perform default-feature unification in nix, because some of the dependencies - # on the nix side depend on feature values. cargoManifest = lib.importTOML "${inputs.self}/Cargo.toml"; - allDefaultFeatures = cargoManifest.features.default; - allFeatures = lib.unique ( - lib.remove "default" (lib.attrNames cargoManifest.features) ++ - lib.attrNames - (lib.filterAttrs (_: dependency: dependency.optional or false) - cargoManifest.dependencies)); - features' = lib.unique - (features ++ - lib.optionals default-features allDefaultFeatures ++ - lib.optionals all-features allFeatures); buildDepsOnlyEnv = { @@ -104,10 +91,13 @@ craneLib.buildPackage (commonAttrs // { env = buildDepsOnlyEnv; }); - cargoExtraArgs = "--locked --no-default-features " + cargoExtraArgs = "--locked " + lib.optionalString - (features' != []) - "--features " + (builtins.concatStringsSep "," features'); + (!default-features) + "--no-default-features " + + lib.optionalString + (features != []) + "--features " + (builtins.concatStringsSep "," features); env = buildPackageEnv; From a3da77ce2c95ce5fde7980f320b206b700069030 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 26 Jul 2025 19:32:45 +0200 Subject: [PATCH 580/617] Add a couple extra Sync bounds Not necessary right now, but required for axum 0.8. --- src/api/ruma_wrapper/axum.rs | 1 + src/cli/serve.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index 712386d9..a514c6b8 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -402,6 +402,7 @@ async fn ar_from_request_inner( impl FromRequest for Ar where T: IncomingRequest, + S: Sync, { type Rejection = Error; diff --git a/src/cli/serve.rs b/src/cli/serve.rs index e1bc79c7..c51c1232 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -209,13 +209,14 @@ struct ServerSpawner<'cfg, M> { impl<'cfg, M> ServerSpawner<'cfg, M> where - M: Layer + Clone + Send + 'static, + M: Layer + Clone + Send + Sync + 'static, M::Service: Service< axum::extract::Request, Response = axum::response::Response, Error = Infallible, > + Clone + Send + + Sync + 'static, >::Future: Send + 'static, { @@ -936,7 +937,7 @@ macro_rules! impl_ruma_handler { where Req: IncomingRequest + Send + 'static, Resp: IntoResponse, - F: FnOnce($($ty,)* Ar) -> Fut + Clone + Send + 'static, + F: FnOnce($($ty,)* Ar) -> Fut + Clone + Send + Sync + 'static, Fut: Future> + Send, E: IntoResponse, From c713e9f21f4c38b676aa2eec412784405f36d8e0 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 26 Jul 2025 19:33:17 +0200 Subject: [PATCH 581/617] Remove no-op #[async_trait] attribute --- src/cli/serve.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/serve.rs b/src/cli/serve.rs index c51c1232..7a528748 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -930,7 +930,6 @@ pub(crate) trait RumaHandler { macro_rules! impl_ruma_handler { ( $($ty:ident),* $(,)? ) => { - #[axum::async_trait] #[allow(non_snake_case)] impl RumaHandler<($($ty,)* Ar,)> for F From 27667911ed4e4f3d3aaec0d12cf4f22c232707e1 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 31 Jul 2025 13:30:24 -0700 Subject: [PATCH 582/617] upgrade docker images to lix 2.93.3 --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7e271a82..9b116e81 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,14 +38,14 @@ before_script: cache-ci-deps: stage: ci - image: nixos/nix:2.18.2 + image: git.lix.systems/lix-project/lix:2.93.3 rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH script: direnv exec . job cache-ci-deps ci: stage: ci - image: nixos/nix:2.18.2 + image: git.lix.systems/lix-project/lix:2.93.3 rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' script: @@ -57,7 +57,7 @@ ci: artifacts: stage: artifacts - image: nixos/nix:2.18.2 + image: git.lix.systems/lix-project/lix:2.93.3 rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_PIPELINE_SOURCE == 'merge_request_event' @@ -70,7 +70,7 @@ artifacts: pages: stage: deploy - image: nixos/nix:2.18.2 + image: git.lix.systems/lix-project/lix:2.93.3 rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH script: From cf4f2fe51ba3bbe4dd77caf345785bcfe6ba573d Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 09:26:34 -0700 Subject: [PATCH 583/617] use more readarray The first part replaces a loop which is probably a performance improvement, and the second part addresses a shellcheck complaint about not declaring and assigning separately. --- bin/nix-build-and-cache | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 4c2a8d71..67e48ff0 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -25,12 +25,11 @@ just() { # Find all output paths of the installables and their build dependencies readarray -t derivations < <(nix path-info --derivation "$@") - cache=() - for derivation in "${derivations[@]}"; do - cache+=( - "$(nix-store --query --requisites --include-outputs "$derivation")" - ) - done + readarray -t cache < <( + xargs \ + nix-store --query --requisites --include-outputs \ + <<< "${derivations[*]}" + ) # Upload them to Attic # @@ -62,12 +61,12 @@ ci() { # Build and cache all the package outputs packages() { - declare -a cache="($( + readarray -t cache < <( nix flake show --json 2> /dev/null | nix run --inputs-from "$toplevel" nixpkgs#jq -- \ -r \ - '.packages."x86_64-linux" | keys | map("'"$toplevel"'#" + .) | @sh' - ))" + '.packages."x86_64-linux" | keys | map("'"$toplevel"'#" + .) | .[]' + ) just "${cache[@]}" } From be3822d5859e60306a5bdced9b075c0d7528fcef Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 10:03:14 -0700 Subject: [PATCH 584/617] drop eval Shellcheck says so. --- bin/nix-build-and-cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 67e48ff0..54e45aee 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -71,4 +71,4 @@ packages() { just "${cache[@]}" } -eval "$@" +"$@" From 85b9080c77848839d25fba72ac4f4adb3ccb4fd9 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 10:00:48 -0700 Subject: [PATCH 585/617] change working directory Did you know installables are parsed with regex? This mitigates how horrifying that is. --- bin/nix-build-and-cache | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 54e45aee..fe1ed381 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -2,8 +2,6 @@ set -euo pipefail -toplevel="$(git rev-parse --show-toplevel)" - # Build and cache the specified arguments just() { if command -v nom &> /dev/null; then @@ -17,7 +15,7 @@ just() { return fi - nix run --inputs-from "$toplevel" attic -- \ + nix run --inputs-from . attic -- \ login \ "$ATTIC_SERVER" \ "$ATTIC_ENDPOINT" \ @@ -38,7 +36,7 @@ just() { # store paths include a newline in them. ( IFS=$'\n' - nix shell --inputs-from "$toplevel" attic -c xargs \ + nix shell --inputs-from . attic -c xargs \ attic push "$ATTIC_SERVER:$ATTIC_CACHE" <<< "${cache[*]}" ) } @@ -46,10 +44,10 @@ just() { # Build and cache things needed for CI ci() { cache=( - --inputs-from "$toplevel" + --inputs-from . # Keep sorted - "$toplevel#devShells.x86_64-linux.default" + ".#devShells.x86_64-linux.default" attic#default nixpkgs#direnv nixpkgs#jq @@ -63,12 +61,14 @@ ci() { packages() { readarray -t cache < <( nix flake show --json 2> /dev/null | - nix run --inputs-from "$toplevel" nixpkgs#jq -- \ + nix run --inputs-from . nixpkgs#jq -- \ -r \ - '.packages."x86_64-linux" | keys | map("'"$toplevel"'#" + .) | .[]' + '.packages."x86_64-linux" | keys | map(".#" + .) | .[]' ) just "${cache[@]}" } +pushd "$(git rev-parse --show-toplevel)" > /dev/null "$@" +popd > /dev/null From 7fc39b1845d444561b6845d9bc923f487e8afca6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 09:26:34 -0700 Subject: [PATCH 586/617] simplify attic call The comment isn't really needed since the reasoning applies to the other instances of readarray and here-strings too, and the IFS bit is unnecessary. --- bin/nix-build-and-cache | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index fe1ed381..3810cd19 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -15,11 +15,8 @@ just() { return fi - nix run --inputs-from . attic -- \ - login \ - "$ATTIC_SERVER" \ - "$ATTIC_ENDPOINT" \ - "$ATTIC_TOKEN" + nix run --inputs-from . attic#default -- \ + login "$ATTIC_SERVER" "$ATTIC_ENDPOINT" "$ATTIC_TOKEN" # Find all output paths of the installables and their build dependencies readarray -t derivations < <(nix path-info --derivation "$@") @@ -29,15 +26,13 @@ just() { <<< "${derivations[*]}" ) - # Upload them to Attic - # - # Use `xargs` and a here-string because something would probably explode if - # several thousand arguments got passed to a command at once. Hopefully no - # store paths include a newline in them. + # Upload them to Attic. It seems to insist on newlines to separate the + # paths. ( IFS=$'\n' - nix shell --inputs-from . attic -c xargs \ - attic push "$ATTIC_SERVER:$ATTIC_CACHE" <<< "${cache[*]}" + nix run --inputs-from . attic#default -- \ + push --stdin --no-closure "$ATTIC_SERVER:$ATTIC_CACHE" \ + <<< "${cache[*]}" ) } From 47c210d5deb8d26728a6098bcfc3f8ef01b68065 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 09:40:08 -0700 Subject: [PATCH 587/617] reorder attic token check --- bin/nix-build-and-cache | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index 3810cd19..c0969a24 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -10,14 +10,6 @@ just() { nix build "$@" fi - if [ -z ${ATTIC_TOKEN+x} ]; then - echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache" - return - fi - - nix run --inputs-from . attic#default -- \ - login "$ATTIC_SERVER" "$ATTIC_ENDPOINT" "$ATTIC_TOKEN" - # Find all output paths of the installables and their build dependencies readarray -t derivations < <(nix path-info --derivation "$@") readarray -t cache < <( @@ -26,6 +18,14 @@ just() { <<< "${derivations[*]}" ) + if [ -z ${ATTIC_TOKEN+x} ]; then + echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache" + return + fi + + nix run --inputs-from . attic#default -- \ + login "$ATTIC_SERVER" "$ATTIC_ENDPOINT" "$ATTIC_TOKEN" + # Upload them to Attic. It seems to insist on newlines to separate the # paths. ( From 132528b545cef078776e8b75559bb3dd50ccfed2 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 09:56:56 -0700 Subject: [PATCH 588/617] improve variable names' clarity --- bin/nix-build-and-cache | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index c0969a24..ebfcf4f8 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -12,7 +12,7 @@ just() { # Find all output paths of the installables and their build dependencies readarray -t derivations < <(nix path-info --derivation "$@") - readarray -t cache < <( + readarray -t upload_paths < <( xargs \ nix-store --query --requisites --include-outputs \ <<< "${derivations[*]}" @@ -32,13 +32,13 @@ just() { IFS=$'\n' nix run --inputs-from . attic#default -- \ push --stdin --no-closure "$ATTIC_SERVER:$ATTIC_CACHE" \ - <<< "${cache[*]}" + <<< "${upload_paths[*]}" ) } # Build and cache things needed for CI ci() { - cache=( + installables=( --inputs-from . # Keep sorted @@ -49,19 +49,19 @@ ci() { nixpkgs#nix-direnv ) - just "${cache[@]}" + just "${installables[@]}" } # Build and cache all the package outputs packages() { - readarray -t cache < <( + readarray -t installables < <( nix flake show --json 2> /dev/null | nix run --inputs-from . nixpkgs#jq -- \ -r \ '.packages."x86_64-linux" | keys | map(".#" + .) | .[]' ) - just "${cache[@]}" + just "${installables[@]}" } pushd "$(git rev-parse --show-toplevel)" > /dev/null From 547881fb4abad3d13ff45520aa40a5dcaba7061e Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 09:52:32 -0700 Subject: [PATCH 589/617] show path upload count --- bin/nix-build-and-cache | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index ebfcf4f8..f7f5223e 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -18,6 +18,8 @@ just() { <<< "${derivations[*]}" ) + echo "Found ${#upload_paths[@]} paths to upload" + if [ -z ${ATTIC_TOKEN+x} ]; then echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache" return From 38202813ff04b88e7786d3737efef4cec1ec670a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Wed, 30 Jul 2025 11:06:58 -0700 Subject: [PATCH 590/617] remove nix-direnv It doesn't actually do anything unless more configuration is added, but it's not really necessary for this use case so removing it is easier. --- .gitlab-ci.yml | 4 ++-- bin/nix-build-and-cache | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9b116e81..0f93302e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,8 +26,8 @@ before_script: - if command -v nix > /dev/null; then echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null; then echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf; fi - # Install direnv and nix-direnv - - if command -v nix > /dev/null; then nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv; fi + # Install direnv + - if command -v nix > /dev/null; then nix profile install --impure --inputs-from . nixpkgs#direnv; fi # Allow .envrc - if command -v nix > /dev/null; then direnv allow; fi diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache index f7f5223e..f9eaee1a 100755 --- a/bin/nix-build-and-cache +++ b/bin/nix-build-and-cache @@ -48,7 +48,6 @@ ci() { attic#default nixpkgs#direnv nixpkgs#jq - nixpkgs#nix-direnv ) just "${installables[@]}" From 5e9c1f9ee3fd987e12860f39739f0f8e5df39ae6 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 31 Jul 2025 12:40:24 -0700 Subject: [PATCH 591/617] combine conditions in before_script --- .gitlab-ci.yml | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0f93302e..1813c8c8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,33 +8,37 @@ variables: TERM: ansi before_script: - # Enable nix-command and flakes - - if command -v nix > /dev/null; then echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf; fi + - | + if command -v nix > /dev/null; then + # Enable nix-command and flakes + echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf - # Disable IFD, to ensure we are able to build without it - - if command -v nix > /dev/null; then echo "allow-import-from-derivation = false" >> /etc/nix/nix.conf; fi + # Disable IFD, to ensure we are able to build without it + echo "allow-import-from-derivation = false" >> /etc/nix/nix.conf - # Add our own binary cache - - if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ] && [ -n "$ATTIC_CACHE" ]; then echo "extra-substituters = $ATTIC_ENDPOINT/$ATTIC_CACHE" >> /etc/nix/nix.conf; fi - - if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi + # Add crane binary cache + echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf + echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf - # Add crane binary cache - - if command -v nix > /dev/null; then echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf; fi - - if command -v nix > /dev/null; then echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf; fi + # Add nix-community binary cache + echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf + echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf - # Add nix-community binary cache - - if command -v nix > /dev/null; then echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf; fi - - if command -v nix > /dev/null; then echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf; fi + # Add our own binary cache + if [ -n "$ATTIC_ENDPOINT" ] && [ -n "$ATTIC_CACHE" ] && [ -n "$ATTIC_PUBLIC_KEY" ]; then + echo "extra-substituters = $ATTIC_ENDPOINT/$ATTIC_CACHE" >> /etc/nix/nix.conf + echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf + fi - # Install direnv - - if command -v nix > /dev/null; then nix profile install --impure --inputs-from . nixpkgs#direnv; fi + # Install direnv + nix profile install --impure --inputs-from . nixpkgs#direnv - # Allow .envrc - - if command -v nix > /dev/null; then direnv allow; fi - - # Set CARGO_HOME to a cacheable path - - export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo" + # Allow .envrc + direnv allow + fi + # Set CARGO_HOME to a cacheable path + export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo" cache-ci-deps: stage: ci From e31f4454bd330f75750308f6fbe69221d3105e0c Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Thu, 31 Jul 2025 12:40:54 -0700 Subject: [PATCH 592/617] assume nix is available --- .gitlab-ci.yml | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1813c8c8..911feeab 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,34 +9,32 @@ variables: before_script: - | - if command -v nix > /dev/null; then - # Enable nix-command and flakes - echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf + # Enable nix-command and flakes + echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf - # Disable IFD, to ensure we are able to build without it - echo "allow-import-from-derivation = false" >> /etc/nix/nix.conf + # Disable IFD, to ensure we are able to build without it + echo "allow-import-from-derivation = false" >> /etc/nix/nix.conf - # Add crane binary cache - echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf - echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf + # Add crane binary cache + echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf + echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf - # Add nix-community binary cache - echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf - echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf + # Add nix-community binary cache + echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf + echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf - # Add our own binary cache - if [ -n "$ATTIC_ENDPOINT" ] && [ -n "$ATTIC_CACHE" ] && [ -n "$ATTIC_PUBLIC_KEY" ]; then - echo "extra-substituters = $ATTIC_ENDPOINT/$ATTIC_CACHE" >> /etc/nix/nix.conf - echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf - fi - - # Install direnv - nix profile install --impure --inputs-from . nixpkgs#direnv - - # Allow .envrc - direnv allow + # Add our own binary cache + if [ -n "$ATTIC_ENDPOINT" ] && [ -n "$ATTIC_CACHE" ] && [ -n "$ATTIC_PUBLIC_KEY" ]; then + echo "extra-substituters = $ATTIC_ENDPOINT/$ATTIC_CACHE" >> /etc/nix/nix.conf + echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf fi + # Install direnv + nix profile install --impure --inputs-from . nixpkgs#direnv + + # Allow .envrc + direnv allow + # Set CARGO_HOME to a cacheable path export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo" From a3a2485edbc38018a31e509948d6124a49c7779a Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 20 Jul 2025 16:11:59 -0700 Subject: [PATCH 593/617] improve link accessibility The previous text causes lints to fire in new versions of markdownlint. --- book/introduction.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/book/introduction.md b/book/introduction.md index e03e4e9e..bc38b0f5 100644 --- a/book/introduction.md +++ b/book/introduction.md @@ -76,13 +76,13 @@ our ability to accomplish our goals: [`envsubst`][envsubst]. * Configuration compatibility with Conduit * To provide a secure and ergonomic configuration experience, breaking changes - are required. However, we do intend to provide a migration tool to ease - migration; this feature is tracked [here][migration-tool]. + are required. However, [we do intend to provide a migration tool to ease + migration][migration-tool]. * Perfect database compatibility with Conduit - * The current database compatibility status can be tracked [here][db-compat]. - In the long run, it's inevitable that changes will be made to Conduit that - we won't want to pull in, or that we need to make changes that Conduit won't - want to pull in. + * [This issue tracks the database compatibility status][db-compat]. In the + long run, it's inevitable that changes will be made to Conduit that we won't + want to pull in, or that we need to make changes that Conduit won't want to + pull in. [envsubst]: https://github.com/a8m/envsubst [migration-tool]: https://gitlab.computer.surgery/matrix/grapevine/-/issues/38 From d2ee80db942a0493550f5e8ae764f8fc96775a56 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 20 Jul 2025 13:03:00 -0700 Subject: [PATCH 594/617] update flake.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Updated input 'attic': 'github:zhaofengli/attic/ff8a897d1f4408ebbf4d45fa9049c06b3e1e3f4e' (2025-02-02) → 'github:zhaofengli/attic/24fad0622fc9404c69e83bab7738359c5be4988e' (2025-07-11) • Updated input 'attic/crane': 'github:ipetkov/crane/4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4' (2024-08-06) → 'github:ipetkov/crane/aed2020fd3dc26e1e857d4107a5a67a33ab6c1fd' (2025-07-03) • Removed input 'attic/crane/nixpkgs' • Updated input 'attic/flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) → 'github:edolstra/flake-compat/9100a0f413b0c601e0533d1d94ffd501ce2e7885' (2025-05-12) • Updated input 'attic/flake-parts': 'github:hercules-ci/flake-parts/8471fe90ad337a8074e957b69ca4d0089218391d' (2024-08-01) → 'github:hercules-ci/flake-parts/77826244401ea9de6e3bac47c2db46005e1f30b5' (2025-07-01) • Updated input 'attic/nix-github-actions': 'github:nix-community/nix-github-actions/e04df33f62cdcf93d73e9a04142464753a16db67' (2024-10-24) → 'github:nix-community/nix-github-actions/f4158fa080ef4503c8f4c820967d946c2af31ec9' (2025-01-21) • Updated input 'attic/nixpkgs': 'github:NixOS/nixpkgs/159be5db480d1df880a0135ca0bfed84c2f88353' (2024-09-11) → 'github:NixOS/nixpkgs/9b008d60392981ad674e04016d25619281550a9d' (2025-07-08) • Updated input 'attic/nixpkgs-stable': 'github:NixOS/nixpkgs/797f7dc49e0bc7fab4b57c021cdf68f595e47841' (2024-08-22) → 'github:NixOS/nixpkgs/29e290002bfff26af1db6f64d070698019460302' (2025-07-05) • Updated input 'crane': 'github:ipetkov/crane/70947c1908108c0c551ddfd73d4f750ff2ea67cd' (2025-03-19) → 'github:ipetkov/crane/544d09fecc8c2338542c57f3f742f1a0c8c71e13' (2025-07-19) • Updated input 'fenix': 'github:nix-community/fenix/7d9ba794daf5e8cc7ee728859bc688d8e26d5f06' (2025-03-20) → 'github:nix-community/fenix/62105e0745d7450976b26dbd1497b8cbe15eb9ff' (2025-07-20) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/15d87419f1a123d8f888d608129c3ce3ff8f13d4' (2025-03-18) → 'github:rust-lang/rust-analyzer/ed193af36937d2fd4bb14a815ec589875c5c7304' (2025-07-19) • Updated input 'flake-compat': 'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec' (2024-12-04) → 'github:edolstra/flake-compat/9100a0f413b0c601e0533d1d94ffd501ce2e7885' (2025-05-12) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/698214a32beb4f4c8e3942372c694f40848b360d' (2025-03-25) → 'github:NixOS/nixpkgs/c87b95e25065c028d31a94f06a62927d18763fdf' (2025-07-19) --- flake.lock | 80 +++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/flake.lock b/flake.lock index 00b27744..11986b29 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1738524606, - "narHash": "sha256-hPYEJ4juK3ph7kbjbvv7PlU1D9pAkkhl+pwx8fZY53U=", + "lastModified": 1752217044, + "narHash": "sha256-5TomR72rn4q+5poQcN6EnanxeXKqJSqWVAoDAFN0lUc=", "owner": "zhaofengli", "repo": "attic", - "rev": "ff8a897d1f4408ebbf4d45fa9049c06b3e1e3f4e", + "rev": "24fad0622fc9404c69e83bab7738359c5be4988e", "type": "github" }, "original": { @@ -25,18 +25,12 @@ } }, "crane": { - "inputs": { - "nixpkgs": [ - "attic", - "nixpkgs" - ] - }, "locked": { - "lastModified": 1722960479, - "narHash": "sha256-NhCkJJQhD5GUib8zN9JrmYGMwt4lCRp6ZVNzIiYCl0Y=", + "lastModified": 1751562746, + "narHash": "sha256-smpugNIkmDeicNz301Ll1bD7nFOty97T79m4GUMUczA=", "owner": "ipetkov", "repo": "crane", - "rev": "4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4", + "rev": "aed2020fd3dc26e1e857d4107a5a67a33ab6c1fd", "type": "github" }, "original": { @@ -47,11 +41,11 @@ }, "crane_2": { "locked": { - "lastModified": 1742394900, - "narHash": "sha256-vVOAp9ahvnU+fQoKd4SEXB2JG2wbENkpqcwlkIXgUC0=", + "lastModified": 1752946753, + "narHash": "sha256-g5uP3jIj+STUcfTJDKYopxnSijs2agRg13H0SGL5iE4=", "owner": "ipetkov", "repo": "crane", - "rev": "70947c1908108c0c551ddfd73d4f750ff2ea67cd", + "rev": "544d09fecc8c2338542c57f3f742f1a0c8c71e13", "type": "github" }, "original": { @@ -69,11 +63,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1742452566, - "narHash": "sha256-sVuLDQ2UIWfXUBbctzrZrXM2X05YjX08K7XHMztt36E=", + "lastModified": 1752993983, + "narHash": "sha256-3YKCySMNhFDdHbFiRS4QbEwk0U5l42NMD1scDtniESY=", "owner": "nix-community", "repo": "fenix", - "rev": "7d9ba794daf5e8cc7ee728859bc688d8e26d5f06", + "rev": "62105e0745d7450976b26dbd1497b8cbe15eb9ff", "type": "github" }, "original": { @@ -86,11 +80,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -102,11 +96,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -124,11 +118,11 @@ ] }, "locked": { - "lastModified": 1722555600, - "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", + "lastModified": 1751413152, + "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", + "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", "type": "github" }, "original": { @@ -180,11 +174,11 @@ ] }, "locked": { - "lastModified": 1729742964, - "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=", + "lastModified": 1737420293, + "narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=", "owner": "nix-community", "repo": "nix-github-actions", - "rev": "e04df33f62cdcf93d73e9a04142464753a16db67", + "rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9", "type": "github" }, "original": { @@ -195,11 +189,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1726042813, - "narHash": "sha256-LnNKCCxnwgF+575y0pxUdlGZBO/ru1CtGHIqQVfvjlA=", + "lastModified": 1751949589, + "narHash": "sha256-mgFxAPLWw0Kq+C8P3dRrZrOYEQXOtKuYVlo9xvPntt8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "159be5db480d1df880a0135ca0bfed84c2f88353", + "rev": "9b008d60392981ad674e04016d25619281550a9d", "type": "github" }, "original": { @@ -211,27 +205,27 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1724316499, - "narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=", + "lastModified": 1751741127, + "narHash": "sha256-t75Shs76NgxjZSgvvZZ9qOmz5zuBE8buUaYD28BMTxg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "797f7dc49e0bc7fab4b57c021cdf68f595e47841", + "rev": "29e290002bfff26af1db6f64d070698019460302", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-24.05", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1742889210, - "narHash": "sha256-hw63HnwnqU3ZQfsMclLhMvOezpM7RSB0dMAtD5/sOiw=", + "lastModified": 1752950548, + "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "698214a32beb4f4c8e3942372c694f40848b360d", + "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", "type": "github" }, "original": { @@ -273,11 +267,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1742296961, - "narHash": "sha256-gCpvEQOrugHWLimD1wTFOJHagnSEP6VYBDspq96Idu0=", + "lastModified": 1752913824, + "narHash": "sha256-kRpDlijAr4p5VmcPSRw2mfhaBZ4cE3EDWzqLDIbASgA=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "15d87419f1a123d8f888d608129c3ce3ff8f13d4", + "rev": "ed193af36937d2fd4bb14a815ec589875c5c7304", "type": "github" }, "original": { From e65815c82edae9d62424b92c67b25d1ebea14f28 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 19 Jul 2025 15:48:54 -0700 Subject: [PATCH 595/617] remove unused lifetime parameter from utils::calculate_hash This doesn't trigger a warning before rustc 1.58.0, for whatever reason. --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 48803d82..fcecdb9c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -112,7 +112,7 @@ where } #[tracing::instrument(skip(keys))] -pub(crate) fn calculate_hash<'a, I, T>(keys: I) -> Vec +pub(crate) fn calculate_hash(keys: I) -> Vec where I: IntoIterator, T: AsRef<[u8]>, From fb4d168921bd59c5ab98ff257cc1d88d9e4da025 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 20 Jul 2025 12:51:25 -0700 Subject: [PATCH 596/617] fix new lints from clippy 0.1.85 --- src/api/client_server/sync/msc3575.rs | 4 ++-- src/api/client_server/sync/v3.rs | 2 +- src/database/key_value/pusher.rs | 2 +- src/observability.rs | 2 +- src/service/rooms/pdu_metadata.rs | 8 ++++---- src/service/rooms/spaces.rs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/api/client_server/sync/msc3575.rs b/src/api/client_server/sync/msc3575.rs index 52ce877d..31333eaf 100644 --- a/src/api/client_server/sync/msc3575.rs +++ b/src/api/client_server/sync/msc3575.rs @@ -148,8 +148,8 @@ pub(crate) async fn sync_events_v4_route( "", )?; - let joined_since_last_sync = since_sender_member - .map_or(true, |member| { + let joined_since_last_sync = + since_sender_member.is_none_or(|member| { member.membership != MembershipState::Join }); diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index 8046bc0f..9ac70a73 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -495,7 +495,7 @@ async fn load_joined_room( }); let joined_since_last_sync = since_sender_member - .map_or(true, |member| member.membership != MembershipState::Join); + .is_none_or(|member| member.membership != MembershipState::Join); if since_shortstatehash.is_none() || joined_since_last_sync { // Probably since = 0, we will do an initial sync diff --git a/src/database/key_value/pusher.rs b/src/database/key_value/pusher.rs index bd80288a..000c9a5d 100644 --- a/src/database/key_value/pusher.rs +++ b/src/database/key_value/pusher.rs @@ -27,7 +27,7 @@ impl service::pusher::Data for KeyValueDatabase { let mut key = sender.as_bytes().to_vec(); key.push(0xFF); key.extend_from_slice(ids.pushkey.as_bytes()); - self.senderkey_pusher.remove(&key).map_err(Into::into) + self.senderkey_pusher.remove(&key) } } } diff --git a/src/observability.rs b/src/observability.rs index 44e06c91..0f136306 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -555,5 +555,5 @@ pub(crate) fn init_for_cli( let subscriber = Registry::default().with(log_layer); - tracing::subscriber::set_global_default(subscriber).map_err(Into::into) + tracing::subscriber::set_global_default(subscriber) } diff --git a/src/service/rooms/pdu_metadata.rs b/src/service/rooms/pdu_metadata.rs index d3854523..92e0a458 100644 --- a/src/service/rooms/pdu_metadata.rs +++ b/src/service/rooms/pdu_metadata.rs @@ -72,13 +72,13 @@ impl Service { r.as_ref().map_or(true, |(_, pdu)| { filter_event_type .as_ref() - .map_or(true, |t| &&pdu.kind == t) + .is_none_or(|t| &&pdu.kind == t) && if let Ok(content) = serde_json::from_str::< ExtractRelatesToEventId, >( pdu.content.get() ) { - filter_rel_type.as_ref().map_or(true, |r| { + filter_rel_type.as_ref().is_none_or(|r| { &&content.relates_to.rel_type == r }) } else { @@ -127,13 +127,13 @@ impl Service { r.as_ref().map_or(true, |(_, pdu)| { filter_event_type .as_ref() - .map_or(true, |t| &&pdu.kind == t) + .is_none_or(|t| &&pdu.kind == t) && if let Ok(content) = serde_json::from_str::< ExtractRelatesToEventId, >( pdu.content.get() ) { - filter_rel_type.as_ref().map_or(true, |r| { + filter_rel_type.as_ref().is_none_or(|r| { &&content.relates_to.rel_type == r }) } else { diff --git a/src/service/rooms/spaces.rs b/src/service/rooms/spaces.rs index bfcbb072..d3c52b1d 100644 --- a/src/service/rooms/spaces.rs +++ b/src/service/rooms/spaces.rs @@ -148,7 +148,7 @@ impl Service { ) .ok() .map(|c| c.via) - .map_or(true, |v| v.is_empty()) + .is_none_or(|v| v.is_empty()) { continue; } From fa6fb37a2c3b2d888362312333aac6c8c8827acd Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 19 Jul 2025 15:39:18 -0700 Subject: [PATCH 597/617] bump rust version to 1.85.0 base64ct 1.8 (a transitive dependency of argon2) requires >=1.85.0. --- Cargo.toml | 2 +- flake.nix | 4 ++-- rust-toolchain.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c61f19e..bc9bc00e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ version = "0.1.0" edition = "2021" # See also `rust-toolchain.toml` -rust-version = "1.84.0" +rust-version = "1.85.0" [lints] workspace = true diff --git a/flake.nix b/flake.nix index ba839631..3c185298 100644 --- a/flake.nix +++ b/flake.nix @@ -15,8 +15,8 @@ let rust-manifest = builtins.fetchurl { # Keep version in sync with rust-toolchain.toml - url = "https://static.rust-lang.org/dist/channel-rust-1.84.0.toml"; - sha256 = "sha256-lMLAupxng4Fd9F1oDw8gx+qA0RuF7ou7xhNU8wgs0PU="; + url = "https://static.rust-lang.org/dist/channel-rust-1.85.0.toml"; + sha256 = "sha256-AJ6LX/Q/Er9kS15bn9iflkUwcgYqRQxiOIL2ToVAXaU="; }; # Keep sorted diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 244b9085..4c26c2b4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,7 +9,7 @@ # If you're having trouble making the relevant changes, bug a maintainer. [toolchain] -channel = "1.84.0" +channel = "1.85.0" components = [ # For rust-analyzer "rust-src", From 595f35b6735b6293f5a438069239c428c636683f Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 21 Jul 2025 09:33:01 -0700 Subject: [PATCH 598/617] fix lints for upcoming 1.88.0 toolchain --- src/api/client_server/account.rs | 12 ++++++------ src/api/client_server/alias.rs | 2 +- src/api/client_server/device.rs | 8 ++++---- src/api/client_server/keys.rs | 4 ++-- src/api/client_server/report.rs | 4 ++-- src/api/ruma_wrapper/axum.rs | 2 +- src/api/server_server.rs | 2 +- src/database/abstraction/watchers.rs | 2 +- src/main.rs | 2 +- src/service/admin.rs | 12 +++++++----- src/service/globals.rs | 4 ++-- src/service/rooms/auth_chain.rs | 2 +- src/service/rooms/event_handler.rs | 5 +---- src/service/rooms/state.rs | 2 +- src/service/rooms/state_compressor.rs | 4 ++-- src/service/rooms/timeline.rs | 4 ++-- src/service/users.rs | 6 +++--- src/utils/error.rs | 4 ++-- src/utils/on_demand_hashmap.rs | 2 +- 19 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 1a569b41..b45af7df 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -198,7 +198,7 @@ pub(crate) async fn register_route( &uiaainfo, )?; if !worked { - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } // Success! } else if let Some(json) = body.json_body { @@ -213,7 +213,7 @@ pub(crate) async fn register_route( &uiaainfo, &json, )?; - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } @@ -346,13 +346,13 @@ pub(crate) async fn change_password_route( &uiaainfo, )?; if !worked { - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } @@ -434,13 +434,13 @@ pub(crate) async fn deactivate_route( &uiaainfo, )?; if !worked { - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index b2fbcf65..16052d06 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -164,7 +164,7 @@ pub(crate) async fn get_alias_helper( } } } - }; + } let Some(room_id) = room_id else { return Err(Error::BadRequest( diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index f94cae6e..adecd073 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -107,13 +107,13 @@ pub(crate) async fn delete_device_route( &uiaainfo, )?; if !worked { - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } @@ -161,13 +161,13 @@ pub(crate) async fn delete_devices_route( &uiaainfo, )?; if !worked { - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index d2e3006f..7c22d56e 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -135,13 +135,13 @@ pub(crate) async fn upload_signing_keys_route( &uiaainfo, )?; if !worked { - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } // Success! } else if let Some(json) = body.json_body { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); services().uiaa.create(sender_user, sender_device, &uiaainfo, &json)?; - return Err(Error::Uiaa(uiaainfo)); + return Err(Error::Uiaa(Box::new(uiaainfo))); } else { return Err(Error::BadRequest(ErrorKind::NotJson, "Not json.")); } diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index edf18057..e822f7ae 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -26,14 +26,14 @@ pub(crate) async fn report_event_route( ErrorKind::InvalidParam, "Invalid score, must be within 0 to -100", )); - }; + } if let Some(true) = body.reason.clone().map(|s| s.chars().count() > 250) { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Reason too long, should be 250 characters or fewer", )); - }; + } services().admin.send_message(message::RoomMessageEventContent::text_html( format!( diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index a514c6b8..d3fb0811 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -263,7 +263,7 @@ async fn ar_from_request_inner( if let Some(json_body) = &json_body { request_map.insert("content".to_owned(), json_body.clone()); - }; + } let keys_result = services() .rooms diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 5bb7b554..abd111f4 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -217,7 +217,7 @@ where serde_json::from_slice(http_request.body()) .expect("body is valid json, we just created it"), ); - }; + } request_map .insert("method".to_owned(), T::METADATA.method.to_string().into()); diff --git a/src/database/abstraction/watchers.rs b/src/database/abstraction/watchers.rs index b31bd082..3f33e537 100644 --- a/src/database/abstraction/watchers.rs +++ b/src/database/abstraction/watchers.rs @@ -54,6 +54,6 @@ impl Watchers { tx.0.send(()).expect("channel should still be open"); } } - }; + } } } diff --git a/src/main.rs b/src/main.rs index fc6a3459..17ddfb2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,7 +72,7 @@ fn set_application_state(state: ApplicationState) { ]); } ApplicationState::Stopping => notify(&[NotifyState::Stopping]), - }; + } } } diff --git a/src/service/admin.rs b/src/service/admin.rs index 0f15d5bc..dcd971af 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -246,7 +246,7 @@ impl TracingFilterCommand { #[derive(Debug)] pub(crate) enum AdminRoomEvent { ProcessMessage(String), - SendMessage(RoomMessageEventContent), + SendMessage(Box), } pub(crate) struct Service { @@ -297,7 +297,7 @@ impl Service { grapevine_room: &OwnedRoomId, ) { let message_content = match event { - AdminRoomEvent::SendMessage(content) => content, + AdminRoomEvent::SendMessage(content) => *content, AdminRoomEvent::ProcessMessage(room_message) => { self.process_admin_message(room_message).await } @@ -343,7 +343,9 @@ impl Service { &self, message_content: RoomMessageEventContent, ) { - self.sender.send(AdminRoomEvent::SendMessage(message_content)).unwrap(); + self.sender + .send(AdminRoomEvent::SendMessage(Box::new(message_content))) + .unwrap(); } // Parse and process a message from the admin room @@ -701,7 +703,7 @@ impl Service { return Ok(RoomMessageEventContent::text_plain( "The specified user is not from this server!", )); - }; + } // Check if the specified user is valid if !services().users.exists(&user_id)? @@ -1234,7 +1236,7 @@ impl Service { return Ok(RoomMessageEventContent::text_plain(format!( "Failed to reload filter: {e}" ))); - }; + } return Ok(RoomMessageEventContent::text_plain( "Filter reloaded", diff --git a/src/service/globals.rs b/src/service/globals.rs index 524c18d3..33dac38e 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -329,7 +329,7 @@ impl Service { error!(config=?s.config.default_room_version, fallback=?crate::config::default_default_room_version(), "Room version in config isn't supported, falling back to default version"); s.config.default_room_version = crate::config::default_default_room_version(); - }; + } Ok(s) } @@ -526,7 +526,7 @@ impl Service { Grapevine user", ); } - }; + } } pub(crate) fn supported_room_versions(&self) -> Vec { diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 3c8e4b38..6711263d 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -157,7 +157,7 @@ impl Service { if i % 100 == 0 { tokio::task::yield_now().await; } - }; + } } debug!( chunk_cache_length = ?chunk_cache.len(), diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index cbe8079d..96e33f5f 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -1126,10 +1126,7 @@ impl Service { services() .rooms .auth_chain - .get_auth_chain( - room_id, - state.iter().map(|(_, id)| id.clone()).collect(), - ) + .get_auth_chain(room_id, state.values().cloned().collect()) .await? .collect(), ); diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 5db98287..bf028f71 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -155,7 +155,7 @@ impl Service { .invalidate_cache(&pdu.room_id) .await; } - _ => continue, + _ => {} } } diff --git a/src/service/rooms/state_compressor.rs b/src/service/rooms/state_compressor.rs index 60fbb9c5..304cd9cb 100644 --- a/src/service/rooms/state_compressor.rs +++ b/src/service/rooms/state_compressor.rs @@ -257,7 +257,7 @@ impl Service { )?; return Ok(()); - }; + } // Else we have two options. // 1. We add the current diff on top of the parent layer. @@ -374,7 +374,7 @@ impl Service { 2, states_parents, )?; - }; + } Ok((new_shortstatehash, statediffnew, statediffremoved)) } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 93bf6c53..10fabf40 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -442,7 +442,7 @@ impl Service { highlight = true; } _ => {} - }; + } } if notify { @@ -497,7 +497,7 @@ impl Service { )? { self.redact_pdu(redact_id, pdu, shortroomid)?; } - }; + } } TimelineEventType::SpaceChild => { if pdu.state_key.is_some() { diff --git a/src/service/users.rs b/src/service/users.rs index b05a2ccb..9df6e835 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -96,12 +96,12 @@ impl Service { if let Some(cached_list) = cached.lists.get(list_id) { if list.sort.is_empty() { list.sort.clone_from(&cached_list.sort); - }; + } if list.room_details.required_state.is_empty() { list.room_details .required_state .clone_from(&cached_list.room_details.required_state); - }; + } list.room_details.timeline_limit = list .room_details .timeline_limit @@ -151,7 +151,7 @@ impl Service { if list.bump_event_types.is_empty() { list.bump_event_types .clone_from(&cached_list.bump_event_types); - }; + } } cached.lists.insert(list_id.clone(), list.clone()); } diff --git a/src/utils/error.rs b/src/utils/error.rs index 0b5420b3..9e36c3b3 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -66,7 +66,7 @@ pub(crate) enum Error { /// Don't create this directly. Use [`Error::bad_database`] instead. BadDatabase(&'static str), #[error("uiaa")] - Uiaa(UiaaInfo), + Uiaa(Box), #[error("{}: {}", .0.errcode(), .1)] BadRequest(ErrorKind, &'static str), // This is only needed for when a room alias already exists @@ -106,7 +106,7 @@ impl Error { }; if let Self::Uiaa(uiaainfo) = self { - return Ra(UiaaResponse::AuthResponse(uiaainfo.clone())); + return Ra(UiaaResponse::AuthResponse(*uiaainfo.clone())); } if let Self::Federation(origin, error) = self { diff --git a/src/utils/on_demand_hashmap.rs b/src/utils/on_demand_hashmap.rs index 69e8bac9..9f74e3d6 100644 --- a/src/utils/on_demand_hashmap.rs +++ b/src/utils/on_demand_hashmap.rs @@ -201,7 +201,7 @@ impl Drop for EntryDropGuard { .send(self.key.take().expect("drop should only be called once")) { warn!(%error, "Failed to send cleanup message"); - }; + } } } From 13203d3b4542d1d582a8d0dd068c9713ce8ea3b4 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 21 Jul 2025 09:31:18 -0700 Subject: [PATCH 599/617] update rust to 1.88.0 --- flake.nix | 4 ++-- rust-toolchain.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 3c185298..18cf25d4 100644 --- a/flake.nix +++ b/flake.nix @@ -15,8 +15,8 @@ let rust-manifest = builtins.fetchurl { # Keep version in sync with rust-toolchain.toml - url = "https://static.rust-lang.org/dist/channel-rust-1.85.0.toml"; - sha256 = "sha256-AJ6LX/Q/Er9kS15bn9iflkUwcgYqRQxiOIL2ToVAXaU="; + url = "https://static.rust-lang.org/dist/channel-rust-1.88.0.toml"; + sha256 = "sha256-Qxt8XAuaUR2OMdKbN4u8dBJOhSHxS+uS06Wl9+flVEk="; }; # Keep sorted diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4c26c2b4..b892bc27 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,7 +9,7 @@ # If you're having trouble making the relevant changes, bug a maintainer. [toolchain] -channel = "1.85.0" +channel = "1.88.0" components = [ # For rust-analyzer "rust-src", From 26489910921d362b8d0a1f69f9974ee0a58e07d4 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 20 Jul 2025 13:55:06 -0700 Subject: [PATCH 600/617] fix lints revealed by tracing change changes #[instrument] so that several lints that were previously hidden on instrumented functions are now visible. --- src/api/client_server/media.rs | 4 ++-- src/api/client_server/membership.rs | 4 ++-- src/api/client_server/sync/v3.rs | 1 + src/api/server_server.rs | 3 ++- src/service/pdu.rs | 36 ++++------------------------- src/service/pusher.rs | 2 ++ src/service/rooms/auth_chain.rs | 2 ++ src/service/rooms/event_handler.rs | 3 +++ src/service/rooms/state.rs | 6 ++++- src/service/rooms/state_accessor.rs | 2 ++ src/service/rooms/state_cache.rs | 8 ++++++- src/service/rooms/timeline.rs | 6 +++-- 12 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index ca9dda86..f7ffa413 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -320,7 +320,7 @@ async fn get_remote_content_via_legacy_api( } #[tracing::instrument] -pub(crate) async fn get_remote_content( +async fn get_remote_content( mxc: &MxcData<'_>, ) -> Result { let fed_result = get_remote_content_via_federation_api(mxc).await; @@ -785,7 +785,7 @@ async fn get_remote_thumbnail_via_legacy_api( } #[tracing::instrument] -pub(crate) async fn get_remote_thumbnail( +async fn get_remote_thumbnail( server_name: &ruma::ServerName, request: authenticated_media_fed::get_content_thumbnail::v1::Request, ) -> Result { diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 21a75be9..20c48cf4 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1521,7 +1521,7 @@ pub(crate) async fn leave_room( services().rooms.state_cache.update_membership( room_id, user_id, - MembershipState::Leave, + &MembershipState::Leave, user_id, None, true, @@ -1575,7 +1575,7 @@ pub(crate) async fn leave_room( services().rooms.state_cache.update_membership( room_id, user_id, - MembershipState::Leave, + &MembershipState::Leave, user_id, last_state, true, diff --git a/src/api/client_server/sync/v3.rs b/src/api/client_server/sync/v3.rs index 9ac70a73..3108ff9c 100644 --- a/src/api/client_server/sync/v3.rs +++ b/src/api/client_server/sync/v3.rs @@ -915,6 +915,7 @@ async fn load_joined_room( }) } +#[allow(clippy::too_many_lines)] #[tracing::instrument(skip_all)] async fn collect_left_rooms( ctx: &SyncContext<'_>, diff --git a/src/api/server_server.rs b/src/api/server_server.rs index abd111f4..793cbbd2 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -147,6 +147,7 @@ pub(crate) enum AllowLoopbackRequests { No, } +#[allow(clippy::too_many_lines)] #[tracing::instrument(skip(request, log_error, allow_loopback), fields(url))] pub(crate) async fn send_request( destination: &ServerName, @@ -1883,7 +1884,7 @@ pub(crate) async fn create_invite_route( services().rooms.state_cache.update_membership( &body.room_id, &invited_user, - MembershipState::Invite, + &MembershipState::Invite, &sender, Some(invite_state), true, diff --git a/src/service/pdu.rs b/src/service/pdu.rs index fe13f1d2..3df17326 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -4,10 +4,9 @@ use ruma::{ canonical_json::redact_content_in_place, events::{ room::member::RoomMemberEventContent, - space::child::HierarchySpaceChildEvent, AnyEphemeralRoomEvent, - AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, - AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, - TimelineEventType, + space::child::HierarchySpaceChildEvent, AnyMessageLikeEvent, + AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent, + AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, TimelineEventType, }, serde::Raw, state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, @@ -60,7 +59,7 @@ impl PduEvent { #[tracing::instrument(skip(self))] pub(crate) fn redact( &mut self, - room_version_id: RoomVersionId, + room_version_id: &RoomVersionId, reason: &PduEvent, ) -> crate::Result<()> { self.unsigned = None; @@ -71,7 +70,7 @@ impl PduEvent { })?; redact_content_in_place( &mut content, - &room_version_id, + room_version_id, self.kind.to_string(), ) .map_err(|e| { @@ -222,31 +221,6 @@ impl PduEvent { serde_json::from_value(json).expect("Raw::from_value always works") } - /// This only works for events that are also AnyRoomEvents. - #[tracing::instrument(skip(self))] - pub(crate) fn to_any_event(&self) -> Raw { - let mut json = json!({ - "content": self.content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - "room_id": self.room_id, - }); - - if let Some(unsigned) = &self.unsigned { - json["unsigned"] = json!(unsigned); - } - if let Some(state_key) = &self.state_key { - json["state_key"] = json!(state_key); - } - if let Some(redacts) = &self.redacts { - json["redacts"] = json!(redacts); - } - - serde_json::from_value(json).expect("Raw::from_value always works") - } - #[tracing::instrument(skip(self))] pub(crate) fn to_room_event(&self) -> Raw { let (redacts, content) = self.copy_redacts(); diff --git a/src/service/pusher.rs b/src/service/pusher.rs index 1b5d375d..8f58cea8 100644 --- a/src/service/pusher.rs +++ b/src/service/pusher.rs @@ -210,6 +210,8 @@ impl Service { Ok(()) } + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] #[tracing::instrument(skip(self, user, ruleset, pdu))] pub(crate) fn get_actions<'a>( &self, diff --git a/src/service/rooms/auth_chain.rs b/src/service/rooms/auth_chain.rs index 6711263d..2c7a4787 100644 --- a/src/service/rooms/auth_chain.rs +++ b/src/service/rooms/auth_chain.rs @@ -182,6 +182,8 @@ impl Service { })) } + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] #[tracing::instrument(skip(self))] fn get_auth_chain_inner( &self, diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 96e33f5f..07eed506 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -82,6 +82,7 @@ impl Service { /// 13. Use state resolution to find new room state /// 14. Check if the event passes auth based on the "current state" of the /// room, if not soft fail it + #[allow(clippy::too_many_lines)] #[tracing::instrument(skip(self, value, is_timeline_event, pub_key_map))] pub(crate) async fn handle_incoming_pdu<'a>( &self, @@ -535,6 +536,7 @@ impl Service { }) } + #[allow(clippy::too_many_lines)] #[tracing::instrument(skip_all, fields( incoming_pdu = %incoming_pdu.event_id, ))] @@ -1869,6 +1871,7 @@ impl Service { /// Search the DB for the signing keys of the given server, if we don't have /// them fetch them from the server and save to our DB. + #[allow(clippy::too_many_lines)] #[tracing::instrument( skip(self, signature_ids), fields(signature_ids = debug_slice_truncated(&signature_ids, 3)) diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index bf028f71..e692251d 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -142,7 +142,7 @@ impl Service { services().rooms.state_cache.update_membership( room_id, &user_id, - membership, + &membership, &pdu.sender, None, false, @@ -319,6 +319,8 @@ impl Service { /// Gather events to help the invited user identify the room /// /// Also includes the invite event itself. + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] #[tracing::instrument(skip(self, invite_event))] pub(crate) fn get_helpful_invite_events( &self, @@ -365,6 +367,8 @@ impl Service { } /// Returns the value of a field of an `m.room.create` event's `content`. + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] #[tracing::instrument(skip(self))] pub(crate) fn get_create_content( &self, diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index e9794e53..3c859a01 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -140,6 +140,8 @@ impl Service { /// Returns a single PDU from `room_id` with key (`event_type`, /// `state_key`). + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] #[tracing::instrument(skip(self))] pub(crate) fn state_get_id( &self, diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index 3e9608a3..1bf0fdbc 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -47,12 +47,14 @@ impl Service { } /// Update current membership data. + // Allowed because this function uses `services()` + #[allow(clippy::unused_self)] #[tracing::instrument(skip(self, last_state))] pub(crate) fn update_membership( &self, room_id: &RoomId, user_id: &UserId, - membership: MembershipState, + membership: &MembershipState, sender: &UserId, last_state: Option>>, update_joined_count: bool, @@ -404,6 +406,8 @@ impl Service { } /// Returns an iterator over all invited members of a room. + // Unused, but needed for consistency with other methods + #[allow(dead_code)] #[tracing::instrument(skip(self))] pub(crate) fn room_members_invited<'a>( &'a self, @@ -504,6 +508,8 @@ impl Service { self.db.is_invited(user_id, room_id) } + // Unused, but needed for consistency with other methods + #[allow(dead_code)] #[tracing::instrument(skip(self))] pub(crate) fn is_left( &self, diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 10fabf40..8ee8669b 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -277,6 +277,7 @@ impl Service { /// happens in `append_pdu`. /// /// Returns pdu id + #[allow(clippy::too_many_lines)] #[tracing::instrument(skip(self, pdu, pdu_json, leaves))] pub(crate) async fn append_pdu( &self, @@ -572,7 +573,7 @@ impl Service { services().rooms.state_cache.update_membership( &pdu.room_id, &target_user_id, - membership, + &membership, &pdu.sender, invite_state, true, @@ -937,6 +938,7 @@ impl Service { /// Creates a new persisted data unit and adds it to a room. This function /// takes a roomid_mutex_state, meaning that only this function is able /// to mutate the room state. + #[allow(clippy::too_many_lines)] #[tracing::instrument(skip(self))] pub(crate) async fn build_and_append_pdu( &self, @@ -1259,7 +1261,7 @@ impl Service { .rooms .state .get_create_content::(&pdu.room_id)?; - pdu.redact(room_version_id, reason)?; + pdu.redact(&room_version_id, reason)?; self.replace_pdu( &pdu_id, From 98222b38c277164b5a9263026b25ddcc5c88844c Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 20 Jul 2025 13:00:11 -0700 Subject: [PATCH 601/617] bump cargo dependencies There are two new default features: 'system-proxy' for reqwest, which we are not enabling because it only does anything on windows and mac, which we don't support. 'prefer-post-quantum' for rustls, which we are not enabling because it only does anything for aws-lc-rs, which we don't use. --- Cargo.lock | 794 +++++++++++++++++++++++++---------------------------- Cargo.toml | 30 +- 2 files changed, 386 insertions(+), 438 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3dd7de4..51604973 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -28,15 +28,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arc-swap" @@ -64,9 +64,9 @@ checksum = "9dbc3a507a82b17ba0d98f6ce8fd6954ea0c8152e98009d36a40d8dcc8ce078a" [[package]] name = "assert_cmd" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" dependencies = [ "anstyle", "bstr", @@ -125,9 +125,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" @@ -231,9 +231,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -244,12 +244,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -258,9 +252,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bindgen" @@ -268,7 +262,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.12.1", @@ -288,7 +282,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -308,9 +302,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "blake2" @@ -332,9 +326,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -343,15 +337,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder-lite" @@ -377,9 +371,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -397,9 +391,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -420,9 +414,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.34" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ "clap_builder", "clap_derive", @@ -430,9 +424,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.34" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" dependencies = [ "anstyle", "clap_lex", @@ -441,9 +435,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2", @@ -453,9 +447,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "color_quant" @@ -483,15 +477,15 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" +checksum = "b98d1483e98c9d67f341ab4b3915cfdc54740bd6f5cccc9226ee0535d86aa8fb" [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -514,9 +508,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -560,9 +554,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "date_header" @@ -572,9 +566,9 @@ checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "zeroize", @@ -635,9 +629,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -689,12 +683,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -732,9 +726,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -772,9 +766,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" +checksum = "88d7be93788013f265201256d58f04936a8079ad5dc898743aa20525f503b683" dependencies = [ "autocfg", "tokio", @@ -862,22 +856,22 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", @@ -889,9 +883,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -919,7 +913,7 @@ dependencies = [ "axum", "axum-extra", "axum-server", - "base64 0.22.1", + "base64", "bytes", "clap", "futures-util", @@ -977,9 +971,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", @@ -987,7 +981,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.8.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -1002,9 +996,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "foldhash", ] @@ -1015,16 +1009,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.4", ] [[package]] name = "headers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "headers-core", "http", @@ -1050,9 +1044,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hmac" @@ -1063,17 +1057,6 @@ dependencies = [ "digest", ] -[[package]] -name = "hostname" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" -dependencies = [ - "cfg-if", - "libc", - "windows", -] - [[package]] name = "html-escape" version = "0.2.13" @@ -1161,11 +1144,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", @@ -1192,18 +1174,23 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1211,21 +1198,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1234,31 +1222,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1266,67 +1234,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "0.4.0" @@ -1350,9 +1305,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1386,39 +1341,48 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "serde", ] [[package]] name = "insta" -version = "1.42.2" +version = "1.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084" +checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371" dependencies = [ "console", - "linked-hash-map", "once_cell", "pest", "pest_derive", - "pin-project", "regex", "serde", "similar", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipconfig" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg", @@ -1430,6 +1394,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1465,10 +1439,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -1506,7 +1481,7 @@ version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64 0.22.1", + "base64", "js-sys", "pem", "ring", @@ -1549,18 +1524,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -1593,21 +1568,21 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1628,6 +1603,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lz4-sys" version = "1.11.1+lz4-1.10.0" @@ -1661,9 +1642,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" @@ -1679,9 +1660,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -1689,13 +1670,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -1721,7 +1702,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -1789,9 +1770,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -1808,9 +1789,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl-probe" @@ -1913,9 +1894,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -1923,9 +1904,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -1951,7 +1932,7 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ - "base64 0.22.1", + "base64", "serde", ] @@ -1963,9 +1944,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -1974,9 +1955,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -1984,9 +1965,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", @@ -1997,11 +1978,10 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -2109,6 +2089,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2165,9 +2154,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2228,9 +2217,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", "cfg_aliases", @@ -2239,7 +2228,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -2248,13 +2237,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.10" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", - "getrandom 0.3.2", - "rand 0.9.0", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", "ring", "rustc-hash 2.1.1", "rustls", @@ -2268,14 +2258,14 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -2291,9 +2281,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -2308,13 +2298,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy", ] [[package]] @@ -2343,7 +2332,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -2352,16 +2341,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -2410,14 +2399,13 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -2425,17 +2413,13 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", "rustls-native-certs", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -2443,24 +2427,20 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls", - "tokio-socks", "tower 0.5.2", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] name = "resolv-conf" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" -dependencies = [ - "hostname", -] +checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" [[package]] name = "ring" @@ -2470,7 +2450,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -2537,12 +2517,12 @@ version = "0.15.2" source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "as_variant", - "base64 0.22.1", + "base64", "bytes", "form_urlencoded", - "getrandom 0.2.15", + "getrandom 0.2.16", "http", - "indexmap 2.8.0", + "indexmap 2.10.0", "js_int", "konst", "percent-encoding", @@ -2568,7 +2548,7 @@ version = "0.30.2" source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ "as_variant", - "indexmap 2.8.0", + "indexmap 2.10.0", "js_int", "js_option", "percent-encoding", @@ -2657,7 +2637,7 @@ name = "ruma-signatures" version = "0.17.1" source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" dependencies = [ - "base64 0.22.1", + "base64", "ed25519-dalek", "pkcs8", "rand 0.8.5", @@ -2688,7 +2668,7 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e34486da88d8e051c7c0e23c3f15fd806ea8546260aa2fec247e97242ec143" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2724,9 +2704,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -2751,22 +2731,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", @@ -2800,18 +2780,19 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", + "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -2820,9 +2801,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -2860,7 +2841,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -2910,7 +2891,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4" dependencies = [ "form_urlencoded", - "indexmap 2.8.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -2918,9 +2899,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" dependencies = [ "itoa", "memchr", @@ -2940,9 +2921,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -2965,7 +2946,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -2996,9 +2977,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3022,9 +3003,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -3070,29 +3051,36 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -3117,23 +3105,22 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", "syn", ] @@ -3154,9 +3141,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -3174,9 +3161,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -3185,12 +3172,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3254,12 +3241,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -3295,9 +3281,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -3320,19 +3306,21 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.1" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3356,18 +3344,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-socks" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" -dependencies = [ - "either", - "futures-util", - "thiserror 1.0.69", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.17" @@ -3381,9 +3357,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -3394,9 +3370,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -3406,26 +3382,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tonic" version = "0.12.3" @@ -3435,7 +3418,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.22.1", + "base64", "bytes", "h2", "http", @@ -3447,7 +3430,7 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "socket2", + "socket2 0.5.10", "tokio", "tokio-stream", "tower 0.4.13", @@ -3493,14 +3476,16 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.2" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytes", + "futures-util", "http", "http-body", + "iri-string", "pin-project-lite", "tower 0.5.2", "tower-layer", @@ -3533,9 +3518,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -3544,9 +3529,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -3683,9 +3668,9 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typewit" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" +checksum = "97e72ba082eeb9da9dc68ff5a2bf727ef6ce362556e8d29ec1aed3bd05e7d86a" dependencies = [ "typewit_proc_macros", ] @@ -3747,12 +3732,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -3767,11 +3746,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -3812,9 +3793,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -3918,9 +3899,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "widestring" @@ -3956,59 +3937,11 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.53.0", -] - -[[package]] -name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-sys" @@ -4037,6 +3970,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4070,10 +4012,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -4224,9 +4167,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -4247,20 +4190,14 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xdg" @@ -4270,9 +4207,9 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -4282,9 +4219,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -4294,18 +4231,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", @@ -4340,10 +4277,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -4352,9 +4300,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", @@ -4380,9 +4328,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-jpeg" -version = "0.4.14" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index bc9bc00e..f923d9a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,11 +91,11 @@ workspace = true argon2 = "0.5.3" async-trait = "0.1.88" axum = { version = "0.7.9", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tokio", "tracing"] } -axum-extra = { version = "0.9.5", features = ["typed-header"] } +axum-extra = { version = "0.9.6", features = ["typed-header"] } axum-server = { git = "https://gitlab.computer.surgery/matrix/thirdparty/axum-server.git", rev = "v0.7.2+grapevine-1", version = "0.7.2", features = ["tls-rustls-no-provider"] } base64 = "0.22.1" bytes = "1.10.1" -clap = { version = "4.5.34", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } +clap = { version = "4.5.42", default-features = false, features = ["std", "derive", "help", "usage", "error-context", "string", "wrap_help"] } futures-util = { version = "0.3.31", default-features = false } hmac = "0.12.1" html-escape = "0.2.13" @@ -105,38 +105,38 @@ hyper = "1.6.0" image = { version = "0.25.6", default-features = false, features = ["jpeg", "png", "gif"] } jsonwebtoken = "9.3.1" lru-cache = "0.1.2" -num_cpus = "1.16.0" +num_cpus = "1.17.0" opentelemetry = "0.24.0" opentelemetry-jaeger-propagator = "0.3.0" opentelemetry-otlp = "0.17.0" opentelemetry-prometheus = "0.17.0" opentelemetry_sdk = { version = "0.24.1", features = ["rt-tokio"] } -parking_lot = { version = "0.12.3", optional = true } +parking_lot = { version = "0.12.4", optional = true } phf = { version = "0.11.3", features = ["macros"] } pin-project-lite = "0.2.16" prometheus = "0.13.4" proxy-header = { version = "0.1.2", features = ["tokio"] } rand = "0.8.5" regex = "1.11.1" -reqwest = { version = "0.12.15", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } +reqwest = { version = "0.12.22", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.14" rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://gitlab.computer.surgery/matrix/ruma.git", rev = "ruma-0.12.2+grapevine-1", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.34.0", optional = true, features = ["bundled"] } -rustls = { version = "0.23.25", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } +rustls = { version = "0.23.31", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.5", optional = true } serde = { version = "1.0.219", features = ["rc"] } serde_html_form = "0.2.7" -serde_json = { version = "1.0.140", features = ["raw_value"] } +serde_json = { version = "1.0.141", features = ["raw_value"] } serde_yaml = "0.9.34" sha-1 = "0.10.1" -strum = { version = "0.27.1", features = ["derive"] } +strum = { version = "0.27.2", features = ["derive"] } thiserror = "2.0.12" -thread_local = "1.1.8" -tokio = { version = "1.44.1", features = ["fs", "macros", "signal", "sync"] } -toml = "0.8.20" +thread_local = "1.1.9" +tokio = { version = "1.47.0", features = ["fs", "macros", "signal", "sync"] } +toml = "0.8.23" tower = { version = "0.5.2", features = ["util"] } -tower-http = { version = "0.6.2", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } +tower-http = { version = "0.6.6", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] } tracing = { version = "0.1.41", features = [] } tracing-flame = "0.2.0" tracing-opentelemetry = "0.25.0" @@ -148,10 +148,10 @@ xdg = "2.5.2" nix = { version = "0.29", features = ["resource", "time"] } [dev-dependencies] -assert_cmd = "2.0.16" -insta = { version = "1.42.2", features = ["filters", "json", "redactions"] } +assert_cmd = "2.0.17" +insta = { version = "1.43.1", features = ["filters", "json", "redactions"] } predicates = "3.1.3" -tempfile = "3.19.1" +tempfile = "3.20.0" [profile.dev.package.insta] opt-level = 3 From d87848b9a6073d8497a5f860876bc8dec858dda7 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 8 Aug 2025 22:08:54 +0200 Subject: [PATCH 602/617] Remove support for MSC3575 (sliding sync) Co-authored-by: Olivia Lee --- Cargo.toml | 2 +- book/changelog.md | 2 + src/api/client_server/sync.rs | 1 - src/api/client_server/sync/msc3575.rs | 671 -------------------------- src/api/well_known.rs | 9 - src/cli/serve.rs | 1 - src/config.rs | 3 - src/service/rooms/state_accessor.rs | 40 +- src/service/users.rs | 233 +-------- 9 files changed, 8 insertions(+), 954 deletions(-) delete mode 100644 src/api/client_server/sync/msc3575.rs diff --git a/Cargo.toml b/Cargo.toml index f923d9a5..74a24679 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ regex = "1.11.1" reqwest = { version = "0.12.22", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.14" rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } -ruma = { git = "https://gitlab.computer.surgery/matrix/ruma.git", rev = "ruma-0.12.2+grapevine-1", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "ring-compat", "unstable-unspecified" ] } +ruma = { git = "https://gitlab.computer.surgery/matrix/ruma.git", rev = "ruma-0.12.2+grapevine-1", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.34.0", optional = true, features = ["bundled"] } rustls = { version = "0.23.31", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.5", optional = true } diff --git a/book/changelog.md b/book/changelog.md index 3729bfbb..40a8abea 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -87,6 +87,8 @@ This will be the first release of Grapevine since it was forked from Conduit * Instead, it is now possible to configure each cache capacity individually. 10. Remove jemalloc support. ([!93](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/193)) +11. Removed support for MSC3575 (sliding sync), which has been closed. + ([!198](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/198)) ### Changed diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 2a17aa17..4ad37425 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -5,7 +5,6 @@ use crate::{ service::rooms::timeline::PduCount, services, Error, PduEvent, Result, }; -pub(crate) mod msc3575; pub(crate) mod v3; fn load_timeline( diff --git a/src/api/client_server/sync/msc3575.rs b/src/api/client_server/sync/msc3575.rs deleted file mode 100644 index 31333eaf..00000000 --- a/src/api/client_server/sync/msc3575.rs +++ /dev/null @@ -1,671 +0,0 @@ -//! [MSC3575], aka Sliding Sync, aka Sync v3 (even though the endpoint is called -//! /v4) support -//! -//! [MSC3575]: https://github.com/matrix-org/matrix-spec-proposals/pull/3575 - -use std::{ - collections::{BTreeMap, BTreeSet, HashSet}, - time::Duration, -}; - -use ruma::{ - api::client::{ - sync::sync_events::{ - self, v4::SlidingOp, DeviceLists, UnreadNotificationsCount, - }, - uiaa::UiaaResponse, - }, - events::{ - room::member::{MembershipState, RoomMemberEventContent}, - StateEventType, TimelineEventType, - }, - uint, JsOption, UInt, UserId, -}; -use tracing::{debug, error}; - -use super::{load_timeline, share_encrypted_room}; -use crate::{ - service::{account_data, rooms::timeline::PduCount}, - services, Ar, Error, Ra, Result, -}; - -#[allow(clippy::too_many_lines)] -pub(crate) async fn sync_events_v4_route( - body: Ar, -) -> Result, Ra> { - let sender_user = body.sender_user.expect("user is authenticated"); - let sender_device = body.sender_device.expect("user is authenticated"); - let mut body = 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.next_count()?; - - let globalsince = - body.pos.as_ref().and_then(|string| string.parse().ok()).unwrap_or(0); - - if globalsince == 0 { - if let Some(conn_id) = &body.conn_id { - services().users.forget_sync_request_connection( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - ); - } - } - - // Get sticky parameters from cache - let known_rooms = services().users.update_sync_request_with_cache( - sender_user.clone(), - sender_device.clone(), - &mut body, - ); - - let all_joined_rooms = services() - .rooms - .state_cache - .rooms_joined(&sender_user) - .filter_map(Result::ok) - .collect::>(); - - if body.extensions.to_device.enabled.unwrap_or(false) { - services().users.remove_to_device_events( - &sender_user, - &sender_device, - globalsince, - )?; - } - - // Users that have left any encrypted rooms the sender was in - let mut left_encrypted_users = HashSet::new(); - let mut device_list_changes = HashSet::new(); - let mut device_list_left = HashSet::new(); - - if body.extensions.e2ee.enabled.unwrap_or(false) { - // Look for device list updates of this account - device_list_changes.extend( - services() - .users - .keys_changed(sender_user.as_ref(), globalsince, None) - .filter_map(Result::ok), - ); - - for room_id in &all_joined_rooms { - let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - else { - error!(%room_id, "Room has no state"); - continue; - }; - - let since_shortstatehash = services() - .rooms - .user - .get_token_shortstatehash(room_id, globalsince)?; - - let since_sender_member: Option = - since_shortstatehash - .and_then(|shortstatehash| { - services() - .rooms - .state_accessor - .state_get( - shortstatehash, - &StateEventType::RoomMember, - sender_user.as_str(), - ) - .transpose() - }) - .transpose()? - .and_then(|pdu| { - serde_json::from_str(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid PDU in database.") - }) - .ok() - }); - - let encrypted_room = services() - .rooms - .state_accessor - .state_get( - current_shortstatehash, - &StateEventType::RoomEncryption, - "", - )? - .is_some(); - - if let Some(since_shortstatehash) = since_shortstatehash { - // Skip if there are only timeline changes - if since_shortstatehash == current_shortstatehash { - continue; - } - - let since_encryption = - services().rooms.state_accessor.state_get( - since_shortstatehash, - &StateEventType::RoomEncryption, - "", - )?; - - let joined_since_last_sync = - since_sender_member.is_none_or(|member| { - member.membership != MembershipState::Join - }); - - let new_encrypted_room = - encrypted_room && since_encryption.is_none(); - if encrypted_room { - let current_state_ids = services() - .rooms - .state_accessor - .state_full_ids(current_shortstatehash) - .await?; - let since_state_ids = services() - .rooms - .state_accessor - .state_full_ids(since_shortstatehash) - .await?; - - for (key, event_id) in current_state_ids { - if since_state_ids.get(&key) != Some(&event_id) { - let Some(pdu) = - services().rooms.timeline.get_pdu(&event_id)? - else { - error!(%event_id, "Event in state not found"); - continue; - }; - if pdu.kind == TimelineEventType::RoomMember { - if let Some(state_key) = &pdu.state_key { - let user_id = - UserId::parse(state_key.clone()) - .map_err(|_| { - Error::bad_database( - "Invalid UserId in member \ - PDU.", - ) - })?; - - if user_id == sender_user { - continue; - } - - let new_membership = - serde_json::from_str::< - RoomMemberEventContent, - >( - pdu.content.get() - ) - .map_err(|_| { - Error::bad_database( - "Invalid PDU in database.", - ) - })? - .membership; - - match new_membership { - MembershipState::Join => { - // A new user joined an encrypted - // room - if !share_encrypted_room( - &sender_user, - &user_id, - room_id, - )? { - device_list_changes - .insert(user_id); - } - } - MembershipState::Leave => { - // Write down users that have left - // encrypted rooms we are in - left_encrypted_users - .insert(user_id); - } - _ => {} - } - } - } - } - } - if joined_since_last_sync || new_encrypted_room { - // If the user is in a new encrypted room, give them all - // joined users - device_list_changes.extend( - services() - .rooms - .state_cache - .room_members(room_id) - .flatten() - .filter(|user_id| { - // Don't send key updates from the sender to - // the sender - &sender_user != user_id - }) - .filter(|user_id| { - // Only send keys if the sender doesn't - // share an encrypted room with the target - // already - !share_encrypted_room( - &sender_user, - user_id, - room_id, - ) - .unwrap_or(false) - }), - ); - } - } - } - // Look for device list updates in this room - device_list_changes.extend( - services() - .users - .keys_changed(room_id.as_ref(), globalsince, None) - .filter_map(Result::ok), - ); - } - for user_id in left_encrypted_users { - let dont_share_encrypted_room = services() - .rooms - .user - .get_shared_rooms(vec![sender_user.clone(), user_id.clone()])? - .filter_map(Result::ok) - .filter_map(|other_room_id| { - Some( - services() - .rooms - .state_accessor - .room_state_get( - &other_room_id, - &StateEventType::RoomEncryption, - "", - ) - .ok()? - .is_some(), - ) - }) - .all(|encrypted| !encrypted); - // If the user doesn't share an encrypted room with the target - // anymore, we need to tell them - if dont_share_encrypted_room { - device_list_left.insert(user_id); - } - } - } - - let mut lists = BTreeMap::new(); - // and required state - let mut todo_rooms = BTreeMap::new(); - - for (list_id, list) in body.lists { - if list.filters.and_then(|f| f.is_invite).unwrap_or(false) { - continue; - } - - let mut new_known_rooms = BTreeSet::new(); - - lists.insert( - list_id.clone(), - sync_events::v4::SyncList { - ops: list - .ranges - .into_iter() - .map(|mut r| { - r.0 = r.0.clamp( - uint!(0), - UInt::try_from(all_joined_rooms.len() - 1) - .unwrap_or(UInt::MAX), - ); - r.1 = r.1.clamp( - r.0, - UInt::try_from(all_joined_rooms.len() - 1) - .unwrap_or(UInt::MAX), - ); - let room_ids = all_joined_rooms[r - .0 - .try_into() - .unwrap_or(usize::MAX) - ..=r.1.try_into().unwrap_or(usize::MAX)] - .to_vec(); - new_known_rooms.extend(room_ids.iter().cloned()); - for room_id in &room_ids { - let todo_room = todo_rooms - .entry(room_id.clone()) - .or_insert((BTreeSet::new(), 0, u64::MAX)); - let limit = list - .room_details - .timeline_limit - .map_or(10, u64::from) - .min(100); - todo_room.0.extend( - list.room_details - .required_state - .iter() - .cloned(), - ); - todo_room.1 = todo_room.1.max(limit); - // 0 means unknown because it got out of date - todo_room.2 = todo_room.2.min( - known_rooms - .get(&list_id) - .and_then(|k| k.get(room_id)) - .copied() - .unwrap_or(0), - ); - } - sync_events::v4::SyncOp { - op: SlidingOp::Sync, - range: Some(r), - index: None, - room_ids, - room_id: None, - } - }) - .collect(), - count: UInt::try_from(all_joined_rooms.len()) - .unwrap_or(UInt::MAX), - }, - ); - - if let Some(conn_id) = &body.conn_id { - services().users.update_sync_known_rooms( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - list_id, - new_known_rooms, - globalsince, - ); - } - } - - let mut known_subscription_rooms = BTreeSet::new(); - for (room_id, room) in &body.room_subscriptions { - if !services().rooms.metadata.exists(room_id)? { - continue; - } - let todo_room = todo_rooms.entry(room_id.clone()).or_insert(( - BTreeSet::new(), - 0, - u64::MAX, - )); - let limit = room.timeline_limit.map_or(10, u64::from).min(100); - todo_room.0.extend(room.required_state.iter().cloned()); - todo_room.1 = todo_room.1.max(limit); - // 0 means unknown because it got out of date - todo_room.2 = todo_room.2.min( - known_rooms - .get("subscriptions") - .and_then(|k| k.get(room_id)) - .copied() - .unwrap_or(0), - ); - known_subscription_rooms.insert(room_id.clone()); - } - - for r in body.unsubscribe_rooms { - known_subscription_rooms.remove(&r); - body.room_subscriptions.remove(&r); - } - - if let Some(conn_id) = &body.conn_id { - services().users.update_sync_known_rooms( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - "subscriptions".to_owned(), - known_subscription_rooms, - globalsince, - ); - } - - if let Some(conn_id) = &body.conn_id { - services().users.update_sync_subscriptions( - sender_user.clone(), - sender_device.clone(), - conn_id.clone(), - body.room_subscriptions, - ); - } - - let mut rooms = BTreeMap::new(); - for (room_id, (required_state_request, timeline_limit, roomsince)) in - &todo_rooms - { - let roomsincecount = PduCount::Normal(*roomsince); - - let (timeline_pdus, limited) = load_timeline( - &sender_user, - room_id, - roomsincecount, - *timeline_limit, - )?; - - if roomsince != &0 && timeline_pdus.is_empty() { - continue; - } - - 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(), - })) - })? - .or_else(|| (roomsince != &0).then(|| roomsince.to_string())); - - let room_events: Vec<_> = timeline_pdus - .iter() - .map(|(_, pdu)| pdu.to_sync_room_event()) - .collect(); - - let required_state = required_state_request - .iter() - .filter_map(|state| { - services() - .rooms - .state_accessor - .room_state_get(room_id, &state.0, &state.1) - .ok() - .flatten() - .map(|state| state.to_sync_state_event()) - }) - .collect(); - - // Heroes - let heroes = services() - .rooms - .state_cache - .room_members(room_id) - .filter_map(Result::ok) - .filter(|member| member != &sender_user) - .filter_map(|member| { - services() - .rooms - .state_accessor - .get_member(room_id, &member) - .ok() - .flatten() - .map(|memberevent| { - ( - memberevent - .displayname - .unwrap_or_else(|| member.to_string()), - memberevent.avatar_url, - ) - }) - }) - .take(5) - .collect::>(); - let name = match &*heroes { - [] => None, - [only] => Some(only.0.clone()), - [firsts @ .., last] => Some({ - let firsts = firsts - .iter() - .map(|h| h.0.clone()) - .collect::>() - .join(", "); - - format!("{firsts} and {}", last.0) - }), - }; - - let avatar = if let [only] = &*heroes { - only.1.clone() - } else { - None - }; - - rooms.insert( - room_id.clone(), - sync_events::v4::SlidingSyncRoom { - name: services() - .rooms - .state_accessor - .get_name(room_id)? - .or(name), - avatar: if let Some(avatar) = avatar { - JsOption::Some(avatar) - } else { - match services().rooms.state_accessor.get_avatar(room_id)? { - JsOption::Some(avatar) => { - JsOption::from_option(avatar.url) - } - JsOption::Null => JsOption::Null, - JsOption::Undefined => JsOption::Undefined, - } - }, - initial: Some(roomsince == &0), - is_dm: None, - invite_state: None, - unread_notifications: UnreadNotificationsCount { - highlight_count: Some( - services() - .rooms - .user - .highlight_count(&sender_user, room_id)? - .try_into() - .expect("notification count can't go that high"), - ), - notification_count: Some( - services() - .rooms - .user - .notification_count(&sender_user, room_id)? - .try_into() - .expect("notification count can't go that high"), - ), - }, - timeline: room_events, - required_state, - prev_batch, - limited, - joined_count: Some( - services() - .rooms - .state_cache - .room_joined_count(room_id)? - .map(UInt::new_saturating) - .unwrap_or(uint!(0)), - ), - invited_count: Some( - services() - .rooms - .state_cache - .room_invited_count(room_id)? - .map(UInt::new_saturating) - .unwrap_or(uint!(0)), - ), - // Count events in timeline greater than global sync counter - num_live: None, - timestamp: None, - // TODO - heroes: 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); - } - match tokio::time::timeout(duration, watcher).await { - Ok(x) => x.expect("watcher should succeed"), - Err(error) => debug!(%error, "Timed out"), - }; - } - - Ok(Ra(sync_events::v4::Response { - initial: globalsince == 0, - txn_id: body.txn_id.clone(), - pos: next_batch.to_string(), - lists, - rooms, - extensions: sync_events::v4::Extensions { - to_device: body - .extensions - .to_device - .enabled - .unwrap_or(false) - .then(|| { - services() - .users - .get_to_device_events(&sender_user, &sender_device) - .map(|events| sync_events::v4::ToDevice { - events, - next_batch: next_batch.to_string(), - }) - }) - .transpose()?, - e2ee: sync_events::v4::E2EE { - device_lists: DeviceLists { - changed: device_list_changes.into_iter().collect(), - left: device_list_left.into_iter().collect(), - }, - device_one_time_keys_count: services() - .users - .count_one_time_keys(&sender_user, &sender_device)?, - // Fallback keys are not yet supported - device_unused_fallback_key_types: None, - }, - account_data: sync_events::v4::AccountData { - global: if body.extensions.account_data.enabled.unwrap_or(false) - { - services() - .account_data - .global_changes_since(&sender_user, globalsince)? - .into_iter() - .map(|(event_type, content)| { - account_data::raw_global_event_from_parts( - &event_type, - &content, - ) - }) - .collect() - } else { - 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/well_known.rs b/src/api/well_known.rs index 44210e55..edb5a347 100644 --- a/src/api/well_known.rs +++ b/src/api/well_known.rs @@ -37,14 +37,5 @@ pub(crate) async fn client(_: Ar) -> Ra { Ra(client::Response { homeserver: client::HomeserverInfo::new(base_url.clone()), identity_server: None, - sliding_sync_proxy: services() - .globals - .config - .server_discovery - .client - .advertise_sliding_sync - .then_some(client::SlidingSyncProxyInfo { - url: base_url, - }), }) } diff --git a/src/cli/serve.rs b/src/cli/serve.rs index 7a528748..f0e0f969 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -651,7 +651,6 @@ fn client_routes() -> Router { .ruma_route(c2s::get_state_events_route) .ruma_route(c2s::get_state_events_for_key_route) .ruma_route(c2s::v3::sync_events_route) - .ruma_route(c2s::msc3575::sync_events_v4_route) .ruma_route(c2s::get_context_route) .ruma_route(c2s::get_message_events_route) .ruma_route(c2s::search_events_route) diff --git a/src/config.rs b/src/config.rs index b597e4a9..5924d232 100644 --- a/src/config.rs +++ b/src/config.rs @@ -151,9 +151,6 @@ pub(crate) struct ServerServerDiscovery { pub(crate) struct ClientServerDiscovery { /// The base URL to make client-server API requests to pub(crate) base_url: Url, - - #[serde(default, rename = "advertise_buggy_sliding_sync")] - pub(crate) advertise_sliding_sync: bool, } #[derive(Debug, Deserialize)] diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 3c859a01..78af94ed 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -7,7 +7,6 @@ use lru_cache::LruCache; use ruma::{ events::{ room::{ - avatar::RoomAvatarEventContent, history_visibility::{ HistoryVisibility, RoomHistoryVisibilityEventContent, }, @@ -18,8 +17,8 @@ use ruma::{ StateEventType, }, state_res::Event, - EventId, JsOption, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, - ServerName, UserId, + EventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, + UserId, }; use serde_json::value::to_raw_value; use tracing::{error, warn}; @@ -508,23 +507,6 @@ impl Service { ) } - #[tracing::instrument(skip(self))] - pub(crate) fn get_avatar( - &self, - room_id: &RoomId, - ) -> Result> { - self.room_state_get(room_id, &StateEventType::RoomAvatar, "")?.map_or( - Ok(JsOption::Undefined), - |s| { - serde_json::from_str(s.content.get()).map_err(|_| { - Error::bad_database( - "Invalid room avatar event in database.", - ) - }) - }, - ) - } - // Allowed because this function uses `services()` #[allow(clippy::unused_self)] #[tracing::instrument(skip(self), ret(level = "trace"))] @@ -553,24 +535,6 @@ impl Service { .is_ok() } - #[tracing::instrument(skip(self))] - pub(crate) fn get_member( - &self, - room_id: &RoomId, - user_id: &UserId, - ) -> Result> { - self.room_state_get( - room_id, - &StateEventType::RoomMember, - user_id.as_str(), - )? - .map_or(Ok(None), |s| { - serde_json::from_str(s.content.get()).map_err(|_| { - Error::bad_database("Invalid room member event in database.") - }) - }) - } - /// Checks if a given user can redact a given event /// /// If `federation` is `true`, it allows redaction events from any user of diff --git a/src/service/users.rs b/src/service/users.rs index 9df6e835..dbacb715 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -1,23 +1,12 @@ -use std::{ - collections::{BTreeMap, BTreeSet}, - mem, - sync::{Arc, Mutex}, -}; +use std::{collections::BTreeMap, mem}; use ruma::{ - api::client::{ - device::Device, - filter::FilterDefinition, - sync::sync_events::{ - self, - v4::{ExtensionsConfig, SyncRequestList}, - }, - }, + api::client::{device::Device, filter::FilterDefinition}, encryption::{CrossSigningKey, DeviceKeys, OneTimeKey}, events::AnyToDeviceEvent, serde::Raw, DeviceId, OneTimeKeyAlgorithm, OneTimeKeyName, OwnedDeviceId, OwnedKeyId, - OwnedMxcUri, OwnedOneTimeKeyId, OwnedRoomId, OwnedUserId, UInt, UserId, + OwnedMxcUri, OwnedOneTimeKeyId, OwnedUserId, UInt, UserId, }; use crate::{services, Error, Result}; @@ -26,30 +15,14 @@ mod data; pub(crate) use data::Data; -pub(crate) struct SlidingSyncCache { - lists: BTreeMap, - subscriptions: BTreeMap, - // For every room, the roomsince number - known_rooms: BTreeMap>, - extensions: ExtensionsConfig, -} - pub(crate) struct Service { pub(crate) db: &'static dyn Data, - #[allow(clippy::type_complexity)] - pub(crate) connections: Mutex< - BTreeMap< - (OwnedUserId, OwnedDeviceId, String), - Arc>, - >, - >, } impl Service { pub(crate) fn new(db: &'static dyn Data) -> Self { Self { db, - connections: Mutex::new(BTreeMap::new()), } } @@ -58,206 +31,6 @@ impl Service { self.db.exists(user_id) } - pub(crate) fn forget_sync_request_connection( - &self, - user_id: OwnedUserId, - device_id: OwnedDeviceId, - conn_id: String, - ) { - self.connections.lock().unwrap().remove(&(user_id, device_id, conn_id)); - } - - #[allow(clippy::too_many_lines)] - pub(crate) fn update_sync_request_with_cache( - &self, - user_id: OwnedUserId, - device_id: OwnedDeviceId, - request: &mut sync_events::v4::Request, - ) -> BTreeMap> { - let Some(conn_id) = request.conn_id.clone() else { - return BTreeMap::new(); - }; - - let mut cache = self.connections.lock().unwrap(); - let cached = Arc::clone( - cache.entry((user_id, device_id, conn_id)).or_insert_with(|| { - Arc::new(Mutex::new(SlidingSyncCache { - lists: BTreeMap::new(), - subscriptions: BTreeMap::new(), - known_rooms: BTreeMap::new(), - extensions: ExtensionsConfig::default(), - })) - }), - ); - let cached = &mut cached.lock().unwrap(); - drop(cache); - - for (list_id, list) in &mut request.lists { - if let Some(cached_list) = cached.lists.get(list_id) { - if list.sort.is_empty() { - list.sort.clone_from(&cached_list.sort); - } - if list.room_details.required_state.is_empty() { - list.room_details - .required_state - .clone_from(&cached_list.room_details.required_state); - } - list.room_details.timeline_limit = list - .room_details - .timeline_limit - .or(cached_list.room_details.timeline_limit); - list.include_old_rooms = list - .include_old_rooms - .clone() - .or(cached_list.include_old_rooms.clone()); - match (&mut list.filters, cached_list.filters.clone()) { - (Some(list_filters), Some(cached_filters)) => { - list_filters.is_dm = - list_filters.is_dm.or(cached_filters.is_dm); - if list_filters.spaces.is_empty() { - list_filters.spaces = cached_filters.spaces; - } - list_filters.is_encrypted = list_filters - .is_encrypted - .or(cached_filters.is_encrypted); - list_filters.is_invite = - list_filters.is_invite.or(cached_filters.is_invite); - if list_filters.room_types.is_empty() { - list_filters.room_types = cached_filters.room_types; - } - if list_filters.not_room_types.is_empty() { - list_filters.not_room_types = - cached_filters.not_room_types; - } - list_filters.room_name_like = list_filters - .room_name_like - .clone() - .or(cached_filters.room_name_like); - if list_filters.tags.is_empty() { - list_filters.tags = cached_filters.tags; - } - if list_filters.not_tags.is_empty() { - list_filters.not_tags = cached_filters.not_tags; - } - } - (_, Some(cached_filters)) => { - list.filters = Some(cached_filters); - } - (Some(list_filters), _) => { - list.filters = Some(list_filters.clone()); - } - (..) => {} - } - if list.bump_event_types.is_empty() { - list.bump_event_types - .clone_from(&cached_list.bump_event_types); - } - } - cached.lists.insert(list_id.clone(), list.clone()); - } - - cached.subscriptions.extend( - request - .room_subscriptions - .iter() - .map(|(k, v)| (k.clone(), v.clone())), - ); - request.room_subscriptions.extend( - cached.subscriptions.iter().map(|(k, v)| (k.clone(), v.clone())), - ); - - request.extensions.e2ee.enabled = - request.extensions.e2ee.enabled.or(cached.extensions.e2ee.enabled); - - request.extensions.to_device.enabled = request - .extensions - .to_device - .enabled - .or(cached.extensions.to_device.enabled); - - request.extensions.account_data.enabled = request - .extensions - .account_data - .enabled - .or(cached.extensions.account_data.enabled); - request.extensions.account_data.lists = request - .extensions - .account_data - .lists - .clone() - .or(cached.extensions.account_data.lists.clone()); - request.extensions.account_data.rooms = request - .extensions - .account_data - .rooms - .clone() - .or(cached.extensions.account_data.rooms.clone()); - - cached.extensions = request.extensions.clone(); - - cached.known_rooms.clone() - } - - pub(crate) fn update_sync_subscriptions( - &self, - user_id: OwnedUserId, - device_id: OwnedDeviceId, - conn_id: String, - subscriptions: BTreeMap, - ) { - let mut cache = self.connections.lock().unwrap(); - let cached = Arc::clone( - cache.entry((user_id, device_id, conn_id)).or_insert_with(|| { - Arc::new(Mutex::new(SlidingSyncCache { - lists: BTreeMap::new(), - subscriptions: BTreeMap::new(), - known_rooms: BTreeMap::new(), - extensions: ExtensionsConfig::default(), - })) - }), - ); - let cached = &mut cached.lock().unwrap(); - drop(cache); - - cached.subscriptions = subscriptions; - } - - pub(crate) fn update_sync_known_rooms( - &self, - user_id: OwnedUserId, - device_id: OwnedDeviceId, - conn_id: String, - list_id: String, - new_cached_rooms: BTreeSet, - globalsince: u64, - ) { - let mut cache = self.connections.lock().unwrap(); - let cached = Arc::clone( - cache.entry((user_id, device_id, conn_id)).or_insert_with(|| { - Arc::new(Mutex::new(SlidingSyncCache { - lists: BTreeMap::new(), - subscriptions: BTreeMap::new(), - known_rooms: BTreeMap::new(), - extensions: ExtensionsConfig::default(), - })) - }), - ); - let cached = &mut cached.lock().unwrap(); - drop(cache); - - for (roomid, lastsince) in - cached.known_rooms.entry(list_id.clone()).or_default().iter_mut() - { - if !new_cached_rooms.contains(roomid) { - *lastsince = 0; - } - } - let list = cached.known_rooms.entry(list_id).or_default(); - for roomid in new_cached_rooms { - list.insert(roomid, globalsince); - } - } - /// Check if account is deactivated pub(crate) fn is_deactivated(&self, user_id: &UserId) -> Result { self.db.is_deactivated(user_id) From 6bf289a71463b3caf4f71b3abf607f9684755845 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 9 Aug 2025 00:13:10 +0200 Subject: [PATCH 603/617] Remove unused parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … of filter_keys_server_map and filter_keys_single_server. --- src/api/client_server/membership.rs | 8 +++----- src/service/globals.rs | 4 +--- src/service/rooms/event_handler.rs | 8 +++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 20c48cf4..0a0fed5f 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1250,11 +1250,9 @@ async fn validate_and_add_event_id( let unfiltered_keys = (*pub_key_map.read().await).clone(); - let keys = services().globals.filter_keys_server_map( - unfiltered_keys, - origin_server_ts, - room_version, - ); + let keys = services() + .globals + .filter_keys_server_map(unfiltered_keys, origin_server_ts); if let Err(error) = ruma::signatures::verify_event(&keys, &value, room_version) diff --git a/src/service/globals.rs b/src/service/globals.rs index 33dac38e..76817fcf 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -573,11 +573,10 @@ impl Service { &self, keys: BTreeMap, timestamp: MilliSecondsSinceUnixEpoch, - room_version_id: &RoomVersionId, ) -> BTreeMap> { keys.into_iter() .filter_map(|(server, keys)| { - self.filter_keys_single_server(keys, timestamp, room_version_id) + self.filter_keys_single_server(keys, timestamp) .map(|keys| (server, keys)) }) .collect() @@ -590,7 +589,6 @@ impl Service { &self, keys: SigningKeys, timestamp: MilliSecondsSinceUnixEpoch, - _room_version_id: &RoomVersionId, ) -> Option> { let all_valid = keys.valid_until_ts > timestamp; diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 07eed506..70a2a94b 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -365,11 +365,9 @@ impl Service { // Removing all the expired keys, unless the room version allows // stale keys - let filtered_keys = services().globals.filter_keys_server_map( - pkey_map, - origin_server_ts, - room_version_id, - ); + let filtered_keys = services() + .globals + .filter_keys_server_map(pkey_map, origin_server_ts); let mut val = match ruma::signatures::verify_event( &filtered_keys, From ab5f76f49fa4a6c6638624c52c939f9b224806a7 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 8 Aug 2025 22:01:23 +0200 Subject: [PATCH 604/617] Put ruma dependency in its own TOML table --- Cargo.toml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 74a24679..fe7743de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,6 @@ regex = "1.11.1" reqwest = { version = "0.12.22", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.14" rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } -ruma = { git = "https://gitlab.computer.surgery/matrix/ruma.git", rev = "ruma-0.12.2+grapevine-1", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.34.0", optional = true, features = ["bundled"] } rustls = { version = "0.23.31", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.5", optional = true } @@ -144,6 +143,23 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] } trust-dns-resolver = "0.23.2" xdg = "2.5.2" +[dependencies.ruma] +git = "https://gitlab.computer.surgery/matrix/ruma.git" +rev = "ruma-0.12.2+grapevine-1" +features = [ + "compat", + "rand", + "appservice-api-c", + "client-api", + "federation-api", + "push-gateway-api-c", + "server-util", + "state-res", + "unstable-msc2448", + "ring-compat", + "unstable-unspecified", +] + [target.'cfg(unix)'.dependencies] nix = { version = "0.29", features = ["resource", "time"] } From b94cc429b72b14b4d85f70ddda09ef7809f46241 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 13 Jul 2025 12:59:04 -0700 Subject: [PATCH 605/617] Bump ruma to 1387667de806c37a6d7f72125117009bd618e32a The significant change is 26edd40a704040e7104161da81c9bae91b7ddcaa, which removes the global compat feature, so that each compat feature must now be enabled individually. We're using the slightly later 1387667d because it has a bugfix that ruma needs to compile. There are a few ruma compat features that were not previously part of the global compat feature: - compat-arbitrary-length-ids - compat-upload-signature - compat-encrypted-stickers I have not enabled these here, to avoid a behavior change. --- Cargo.lock | 52 +++++++++++++++--------------------- Cargo.toml | 17 ++++++++---- src/api/ruma_wrapper/axum.rs | 13 ++++++--- src/api/server_server.rs | 2 +- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51604973..a6b4fed7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2458,8 +2458,8 @@ dependencies = [ [[package]] name = "ruma" -version = "0.12.2" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +version = "0.12.1" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "assign", "js_int", @@ -2470,7 +2470,6 @@ dependencies = [ "ruma-events", "ruma-federation-api", "ruma-push-gateway-api", - "ruma-server-util", "ruma-signatures", "ruma-state-res", "web-time", @@ -2479,7 +2478,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "js_int", "ruma-common", @@ -2490,8 +2489,8 @@ dependencies = [ [[package]] name = "ruma-client-api" -version = "0.20.2" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +version = "0.20.1" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "as_variant", "assign", @@ -2513,8 +2512,8 @@ dependencies = [ [[package]] name = "ruma-common" -version = "0.15.2" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +version = "0.15.1" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "as_variant", "base64", @@ -2544,8 +2543,8 @@ dependencies = [ [[package]] name = "ruma-events" -version = "0.30.2" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +version = "0.30.1" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2567,11 +2566,13 @@ dependencies = [ [[package]] name = "ruma-federation-api" -version = "0.11.1" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +version = "0.11.0" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "bytes", + "headers", "http", + "http-auth", "httparse", "js_int", "memchr", @@ -2581,12 +2582,14 @@ dependencies = [ "ruma-events", "serde", "serde_json", + "thiserror 2.0.12", + "tracing", ] [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2595,7 +2598,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2610,7 +2613,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "js_int", "ruma-common", @@ -2619,23 +2622,10 @@ dependencies = [ "serde_json", ] -[[package]] -name = "ruma-server-util" -version = "0.5.0" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" -dependencies = [ - "headers", - "http", - "http-auth", - "ruma-common", - "thiserror 2.0.12", - "tracing", -] - [[package]] name = "ruma-signatures" -version = "0.17.1" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +version = "0.17.0" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "base64", "ed25519-dalek", @@ -2651,7 +2641,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://gitlab.computer.surgery/matrix/ruma.git?rev=ruma-0.12.2%2Bgrapevine-1#4208e8c4c8c2b0e7686aefcdbebf7f2f1d08ee29" +source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index fe7743de..685c0e6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,20 +144,27 @@ trust-dns-resolver = "0.23.2" xdg = "2.5.2" [dependencies.ruma] -git = "https://gitlab.computer.surgery/matrix/ruma.git" -rev = "ruma-0.12.2+grapevine-1" +git = "https://github.com/ruma/ruma.git" +rev = "1387667de806c37a6d7f72125117009bd618e32a" features = [ - "compat", + "compat-server-signing-key-version", + "compat-empty-string-null", + "compat-null", + "compat-optional", + "compat-unset-avatar", + "compat-get-3pids", + "compat-signature-id", + "compat-tag-info", + "compat-optional-txn-pdus", + "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", - "server-util", "state-res", "unstable-msc2448", "ring-compat", - "unstable-unspecified", ] [target.'cfg(unix)'.dependencies] diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index d3fb0811..01f80fb4 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -17,10 +17,9 @@ use http::{Request, StatusCode}; use http_body_util::BodyExt; use ruma::{ api::{ - client::error::ErrorKind, AuthScheme, IncomingRequest, Metadata, - OutgoingResponse, + client::error::ErrorKind, federation::authentication::XMatrix, + AuthScheme, IncomingRequest, Metadata, OutgoingResponse, }, - server_util::authorization::XMatrix, CanonicalJsonValue, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId, }; @@ -162,6 +161,7 @@ async fn ar_from_request_inner( ( AuthScheme::None | AuthScheme::AppserviceToken + | AuthScheme::AppserviceTokenOptional | AuthScheme::AccessTokenOptional, Token::Appservice(info), ) => (None, None, None, Some(*info)), @@ -329,6 +329,7 @@ async fn ar_from_request_inner( ( AuthScheme::None | AuthScheme::AppserviceToken + | AuthScheme::AppserviceTokenOptional | AuthScheme::AccessTokenOptional, Token::None, ) => (None, None, None, None), @@ -341,7 +342,11 @@ async fn ar_from_request_inner( "Only server signatures should be used on this endpoint.", )); } - (AuthScheme::AppserviceToken, Token::User(_)) => { + ( + AuthScheme::AppserviceToken + | AuthScheme::AppserviceTokenOptional, + Token::User(_), + ) => { return Err(Error::BadRequest( ErrorKind::Unauthorized, "Only appservice access tokens should be used on this \ diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 793cbbd2..0f20a858 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -16,6 +16,7 @@ use ruma::{ client::error::{Error as RumaError, ErrorKind}, federation::{ authenticated_media, + authentication::XMatrix, authorization::get_event_authorization, backfill::get_backfill, device::get_devices::{self, v1::UserDevice}, @@ -53,7 +54,6 @@ use ruma::{ StateEventType, TimelineEventType, }, serde::{Base64, JsonObject, Raw}, - server_util::authorization::XMatrix, state_res::Event, to_device::DeviceIdOrAllDevices, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, From d283da51c627fac4600edfecc8fe5001ab251151 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 8 Aug 2025 23:00:59 +0200 Subject: [PATCH 606/617] Upgrade rust-rocksdb --- Cargo.lock | 79 +++++++++++++++++++----------------------------------- Cargo.toml | 2 +- flake.lock | 8 +++--- flake.nix | 2 +- 4 files changed, 34 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6b4fed7..274e64f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,26 +256,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags 2.9.1", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn", -] - [[package]] name = "bindgen" version = "0.71.1" @@ -289,7 +269,25 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bindgen" +version = "0.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", "shlex", "syn", ] @@ -1190,7 +1188,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.0", "tokio", "tower-service", "tracing", @@ -1404,15 +1402,6 @@ dependencies = [ "serde", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -1516,12 +1505,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.174" @@ -1535,7 +1518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.53.3", ] [[package]] @@ -2226,7 +2209,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "socket2 0.5.10", "thiserror 2.0.12", @@ -2246,7 +2229,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "rustls-pki-types", "slab", @@ -2668,11 +2651,11 @@ dependencies = [ [[package]] name = "rust-librocksdb-sys" -version = "0.32.0+9.10.0" +version = "0.38.0+10.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50146b7fadd68926e9dcb902bf0515783aaaf5f73af26949f188aead5ede8cd0" +checksum = "455560a01423fff351732e26cb436b4ab43805642e0783d7db2c12d372849cf7" dependencies = [ - "bindgen 0.69.5", + "bindgen 0.72.0", "bzip2-sys", "cc", "glob", @@ -2684,9 +2667,9 @@ dependencies = [ [[package]] name = "rust-rocksdb" -version = "0.36.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf088a714aa3fad699f7dbe06047ca732c09629a2f9b28aa16ca6d3897a4c2f" +checksum = "41be0c32ea0155c97e2d19c2276739a2ec17606d0b89485ca5124c4b14e2a38f" dependencies = [ "libc", "rust-librocksdb-sys", @@ -2698,12 +2681,6 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" diff --git a/Cargo.toml b/Cargo.toml index 685c0e6f..bdea44fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,7 @@ rand = "0.8.5" regex = "1.11.1" reqwest = { version = "0.12.22", default-features = false, features = ["http2", "rustls-tls-native-roots", "socks"] } ring = "0.17.14" -rocksdb = { package = "rust-rocksdb", version = "0.36.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } +rocksdb = { package = "rust-rocksdb", version = "0.42.1", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } rusqlite = { version = "0.34.0", optional = true, features = ["bundled"] } rustls = { version = "0.23.31", default-features = false, features = ["ring", "log", "logging", "std", "tls12"] } sd-notify = { version = "0.4.5", optional = true } diff --git a/flake.lock b/flake.lock index 11986b29..4a278e21 100644 --- a/flake.lock +++ b/flake.lock @@ -238,16 +238,16 @@ "rocksdb": { "flake": false, "locked": { - "lastModified": 1734381914, - "narHash": "sha256-G+DlQwEUyd7JOCjS1Hg1cKWmA/qAiK8UpUIKcP+riGQ=", + "lastModified": 1752084860, + "narHash": "sha256-mKh6zsmxsiUix4LX+npiytmKvLbo6WNA9y4Ns/EY+bE=", "owner": "facebook", "repo": "rocksdb", - "rev": "ae8fb3e5000e46d8d4c9dbf3a36019c0aaceebff", + "rev": "410c5623195ecbe4699b9b5a5f622c7325cec6fe", "type": "github" }, "original": { "owner": "facebook", - "ref": "v9.10.0", + "ref": "v10.4.2", "repo": "rocksdb", "type": "github" } diff --git a/flake.nix b/flake.nix index 18cf25d4..5d62b734 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,7 @@ flake-utils.url = "github:numtide/flake-utils?ref=main"; nix-filter.url = "github:numtide/nix-filter?ref=main"; nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - rocksdb = { url = "github:facebook/rocksdb?ref=v9.10.0"; flake = false; }; + rocksdb = { url = "github:facebook/rocksdb?ref=v10.4.2"; flake = false; }; }; outputs = inputs: From 98dee6ad49f5acb6daad3e489e29d4e2e77b23ba Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 13 Jul 2025 21:46:25 -0700 Subject: [PATCH 607/617] Bump ruma to 649d683f3f5b8f4f7eb1e728443e4baf25cfebca (stateres refactor #1) The only change to the internal interface for this one is removing the current_third_party_invite argument from state_res::auth_check. Ruma now fetches the event using the fetch_state closure. This is convenient for us, because we previously didn't bother to implement it. --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 2 +- src/api/client_server/membership.rs | 2 -- src/service/rooms/event_handler.rs | 5 ----- src/service/rooms/timeline.rs | 19 ++++++++----------- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 274e64f8..e4a2a025 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "js_int", "ruma-common", @@ -2473,7 +2473,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "as_variant", "assign", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "as_variant", "base64", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2550,7 +2550,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "js_int", "ruma-common", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "base64", "ed25519-dalek", @@ -2624,7 +2624,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=1387667de806c37a6d7f72125117009bd618e32a#1387667de806c37a6d7f72125117009bd618e32a" +source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index bdea44fb..e3759081 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "1387667de806c37a6d7f72125117009bd618e32a" +rev = "649d683f3f5b8f4f7eb1e728443e4baf25cfebca" features = [ "compat-server-signing-key-version", "compat-empty-string-null", diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 0a0fed5f..6fb9438a 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1041,8 +1041,6 @@ async fn join_room_by_id_helper( Error::UnsupportedRoomVersion(room_version_id.clone()) })?, &parsed_join_pdu, - // TODO: third party invite - None::, |k, s| { services() .rooms diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 70a2a94b..90711296 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -507,8 +507,6 @@ impl Service { if !state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, - // TODO: third party invite - None::, |k, s| auth_events.get(&(k.to_string().into(), s.to_owned())), ) .map_err(|_e| { @@ -878,8 +876,6 @@ impl Service { let check_result = state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, - // TODO: third party invite - None::, |k, s| { services() .rooms @@ -923,7 +919,6 @@ impl Service { let soft_fail = !state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, - None::, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) .map_err(|_e| { diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 8ee8669b..1c12fbb6 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -862,17 +862,14 @@ impl Service { signatures: None, }; - let auth_check = state_res::auth_check( - &ruma_room_version, - &pdu, - // TODO: third_party_invite - None::, - |k, s| auth_events.get(&(k.clone(), s.to_owned())), - ) - .map_err(|error| { - error!(%error, "Auth check failed"); - Error::BadDatabase("Auth check failed.") - })?; + let auth_check = + state_res::auth_check(&ruma_room_version, &pdu, |k, s| { + auth_events.get(&(k.clone(), s.to_owned())) + }) + .map_err(|error| { + error!(%error, "Auth check failed"); + Error::BadDatabase("Auth check failed.") + })?; if !auth_check { return Err(Error::BadRequest( From 15ab91c0125fd11c2e91c51f044e793b2a1145f1 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 13 Jul 2025 18:16:56 -0700 Subject: [PATCH 608/617] Bump ruma to d84760a925d8e5830d668b031fec53ddf9101814 (drop compat-signature-id) --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4a2a025..fd93a4d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "js_int", "ruma-common", @@ -2473,7 +2473,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "as_variant", "assign", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "as_variant", "base64", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2550,7 +2550,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "js_int", "ruma-common", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "base64", "ed25519-dalek", @@ -2624,7 +2624,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=649d683f3f5b8f4f7eb1e728443e4baf25cfebca#649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index e3759081..94296359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "649d683f3f5b8f4f7eb1e728443e4baf25cfebca" +rev = "d84760a925d8e5830d668b031fec53ddf9101814" features = [ "compat-server-signing-key-version", "compat-empty-string-null", @@ -153,7 +153,6 @@ features = [ "compat-optional", "compat-unset-avatar", "compat-get-3pids", - "compat-signature-id", "compat-tag-info", "compat-optional-txn-pdus", From 61d2ff7183f5dd23615199530dd31b4773d4f353 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Sun, 10 Aug 2025 14:53:38 -0700 Subject: [PATCH 609/617] care less about room versions prior to 3 --- src/service/rooms/event_handler.rs | 2 -- src/service/rooms/state_accessor.rs | 13 ++----------- src/service/rooms/timeline.rs | 4 ---- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 90711296..6ea2005c 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -939,7 +939,6 @@ impl Service { redact_id, &incoming_pdu.sender, &incoming_pdu.room_id, - true, )? } else { false @@ -949,7 +948,6 @@ impl Service { redact_id, &incoming_pdu.sender, &incoming_pdu.room_id, - true, )? } else { false diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 78af94ed..1ab91a5e 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -536,17 +536,12 @@ impl Service { } /// Checks if a given user can redact a given event - /// - /// If `federation` is `true`, it allows redaction events from any user of - /// the same server as the original event sender, [as required by room - /// versions >= v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions) #[tracing::instrument(skip(self))] pub(crate) fn user_can_redact( &self, redacts: &EventId, sender: &UserId, room_id: &RoomId, - federation: bool, ) -> Result { self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")? .map_or_else( @@ -583,12 +578,8 @@ impl Service { .timeline .get_pdu(redacts) { - if federation { - pdu.sender().server_name() - == sender.server_name() - } else { - pdu.sender == sender - } + pdu.sender().server_name() + == sender.server_name() } else { false } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 1c12fbb6..53797af8 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -484,7 +484,6 @@ impl Service { redact_id, &pdu.sender, &pdu.room_id, - false, )? { self.redact_pdu(redact_id, pdu, shortroomid)?; } @@ -494,7 +493,6 @@ impl Service { redact_id, &pdu.sender, &pdu.room_id, - false, )? { self.redact_pdu(redact_id, pdu, shortroomid)?; } @@ -1078,7 +1076,6 @@ impl Service { redact_id, &pdu.sender, &pdu.room_id, - false, )? { return Err(Error::BadRequest( ErrorKind::forbidden(), @@ -1091,7 +1088,6 @@ impl Service { redact_id, &pdu.sender, &pdu.room_id, - false, )? { return Err(Error::BadRequest( ErrorKind::forbidden(), From aade2e17c2913700856f7e0951c1a96252190ed7 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 19 Jul 2025 22:26:49 -0700 Subject: [PATCH 610/617] Refactor redaction permission checks --- src/service/pdu.rs | 31 +++++++++++- src/service/rooms/event_handler.rs | 41 +++------------ src/service/rooms/state_accessor.rs | 35 +++++++++++-- src/service/rooms/timeline.rs | 78 ++++++----------------------- 4 files changed, 83 insertions(+), 102 deletions(-) diff --git a/src/service/pdu.rs b/src/service/pdu.rs index 3df17326..35a6e43a 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -20,7 +20,7 @@ use serde_json::{ }; use tracing::warn; -use crate::Error; +use crate::{utils::room_version::RoomVersion, Error}; /// Content hashes of a PDU. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -104,6 +104,35 @@ impl PduEvent { unsigned.redacted_because.is_some() } + /// Returns the redaction target of an event, if present. + /// + /// For versions =v11, it is part of the room content. + // Allowed because we don't use state_res::Event anywhere in grapevine, it's + // just implemented so we can pass Pdu to ruma stateres. + #[expect(clippy::same_name_method)] + pub(crate) fn redacts( + &self, + room_version: &RoomVersion, + ) -> Option> { + if room_version.redaction_event_redacts_in_content { + #[derive(Deserialize)] + struct ExtractRedacts<'a> { + #[serde(borrow)] + redacts: Option>, + } + let extract = + serde_json::from_str::>(self.content.get()) + .map_err(|_| { + Error::bad_database("Invalid content in redaction pdu.") + }) + .ok()?; + extract.redacts + } else { + self.redacts.as_ref().map(|e| Cow::Borrowed(&**e)) + } + } + pub(crate) fn remove_transaction_id(&mut self) -> crate::Result<()> { if let Some(unsigned) = &self.unsigned { let mut unsigned: BTreeMap> = diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 6ea2005c..e88ba3a3 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -19,11 +19,8 @@ use ruma::{ }, }, events::{ - room::{ - redaction::RoomRedactionEventContent, - server_acl::RoomServerAclEventContent, - }, - StateEventType, TimelineEventType, + room::server_acl::RoomServerAclEventContent, StateEventType, + TimelineEventType, }, int, state_res::{self, StateMap}, @@ -43,7 +40,7 @@ use super::{ use crate::{ service::{globals::SigningKeys, pdu, rooms::state::ExtractVersion}, services, - utils::{debug_slice_truncated, room_version::RoomVersion}, + utils::debug_slice_truncated, Error, PduEvent, Result, }; @@ -568,7 +565,6 @@ impl Service { "Upgrading event to timeline pdu", ); - let room_version = RoomVersion::try_from(room_version_id)?; let ruma_room_version = state_res::RoomVersion::new(room_version_id) .map_err(|_| { Error::UnsupportedRoomVersion(room_version_id.clone()) @@ -925,33 +921,10 @@ impl Service { Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") })? || incoming_pdu.kind == TimelineEventType::RoomRedaction - && if room_version.redaction_event_redacts_in_content { - let content = - serde_json::from_str::( - incoming_pdu.content.get(), - ) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; - - if let Some(redact_id) = &content.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - )? - } else { - false - } - } else if let Some(redact_id) = &incoming_pdu.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - )? - } else { - false - }; + && services() + .rooms + .state_accessor + .redaction_event_allowed(&incoming_pdu)?; // 13. Use state resolution to find new room state diff --git a/src/service/rooms/state_accessor.rs b/src/service/rooms/state_accessor.rs index 1ab91a5e..a72a9f44 100644 --- a/src/service/rooms/state_accessor.rs +++ b/src/service/rooms/state_accessor.rs @@ -14,7 +14,7 @@ use ruma::{ name::RoomNameEventContent, power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, }, - StateEventType, + StateEventType, TimelineEventType, }, state_res::Event, EventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, @@ -26,9 +26,9 @@ use tracing::{error, warn}; use super::short::{ShortStateHash, ShortStateKey}; use crate::{ observability::{FoundIn, Lookup, METRICS}, - service::{globals::marker, pdu::PduBuilder}, + service::{globals::marker, pdu::PduBuilder, rooms::state::ExtractVersion}, services, - utils::on_demand_hashmap::KeyToken, + utils::{on_demand_hashmap::KeyToken, room_version::RoomVersion}, Error, PduEvent, Result, }; @@ -521,7 +521,7 @@ impl Service { .expect("Event content always serializes"); let new_event = PduBuilder { - event_type: ruma::events::TimelineEventType::RoomMember, + event_type: TimelineEventType::RoomMember, content, unsigned: None, state_key: Some(target_user.into()), @@ -592,4 +592,31 @@ impl Service { }, ) } + + /// Checks whether a redaction event is authorized against the current state + /// of it's room. + /// + /// Panics if `event` is not a `m.room.redaction` event. + pub(crate) fn redaction_event_allowed( + &self, + event: &PduEvent, + ) -> Result { + assert_eq!( + event.kind, + TimelineEventType::RoomRedaction, + "event must be a redaction event" + ); + + let room_version_id = services() + .rooms + .state + .get_create_content::(&event.room_id)?; + let room_version = RoomVersion::try_from(&room_version_id)?; + + let Some(redacted_id) = event.redacts(&room_version) else { + return Ok(false); + }; + + self.user_can_redact(&redacted_id, &event.sender, &event.room_id) + } } diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 53797af8..3635b082 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -14,7 +14,6 @@ use ruma::{ create::RoomCreateEventContent, encrypted::Relation, member::MembershipState, message::RoomMessageEventContent, power_levels::RoomPowerLevelsEventContent, - redaction::RoomRedactionEventContent, }, StateEventType, TimelineEventType, }, @@ -472,29 +471,14 @@ impl Service { .state .get_create_content::(&pdu.room_id)?; let room_version = RoomVersion::try_from(&room_version_id)?; - if room_version.redaction_event_redacts_in_content { - let content = serde_json::from_str::< - RoomRedactionEventContent, - >(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; - if let Some(redact_id) = &content.redacts { - if services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - )? { - self.redact_pdu(redact_id, pdu, shortroomid)?; - } - } - } else if let Some(redact_id) = &pdu.redacts { - if services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - )? { - self.redact_pdu(redact_id, pdu, shortroomid)?; + + if let Some(redact_id) = pdu.redacts(&room_version) { + if services() + .rooms + .state_accessor + .redaction_event_allowed(pdu)? + { + self.redact_pdu(&redact_id, pdu, shortroomid)?; } } } @@ -1056,45 +1040,13 @@ impl Service { // If redaction event is not authorized, do not append it to the // timeline - if pdu.kind == TimelineEventType::RoomRedaction { - let room_version_id = services() - .rooms - .state - .get_create_content::(&pdu.room_id)?; - let room_version = RoomVersion::try_from(&room_version_id)?; - if room_version.redaction_event_redacts_in_content { - let content = - serde_json::from_str::( - pdu.content.get(), - ) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; - - if let Some(redact_id) = &content.redacts { - if !services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - )? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User cannot redact this event.", - )); - } - } - } else if let Some(redact_id) = &pdu.redacts { - if !services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - )? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User cannot redact this event.", - )); - } - } + if pdu.kind == TimelineEventType::RoomRedaction + && !services().rooms.state_accessor.redaction_event_allowed(&pdu)? + { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "User cannot redact this event.", + )); } // We append to state before appending the pdu, so we don't have a From 644639ad10906402a011ce04e2452bfe7ad0de6c Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 19 Jul 2025 16:53:45 -0700 Subject: [PATCH 611/617] Refactor soft fail check --- src/service/rooms/event_handler.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index e88ba3a3..1307e52d 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -912,19 +912,22 @@ impl Service { &incoming_pdu.content, )?; - let soft_fail = !state_res::event_auth::auth_check( + let auth_fail_against_current = !state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) .map_err(|_e| { Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") - })? || incoming_pdu.kind + })?; + + let cannot_redact = incoming_pdu.kind == TimelineEventType::RoomRedaction - && services() + && !services() .rooms .state_accessor .redaction_event_allowed(&incoming_pdu)?; + let soft_fail = auth_fail_against_current || cannot_redact; // 13. Use state resolution to find new room state From d8ec9615897746b0cae7997ab32f59479922db8c Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 19 Jul 2025 15:08:11 -0700 Subject: [PATCH 612/617] Bump ruma to e8b0876dda083433a7f9181d47d0aff5a5e05497 (auth check return type change) --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 2 +- src/api/client_server/membership.rs | 9 +-------- src/service/rooms/event_handler.rs | 23 +++++------------------ src/service/rooms/timeline.rs | 22 +++++++--------------- 5 files changed, 25 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd93a4d6..7569469c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "js_int", "ruma-common", @@ -2473,7 +2473,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "as_variant", "assign", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "as_variant", "base64", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2550,7 +2550,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "js_int", "ruma-common", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "base64", "ed25519-dalek", @@ -2624,7 +2624,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=d84760a925d8e5830d668b031fec53ddf9101814#d84760a925d8e5830d668b031fec53ddf9101814" +source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index 94296359..9f864aed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "d84760a925d8e5830d668b031fec53ddf9101814" +rev = "e8b0876dda083433a7f9181d47d0aff5a5e05497" features = [ "compat-server-signing-key-version", "compat-empty-string-null", diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 6fb9438a..9415e77c 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -1036,7 +1036,7 @@ async fn join_room_by_id_helper( } info!("Running send_join auth check"); - let authenticated = state_res::event_auth::auth_check( + state_res::event_auth::auth_check( &state_res::RoomVersion::new(&room_version_id).map_err(|_| { Error::UnsupportedRoomVersion(room_version_id.clone()) })?, @@ -1065,13 +1065,6 @@ async fn join_room_by_id_helper( Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed") })?; - if !authenticated { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Auth check failed", - )); - } - info!("Saving state from send_join"); let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state( diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 1307e52d..e53e883d 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -501,19 +501,14 @@ impl Service { )); } - if !state_res::event_auth::auth_check( + state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, |k, s| auth_events.get(&(k.to_string().into(), s.to_owned())), ) .map_err(|_e| { Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed") - })? { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Auth check failed", - )); - } + })?; debug!("Validation successful"); @@ -869,7 +864,7 @@ impl Service { debug!("Starting auth check"); // 11. Check the auth of the event passes based on the state of the // event - let check_result = state_res::event_auth::auth_check( + state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, |k, s| { @@ -895,12 +890,6 @@ impl Service { .map_err(|_e| { Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") })?; - - if !check_result { - return Err(Error::bad_database( - "Event has failed auth check with state at the event.", - )); - } debug!("Auth check succeeded"); // Soft fail check before doing state res @@ -912,14 +901,12 @@ impl Service { &incoming_pdu.content, )?; - let auth_fail_against_current = !state_res::event_auth::auth_check( + let auth_fail_against_current = state_res::event_auth::auth_check( &ruma_room_version, &incoming_pdu, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) - .map_err(|_e| { - Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") - })?; + .is_err(); let cannot_redact = incoming_pdu.kind == TimelineEventType::RoomRedaction diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 3635b082..7ea2e3dc 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -844,21 +844,13 @@ impl Service { signatures: None, }; - let auth_check = - state_res::auth_check(&ruma_room_version, &pdu, |k, s| { - auth_events.get(&(k.clone(), s.to_owned())) - }) - .map_err(|error| { - error!(%error, "Auth check failed"); - Error::BadDatabase("Auth check failed.") - })?; - - if !auth_check { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "Event is not authorized.", - )); - } + state_res::auth_check(&ruma_room_version, &pdu, |k, s| { + auth_events.get(&(k.clone(), s.to_owned())) + }) + .map_err(|error| { + error!(%error, "Auth check failed"); + Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") + })?; // Hash and sign let mut pdu_json = utils::to_canonical_object(&pdu) From b5bc53bb2ddd9e6111c66a92a98229eaf647ebf0 Mon Sep 17 00:00:00 2001 From: Kierre Date: Thu, 21 Aug 2025 17:43:56 -0400 Subject: [PATCH 613/617] Add configuration option to allow invalid TLS certificates --- book/changelog.md | 2 ++ src/config.rs | 2 ++ src/service/globals.rs | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/book/changelog.md b/book/changelog.md index 40a8abea..9614862f 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -336,3 +336,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!189](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/189)) 28. Added the ability to listen on Unix sockets ([!187](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/187)) +29. Added the ability to allow invalid TLS certificates + ([!203](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/203)) diff --git a/src/config.rs b/src/config.rs index 5924d232..a485d4d3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -439,6 +439,7 @@ pub(crate) struct ObservabilityConfig { #[serde(default)] pub(crate) struct FederationConfig { pub(crate) enable: bool, + pub(crate) allow_invalid_tls_certificates: bool, pub(crate) self_test: bool, pub(crate) trusted_servers: Vec, pub(crate) max_fetch_prev_events: u16, @@ -456,6 +457,7 @@ impl Default for FederationConfig { ], max_fetch_prev_events: 100, max_concurrent_requests: 100, + allow_invalid_tls_certificates: false, old_verify_keys: BTreeMap::new(), } } diff --git a/src/service/globals.rs b/src/service/globals.rs index 76817fcf..c0dff71e 100644 --- a/src/service/globals.rs +++ b/src/service/globals.rs @@ -215,6 +215,9 @@ impl Resolve for FederationResolver { impl Service { #[tracing::instrument(skip_all)] + // there are a lot of fields to initialize, not easy to break up but logic + // is fairly linear + #[allow(clippy::too_many_lines)] pub(crate) fn new( db: &'static dyn Data, config: Config, @@ -258,6 +261,14 @@ impl Service { let default_client = reqwest_client_builder(&config)? .dns_resolver(default_resolver) .build()?; + + if config.federation.allow_invalid_tls_certificates { + warn!( + "TLS certificate validation is disabled, this is insecure and \ + should not be used in production" + ); + } + let federation_client = reqwest_client_builder(&config)? .dns_resolver(federation_resolver) .build()?; @@ -647,6 +658,9 @@ fn reqwest_client_builder(config: &Config) -> Result { .pool_max_idle_per_host(0) .connect_timeout(Duration::from_secs(30)) .timeout(Duration::from_secs(60 * 3)) + .danger_accept_invalid_certs( + config.federation.allow_invalid_tls_certificates, + ) .user_agent(format!("{}/{}", env!("CARGO_PKG_NAME"), crate::version())); if let Some(proxy) = config.proxy.to_proxy()? { From c4c294d64cc809925ed74470cb5c2c31a542588e Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Tue, 29 Jul 2025 22:37:33 -0700 Subject: [PATCH 614/617] Bump ruma to 3924283c5e94d2020f0991ce35c27667f8c61f0a (add RoomVersion arg to auth_types_for_event) --- Cargo.lock | 23 ++++++++++++----------- Cargo.toml | 2 +- src/service/rooms/event_handler.rs | 1 + src/service/rooms/state.rs | 14 ++++++++++---- src/service/rooms/timeline.rs | 1 + 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7569469c..16e2913b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "js_int", "ruma-common", @@ -2473,7 +2473,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "as_variant", "assign", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "as_variant", "base64", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2550,7 +2550,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "js_int", "ruma-common", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "base64", "ed25519-dalek", @@ -2624,11 +2624,12 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=e8b0876dda083433a7f9181d47d0aff5a5e05497#e8b0876dda083433a7f9181d47d0aff5a5e05497" +source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" dependencies = [ "js_int", "ruma-common", "ruma-events", + "ruma-signatures", "serde", "serde_json", "thiserror 2.0.12", diff --git a/Cargo.toml b/Cargo.toml index 9f864aed..12b71382 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "e8b0876dda083433a7f9181d47d0aff5a5e05497" +rev = "3924283c5e94d2020f0991ce35c27667f8c61f0a" features = [ "compat-server-signing-key-version", "compat-empty-string-null", diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index e53e883d..5ae78863 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -895,6 +895,7 @@ impl Service { // Soft fail check before doing state res let auth_events = services().rooms.state.get_auth_events( room_id, + &ruma_room_version, &incoming_pdu.kind, &incoming_pdu.sender, incoming_pdu.state_key.as_deref(), diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index e692251d..72051f57 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -430,10 +430,11 @@ impl Service { } /// This fetches auth events from the current state. - #[tracing::instrument(skip(self))] + #[tracing::instrument(skip(self, room_version))] pub(crate) fn get_auth_events( &self, room_id: &RoomId, + room_version: &state_res::RoomVersion, kind: &TimelineEventType, sender: &UserId, state_key: Option<&str>, @@ -444,9 +445,14 @@ impl Service { return Ok(HashMap::new()); }; - let auth_events = - state_res::auth_types_for_event(kind, sender, state_key, content) - .expect("content is a valid JSON object"); + let auth_events = state_res::auth_types_for_event( + kind, + sender, + state_key, + content, + room_version, + ) + .expect("content is a valid JSON object"); let mut sauthevents = auth_events .into_iter() diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 7ea2e3dc..9e934ad9 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -777,6 +777,7 @@ impl Service { let auth_events = services().rooms.state.get_auth_events( room_id, + &ruma_room_version, &event_type, sender, state_key.as_deref(), From 8c9800735bdb7840190ce8601c8559ff22644de4 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sun, 13 Jul 2025 22:17:29 -0700 Subject: [PATCH 615/617] Bump ruma to 2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b (RoomVersion -> RoomVersionRules) Co-authored-by: Jonas Platte --- Cargo.lock | 22 +++++----- Cargo.toml | 2 +- src/api/client_server/membership.rs | 59 +++++++++++++++++--------- src/api/server_server.rs | 19 +++++++-- src/service/admin.rs | 5 ++- src/service/pdu.rs | 21 ++++----- src/service/rooms/event_handler.rs | 66 ++++++++++++++--------------- src/service/rooms/state.rs | 11 ++--- src/service/rooms/timeline.rs | 26 +++++++----- src/utils/room_version.rs | 8 ++-- 10 files changed, 133 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16e2913b..f3fd8056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "js_int", "ruma-common", @@ -2473,7 +2473,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "as_variant", "assign", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "as_variant", "base64", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2550,7 +2550,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "js_int", "ruma-common", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "base64", "ed25519-dalek", @@ -2624,7 +2624,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=3924283c5e94d2020f0991ce35c27667f8c61f0a#3924283c5e94d2020f0991ce35c27667f8c61f0a" +source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index 12b71382..1c8b224a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "3924283c5e94d2020f0991ce35c27667f8c61f0a" +rev = "2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" features = [ "compat-server-signing-key-version", "compat-empty-string-null", diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index 9415e77c..e0afec6b 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -25,9 +25,10 @@ use ruma::{ }, StateEventType, TimelineEventType, }, + room_version_rules::RoomVersionRules, state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, - OwnedUserId, RoomId, RoomVersionId, UserId, + OwnedUserId, RoomId, UserId, }; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; @@ -662,6 +663,10 @@ async fn join_room_by_id_helper( )) } }; + let room_version_rules = room_version_id + .rules() + .expect("ruma should support all room versions we advertise"); + let mut join_event_stub: CanonicalJsonObject = serde_json::from_str( make_join_response.event.get(), ) @@ -716,7 +721,7 @@ async fn join_room_by_id_helper( services().globals.server_name().as_str(), services().globals.keypair(), &mut join_event_stub, - &room_version_id, + &room_version_rules.redaction, ) .expect("event is valid, we just created it"); @@ -725,7 +730,7 @@ async fn join_room_by_id_helper( "${}", ruma::signatures::reference_hash( &join_event_stub, - &room_version_id + &room_version_rules ) .expect("ruma can calculate reference hashes") ); @@ -761,7 +766,7 @@ async fn join_room_by_id_helper( }; let Ok((signed_event_id, signed_value)) = - gen_event_id_canonical_json(&signed_raw, &room_version_id) + gen_event_id_canonical_json(&signed_raw, &room_version_rules) else { // Event could not be converted to canonical json return Err(Error::BadRequest( @@ -814,6 +819,9 @@ async fn join_room_by_id_helper( )) } }; + let room_version_rules = room_version_id + .rules() + .expect("ruma should support all room versions we advertise"); let mut join_event_stub: CanonicalJsonObject = serde_json::from_str( make_join_response.event.get(), @@ -871,7 +879,7 @@ async fn join_room_by_id_helper( services().globals.server_name().as_str(), services().globals.keypair(), &mut join_event_stub, - &room_version_id, + &room_version_rules.redaction, ) .expect("event is valid, we just created it"); @@ -880,7 +888,7 @@ async fn join_room_by_id_helper( "${}", ruma::signatures::reference_hash( &join_event_stub, - &room_version_id + &room_version_rules ) .expect("ruma can calculate reference hashes") ); @@ -920,7 +928,7 @@ async fn join_room_by_id_helper( restricted joins. Adding signature to our event" ); let Ok((signed_event_id, signed_value)) = - gen_event_id_canonical_json(signed_raw, &room_version_id) + gen_event_id_canonical_json(signed_raw, &room_version_rules) else { // Event could not be converted to canonical json return Err(Error::BadRequest( @@ -985,14 +993,14 @@ async fn join_room_by_id_helper( .event_handler .fetch_join_signing_keys( &send_join_response, - &room_version_id, + &room_version_rules, &pub_key_map, ) .await?; info!("Going through send_join response room_state"); for result in send_join_response.room_state.state.iter().map(|pdu| { - validate_and_add_event_id(pdu, &room_version_id, &pub_key_map) + validate_and_add_event_id(pdu, &room_version_rules, &pub_key_map) }) { let Ok((event_id, value)) = result.await else { continue; @@ -1025,7 +1033,11 @@ async fn join_room_by_id_helper( info!("Going through send_join response auth_chain"); for result in send_join_response.room_state.auth_chain.iter().map(|pdu| { - validate_and_add_event_id(pdu, &room_version_id, &pub_key_map) + validate_and_add_event_id( + pdu, + &room_version_rules, + &pub_key_map, + ) }) { let Ok((event_id, value)) = result.await else { @@ -1037,9 +1049,7 @@ async fn join_room_by_id_helper( info!("Running send_join auth check"); state_res::event_auth::auth_check( - &state_res::RoomVersion::new(&room_version_id).map_err(|_| { - Error::UnsupportedRoomVersion(room_version_id.clone()) - })?, + &room_version_rules.authorization, &parsed_join_pdu, |k, s| { services() @@ -1171,7 +1181,7 @@ async fn make_join_request( async fn validate_and_add_event_id( pdu: &RawJsonValue, - room_version: &RoomVersionId, + room_version_rules: &RoomVersionRules, pub_key_map: &RwLock>, ) -> Result<(OwnedEventId, CanonicalJsonObject)> { let mut value: CanonicalJsonObject = serde_json::from_str(pdu.get()) @@ -1181,7 +1191,7 @@ async fn validate_and_add_event_id( })?; let event_id = EventId::parse(format!( "${}", - ruma::signatures::reference_hash(&value, room_version) + ruma::signatures::reference_hash(&value, room_version_rules) .expect("ruma can calculate reference hashes") )) .expect("ruma's reference hashes are valid event ids"); @@ -1246,7 +1256,7 @@ async fn validate_and_add_event_id( .filter_keys_server_map(unfiltered_keys, origin_server_ts); if let Err(error) = - ruma::signatures::verify_event(&keys, &value, room_version) + ruma::signatures::verify_event(&keys, &value, room_version_rules) { warn!( %event_id, @@ -1319,6 +1329,9 @@ pub(crate) async fn invite_helper( .rooms .state .get_create_content::(room_id)?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id)); + }; let response = services() .sending @@ -1341,7 +1354,7 @@ pub(crate) async fn invite_helper( // We do not add the event_id field to the pdu here because of signature // and hashes checks let Ok((event_id, value)) = - gen_event_id_canonical_json(&response.event, &room_version_id) + gen_event_id_canonical_json(&response.event, &room_version_rules) else { // Event could not be converted to canonical json return Err(Error::BadRequest( @@ -1631,6 +1644,9 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { )) } }; + let room_version_rules = room_version_id + .rules() + .expect("ruma should support all room versions we advertise"); let mut leave_event_stub = serde_json::from_str::( make_leave_response.event.get(), @@ -1666,15 +1682,18 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> { services().globals.server_name().as_str(), services().globals.keypair(), &mut leave_event_stub, - &room_version_id, + &room_version_rules.redaction, ) .expect("event is valid, we just created it"); // Generate event id let event_id = EventId::parse(format!( "${}", - ruma::signatures::reference_hash(&leave_event_stub, &room_version_id) - .expect("ruma can calculate reference hashes") + ruma::signatures::reference_hash( + &leave_event_stub, + &room_version_rules + ) + .expect("ruma can calculate reference hashes") )) .expect("ruma's reference hashes are valid event ids"); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 0f20a858..39b208f0 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -718,9 +718,12 @@ pub(crate) fn parse_incoming_pdu( .rooms .state .get_create_content::(&room_id)?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id)); + }; let Ok((event_id, value)) = - gen_event_id_canonical_json(pdu, &room_version_id) + gen_event_id_canonical_json(pdu, &room_version_rules) else { // Event could not be converted to canonical json return Err(Error::BadRequest( @@ -1634,8 +1637,12 @@ async fn create_join_event( // hashes checks let room_version_id = services().rooms.state.get_create_content::(room_id)?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id)); + }; + let Ok((event_id, value)) = - gen_event_id_canonical_json(pdu, &room_version_id) + gen_event_id_canonical_json(pdu, &room_version_rules) else { // Event could not be converted to canonical json return Err(Error::BadRequest( @@ -1787,6 +1794,10 @@ pub(crate) async fn create_invite_route( "Server does not support this room version.", )); } + let room_version_rules = body + .room_version + .rules() + .expect("ruma should support all room versions we advertise"); let mut signed_event = utils::to_canonical_object(&body.event).map_err(|_| { @@ -1800,7 +1811,7 @@ pub(crate) async fn create_invite_route( services().globals.server_name().as_str(), services().globals.keypair(), &mut signed_event, - &body.room_version, + &room_version_rules.redaction, ) .map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event.") @@ -1809,7 +1820,7 @@ pub(crate) async fn create_invite_route( // Generate event id let event_id = EventId::parse(format!( "${}", - ruma::signatures::reference_hash(&signed_event, &body.room_version) + ruma::signatures::reference_hash(&signed_event, &room_version_rules) .expect("ruma can calculate reference hashes") )) .expect("ruma's reference hashes are valid event ids"); diff --git a/src/service/admin.rs b/src/service/admin.rs index dcd971af..a63c3133 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -22,10 +22,11 @@ use ruma::{ }, TimelineEventType, }, + room_version_rules::RoomVersionRules, serde::Raw, signatures::verify_json, EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, - OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, + OwnedServerName, RoomId, ServerName, UserId, }; use serde_json::value::to_raw_value; use tokio::sync::{mpsc, Mutex, RwLock}; @@ -592,7 +593,7 @@ impl Service { Ok(value) => { match ruma::signatures::reference_hash( &value, - &RoomVersionId::V6, + &RoomVersionRules::V6, ) { Ok(hash) => { let event_id = diff --git a/src/service/pdu.rs b/src/service/pdu.rs index 35a6e43a..59e870d3 100644 --- a/src/service/pdu.rs +++ b/src/service/pdu.rs @@ -8,10 +8,11 @@ use ruma::{ AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, TimelineEventType, }, + room_version_rules::{RedactionRules, RoomVersionRules}, serde::Raw, state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, - RoomVersionId, UInt, UserId, + UInt, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::{ @@ -59,7 +60,7 @@ impl PduEvent { #[tracing::instrument(skip(self))] pub(crate) fn redact( &mut self, - room_version_id: &RoomVersionId, + rules: &RedactionRules, reason: &PduEvent, ) -> crate::Result<()> { self.unsigned = None; @@ -68,14 +69,10 @@ impl PduEvent { serde_json::from_str(self.content.get()).map_err(|_| { Error::bad_database("PDU in db has invalid content.") })?; - redact_content_in_place( - &mut content, - room_version_id, - self.kind.to_string(), - ) - .map_err(|e| { - Error::Redaction(self.sender.server_name().to_owned(), e) - })?; + redact_content_in_place(&mut content, rules, self.kind.to_string()) + .map_err(|e| { + Error::Redaction(self.sender.server_name().to_owned(), e) + })?; self.unsigned = Some(to_raw_value(&json!({ "redacted_because": serde_json::to_value(reason).expect("to_value(PduEvent) always works") @@ -489,7 +486,7 @@ impl Ord for PduEvent { /// CanonicalJsonValue>`. pub(crate) fn gen_event_id_canonical_json( pdu: &RawJsonValue, - room_version_id: &RoomVersionId, + rules: &RoomVersionRules, ) -> crate::Result<(OwnedEventId, CanonicalJsonObject)> { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|error| { @@ -500,7 +497,7 @@ pub(crate) fn gen_event_id_canonical_json( let event_id = format!( "${}", // Anything higher than version3 behaves the same - ruma::signatures::reference_hash(&value, room_version_id) + ruma::signatures::reference_hash(&value, rules) .expect("ruma can calculate reference hashes") ) .try_into() diff --git a/src/service/rooms/event_handler.rs b/src/service/rooms/event_handler.rs index 5ae78863..2c28c025 100644 --- a/src/service/rooms/event_handler.rs +++ b/src/service/rooms/event_handler.rs @@ -23,6 +23,7 @@ use ruma::{ TimelineEventType, }, int, + room_version_rules::RoomVersionRules, state_res::{self, StateMap}, uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedServerSigningKeyId, @@ -121,13 +122,16 @@ impl Service { .rooms .state .get_create_content::(room_id)?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id.clone())); + }; let (incoming_pdu, val) = self .handle_outlier_pdu( origin, event_id, room_id, - &room_version_id, + &room_version_rules, value, false, pub_key_map, @@ -151,7 +155,7 @@ impl Service { .fetch_unknown_prev_events( origin, room_id, - &room_version_id, + &room_version_rules, pub_key_map, incoming_pdu.prev_events.clone(), ) @@ -303,7 +307,7 @@ impl Service { origin: &'a ServerName, event_id: &'a EventId, room_id: &'a RoomId, - room_version_id: &'a RoomVersionId, + room_version_rules: &'a RoomVersionRules, mut value: CanonicalJsonObject, auth_events_known: bool, pub_key_map: &'a RwLock>, @@ -315,11 +319,6 @@ impl Service { // 2. Check signatures, otherwise drop // 3. check content hash, redact if doesn't match - let ruma_room_version = - state_res::RoomVersion::new(room_version_id).map_err(|_| { - Error::UnsupportedRoomVersion(room_version_id.clone()) - })?; - // TODO: For RoomVersion6 we must check that Raw<..> is canonical, // do we anywhere? // @@ -369,7 +368,7 @@ impl Service { let mut val = match ruma::signatures::verify_event( &filtered_keys, &value, - room_version_id, + room_version_rules, ) { Err(error) => { // Drop @@ -384,7 +383,7 @@ impl Service { warn!(%event_id, "Calculated hash does not match"); let Ok(obj) = ruma::canonical_json::redact( value, - room_version_id, + &room_version_rules.redaction, None, ) else { return Err(Error::BadRequest( @@ -445,7 +444,7 @@ impl Service { .map(|x| Arc::from(&**x)) .collect::>(), room_id, - room_version_id, + room_version_rules, pub_key_map, ) .await; @@ -502,7 +501,7 @@ impl Service { } state_res::event_auth::auth_check( - &ruma_room_version, + &room_version_rules.authorization, &incoming_pdu, |k, s| auth_events.get(&(k.to_string().into(), s.to_owned())), ) @@ -560,10 +559,9 @@ impl Service { "Upgrading event to timeline pdu", ); - let ruma_room_version = state_res::RoomVersion::new(room_version_id) - .map_err(|_| { - Error::UnsupportedRoomVersion(room_version_id.clone()) - })?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id.clone())); + }; // 10. Fetch missing state and auth chain events by calling /state_ids // at backwards extremities doing all the checks in this list @@ -710,7 +708,7 @@ impl Service { let lock = services().globals.stateres_mutex.lock(); let result = state_res::resolve( - room_version_id, + &room_version_rules.authorization, &fork_states, auth_chain_sets, |event_id| { @@ -779,7 +777,7 @@ impl Service { origin, &collect, room_id, - room_version_id, + &room_version_rules, pub_key_map, ) .await; @@ -865,7 +863,7 @@ impl Service { // 11. Check the auth of the event passes based on the state of the // event state_res::event_auth::auth_check( - &ruma_room_version, + &room_version_rules.authorization, &incoming_pdu, |k, s| { services() @@ -895,7 +893,7 @@ impl Service { // Soft fail check before doing state res let auth_events = services().rooms.state.get_auth_events( room_id, - &ruma_room_version, + &room_version_rules.authorization, &incoming_pdu.kind, &incoming_pdu.sender, incoming_pdu.state_key.as_deref(), @@ -903,7 +901,7 @@ impl Service { )?; let auth_fail_against_current = state_res::event_auth::auth_check( - &ruma_room_version, + &room_version_rules.authorization, &incoming_pdu, |k, s| auth_events.get(&(k.clone(), s.to_owned())), ) @@ -979,7 +977,7 @@ impl Service { } let new_room_state = self - .resolve_state(room_id, room_version_id, state_after) + .resolve_state(room_id, &room_version_rules, state_after) .await?; // Set the new room state to the resolved state @@ -1055,11 +1053,11 @@ impl Service { Ok(pdu_id) } - #[tracing::instrument(skip(self, room_version_id, incoming_state))] + #[tracing::instrument(skip(self, room_version_rules, incoming_state))] async fn resolve_state( &self, room_id: &RoomId, - room_version_id: &RoomVersionId, + room_version_rules: &RoomVersionRules, incoming_state: HashMap>, ) -> Result>> { debug!("Loading current room state ids"); @@ -1121,7 +1119,7 @@ impl Service { let lock = services().globals.stateres_mutex.lock(); let Ok(state) = state_res::resolve( - room_version_id, + &room_version_rules.authorization, &fork_states, auth_chain_sets, fetch_event, @@ -1169,7 +1167,7 @@ impl Service { origin: &'a ServerName, events: &'a [Arc], room_id: &'a RoomId, - room_version_id: &'a RoomVersionId, + room_version_rules: &'a RoomVersionRules, pub_key_map: &'a RwLock>, ) -> BoxFuture<'a, Vec<(Arc, Option)>> { Box::pin(async move { @@ -1271,7 +1269,7 @@ impl Service { let Ok((calculated_event_id, value)) = pdu::gen_event_id_canonical_json( &res.pdu, - room_version_id, + room_version_rules, ) else { back_off((*next_id).to_owned()).await; @@ -1344,7 +1342,7 @@ impl Service { origin, next_id, room_id, - room_version_id, + room_version_rules, value.clone(), true, pub_key_map, @@ -1377,7 +1375,7 @@ impl Service { &self, origin: &ServerName, room_id: &RoomId, - room_version_id: &RoomVersionId, + room_version_rules: &RoomVersionRules, pub_key_map: &RwLock>, initial_set: Vec>, ) -> Result<( @@ -1401,7 +1399,7 @@ impl Service { origin, &[prev_event_id.clone()], room_id, - room_version_id, + room_version_rules, pub_key_map, ) .await @@ -1534,7 +1532,7 @@ impl Service { OwnedServerName, BTreeMap, >, - room_version: &RoomVersionId, + room_version_rules: &RoomVersionRules, pub_key_map: &mut RwLockWriteGuard<'_, BTreeMap>, ) -> Result<()> { let value: CanonicalJsonObject = serde_json::from_str(pdu.get()) @@ -1545,7 +1543,7 @@ impl Service { let event_id = format!( "${}", - ruma::signatures::reference_hash(&value, room_version) + ruma::signatures::reference_hash(&value, room_version_rules) .expect("ruma can calculate reference hashes") ); let event_id = <&EventId>::try_from(event_id.as_str()) @@ -1649,7 +1647,7 @@ impl Service { pub(crate) async fn fetch_join_signing_keys( &self, event: &create_join_event::v2::Response, - room_version: &RoomVersionId, + room_version_rules: &RoomVersionRules, pub_key_map: &RwLock>, ) -> Result<()> { let mut servers: BTreeMap< @@ -1672,7 +1670,7 @@ impl Service { .get_server_keys_from_cache( pdu, &mut servers, - room_version, + room_version_rules, &mut pkm, ) .await diff --git a/src/service/rooms/state.rs b/src/service/rooms/state.rs index 72051f57..91a27049 100644 --- a/src/service/rooms/state.rs +++ b/src/service/rooms/state.rs @@ -11,6 +11,7 @@ use ruma::{ AnyStrippedStateEvent, StateEventType, TimelineEventType, }, room::RoomType, + room_version_rules::AuthorizationRules, serde::Raw, state_res::{self, StateMap}, EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId, @@ -430,11 +431,11 @@ impl Service { } /// This fetches auth events from the current state. - #[tracing::instrument(skip(self, room_version))] + #[tracing::instrument(skip(self, rules))] pub(crate) fn get_auth_events( &self, room_id: &RoomId, - room_version: &state_res::RoomVersion, + rules: &AuthorizationRules, kind: &TimelineEventType, sender: &UserId, state_key: Option<&str>, @@ -446,11 +447,7 @@ impl Service { }; let auth_events = state_res::auth_types_for_event( - kind, - sender, - state_key, - content, - room_version, + kind, sender, state_key, content, rules, ) .expect("content is a valid JSON object"); diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 9e934ad9..bffd52ca 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -770,14 +770,13 @@ impl Service { } })?; - let ruma_room_version = state_res::RoomVersion::new(&room_version_id) - .map_err(|_| { - Error::UnsupportedRoomVersion(room_version_id.clone()) - })?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id)); + }; let auth_events = services().rooms.state.get_auth_events( room_id, - &ruma_room_version, + &room_version_rules.authorization, &event_type, sender, state_key.as_deref(), @@ -845,9 +844,11 @@ impl Service { signatures: None, }; - state_res::auth_check(&ruma_room_version, &pdu, |k, s| { - auth_events.get(&(k.clone(), s.to_owned())) - }) + state_res::auth_check( + &room_version_rules.authorization, + &pdu, + |k, s| auth_events.get(&(k.clone(), s.to_owned())), + ) .map_err(|error| { error!(%error, "Auth check failed"); Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.") @@ -870,7 +871,7 @@ impl Service { services().globals.server_name().as_str(), services().globals.keypair(), &mut pdu_json, - &room_version_id, + &room_version_rules.redaction, ) { Ok(()) => {} Err(e) => { @@ -890,7 +891,7 @@ impl Service { // Generate event id pdu.event_id = EventId::parse_arc(format!( "${}", - ruma::signatures::reference_hash(&pdu_json, &room_version_id) + ruma::signatures::reference_hash(&pdu_json, &room_version_rules) .expect("ruma can calculate reference hashes") )) .expect("ruma's reference hashes are valid event ids"); @@ -1199,7 +1200,10 @@ impl Service { .rooms .state .get_create_content::(&pdu.room_id)?; - pdu.redact(&room_version_id, reason)?; + let Some(room_version_rules) = room_version_id.rules() else { + return Err(Error::UnsupportedRoomVersion(room_version_id)); + }; + pdu.redact(&room_version_rules.redaction, reason)?; self.replace_pdu( &pdu_id, diff --git a/src/utils/room_version.rs b/src/utils/room_version.rs index 389dc367..5e84da65 100644 --- a/src/utils/room_version.rs +++ b/src/utils/room_version.rs @@ -5,12 +5,12 @@ use crate::Error; /// Properties that we care about in grapevine that differ between supported /// room versions. /// -/// This is similar to [`ruma::state_res::RoomVersion`], except that it has -/// properties that are relevant to us instead of only properties relevant to -/// ruma state resolution. +/// This is similar to [`ruma::room_version_rules::RoomVersionRules`], except +/// that it only has the properties that are relevant to us instead of all +/// properties relevant to ruma state resolution. /// /// When branching for different room versions, prefer constructing a -/// `RoomVersion` and branching on it's properties over branching based on the +/// `RoomVersion` and branching on its properties over branching based on the /// [`RoomVersionId`] directly. If there is a relevant property that is not /// already included in `RoomVersion`, add it. In particular, comparisons like /// `room_version_id < RoomVersionId::V11` do not work, because room versions From 9532c853bddf22039cf05b3d9ced5804eb1fc920 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 2 Aug 2025 17:48:45 -0700 Subject: [PATCH 616/617] Bump ruma to daa3c8b014a601fa277e8653dea86e8897b17552 (optional UiaaInfo::params) We could just set None now, but the ruma changelog states that > Servers are encouraged to keep sending it for compatibility with > clients that required it. --- Cargo.lock | 38 ++++++++++++++++---------------- Cargo.toml | 2 +- src/api/client_server/account.rs | 8 +++---- src/api/client_server/device.rs | 4 ++-- src/api/client_server/keys.rs | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3fd8056..e744dc60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,9 +369,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "jobserver", "libc", @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "js_int", "ruma-common", @@ -2473,7 +2473,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "as_variant", "assign", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "as_variant", "base64", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2550,7 +2550,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "js_int", "ruma-common", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "base64", "ed25519-dalek", @@ -2624,7 +2624,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b#2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" dependencies = [ "js_int", "ruma-common", @@ -2867,9 +2867,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -3274,9 +3274,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -4296,9 +4296,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-jpeg" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 1c8b224a..78a5d948 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "2ea8b833e3a80c1d650964a1f3e83ee569cf5c0b" +rev = "daa3c8b014a601fa277e8653dea86e8897b17552" features = [ "compat-server-signing-key-version", "compat-empty-string-null", diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index b45af7df..8d729439 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -165,7 +165,7 @@ pub(crate) async fn register_route( stages: vec![AuthType::RegistrationToken], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; @@ -178,7 +178,7 @@ pub(crate) async fn register_route( stages: vec![AuthType::Dummy], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; @@ -333,7 +333,7 @@ pub(crate) async fn change_password_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; @@ -421,7 +421,7 @@ pub(crate) async fn deactivate_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index adecd073..28130c00 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -94,7 +94,7 @@ pub(crate) async fn delete_device_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; @@ -148,7 +148,7 @@ pub(crate) async fn delete_devices_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 7c22d56e..5437a7da 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -122,7 +122,7 @@ pub(crate) async fn upload_signing_keys_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Box::default(), + params: Some(Box::default()), session: None, auth_error: None, }; From c4abca1eb554e1a1edb9b6adb8f968db5f9d53c2 Mon Sep 17 00:00:00 2001 From: Olivia Lee Date: Sat, 2 Aug 2025 17:45:42 -0700 Subject: [PATCH 617/617] Bump ruma to c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337 (refactor Capabilities) --- Cargo.lock | 34 +++++++++++++-------------- Cargo.toml | 2 +- src/api/client_server/capabilities.rs | 3 ++- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e744dc60..6c7c97d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2441,8 +2441,8 @@ dependencies = [ [[package]] name = "ruma" -version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +version = "0.12.2" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "assign", "js_int", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.12.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "js_int", "ruma-common", @@ -2472,8 +2472,8 @@ dependencies = [ [[package]] name = "ruma-client-api" -version = "0.20.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +version = "0.20.2" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "as_variant", "assign", @@ -2495,8 +2495,8 @@ dependencies = [ [[package]] name = "ruma-common" -version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +version = "0.15.2" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "as_variant", "base64", @@ -2526,8 +2526,8 @@ dependencies = [ [[package]] name = "ruma-events" -version = "0.30.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +version = "0.30.2" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -2549,8 +2549,8 @@ dependencies = [ [[package]] name = "ruma-federation-api" -version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +version = "0.11.1" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "bytes", "headers", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.10.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "js_int", "thiserror 2.0.12", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.15.1" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "cfg-if", "proc-macro-crate", @@ -2596,7 +2596,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.11.0" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "js_int", "ruma-common", @@ -2607,8 +2607,8 @@ dependencies = [ [[package]] name = "ruma-signatures" -version = "0.17.0" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +version = "0.17.1" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "base64", "ed25519-dalek", @@ -2624,7 +2624,7 @@ dependencies = [ [[package]] name = "ruma-state-res" version = "0.13.0" -source = "git+https://github.com/ruma/ruma.git?rev=daa3c8b014a601fa277e8653dea86e8897b17552#daa3c8b014a601fa277e8653dea86e8897b17552" +source = "git+https://github.com/ruma/ruma.git?rev=c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337#c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" dependencies = [ "js_int", "ruma-common", diff --git a/Cargo.toml b/Cargo.toml index 78a5d948..3c722659 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,7 +145,7 @@ xdg = "2.5.2" [dependencies.ruma] git = "https://github.com/ruma/ruma.git" -rev = "daa3c8b014a601fa277e8653dea86e8897b17552" +rev = "c4f467781a7ef330dc0b7eb5d0d0cad77ebc3337" features = [ "compat-server-signing-key-version", "compat-empty-string-null", diff --git a/src/api/client_server/capabilities.rs b/src/api/client_server/capabilities.rs index 515ebfc1..f5542a10 100644 --- a/src/api/client_server/capabilities.rs +++ b/src/api/client_server/capabilities.rs @@ -1,7 +1,8 @@ use std::collections::BTreeMap; use ruma::api::client::discovery::get_capabilities::{ - self, Capabilities, RoomVersionStability, RoomVersionsCapability, + self, + v3::{Capabilities, RoomVersionStability, RoomVersionsCapability}, }; use crate::{services, Ar, Ra, Result};

Report received from: {0:?}\ -
  • Event Info
    • Event ID: {1:?}\ - 🔗
    • Room ID: {2:?}\ -
    • Sent By: {3:?}
  • \ - Report Info
    • Report Score: {4:?}
    • Report Reason: {5}
  • \ -