diff --git a/Cargo.lock b/Cargo.lock index 45a5edd9..225dc09a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,9 +214,9 @@ checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "bytemuck" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40636046a60a45ee5185e885a3ccb771f7a2065fb7cbcc2a7ecfd9896d1c365" +checksum = "db7a1029718df60331e557c9e83a55523c955e5dd2a7bfeffad6bbd50b538ae9" [[package]] name = "byteorder" @@ -275,6 +275,7 @@ dependencies = [ "serde_json", "sled", "thiserror", + "tokio", ] [[package]] @@ -813,9 +814,9 @@ dependencies = [ [[package]] name = "image" -version = "0.23.7" +version = "0.23.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2397fc43bd5648b7117aabb3c5e62d0e62c194826ec77b0b4d0c41e62744635" +checksum = "543904170510c1b5fb65140485d84de4a57fddb2ed685481e9020ce3d2c9f64c" dependencies = [ "bytemuck", "byteorder", @@ -881,9 +882,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.42" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2" +checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73" dependencies = [ "wasm-bindgen", ] @@ -915,9 +916,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.73" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" [[package]] name = "lock_api" @@ -1238,18 +1239,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" +checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" +checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" dependencies = [ "proc-macro2", "quote", @@ -1304,9 +1305,9 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro-hack" -version = "0.5.16" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" +checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" [[package]] name = "proc-macro-nested" @@ -1484,7 +1485,7 @@ dependencies = [ [[package]] name = "rocket" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket.git?rev=8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67#8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67" +source = "git+https://github.com/timokoesters/Rocket.git?branch=empty_parameters#f6d40ecd5d871d97837b3116eb670fb3c06d95b9" dependencies = [ "async-trait", "atomic", @@ -1509,7 +1510,7 @@ dependencies = [ [[package]] name = "rocket_codegen" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket.git?rev=8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67#8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67" +source = "git+https://github.com/timokoesters/Rocket.git?branch=empty_parameters#f6d40ecd5d871d97837b3116eb670fb3c06d95b9" dependencies = [ "devise", "glob", @@ -1521,7 +1522,7 @@ dependencies = [ [[package]] name = "rocket_http" version = "0.5.0-dev" -source = "git+https://github.com/SergioBenitez/Rocket.git?rev=8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67#8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67" +source = "git+https://github.com/timokoesters/Rocket.git?branch=empty_parameters#f6d40ecd5d871d97837b3116eb670fb3c06d95b9" dependencies = [ "cookie", "http", @@ -1543,7 +1544,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.1.0" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "ruma-api", "ruma-client-api", @@ -1558,7 +1559,7 @@ dependencies = [ [[package]] name = "ruma-api" version = "0.16.1" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "http", "percent-encoding", @@ -1573,7 +1574,7 @@ dependencies = [ [[package]] name = "ruma-api-macros" version = "0.16.1" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "proc-macro2", "quote", @@ -1583,7 +1584,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "http", "js_int", @@ -1600,7 +1601,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.2.0" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "js_int", "ruma-serde", @@ -1612,7 +1613,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.21.3" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "js_int", "ruma-common", @@ -1627,7 +1628,7 @@ dependencies = [ [[package]] name = "ruma-events-macros" version = "0.21.3" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "proc-macro2", "quote", @@ -1637,7 +1638,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.0.2" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "js_int", "ruma-api", @@ -1652,7 +1653,7 @@ dependencies = [ [[package]] name = "ruma-identifiers" version = "0.17.1" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "rand", "serde", @@ -1662,7 +1663,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-macros" version = "0.17.1" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "proc-macro2", "quote", @@ -1673,7 +1674,7 @@ dependencies = [ [[package]] name = "ruma-serde" version = "0.2.2" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "form_urlencoded", "itoa", @@ -1685,7 +1686,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.6.0-dev.1" -source = "git+https://github.com/ruma/ruma?rev=e047c647ddcb368e7eb1e05ae8823a9494273457#e047c647ddcb368e7eb1e05ae8823a9494273457" +source = "git+https://github.com/ruma/ruma?rev=d5d2d1d893fa12d27960e4c58d6c09b215d06e95#d5d2d1d893fa12d27960e4c58d6c09b215d06e95" dependencies = [ "base64 0.12.3", "ring", @@ -1837,9 +1838,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" dependencies = [ "itoa", "ryu", @@ -2028,9 +2029,9 @@ checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" [[package]] name = "syn" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" +checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250" dependencies = [ "proc-macro2", "quote", @@ -2338,9 +2339,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.65" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d" +checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c" dependencies = [ "cfg-if", "serde", @@ -2350,9 +2351,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.65" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d" +checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0" dependencies = [ "bumpalo", "lazy_static", @@ -2365,9 +2366,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.15" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ad6e4e8b2b7f8c90b6e09a9b590ea15cb0d1dbe28502b5a405cd95d1981671" +checksum = "95f8d235a77f880bcef268d379810ea6c0af2eacfa90b1ad5af731776e0c4699" dependencies = [ "cfg-if", "js-sys", @@ -2377,9 +2378,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.65" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e" +checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2387,9 +2388,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.65" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6" +checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556" dependencies = [ "proc-macro2", "quote", @@ -2400,15 +2401,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.65" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87" +checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092" [[package]] name = "web-sys" -version = "0.3.42" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2" +checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 02a90cdd..c2607a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,12 @@ edition = "2018" [dependencies] # TODO: This can become optional as soon as proper configs are supported -rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67", features = ["tls"], optional = false } # Used to handle requests -ruma = { git = "https://github.com/ruma/ruma", features = ["rand", "client-api", "federation-api", "unstable-pre-spec", "unstable-synapse-quirks"], rev = "e047c647ddcb368e7eb1e05ae8823a9494273457" } # Used for matrix spec type definitions and helpers +#rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "8d779caa22c63b15a6c3ceb75d8f6d4971b2eb67", features = ["tls"] } # Used to handle requests +rocket = { git = "https://github.com/timokoesters/Rocket.git", branch = "empty_parameters", features = ["tls"] } + +tokio = "0.2.22" # Used for long polling +ruma = { git = "https://github.com/ruma/ruma", features = ["rand", "client-api", "federation-api", "unstable-pre-spec", "unstable-synapse-quirks"], rev = "d5d2d1d893fa12d27960e4c58d6c09b215d06e95" } # Used for matrix spec type definitions and helpers +#ruma = { path = "../ruma/ruma", features = ["rand", "client-api", "federation-api", "unstable-pre-spec", "unstable-synapse-quirks"] } sled = "0.32.0" # Used for storing data permanently log = "0.4.8" # Used for emitting log entries http = "0.2.1" # Used for rocket<->ruma conversions diff --git a/src/client_server.rs b/src/client_server.rs index c5bba03d..4a2f33b3 100644 --- a/src/client_server.rs +++ b/src/client_server.rs @@ -1,5 +1,5 @@ use std::{ - collections::BTreeMap, + collections::{hash_map, BTreeMap, HashMap, HashSet}, convert::{TryFrom, TryInto}, time::{Duration, SystemTime}, }; @@ -11,14 +11,14 @@ use log::warn; #[cfg(not(feature = "conduit_bin"))] use super::State; #[cfg(feature = "conduit_bin")] -use rocket::{delete, get, options, post, put, State}; +use rocket::{delete, get, options, post, put, tokio, State}; use ruma::{ api::client::{ error::ErrorKind, r0::{ account::{ - change_password, deactivate, get_username_availability, register, + change_password, deactivate, get_username_availability, register, whoami, ThirdPartyIdRemovalStatus, }, alias::{create_alias, delete_alias, get_alias}, @@ -35,7 +35,7 @@ use ruma::{ set_room_visibility, }, filter::{self, create_filter, get_filter}, - keys::{self, claim_keys, get_keys, upload_keys}, + keys::{self, claim_keys, get_key_changes, get_keys, upload_keys}, media::{create_content, get_content, get_content_thumbnail, get_media_config}, membership::{ ban_user, forget_room, get_member_events, invite_user, join_room_by_id, @@ -304,6 +304,18 @@ pub fn login_route( .into()) } +#[cfg_attr( + feature = "conduit_bin", + get("/_matrix/client/r0/account/whoami", data = "") +)] +pub fn whoami_route(body: Ruma) -> ConduitResult { + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); + Ok(whoami::Response { + user_id: sender_id.clone(), + } + .into()) +} + #[cfg_attr( feature = "conduit_bin", post("/_matrix/client/r0/logout", data = "") @@ -312,10 +324,10 @@ pub fn logout_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); - db.users.remove_device(&user_id, device_id)?; + db.users.remove_device(&sender_id, device_id)?; Ok(logout::Response.into()) } @@ -328,11 +340,11 @@ pub fn logout_all_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - for device_id in db.users.all_device_ids(user_id) { + for device_id in db.users.all_device_ids(sender_id) { if let Ok(device_id) = device_id { - db.users.remove_device(&user_id, &device_id)?; + db.users.remove_device(&sender_id, &device_id)?; } } @@ -347,7 +359,7 @@ pub fn change_password_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); let mut uiaainfo = UiaaInfo { @@ -361,31 +373,36 @@ pub fn change_password_route( }; if let Some(auth) = &body.auth { - let (worked, uiaainfo) = - db.uiaa - .try_auth(&user_id, device_id, auth, &uiaainfo, &db.users, &db.globals)?; + let (worked, uiaainfo) = db.uiaa.try_auth( + &sender_id, + device_id, + auth, + &uiaainfo, + &db.users, + &db.globals, + )?; if !worked { return Err(Error::Uiaa(uiaainfo)); } // Success! } else { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - db.uiaa.create(&user_id, &device_id, &uiaainfo)?; + db.uiaa.create(&sender_id, &device_id, &uiaainfo)?; return Err(Error::Uiaa(uiaainfo)); } - db.users.set_password(&user_id, &body.new_password)?; + db.users.set_password(&sender_id, &body.new_password)?; // TODO: Read logout_devices field when it's available and respect that, currently not supported in Ruma // See: https://github.com/ruma/ruma/issues/107 // Logout all devices except the current one for id in db .users - .all_device_ids(&user_id) + .all_device_ids(&sender_id) .filter_map(|id| id.ok()) .filter(|id| id != device_id) { - db.users.remove_device(&user_id, &id)?; + db.users.remove_device(&sender_id, &id)?; } Ok(change_password::Response.into()) @@ -399,7 +416,7 @@ pub fn deactivate_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); let mut uiaainfo = UiaaInfo { @@ -414,7 +431,7 @@ pub fn deactivate_route( if let Some(auth) = &body.auth { let (worked, uiaainfo) = db.uiaa.try_auth( - &user_id, + &sender_id, &device_id, auth, &uiaainfo, @@ -427,15 +444,15 @@ pub fn deactivate_route( // Success! } else { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - db.uiaa.create(&user_id, &device_id, &uiaainfo)?; + db.uiaa.create(&sender_id, &device_id, &uiaainfo)?; return Err(Error::Uiaa(uiaainfo)); } // Leave all joined rooms and reject all invitations for room_id in db .rooms - .rooms_joined(&user_id) - .chain(db.rooms.rooms_invited(&user_id)) + .rooms_joined(&sender_id) + .chain(db.rooms.rooms_invited(&sender_id)) { let room_id = room_id?; let event = member::MemberEventContent { @@ -448,18 +465,18 @@ pub fn deactivate_route( db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(event).expect("event is valid, we just created it"), None, - Some(user_id.to_string()), + Some(sender_id.to_string()), None, &db.globals, )?; } // Remove devices and mark account as deactivated - db.users.deactivate_account(&user_id)?; + db.users.deactivate_account(&sender_id)?; Ok(deactivate::Response { id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport, @@ -500,11 +517,11 @@ pub fn get_pushrules_all_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let event = db .account_data - .get::(None, &user_id, EventType::PushRules)? + .get::(None, &sender_id, EventType::PushRules)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "PushRules event not found.", @@ -517,15 +534,11 @@ pub fn get_pushrules_all_route( } #[cfg_attr(feature = "conduit_bin", put( - "/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>", + "/_matrix/client/r0/pushrules/<_>/<_>/<_>", //data = "" ))] -pub fn set_pushrule_route( - //db: State<'_, Database>, +pub fn set_pushrule_route(//db: State<'_, Database>, //body: Ruma, - _scope: String, - _kind: String, - _rule_id: String, ) -> ConduitResult { // TODO warn!("TODO: set_pushrule_route"); @@ -534,26 +547,16 @@ pub fn set_pushrule_route( #[cfg_attr( feature = "conduit_bin", - put("/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>/enabled") + put("/_matrix/client/r0/pushrules/<_>/<_>/<_>/enabled") )] -pub fn set_pushrule_enabled_route( - _scope: String, - _kind: String, - _rule_id: String, -) -> ConduitResult { +pub fn set_pushrule_enabled_route() -> ConduitResult { // TODO warn!("TODO: set_pushrule_enabled_route"); Ok(set_pushrule_enabled::Response.into()) } -#[cfg_attr( - feature = "conduit_bin", - get("/_matrix/client/r0/user/<_user_id>/filter/<_filter_id>") -)] -pub fn get_filter_route( - _user_id: String, - _filter_id: String, -) -> ConduitResult { +#[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/user/<_>/filter/<_>"))] +pub fn get_filter_route() -> ConduitResult { // TODO Ok(get_filter::Response { filter: filter::FilterDefinition { @@ -567,11 +570,8 @@ pub fn get_filter_route( .into()) } -#[cfg_attr( - feature = "conduit_bin", - post("/_matrix/client/r0/user/<_user_id>/filter") -)] -pub fn create_filter_route(_user_id: String) -> ConduitResult { +#[cfg_attr(feature = "conduit_bin", post("/_matrix/client/r0/user/<_>/filter"))] +pub fn create_filter_route() -> ConduitResult { // TODO Ok(create_filter::Response { filter_id: utils::random_string(10), @@ -581,18 +581,13 @@ pub fn create_filter_route(_user_id: String) -> ConduitResult/account_data/<_type>", - data = "" - ) + put("/_matrix/client/r0/user/<_>/account_data/<_>", data = "") )] pub fn set_global_account_data_route( db: State<'_, Database>, body: Ruma, - _user_id: String, - _type: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let content = serde_json::from_str::(body.data.get()) .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?; @@ -601,7 +596,7 @@ pub fn set_global_account_data_route( db.account_data.update( None, - user_id, + sender_id, event_type.clone().into(), &BasicEvent { content: CustomEventContent { @@ -617,60 +612,51 @@ pub fn set_global_account_data_route( #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/r0/user/<_user_id>/account_data/<_type>", - data = "" - ) + get("/_matrix/client/r0/user/<_>/account_data/<_>", data = "") )] pub fn get_global_account_data_route( db: State<'_, Database>, body: Ruma, - _user_id: String, - _type: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let data = db .account_data - .get::( + .get::>( None, - user_id, + sender_id, EventType::try_from(&body.event_type).expect("EventType::try_from can never fail"), )? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; - Ok(get_global_account_data::Response { - account_data: Raw::from(data), - } - .into()) + Ok(get_global_account_data::Response { account_data: data }.into()) } #[cfg_attr( feature = "conduit_bin", - put("/_matrix/client/r0/profile/<_user_id>/displayname", data = "") + put("/_matrix/client/r0/profile/<_>/displayname", data = "") )] pub fn set_displayname_route( db: State<'_, Database>, body: Ruma, - _user_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); db.users - .set_displayname(&user_id, body.displayname.clone())?; + .set_displayname(&sender_id, body.displayname.clone())?; - // Send a new membership event into all joined rooms - for room_id in db.rooms.rooms_joined(&user_id) { + // Send a new membership event and presence update into all joined rooms + for room_id in db.rooms.rooms_joined(&sender_id) { let room_id = room_id?; db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(ruma::events::room::member::MemberEventContent { displayname: body.displayname.clone(), ..serde_json::from_value::>( db.rooms - .room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())? + .room_state_get(&room_id, &EventType::RoomMember, &sender_id.to_string())? .ok_or_else(|| { Error::bad_database( "Tried to send displayname update for user not in the room.", @@ -685,61 +671,60 @@ pub fn set_displayname_route( }) .expect("event is valid, we just created it"), None, - Some(user_id.to_string()), + Some(sender_id.to_string()), None, &db.globals, )?; - } - // Presence update - db.global_edus.update_presence( - ruma::events::presence::PresenceEvent { - content: ruma::events::presence::PresenceEventContent { - avatar_url: db.users.avatar_url(&user_id)?, - currently_active: None, - displayname: db.users.displayname(&user_id)?, - last_active_ago: Some( - utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"), - ), - presence: ruma::presence::PresenceState::Online, - status_msg: None, + // Presence update + db.rooms.edus.update_presence( + &sender_id, + &room_id, + ruma::events::presence::PresenceEvent { + content: ruma::events::presence::PresenceEventContent { + avatar_url: db.users.avatar_url(&sender_id)?, + currently_active: None, + displayname: db.users.displayname(&sender_id)?, + 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_id.clone(), }, - sender: user_id.clone(), - }, - &db.globals, - )?; + &db.globals, + )?; + } Ok(set_display_name::Response.into()) } #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/profile/<_user_id>/displayname", data = "") + get("/_matrix/client/r0/profile/<_>/displayname", data = "") )] pub fn get_displayname_route( db: State<'_, Database>, body: Ruma, - _user_id: String, ) -> ConduitResult { - let user_id = body.body.user_id.clone(); Ok(get_display_name::Response { - displayname: db.users.displayname(&user_id)?, + displayname: db.users.displayname(&body.user_id)?, } .into()) } #[cfg_attr( feature = "conduit_bin", - put("/_matrix/client/r0/profile/<_user_id>/avatar_url", data = "") + put("/_matrix/client/r0/profile/<_>/avatar_url", data = "") )] pub fn set_avatar_url_route( db: State<'_, Database>, body: Ruma, - _user_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); if let Some(avatar_url) = &body.avatar_url { if !avatar_url.starts_with("mxc://") { @@ -753,20 +738,21 @@ pub fn set_avatar_url_route( // TODO also make sure this is valid mxc:// format (not only starting with it) } - db.users.set_avatar_url(&user_id, body.avatar_url.clone())?; + db.users + .set_avatar_url(&sender_id, body.avatar_url.clone())?; - // Send a new membership event into all joined rooms - for room_id in db.rooms.rooms_joined(&user_id) { + // Send a new membership event and presence update into all joined rooms + for room_id in db.rooms.rooms_joined(&sender_id) { let room_id = room_id?; db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(ruma::events::room::member::MemberEventContent { avatar_url: body.avatar_url.clone(), ..serde_json::from_value::>( db.rooms - .room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())? + .room_state_get(&room_id, &EventType::RoomMember, &sender_id.to_string())? .ok_or_else(|| { Error::bad_database( "Tried to send avatar url update for user not in the room.", @@ -781,63 +767,61 @@ pub fn set_avatar_url_route( }) .expect("event is valid, we just created it"), None, - Some(user_id.to_string()), + Some(sender_id.to_string()), None, &db.globals, )?; - } - // Presence update - db.global_edus.update_presence( - ruma::events::presence::PresenceEvent { - content: ruma::events::presence::PresenceEventContent { - avatar_url: db.users.avatar_url(&user_id)?, - currently_active: None, - displayname: db.users.displayname(&user_id)?, - last_active_ago: Some( - utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"), - ), - presence: ruma::presence::PresenceState::Online, - status_msg: None, + // Presence update + db.rooms.edus.update_presence( + &sender_id, + &room_id, + ruma::events::presence::PresenceEvent { + content: ruma::events::presence::PresenceEventContent { + avatar_url: db.users.avatar_url(&sender_id)?, + currently_active: None, + displayname: db.users.displayname(&sender_id)?, + 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_id.clone(), }, - sender: user_id.clone(), - }, - &db.globals, - )?; + &db.globals, + )?; + } Ok(set_avatar_url::Response.into()) } #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/profile/<_user_id>/avatar_url", data = "") + get("/_matrix/client/r0/profile/<_>/avatar_url", data = "") )] pub fn get_avatar_url_route( db: State<'_, Database>, body: Ruma, - _user_id: String, ) -> ConduitResult { - let user_id = body.body.user_id.clone(); Ok(get_avatar_url::Response { - avatar_url: db.users.avatar_url(&user_id)?, + avatar_url: db.users.avatar_url(&body.user_id)?, } .into()) } #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/profile/<_user_id>", data = "") + get("/_matrix/client/r0/profile/<_>", data = "") )] pub fn get_profile_route( db: State<'_, Database>, body: Ruma, - _user_id: String, ) -> ConduitResult { - let user_id = body.body.user_id.clone(); - let avatar_url = db.users.avatar_url(&user_id)?; - let displayname = db.users.displayname(&user_id)?; + let avatar_url = db.users.avatar_url(&body.user_id)?; + let displayname = db.users.displayname(&body.user_id)?; if avatar_url.is_none() && displayname.is_none() { // Return 404 if we don't have a profile for this id @@ -856,33 +840,38 @@ pub fn get_profile_route( #[cfg_attr( feature = "conduit_bin", - put("/_matrix/client/r0/presence/<_user_id>/status", data = "") + put("/_matrix/client/r0/presence/<_>/status", data = "") )] pub fn set_presence_route( db: State<'_, Database>, body: Ruma, - _user_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - db.global_edus.update_presence( - ruma::events::presence::PresenceEvent { - content: ruma::events::presence::PresenceEventContent { - avatar_url: db.users.avatar_url(&user_id)?, - currently_active: None, - displayname: db.users.displayname(&user_id)?, - last_active_ago: Some( - utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"), - ), - presence: body.presence, - status_msg: body.status_msg.clone(), + for room_id in db.rooms.rooms_joined(&sender_id) { + let room_id = room_id?; + + db.rooms.edus.update_presence( + &sender_id, + &room_id, + ruma::events::presence::PresenceEvent { + content: ruma::events::presence::PresenceEventContent { + avatar_url: db.users.avatar_url(&sender_id)?, + currently_active: None, + displayname: db.users.displayname(&sender_id)?, + last_active_ago: Some( + utils::millis_since_unix_epoch() + .try_into() + .expect("time is valid"), + ), + presence: body.presence, + status_msg: body.status_msg.clone(), + }, + sender: sender_id.clone(), }, - sender: user_id.clone(), - }, - &db.globals, - )?; + &db.globals, + )?; + } Ok(set_presence::Response.into()) } @@ -895,26 +884,26 @@ pub fn upload_keys_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); if let Some(one_time_keys) = &body.one_time_keys { for (key_key, key_value) in one_time_keys { db.users - .add_one_time_key(user_id, device_id, key_key, key_value)?; + .add_one_time_key(sender_id, device_id, key_key, key_value, &db.globals)?; } } if let Some(device_keys) = &body.device_keys { // This check is needed to assure that signatures are kept - if db.users.get_device_keys(user_id, device_id)?.is_none() { + if db.users.get_device_keys(sender_id, device_id)?.is_none() { db.users - .add_device_keys(user_id, device_id, device_keys, &db.globals)?; + .add_device_keys(sender_id, device_id, device_keys, &db.rooms, &db.globals)?; } } Ok(upload_keys::Response { - one_time_key_counts: db.users.count_one_time_keys(user_id, device_id)?, + one_time_key_counts: db.users.count_one_time_keys(sender_id, device_id)?, } .into()) } @@ -927,7 +916,7 @@ pub fn get_keys_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let sender_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut master_keys = BTreeMap::new(); let mut self_signing_keys = BTreeMap::new(); @@ -1013,7 +1002,7 @@ pub fn claim_keys_route( for (device_id, key_algorithm) in map { if let Some(one_time_keys) = db.users - .take_one_time_key(user_id, device_id, key_algorithm)? + .take_one_time_key(user_id, device_id, key_algorithm, &db.globals)? { let mut c = BTreeMap::new(); c.insert(one_time_keys.0, one_time_keys.1); @@ -1038,29 +1027,25 @@ pub fn create_backup_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let version = db .key_backups - .create_backup(&user_id, &body.algorithm, &db.globals)?; + .create_backup(&sender_id, &body.algorithm, &db.globals)?; Ok(create_backup::Response { version }.into()) } #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/unstable/room_keys/version/<_version>", - data = "" - ) + put("/_matrix/client/unstable/room_keys/version/<_>", data = "") )] pub fn update_backup_route( db: State<'_, Database>, body: Ruma, - _version: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); db.key_backups - .update_backup(&user_id, &body.version, &body.algorithm, &db.globals)?; + .update_backup(&sender_id, &body.version, &body.algorithm, &db.globals)?; Ok(update_backup::Response.into()) } @@ -1073,11 +1058,11 @@ pub fn get_latest_backup_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let (version, algorithm) = db.key_backups - .get_latest_backup(&user_id)? + .get_latest_backup(&sender_id)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "Key backup does not exist.", @@ -1085,8 +1070,8 @@ pub fn get_latest_backup_route( Ok(get_latest_backup::Response { algorithm, - count: (db.key_backups.count_keys(user_id, &version)? as u32).into(), - etag: db.key_backups.get_etag(user_id, &version)?, + count: (db.key_backups.count_keys(sender_id, &version)? as u32).into(), + etag: db.key_backups.get_etag(sender_id, &version)?, version, } .into()) @@ -1094,29 +1079,25 @@ pub fn get_latest_backup_route( #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/unstable/room_keys/version/<_version>", - data = "" - ) + get("/_matrix/client/unstable/room_keys/version/<_>", data = "") )] pub fn get_backup_route( db: State<'_, Database>, body: Ruma, - _version: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); - let algorithm = - db.key_backups - .get_backup(&user_id, &body.version)? - .ok_or(Error::BadRequest( - ErrorKind::NotFound, - "Key backup does not exist.", - ))?; + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); + let algorithm = db + .key_backups + .get_backup(&sender_id, &body.version)? + .ok_or(Error::BadRequest( + ErrorKind::NotFound, + "Key backup does not exist.", + ))?; Ok(get_backup::Response { algorithm, - count: (db.key_backups.count_keys(user_id, &body.version)? as u32).into(), - etag: db.key_backups.get_etag(user_id, &body.version)?, + count: (db.key_backups.count_keys(sender_id, &body.version)? as u32).into(), + etag: db.key_backups.get_etag(sender_id, &body.version)?, version: body.version.clone(), } .into()) @@ -1131,12 +1112,12 @@ pub fn add_backup_keys_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); for (room_id, room) in &body.rooms { for (session_id, key_data) in &room.sessions { db.key_backups.add_key( - &user_id, + &sender_id, &body.version, &room_id, &session_id, @@ -1147,8 +1128,8 @@ pub fn add_backup_keys_route( } Ok(add_backup_keys::Response { - count: (db.key_backups.count_keys(user_id, &body.version)? as u32).into(), - etag: db.key_backups.get_etag(user_id, &body.version)?, + count: (db.key_backups.count_keys(sender_id, &body.version)? as u32).into(), + etag: db.key_backups.get_etag(sender_id, &body.version)?, } .into()) } @@ -1161,23 +1142,22 @@ pub fn get_backup_keys_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - let rooms = db.key_backups.get_all(&user_id, &body.version)?; + let rooms = db.key_backups.get_all(&sender_id, &body.version)?; Ok(get_backup_keys::Response { rooms }.into()) } #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/read_markers", data = "") + post("/_matrix/client/r0/rooms/<_>/read_markers", data = "") )] pub fn set_read_marker_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let fully_read_event = ruma::events::fully_read::FullyReadEvent { content: ruma::events::fully_read::FullyReadEventContent { @@ -1187,7 +1167,7 @@ pub fn set_read_marker_route( }; db.account_data.update( Some(&body.room_id), - &user_id, + &sender_id, EventType::FullyRead, &fully_read_event, &db.globals, @@ -1196,7 +1176,7 @@ pub fn set_read_marker_route( if let Some(event) = &body.read_receipt { db.rooms.edus.room_read_set( &body.room_id, - &user_id, + &sender_id, db.rooms.get_pdu_count(event)?.ok_or(Error::BadRequest( ErrorKind::InvalidParam, "Event does not exist.", @@ -1205,7 +1185,7 @@ pub fn set_read_marker_route( let mut user_receipts = BTreeMap::new(); user_receipts.insert( - user_id.clone(), + sender_id.clone(), ruma::events::receipt::Receipt { ts: Some(SystemTime::now()), }, @@ -1219,7 +1199,7 @@ pub fn set_read_marker_route( ); db.rooms.edus.roomlatest_update( - &user_id, + &sender_id, &body.room_id, AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt( ruma::events::receipt::ReceiptEvent { @@ -1235,22 +1215,17 @@ pub fn set_read_marker_route( #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/r0/rooms/<_room_id>/typing/<_user_id>", - data = "" - ) + put("/_matrix/client/r0/rooms/<_>/typing/<_>", data = "") )] pub fn create_typing_event_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _user_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); if body.typing { db.rooms.edus.roomactive_add( - &user_id, + &sender_id, &body.room_id, body.timeout.map(|d| d.as_millis() as u64).unwrap_or(30000) + utils::millis_since_unix_epoch(), @@ -1259,7 +1234,7 @@ pub fn create_typing_event_route( } else { db.rooms .edus - .roomactive_remove(&user_id, &body.room_id, &db.globals)?; + .roomactive_remove(&sender_id, &body.room_id, &db.globals)?; } Ok(create_typing_event::Response.into()) @@ -1273,7 +1248,7 @@ pub fn create_room_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let room_id = RoomId::new(db.globals.server_name()); @@ -1296,7 +1271,7 @@ pub fn create_room_route( } })?; - let mut content = ruma::events::room::create::CreateEventContent::new(user_id.clone()); + let mut content = ruma::events::room::create::CreateEventContent::new(sender_id.clone()); content.federate = body.creation_content.as_ref().map_or(true, |c| c.federate); content.predecessor = body .creation_content @@ -1307,7 +1282,7 @@ pub fn create_room_route( // 1. The room create event db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomCreate, serde_json::to_value(content).expect("event is valid, we just created it"), None, @@ -1319,18 +1294,18 @@ pub fn create_room_route( // 2. Let the room creator join db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(member::MemberEventContent { membership: member::MembershipState::Join, - displayname: db.users.displayname(&user_id)?, - avatar_url: db.users.avatar_url(&user_id)?, + displayname: db.users.displayname(&sender_id)?, + avatar_url: db.users.avatar_url(&sender_id)?, is_direct: body.is_direct, third_party_invite: None, }) .expect("event is valid, we just created it"), None, - Some(user_id.to_string()), + Some(sender_id.to_string()), None, &db.globals, )?; @@ -1344,9 +1319,9 @@ pub fn create_room_route( // 3. Power levels let mut users = BTreeMap::new(); - users.insert(user_id.clone(), 100.into()); - for invite_user_id in &body.invite { - users.insert(invite_user_id.clone(), 100.into()); + users.insert(sender_id.clone(), 100.into()); + for invite_ in &body.invite { + users.insert(invite_.clone(), 100.into()); } let power_levels_content = if let Some(power_levels) = &body.power_level_content_override { @@ -1372,7 +1347,7 @@ pub fn create_room_route( }; db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomPowerLevels, power_levels_content, None, @@ -1385,7 +1360,7 @@ pub fn create_room_route( // 4.1 Join Rules db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomJoinRules, match preset { create_room::RoomPreset::PublicChat => serde_json::to_value( @@ -1407,7 +1382,7 @@ pub fn create_room_route( // 4.2 History Visibility db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomHistoryVisibility, serde_json::to_value(history_visibility::HistoryVisibilityEventContent::new( history_visibility::HistoryVisibility::Shared, @@ -1422,7 +1397,7 @@ pub fn create_room_route( // 4.3 Guest Access db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomGuestAccess, match preset { create_room::RoomPreset::PublicChat => serde_json::to_value( @@ -1454,7 +1429,7 @@ pub fn create_room_route( db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), event_type.clone(), serde_json::from_str(content.get()).map_err(|_| { Error::BadRequest(ErrorKind::BadJson, "Invalid initial_state content.") @@ -1470,7 +1445,7 @@ pub fn create_room_route( if let Some(name) = &body.name { db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomName, serde_json::to_value( name::NameEventContent::new(name.clone()) @@ -1487,7 +1462,7 @@ pub fn create_room_route( if let Some(topic) = &body.topic { db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomTopic, serde_json::to_value(topic::TopicEventContent { topic: topic.clone(), @@ -1504,7 +1479,7 @@ pub fn create_room_route( for user in &body.invite { db.rooms.append_pdu( room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(member::MemberEventContent { membership: member::MembershipState::Invite, @@ -1541,12 +1516,12 @@ pub fn joined_rooms_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); Ok(joined_rooms::Response { joined_rooms: db .rooms - .rooms_joined(&user_id) + .rooms_joined(&sender_id) .filter_map(|r| r.ok()) .collect(), } @@ -1555,23 +1530,17 @@ pub fn joined_rooms_route( #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/r0/rooms/<_room_id>/redact/<_event_id>/<_txn_id>", - data = "" - ) + put("/_matrix/client/r0/rooms/<_>/redact/<_>/<_>", data = "") )] pub fn redact_event_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_id: String, - _txn_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let event_id = db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomRedaction, serde_json::to_value(redaction::RedactionEventContent { reason: body.reason.clone(), @@ -1588,12 +1557,11 @@ pub fn redact_event_route( #[cfg_attr( feature = "conduit_bin", - put("/_matrix/client/r0/directory/room/<_room_alias>", data = "") + put("/_matrix/client/r0/directory/room/<_>", data = "") )] pub fn create_alias_route( db: State<'_, Database>, body: Ruma, - _room_alias: String, ) -> ConduitResult { if db.rooms.id_from_alias(&body.room_alias)?.is_some() { return Err(Error::Conflict("Alias already exists.")); @@ -1607,12 +1575,11 @@ pub fn create_alias_route( #[cfg_attr( feature = "conduit_bin", - delete("/_matrix/client/r0/directory/room/<_room_alias>", data = "") + delete("/_matrix/client/r0/directory/room/<_>", data = "") )] pub fn delete_alias_route( db: State<'_, Database>, body: Ruma, - _room_alias: String, ) -> ConduitResult { db.rooms.set_alias(&body.room_alias, None, &db.globals)?; @@ -1621,12 +1588,11 @@ pub fn delete_alias_route( #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/directory/room/<_room_alias>", data = "") + get("/_matrix/client/r0/directory/room/<_>", data = "") )] pub fn get_alias_route( db: State<'_, Database>, body: Ruma, - _room_alias: String, ) -> ConduitResult { if body.room_alias.server_name() != db.globals.server_name() { todo!("ask remote server"); @@ -1649,32 +1615,31 @@ pub fn get_alias_route( #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/join", data = "") + post("/_matrix/client/r0/rooms/<_>/join", data = "") )] pub fn join_room_by_id_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); // TODO: Ask a remote server if we don't have this room let event = member::MemberEventContent { membership: member::MembershipState::Join, - displayname: db.users.displayname(&user_id)?, - avatar_url: db.users.avatar_url(&user_id)?, + displayname: db.users.displayname(&sender_id)?, + avatar_url: db.users.avatar_url(&sender_id)?, is_direct: None, third_party_invite: None, }; db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(event).expect("event is valid, we just created it"), None, - Some(user_id.to_string()), + Some(sender_id.to_string()), None, &db.globals, )?; @@ -1687,12 +1652,11 @@ pub fn join_room_by_id_route( #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/join/<_room_id_or_alias>", data = "") + post("/_matrix/client/r0/join/<_>", data = "") )] pub fn join_room_by_id_or_alias_route( db: State<'_, Database>, body: Ruma, - _room_id_or_alias: String, ) -> ConduitResult { let room_id = RoomId::try_from(body.room_id_or_alias.clone()).or_else(|alias| { Ok::<_, Error>(db.rooms.id_from_alias(&alias)?.ok_or(Error::BadRequest( @@ -1702,7 +1666,7 @@ pub fn join_room_by_id_or_alias_route( })?; let body = Ruma { - user_id: body.user_id.clone(), + sender_id: body.sender_id.clone(), device_id: body.device_id.clone(), json_body: None, body: join_room_by_id::Request { @@ -1712,25 +1676,28 @@ pub fn join_room_by_id_or_alias_route( }; Ok(join_room_by_id_or_alias::Response { - room_id: join_room_by_id_route(db, body, "".to_owned())?.0.room_id, + room_id: join_room_by_id_route(db, body)?.0.room_id, } .into()) } #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/leave", data = "") + post("/_matrix/client/r0/rooms/<_>/leave", data = "") )] pub fn leave_room_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut event = serde_json::from_value::>( db.rooms - .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? + .room_state_get( + &body.room_id, + &EventType::RoomMember, + &sender_id.to_string(), + )? .ok_or(Error::BadRequest( ErrorKind::BadState, "Cannot leave a room you are not a member of.", @@ -1746,11 +1713,11 @@ pub fn leave_room_route( db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(event).expect("event is valid, we just created it"), None, - Some(user_id.to_string()), + Some(sender_id.to_string()), None, &db.globals, )?; @@ -1760,18 +1727,21 @@ pub fn leave_room_route( #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/kick", data = "") + post("/_matrix/client/r0/rooms/<_>/kick", data = "") )] pub fn kick_user_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut event = serde_json::from_value::>( db.rooms - .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? + .room_state_get( + &body.room_id, + &EventType::RoomMember, + &body.user_id.to_string(), + )? .ok_or(Error::BadRequest( ErrorKind::BadState, "Cannot kick member that's not in the room.", @@ -1779,7 +1749,7 @@ pub fn kick_user_route( .content .clone(), ) - .map_err(|_| Error::bad_database("Invalid member event in database."))? + .expect("Raw::from_value always works") .deserialize() .map_err(|_| Error::bad_database("Invalid member event in database."))?; @@ -1788,11 +1758,11 @@ pub fn kick_user_route( db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), // Sender + sender_id.clone(), EventType::RoomMember, serde_json::to_value(event).expect("event is valid, we just created it"), None, - Some(body.body.user_id.to_string()), + Some(body.user_id.to_string()), None, &db.globals, )?; @@ -1802,16 +1772,19 @@ pub fn kick_user_route( #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/rooms/<_room_id>/joined_members", data = "") + get("/_matrix/client/r0/rooms/<_>/joined_members", data = "") )] pub fn joined_members_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(&user_id, &body.room_id).unwrap_or(false) { + if !db + .rooms + .is_joined(&sender_id, &body.room_id) + .unwrap_or(false) + { return Err(Error::BadRequest( ErrorKind::Forbidden, "You aren't a member of the room.", @@ -1837,25 +1810,28 @@ pub fn joined_members_route( #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/ban", data = "") + post("/_matrix/client/r0/rooms/<_>/ban", data = "") )] pub fn ban_user_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); // TODO: reason let event = db .rooms - .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? + .room_state_get( + &body.room_id, + &EventType::RoomMember, + &body.user_id.to_string(), + )? .map_or( Ok::<_, Error>(member::MemberEventContent { membership: member::MembershipState::Ban, - displayname: db.users.displayname(&user_id)?, - avatar_url: db.users.avatar_url(&user_id)?, + displayname: db.users.displayname(&body.user_id)?, + avatar_url: db.users.avatar_url(&body.user_id)?, is_direct: None, third_party_invite: None, }), @@ -1863,7 +1839,7 @@ pub fn ban_user_route( let mut event = serde_json::from_value::>( event.content.clone(), ) - .map_err(|_| Error::bad_database("Invalid member event in database."))? + .expect("Raw::from_value always works") .deserialize() .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = ruma::events::room::member::MembershipState::Ban; @@ -1873,11 +1849,11 @@ pub fn ban_user_route( db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), // Sender + sender_id.clone(), EventType::RoomMember, serde_json::to_value(event).expect("event is valid, we just created it"), None, - Some(body.body.user_id.to_string()), + Some(body.user_id.to_string()), None, &db.globals, )?; @@ -1887,18 +1863,21 @@ pub fn ban_user_route( #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/unban", data = "") + post("/_matrix/client/r0/rooms/<_>/unban", data = "") )] pub fn unban_user_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut event = serde_json::from_value::>( db.rooms - .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? + .room_state_get( + &body.room_id, + &EventType::RoomMember, + &body.user_id.to_string(), + )? .ok_or(Error::BadRequest( ErrorKind::BadState, "Cannot unban a user who is not banned.", @@ -1914,11 +1893,11 @@ pub fn unban_user_route( db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), // Sender + sender_id.clone(), EventType::RoomMember, serde_json::to_value(event).expect("event is valid, we just created it"), None, - Some(body.body.user_id.to_string()), + Some(body.user_id.to_string()), None, &db.globals, )?; @@ -1928,33 +1907,33 @@ pub fn unban_user_route( #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/forget", data = "") + post("/_matrix/client/r0/rooms/<_>/forget", data = "") )] pub fn forget_room_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - db.rooms.forget(&body.room_id, &user_id)?; + db.rooms.forget(&body.room_id, &sender_id)?; Ok(forget_room::Response.into()) } #[cfg_attr( feature = "conduit_bin", - post("/_matrix/client/r0/rooms/<_room_id>/invite", data = "") + post("/_matrix/client/r0/rooms/<_>/invite", data = "") )] pub fn invite_user_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); + if let invite_user::InvitationRecipient::UserId { user_id } = &body.recipient { db.rooms.append_pdu( body.room_id.clone(), - body.user_id.clone().expect("user is authenticated"), + sender_id.clone(), EventType::RoomMember, serde_json::to_value(member::MemberEventContent { membership: member::MembershipState::Invite, @@ -1978,12 +1957,11 @@ pub fn invite_user_route( #[cfg_attr( feature = "conduit_bin", - put("/_matrix/client/r0/directory/list/room/<_room_id>", data = "") + put("/_matrix/client/r0/directory/list/room/<_>", data = "") )] pub async fn set_room_visibility_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { match body.visibility { room::Visibility::Public => db.rooms.set_public(&body.room_id, true)?, @@ -1995,12 +1973,11 @@ pub async fn set_room_visibility_route( #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/directory/list/room/<_room_id>", data = "") + get("/_matrix/client/r0/directory/list/room/<_>", data = "") )] pub async fn get_room_visibility_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { Ok(get_room_visibility::Response { visibility: if db.rooms.is_public_room(&body.room_id)? { @@ -2027,7 +2004,7 @@ pub async fn get_public_rooms_route( server, since, }, - user_id, + sender_id, device_id, json_body, } = body; @@ -2047,7 +2024,7 @@ pub async fn get_public_rooms_route( server, since, }, - user_id, + sender_id, device_id, json_body, }, @@ -2224,16 +2201,15 @@ pub fn search_users_route( #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/rooms/<_room_id>/members", data = "") + get("/_matrix/client/r0/rooms/<_>/members", data = "") )] pub fn get_member_events_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -2265,20 +2241,15 @@ pub fn get_protocols_route() -> ConduitResult { #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/r0/rooms/<_room_id>/event/<_event_id>", - data = "" - ) + get("/_matrix/client/r0/rooms/<_>/event/<_>", data = "") )] pub fn get_room_event_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -2297,26 +2268,20 @@ pub fn get_room_event_route( #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/r0/rooms/<_room_id>/send/<_event_type>/<_txn_id>", - data = "" - ) + put("/_matrix/client/r0/rooms/<_>/send/<_>/<_>", data = "") )] pub fn create_message_event_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_type: String, - _txn_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut unsigned = serde_json::Map::new(); unsigned.insert("transaction_id".to_owned(), body.txn_id.clone().into()); let event_id = db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), + sender_id.clone(), body.event_type.clone(), serde_json::from_str( body.json_body @@ -2335,19 +2300,13 @@ pub fn create_message_event_route( #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>/<_state_key>", - data = "" - ) + put("/_matrix/client/r0/rooms/<_>/state/<_>/<_>", data = "") )] pub fn create_state_event_for_key_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_type: String, - _state_key: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let content = serde_json::from_str::( body.json_body @@ -2386,7 +2345,7 @@ pub fn create_state_event_for_key_route( let event_id = db.rooms.append_pdu( body.room_id.clone(), - user_id.clone(), + sender_id.clone(), body.event_type.clone(), content, None, @@ -2400,16 +2359,11 @@ pub fn create_state_event_for_key_route( #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>", - data = "" - ) + put("/_matrix/client/r0/rooms/<_>/state/<_>", data = "") )] pub fn create_state_event_for_empty_key_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_type: String, ) -> ConduitResult { // This just calls create_state_event_for_key_route let Ruma { @@ -2419,7 +2373,7 @@ pub fn create_state_event_for_empty_key_route( event_type, data, }, - user_id, + sender_id, device_id, json_body, } = body; @@ -2434,13 +2388,10 @@ pub fn create_state_event_for_empty_key_route( data, state_key: "".to_owned(), }, - user_id, + sender_id, device_id, json_body, }, - _room_id, - _event_type, - "".to_owned(), )? .0 .event_id, @@ -2450,16 +2401,15 @@ pub fn create_state_event_for_empty_key_route( #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/rooms/<_room_id>/state", data = "") + get("/_matrix/client/r0/rooms/<_>/state", data = "") )] pub fn get_state_events_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view the room state.", @@ -2479,21 +2429,15 @@ pub fn get_state_events_route( #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>/<_state_key>", - data = "" - ) + get("/_matrix/client/r0/rooms/<_>/state/<_>/<_>", data = "") )] pub fn get_state_events_for_key_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_type: String, - _state_key: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view the room state.", @@ -2517,20 +2461,15 @@ pub fn get_state_events_for_key_route( #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>", - data = "" - ) + get("/_matrix/client/r0/rooms/<_>/state/<_>", data = "") )] pub fn get_state_events_for_empty_key_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_type: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view the room state.", @@ -2556,14 +2495,19 @@ pub fn get_state_events_for_empty_key_route( feature = "conduit_bin", get("/_matrix/client/r0/sync", data = "") )] -pub fn sync_route( +pub async fn sync_events_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - std::thread::sleep(Duration::from_millis(1000)); - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); + // TODO: match body.set_presence { + db.rooms.edus.ping_presence(&sender_id)?; + + // Setup watchers, so if there's no response, we can wait for them + let watcher = db.watch(sender_id, device_id); + let next_batch = db.globals.current_count()?.to_string(); let mut joined_rooms = BTreeMap::new(); @@ -2573,23 +2517,50 @@ pub fn sync_route( .and_then(|string| string.parse().ok()) .unwrap_or(0); - for room_id in db.rooms.rooms_joined(&user_id) { + let mut presence_updates = HashMap::new(); + let mut device_list_updates = HashSet::new(); + + for room_id in db.rooms.rooms_joined(&sender_id) { let room_id = room_id?; - let mut pdus = db + let mut non_timeline_pdus = db .rooms - .pdus_since(&user_id, &room_id, since)? - .filter_map(|r| r.ok()) // Filter out buggy events + .pdus_since(&sender_id, &room_id, since)? + .filter_map(|r| r.ok()); // Filter out buggy events + + // Take the last 10 events for the timeline + let timeline_pdus = non_timeline_pdus + .by_ref() + .rev() + .take(10) + .collect::>() + .into_iter() + .rev() .collect::>(); + // They /sync response doesn't always return all messages, so we say the output is + // limited unless there are events in non_timeline_pdus + //let mut limited = false; + + let mut state_pdus = Vec::new(); + for pdu in non_timeline_pdus { + if pdu.state_key.is_some() { + state_pdus.push(pdu); + } + } + let mut send_member_count = false; let mut joined_since_last_sync = false; let mut send_notification_counts = false; - for pdu in &pdus { + for pdu in db + .rooms + .pdus_since(&sender_id, &room_id, since)? + .filter_map(|r| r.ok()) + { send_notification_counts = true; if pdu.kind == EventType::RoomMember { send_member_count = true; - if !joined_since_last_sync && pdu.state_key == Some(user_id.to_string()) { + if !joined_since_last_sync && pdu.state_key == Some(sender_id.to_string()) { let content = serde_json::from_value::< Raw, >(pdu.content.clone()) @@ -2598,8 +2569,8 @@ pub fn sync_route( .map_err(|_| Error::bad_database("Invalid PDU in database."))?; if content.membership == ruma::events::room::member::MembershipState::Join { joined_since_last_sync = true; - // Both send_member_count and joined_since_last_sync are set. There's nothing more - // to do + // Both send_member_count and joined_since_last_sync are set. There's + // nothing more to do break; } } @@ -2621,14 +2592,14 @@ pub fn sync_route( for hero in db .rooms - .all_pdus(&user_id, &room_id)? + .all_pdus(&sender_id, &room_id)? .filter_map(|pdu| pdu.ok()) // Ignore all broken pdus .filter(|pdu| pdu.kind == EventType::RoomMember) .map(|pdu| { let content = serde_json::from_value::< Raw, >(pdu.content.clone()) - .map_err(|_| Error::bad_database("Invalid member event in database."))? + .expect("Raw::from_value always works") .deserialize() .map_err(|_| Error::bad_database("Invalid member event in database."))?; @@ -2646,7 +2617,7 @@ pub fn sync_route( .content .clone(), ) - .map_err(|_| Error::bad_database("Invalid member event in database."))? + .expect("Raw::from_value always works") .deserialize() .map_err(|_| { Error::bad_database("Invalid member event in database.") @@ -2674,7 +2645,7 @@ pub fn sync_route( // Filter for possible heroes .filter_map(|u| u) { - if heroes.contains(&hero) || hero == user_id.to_string() { + if heroes.contains(&hero) || hero == sender_id.to_string() { continue; } @@ -2692,10 +2663,10 @@ pub fn sync_route( }; let notification_count = if send_notification_counts { - if let Some(last_read) = db.rooms.edus.room_read_get(&room_id, &user_id)? { + if let Some(last_read) = db.rooms.edus.room_read_get(&room_id, &sender_id)? { Some( (db.rooms - .pdus_since(&user_id, &room_id, last_read)? + .pdus_since(&sender_id, &room_id, last_read)? .filter_map(|pdu| pdu.ok()) // Filter out buggy events .filter(|pdu| { matches!( @@ -2713,15 +2684,7 @@ pub fn sync_route( None }; - // They /sync response doesn't always return all messages, so we say the output is - // limited unless there are enough events - let mut limited = true; - pdus = pdus.split_off(pdus.len().checked_sub(10).unwrap_or_else(|| { - limited = false; - 0 - })); - - let prev_batch = pdus.first().map_or(Ok::<_, Error>(None), |e| { + let prev_batch = timeline_pdus.first().map_or(Ok::<_, Error>(None), |e| { Ok(Some( db.rooms .get_pdu_count(&e.event_id)? @@ -2730,7 +2693,7 @@ pub fn sync_route( )) })?; - let room_events = pdus + let room_events = timeline_pdus .into_iter() .map(|pdu| pdu.to_sync_room_event()) .collect::>(); @@ -2763,7 +2726,7 @@ pub fn sync_route( account_data: sync_events::AccountData { events: db .account_data - .changes_since(Some(&room_id), &user_id, since)? + .changes_since(Some(&room_id), &sender_id, since)? .into_iter() .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) @@ -2782,7 +2745,7 @@ pub fn sync_route( notification_count, }, timeline: sync_events::Timeline { - limited: limited || joined_since_last_sync, + limited: false || joined_since_last_sync, prev_batch, events: room_events, }, @@ -2804,12 +2767,53 @@ pub fn sync_route( if !joined_room.is_empty() { joined_rooms.insert(room_id.clone(), joined_room); } + + // Look for device list updates in this room + device_list_updates.extend( + db.users + .keys_changed(&room_id, since, None) + .filter_map(|r| r.ok()), + ); + + // Take presence updates from this room + for (user_id, presence) in + db.rooms + .edus + .presence_since(&room_id, since, &db.rooms, &db.globals)? + { + match presence_updates.entry(user_id) { + hash_map::Entry::Vacant(v) => { + v.insert(presence); + } + hash_map::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); + } + } + } + } } let mut left_rooms = BTreeMap::new(); - for room_id in db.rooms.rooms_left(&user_id) { + for room_id in db.rooms.rooms_left(&sender_id) { let room_id = room_id?; - let pdus = db.rooms.pdus_since(&user_id, &room_id, since)?; + let pdus = db.rooms.pdus_since(&sender_id, &room_id, since)?; let room_events = pdus .filter_map(|pdu| pdu.ok()) // Filter out buggy events .map(|pdu| pdu.to_sync_room_event()) @@ -2856,7 +2860,7 @@ pub fn sync_route( } let mut invited_rooms = BTreeMap::new(); - for room_id in db.rooms.rooms_invited(&user_id) { + for room_id in db.rooms.rooms_invited(&sender_id) { let room_id = room_id?; let invited_room = sync_events::InvitedRoom { @@ -2877,9 +2881,9 @@ pub fn sync_route( // Remove all to-device events the device received *last time* db.users - .remove_to_device_events(user_id, device_id, since)?; + .remove_to_device_events(sender_id, device_id, since)?; - Ok(sync_events::Response { + let response = sync_events::Response { next_batch, rooms: sync_events::Rooms { leave: left_rooms, @@ -2887,29 +2891,15 @@ pub fn sync_route( invite: invited_rooms, }, presence: sync_events::Presence { - events: db - .global_edus - .presence_since(since)? - .map(|edu| { - let mut edu = edu? - .deserialize() - .map_err(|_| Error::bad_database("EDU in database is invalid."))?; - if let Some(timestamp) = edu.content.last_active_ago { - let mut last_active_ago = utils::millis_since_unix_epoch() - .try_into() - .expect("time is valid"); - last_active_ago -= timestamp; - edu.content.last_active_ago = Some(last_active_ago); - } - Ok::<_, Error>(edu.into()) - }) - .filter_map(|edu| edu.ok()) // Filter out buggy events + events: presence_updates + .into_iter() + .map(|(_, v)| Raw::from(v)) .collect(), }, account_data: sync_events::AccountData { events: db .account_data - .changes_since(None, &user_id, since)? + .changes_since(None, &sender_id, since)? .into_iter() .filter_map(|(_, v)| { serde_json::from_str(v.json().get()) @@ -2919,40 +2909,55 @@ pub fn sync_route( .collect::>(), }, device_lists: sync_events::DeviceLists { - changed: if since != 0 { - db.users - .keys_changed(since) - .filter_map(|u| u.ok()) - .collect() // Filter out buggy events - } else { - Vec::new() - }, + changed: device_list_updates.into_iter().collect(), left: Vec::new(), // TODO }, - device_one_time_keys_count: Default::default(), // TODO + device_one_time_keys_count: if db.users.last_one_time_keys_update(sender_id)? > since { + db.users.count_one_time_keys(sender_id, device_id)? + } else { + BTreeMap::new() + }, to_device: sync_events::ToDevice { - events: db.users.get_to_device_events(user_id, device_id)?, + events: db.users.get_to_device_events(sender_id, device_id)?, }, + }; + + // TODO: Retry the endpoint instead of returning (waiting for #118) + if !body.full_state + && response.rooms.is_empty() + && response.presence.is_empty() + && response.account_data.is_empty() + && response.device_lists.is_empty() + && response.device_one_time_keys_count.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(Duration::default()); + if duration.as_secs() > 30 { + duration = Duration::from_secs(30); + } + let mut delay = tokio::time::delay_for(duration); + tokio::select! { + _ = &mut delay => {} + _ = watcher => {} + } } - .into()) + + Ok(response.into()) } #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/r0/rooms/<_room_id>/context/<_event_id>", - data = "" - ) + get("/_matrix/client/r0/rooms/<_>/context/<_>", data = "") )] pub fn get_context_route( db: State<'_, Database>, body: Ruma, - _room_id: String, - _event_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -2975,7 +2980,7 @@ pub fn get_context_route( let events_before = db .rooms - .pdus_until(&user_id, &body.room_id, base_token) + .pdus_until(&sender_id, &body.room_id, base_token) .take( u32::try_from(body.limit).map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") @@ -2985,14 +2990,7 @@ pub fn get_context_route( .filter_map(|r| r.ok()) // Remove buggy events .collect::>(); - let start_token = events_before.last().map_or(Ok(None), |(_, e)| { - Ok::<_, Error>(Some( - db.rooms - .get_pdu_count(&e.event_id)? - .ok_or_else(|| Error::bad_database("Can't find count from event in db."))? - .to_string(), - )) - })?; + let start_token = events_before.last().map(|(count, _)| count.to_string()); let events_before = events_before .into_iter() @@ -3001,7 +2999,7 @@ pub fn get_context_route( let events_after = db .rooms - .pdus_after(&user_id, &body.room_id, base_token) + .pdus_after(&sender_id, &body.room_id, base_token) .take( u32::try_from(body.limit).map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") @@ -3011,15 +3009,7 @@ pub fn get_context_route( .filter_map(|r| r.ok()) // Remove buggy events .collect::>(); - let end_token = if let Some(last_event) = events_after.last() { - Some( - utils::u64_from_bytes(&last_event.0) - .map_err(|_| Error::bad_database("Invalid pdu id in db."))? - .to_string(), - ) - } else { - None - }; + let end_token = events_after.last().map(|(count, _)| count.to_string()); let events_after = events_after .into_iter() @@ -3044,16 +3034,15 @@ pub fn get_context_route( #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/rooms/<_room_id>/messages", data = "") + get("/_matrix/client/r0/rooms/<_>/messages", data = "") )] pub fn get_message_events_route( db: State<'_, Database>, body: Ruma, - _room_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); - if !db.rooms.is_joined(user_id, &body.room_id)? { + if !db.rooms.is_joined(sender_id, &body.room_id)? { return Err(Error::BadRequest( ErrorKind::Forbidden, "You don't have permission to view this room.", @@ -3066,7 +3055,7 @@ pub fn get_message_events_route( .parse() .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from` value."))?; - let to = body.to.as_ref().map(|t| t.as_bytes()); + let to = body.to.as_ref().map(|t| t.parse()); // Use limit or else 10 let limit = body @@ -3078,21 +3067,13 @@ pub fn get_message_events_route( get_message_events::Direction::Forward => { let events_after = db .rooms - .pdus_after(&user_id, &body.room_id, from) + .pdus_after(&sender_id, &body.room_id, from) .take(limit) .filter_map(|r| r.ok()) // Filter out buggy events - .take_while(|(k, _)| Some(&**k) != to) // Stop at `to` + .take_while(|&(k, _)| Some(Ok(k)) != to) // Stop at `to` .collect::>(); - let end_token = if let Some(last_event) = events_after.last() { - Some( - utils::u64_from_bytes(&last_event.0) - .map_err(|_| Error::bad_database("Invalid pdu id in db."))? - .to_string(), - ) - } else { - None - }; + let end_token = events_after.last().map(|(count, _)| count.to_string()); let events_after = events_after .into_iter() @@ -3110,21 +3091,13 @@ pub fn get_message_events_route( get_message_events::Direction::Backward => { let events_before = db .rooms - .pdus_until(&user_id, &body.room_id, from) + .pdus_until(&sender_id, &body.room_id, from) .take(limit) .filter_map(|r| r.ok()) // Filter out buggy events - .take_while(|(k, _)| Some(&**k) != to) // Stop at `to` + .take_while(|&(k, _)| Some(Ok(k)) != to) // Stop at `to` .collect::>(); - let start_token = if let Some(last_event) = events_before.last() { - Some( - utils::u64_from_bytes(&last_event.0) - .map_err(|_| Error::bad_database("Invalid pdu id in db."))? - .to_string(), - ) - } else { - None - }; + let start_token = events_before.last().map(|(count, _)| count.to_string()); let events_before = events_before .into_iter() @@ -3160,25 +3133,20 @@ pub fn publicised_groups_route() -> ConduitResult/<_txn_id>", - data = "" - ) + put("/_matrix/client/r0/sendToDevice/<_>/<_>", data = "") )] pub fn send_event_to_device_route( db: State<'_, Database>, body: Ruma, - _event_type: String, - _txn_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); for (target_user_id, map) in &body.messages { for (target_device_id_maybe, event) in map { match target_device_id_maybe { to_device::DeviceIdOrAllDevices::DeviceId(target_device_id) => { db.users.add_to_device_event( - user_id, + sender_id, &target_user_id, &target_device_id, &body.event_type, @@ -3192,7 +3160,7 @@ pub fn send_event_to_device_route( to_device::DeviceIdOrAllDevices::AllDevices => { for target_device_id in db.users.all_device_ids(&target_user_id) { db.users.add_to_device_event( - user_id, + sender_id, &target_user_id, &target_device_id?, &body.event_type, @@ -3307,11 +3275,11 @@ pub fn get_devices_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let devices = db .users - .all_devices_metadata(user_id) + .all_devices_metadata(sender_id) .filter_map(|r| r.ok()) // Filter out buggy devices .collect::>(); @@ -3327,11 +3295,11 @@ pub fn get_device_route( body: Ruma, _device_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device = db .users - .get_device_metadata(&user_id, &body.body.device_id)? + .get_device_metadata(&sender_id, &body.body.device_id)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?; Ok(get_device::Response { device }.into()) @@ -3346,17 +3314,17 @@ pub fn update_device_route( body: Ruma, _device_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut device = db .users - .get_device_metadata(&user_id, &body.body.device_id)? + .get_device_metadata(&sender_id, &body.body.device_id)? .ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?; device.display_name = body.display_name.clone(); db.users - .update_device_metadata(&user_id, &body.body.device_id, &device)?; + .update_device_metadata(&sender_id, &body.body.device_id, &device)?; Ok(update_device::Response.into()) } @@ -3370,7 +3338,7 @@ pub fn delete_device_route( body: Ruma, _device_id: String, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); // UIAA @@ -3386,7 +3354,7 @@ pub fn delete_device_route( if let Some(auth) = &body.auth { let (worked, uiaainfo) = db.uiaa.try_auth( - &user_id, + &sender_id, &device_id, auth, &uiaainfo, @@ -3399,11 +3367,11 @@ pub fn delete_device_route( // Success! } else { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - db.uiaa.create(&user_id, &device_id, &uiaainfo)?; + db.uiaa.create(&sender_id, &device_id, &uiaainfo)?; return Err(Error::Uiaa(uiaainfo)); } - db.users.remove_device(&user_id, &body.body.device_id)?; + db.users.remove_device(&sender_id, &body.body.device_id)?; Ok(delete_device::Response.into()) } @@ -3416,7 +3384,7 @@ pub fn delete_devices_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); // UIAA @@ -3432,7 +3400,7 @@ pub fn delete_devices_route( if let Some(auth) = &body.auth { let (worked, uiaainfo) = db.uiaa.try_auth( - &user_id, + &sender_id, &device_id, auth, &uiaainfo, @@ -3445,12 +3413,12 @@ pub fn delete_devices_route( // Success! } else { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - db.uiaa.create(&user_id, &device_id, &uiaainfo)?; + db.uiaa.create(&sender_id, &device_id, &uiaainfo)?; return Err(Error::Uiaa(uiaainfo)); } for device_id in &body.devices { - db.users.remove_device(&user_id, &device_id)? + db.users.remove_device(&sender_id, &device_id)? } Ok(delete_devices::Response.into()) @@ -3464,7 +3432,7 @@ pub fn upload_signing_keys_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let device_id = body.device_id.as_ref().expect("user is authenticated"); // UIAA @@ -3480,7 +3448,7 @@ pub fn upload_signing_keys_route( if let Some(auth) = &body.auth { let (worked, uiaainfo) = db.uiaa.try_auth( - &user_id, + &sender_id, &device_id, auth, &uiaainfo, @@ -3493,16 +3461,17 @@ pub fn upload_signing_keys_route( // Success! } else { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); - db.uiaa.create(&user_id, &device_id, &uiaainfo)?; + db.uiaa.create(&sender_id, &device_id, &uiaainfo)?; return Err(Error::Uiaa(uiaainfo)); } if let Some(master_key) = &body.master_key { db.users.add_cross_signing_keys( - user_id, + sender_id, &master_key, &body.self_signing_key, &body.user_signing_key, + &db.rooms, &db.globals, )?; } @@ -3518,7 +3487,7 @@ pub fn upload_signatures_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { - let sender_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); for (user_id, signed_keys) in &body.signed_keys { for (key_id, signed_key) in signed_keys { @@ -3553,8 +3522,14 @@ pub fn upload_signatures_route( ))? .to_owned(), ); - db.users - .sign_key(&user_id, &key_id, signature, &sender_id, &db.globals)?; + db.users.sign_key( + &user_id, + &key_id, + signature, + &sender_id, + &db.rooms, + &db.globals, + )?; } } } @@ -3562,6 +3537,39 @@ pub fn upload_signatures_route( Ok(upload_signatures::Response.into()) } +#[cfg_attr( + feature = "conduit_bin", + get("/_matrix/client/r0/keys/changes", data = "") +)] +pub fn get_key_changes_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); + + let mut device_list_updates = HashSet::new(); + for room_id in db.rooms.rooms_joined(sender_id).filter_map(|r| r.ok()) { + device_list_updates.extend( + db.users + .keys_changed( + &room_id, + 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(|r| r.ok()), + ); + } + Ok(get_key_changes::Response { + changed: device_list_updates.into_iter().collect(), + left: Vec::new(), // TODO + } + .into()) +} + #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/pushers"))] pub fn pushers_route() -> ConduitResult { Ok(get_pushers::Response { @@ -3580,23 +3588,17 @@ pub fn set_pushers_route() -> ConduitResult { #[cfg_attr( feature = "conduit_bin", - put( - "/_matrix/client/r0/user/<_user_id>/rooms/<_room_id>/tags/<_tag>", - data = "" - ) + put("/_matrix/client/r0/user/<_>/rooms/<_>/tags/<_>", data = "") )] pub fn update_tag_route( db: State<'_, Database>, - _user_id: String, - _room_id: String, - _tag: String, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut tags_event = db .account_data - .get::(Some(&body.room_id), user_id, EventType::Tag)? + .get::(Some(&body.room_id), sender_id, EventType::Tag)? .unwrap_or_else(|| ruma::events::tag::TagEvent { content: ruma::events::tag::TagEventContent { tags: BTreeMap::new(), @@ -3609,7 +3611,7 @@ pub fn update_tag_route( db.account_data.update( Some(&body.room_id), - user_id, + sender_id, EventType::Tag, &tags_event, &db.globals, @@ -3620,23 +3622,17 @@ pub fn update_tag_route( #[cfg_attr( feature = "conduit_bin", - delete( - "/_matrix/client/r0/user/<_user_id>/rooms/<_room_id>/tags/<_tag>", - data = "" - ) + delete("/_matrix/client/r0/user/<_>/rooms/<_>/tags/<_>", data = "") )] pub fn delete_tag_route( db: State<'_, Database>, - _user_id: String, - _room_id: String, - _tag: String, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); let mut tags_event = db .account_data - .get::(Some(&body.room_id), user_id, EventType::Tag)? + .get::(Some(&body.room_id), sender_id, EventType::Tag)? .unwrap_or_else(|| ruma::events::tag::TagEvent { content: ruma::events::tag::TagEventContent { tags: BTreeMap::new(), @@ -3646,7 +3642,7 @@ pub fn delete_tag_route( db.account_data.update( Some(&body.room_id), - user_id, + sender_id, EventType::Tag, &tags_event, &db.globals, @@ -3657,23 +3653,18 @@ pub fn delete_tag_route( #[cfg_attr( feature = "conduit_bin", - get( - "/_matrix/client/r0/user/<_user_id>/rooms/<_room_id>/tags", - data = "" - ) + get("/_matrix/client/r0/user/<_>/rooms/<_>/tags", data = "") )] pub fn get_tags_route( db: State<'_, Database>, - _user_id: String, - _room_id: String, body: Ruma, ) -> ConduitResult { - let user_id = body.user_id.as_ref().expect("user is authenticated"); + let sender_id = body.sender_id.as_ref().expect("user is authenticated"); Ok(get_tags::Response { tags: db .account_data - .get::(Some(&body.room_id), user_id, EventType::Tag)? + .get::(Some(&body.room_id), sender_id, EventType::Tag)? .unwrap_or_else(|| ruma::events::tag::TagEvent { content: ruma::events::tag::TagEventContent { tags: BTreeMap::new(), @@ -3686,9 +3677,7 @@ pub fn get_tags_route( } #[cfg(feature = "conduit_bin")] -#[options("/<_segments..>")] -pub fn options_route( - _segments: rocket::http::uri::Segments<'_>, -) -> ConduitResult { +#[options("/<_..>")] +pub fn options_route() -> ConduitResult { Ok(send_event_to_device::Response.into()) } diff --git a/src/database.rs b/src/database.rs index 370fde75..5a1ed0f6 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,5 +1,4 @@ pub(self) mod account_data; -pub(self) mod global_edus; pub(self) mod globals; pub(self) mod key_backups; pub(self) mod media; @@ -12,7 +11,9 @@ use directories::ProjectDirs; use log::info; use std::fs::remove_dir_all; -use rocket::Config; +use futures::StreamExt; +use rocket::{futures, Config}; +use ruma::{DeviceId, UserId}; pub struct Database { pub globals: globals::Globals, @@ -20,7 +21,6 @@ pub struct Database { pub uiaa: uiaa::Uiaa, pub rooms: rooms::Rooms, pub account_data: account_data::AccountData, - pub global_edus: global_edus::GlobalEdus, pub media: media::Media, pub key_backups: key_backups::KeyBackups, pub _db: sled::Db, @@ -75,6 +75,7 @@ impl Database { userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?, token_userdeviceid: db.open_tree("token_userdeviceid")?, onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?, + userid_lastonetimekeyupdate: db.open_tree("userid_lastonetimekeyupdate")?, keychangeid_userid: db.open_tree("devicekeychangeid_userid")?, keyid_key: db.open_tree("keyid_key")?, userid_masterkeyid: db.open_tree("userid_masterkeyid")?, @@ -91,6 +92,8 @@ impl Database { roomlatestid_roomlatest: db.open_tree("roomlatestid_roomlatest")?, // Read receipts roomactiveid_userid: db.open_tree("roomactiveid_userid")?, // Typing notifs roomid_lastroomactiveupdate: db.open_tree("roomid_lastroomactiveupdate")?, + presenceid_presence: db.open_tree("presenceid_presence")?, + userid_lastpresenceupdate: db.open_tree("userid_lastpresenceupdate")?, }, pduid_pdu: db.open_tree("pduid_pdu")?, eventid_pduid: db.open_tree("eventid_pduid")?, @@ -110,9 +113,6 @@ impl Database { account_data: account_data::AccountData { roomuserdataid_accountdata: db.open_tree("roomuserdataid_accountdata")?, }, - global_edus: global_edus::GlobalEdus { - presenceid_presence: db.open_tree("presenceid_presence")?, // Presence - }, media: media::Media { mediaid_file: db.open_tree("mediaid_file")?, }, @@ -124,4 +124,74 @@ impl Database { _db: db, }) } + + pub async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> () { + let mut userid_prefix = user_id.to_string().as_bytes().to_vec(); + userid_prefix.push(0xff); + let mut userdeviceid_prefix = userid_prefix.clone(); + userdeviceid_prefix.extend_from_slice(device_id.as_bytes()); + userdeviceid_prefix.push(0xff); + + let mut futures = futures::stream::FuturesUnordered::new(); + + futures.push(self.users.keychangeid_userid.watch_prefix(b"")); + + // Return when *any* user changed his key + // TODO: only send for user they share a room with + futures.push( + self.users + .todeviceid_events + .watch_prefix(&userdeviceid_prefix), + ); + + futures.push(self.rooms.userroomid_joined.watch_prefix(&userid_prefix)); + futures.push(self.rooms.userroomid_invited.watch_prefix(&userid_prefix)); + futures.push(self.rooms.userroomid_left.watch_prefix(&userid_prefix)); + + // Events for rooms we are in + for room_id in self.rooms.rooms_joined(user_id).filter_map(|r| r.ok()) { + let mut roomid_prefix = room_id.to_string().as_bytes().to_vec(); + roomid_prefix.push(0xff); + + // PDUs + futures.push(self.rooms.pduid_pdu.watch_prefix(&roomid_prefix)); + + // EDUs + futures.push( + self.rooms + .edus + .roomid_lastroomactiveupdate + .watch_prefix(&roomid_prefix), + ); + + futures.push( + self.rooms + .edus + .roomlatestid_roomlatest + .watch_prefix(&roomid_prefix), + ); + + // Room account data + let mut roomuser_prefix = roomid_prefix.clone(); + roomuser_prefix.extend_from_slice(&userid_prefix); + + futures.push( + self.account_data + .roomuserdataid_accountdata + .watch_prefix(&roomuser_prefix), + ); + } + + let mut globaluserdata_prefix = vec![0xff]; + globaluserdata_prefix.extend_from_slice(&userid_prefix); + + futures.push( + self.account_data + .roomuserdataid_accountdata + .watch_prefix(&globaluserdata_prefix), + ); + + // Wait until one of them finds something + futures.next().await; + } } diff --git a/src/database/account_data.rs b/src/database/account_data.rs index 1afbcd61..99e0d5c7 100644 --- a/src/database/account_data.rs +++ b/src/database/account_data.rs @@ -1,5 +1,6 @@ use crate::{utils, Error, Result}; use ruma::{ + api::client::error::ErrorKind, events::{AnyEvent as EduEvent, EventType}, Raw, RoomId, UserId, }; @@ -19,7 +20,7 @@ impl AccountData { room_id: Option<&RoomId>, user_id: &UserId, event_type: EventType, - event: &T, + data: &T, globals: &super::globals::Globals, ) -> Result<()> { let mut prefix = room_id @@ -42,10 +43,16 @@ impl AccountData { key.push(0xff); key.extend_from_slice(event_type.to_string().as_bytes()); - self.roomuserdataid_accountdata.insert( - key, - &*serde_json::to_string(&event).expect("Map::to_string always works"), - )?; + let json = serde_json::to_value(data).expect("all types here can be serialized"); // TODO: maybe add error handling + if json.get("type").is_none() || json.get("content").is_none() { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Account data doesn't have all required fields.", + )); + } + + self.roomuserdataid_accountdata + .insert(key, &*json.to_string())?; Ok(()) } @@ -60,7 +67,7 @@ impl AccountData { self.find_event(room_id, user_id, &kind) .map(|r| { let (_, v) = r?; - serde_json::from_slice(&v).map_err(|_| Error::BadDatabase("could not deserialize")) + serde_json::from_slice(&v).map_err(|_| Error::bad_database("could not deserialize")) }) .transpose() } diff --git a/src/database/global_edus.rs b/src/database/global_edus.rs deleted file mode 100644 index 94f2de82..00000000 --- a/src/database/global_edus.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::{Error, Result}; -use ruma::Raw; - -pub struct GlobalEdus { - //pub globalallid_globalall: sled::Tree, // ToDevice, GlobalAllId = UserId + Count - pub(super) presenceid_presence: sled::Tree, // Presence, PresenceId = Count + UserId -} - -impl GlobalEdus { - /// Adds a global event which will be saved until a new event replaces it (e.g. presence updates). - pub fn update_presence( - &self, - presence: ruma::events::presence::PresenceEvent, - globals: &super::globals::Globals, - ) -> Result<()> { - // Remove old entry - if let Some(old) = self - .presenceid_presence - .iter() - .keys() - .rev() - .filter_map(|r| r.ok()) - .find(|key| { - key.rsplit(|&b| b == 0xff) - .next() - .expect("rsplit always returns an element") - == presence.sender.to_string().as_bytes() - }) - { - // This is the old global_latest - self.presenceid_presence.remove(old)?; - } - - let mut presence_id = globals.next_count()?.to_be_bytes().to_vec(); - presence_id.push(0xff); - presence_id.extend_from_slice(&presence.sender.to_string().as_bytes()); - - self.presenceid_presence.insert( - presence_id, - &*serde_json::to_string(&presence).expect("PresenceEvent can be serialized"), - )?; - - Ok(()) - } - - /// Returns an iterator over the most recent presence updates that happened after the event with id `since`. - pub fn presence_since( - &self, - since: u64, - ) -> Result>>> { - let first_possible_edu = (since + 1).to_be_bytes().to_vec(); // +1 so we don't send the event at since - - Ok(self - .presenceid_presence - .range(&*first_possible_edu..) - .filter_map(|r| r.ok()) - .map(|(_, v)| { - Ok(serde_json::from_slice(&v) - .map_err(|_| Error::bad_database("Invalid presence event in db."))?) - })) - } -} diff --git a/src/database/rooms.rs b/src/database/rooms.rs index 0395cc20..7895f025 100644 --- a/src/database/rooms.rs +++ b/src/database/rooms.rs @@ -611,44 +611,29 @@ impl Rooms { self.pdus_since(user_id, room_id, 0) } - /// Returns an iterator over all events in a room that happened after the event with id `since`. + /// Returns a double-ended iterator over all events in a room that happened after the event with id `since` + /// in chronological order. pub fn pdus_since( &self, user_id: &UserId, room_id: &RoomId, since: u64, - ) -> Result>> { - // Create the first part of the full pdu id - let mut pdu_id = room_id.to_string().as_bytes().to_vec(); - pdu_id.push(0xff); - pdu_id.extend_from_slice(&(since).to_be_bytes()); - - self.pdus_since_pduid(user_id, room_id, &pdu_id) - } - - /// Returns an iterator over all events in a room that happened after the event with id `since`. - pub fn pdus_since_pduid( - &self, - user_id: &UserId, - room_id: &RoomId, - pdu_id: &[u8], - ) -> Result>> { - // Create the first part of the full pdu id + ) -> Result>> { let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); + // Skip the first pdu if it's exactly at since, because we sent that last time + let mut first_pdu_id = prefix.clone(); + first_pdu_id.extend_from_slice(&(since + 1).to_be_bytes()); + + let mut last_pdu_id = prefix.clone(); + last_pdu_id.extend_from_slice(&u64::MAX.to_be_bytes()); + let user_id = user_id.clone(); Ok(self .pduid_pdu - .range(pdu_id..) - // Skip the first pdu if it's exactly at since, because we sent that last time - .skip(if self.pduid_pdu.get(pdu_id)?.is_some() { - 1 - } else { - 0 - }) + .range(first_pdu_id..last_pdu_id) .filter_map(|r| r.ok()) - .take_while(move |(k, _)| k.starts_with(&prefix)) .map(move |(_, v)| { let mut pdu = serde_json::from_slice::(&v) .map_err(|_| Error::bad_database("PDU in db is invalid."))?; @@ -666,7 +651,7 @@ impl Rooms { user_id: &UserId, room_id: &RoomId, until: u64, - ) -> impl Iterator> { + ) -> impl Iterator> { // Create the first part of the full pdu id let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); @@ -677,6 +662,7 @@ impl Rooms { let current: &[u8] = ¤t; let user_id = user_id.clone(); + let prefixlen = prefix.len(); self.pduid_pdu .range(..current) .rev() @@ -688,7 +674,11 @@ impl Rooms { if pdu.sender != user_id { pdu.unsigned.remove("transaction_id"); } - Ok((k, pdu)) + Ok(( + utils::u64_from_bytes(&k[prefixlen..]) + .map_err(|_| Error::bad_database("Invalid pdu id in db."))?, + pdu, + )) }) } @@ -699,7 +689,7 @@ impl Rooms { user_id: &UserId, room_id: &RoomId, from: u64, - ) -> impl Iterator> { + ) -> impl Iterator> { // Create the first part of the full pdu id let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); @@ -710,6 +700,7 @@ impl Rooms { let current: &[u8] = ¤t; let user_id = user_id.clone(); + let prefixlen = prefix.len(); self.pduid_pdu .range(current..) .filter_map(|r| r.ok()) @@ -720,7 +711,11 @@ impl Rooms { if pdu.sender != user_id { pdu.unsigned.remove("transaction_id"); } - Ok((k, pdu)) + Ok(( + utils::u64_from_bytes(&k[prefixlen..]) + .map_err(|_| Error::bad_database("Invalid pdu id in db."))?, + pdu, + )) }) } @@ -919,7 +914,7 @@ impl Rooms { }) } - /// Returns an iterator over all left members of a room. + /// Returns an iterator over all rooms this user joined. pub fn rooms_joined(&self, user_id: &UserId) -> impl Iterator> { self.userroomid_joined .scan_prefix(user_id.to_string()) diff --git a/src/database/rooms/edus.rs b/src/database/rooms/edus.rs index 22d01666..fff30c22 100644 --- a/src/database/rooms/edus.rs +++ b/src/database/rooms/edus.rs @@ -1,15 +1,25 @@ use crate::{utils, Error, Result}; +use js_int::UInt; use ruma::{ - events::{AnyEvent as EduEvent, SyncEphemeralRoomEvent}, + events::{ + presence::{PresenceEvent, PresenceEventContent}, + AnyEvent as EduEvent, SyncEphemeralRoomEvent, + }, + presence::PresenceState, Raw, RoomId, UserId, }; -use std::convert::TryFrom; +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, +}; pub struct RoomEdus { pub(in super::super) roomuserid_lastread: sled::Tree, // RoomUserId = Room + User pub(in super::super) roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Count + UserId pub(in super::super) roomactiveid_userid: sled::Tree, // Typing, RoomActiveId = RoomId + TimeoutTime + Count pub(in super::super) roomid_lastroomactiveupdate: sled::Tree, // LastRoomActiveUpdate = Count + pub(in super::super) presenceid_presence: sled::Tree, // PresenceId = RoomId + Count + UserId + pub(in super::super) userid_lastpresenceupdate: sled::Tree, // LastPresenceUpdate = Count } impl RoomEdus { @@ -263,4 +273,182 @@ impl RoomEdus { })?)) }) } + + /// 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: ruma::events::presence::PresenceEvent, + globals: &super::super::globals::Globals, + ) -> Result<()> { + // TODO: Remove old entry? Or maybe just wipe completely from time to time? + + let count = globals.next_count()?.to_be_bytes(); + + let mut presence_id = room_id.to_string().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.to_string().as_bytes()); + + self.presenceid_presence.insert( + presence_id, + &*serde_json::to_string(&presence).expect("PresenceEvent can be serialized"), + )?; + + self.userid_lastpresenceupdate.insert( + &user_id.to_string().as_bytes(), + &utils::millis_since_unix_epoch().to_be_bytes(), + )?; + + 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.userid_lastpresenceupdate.insert( + &user_id.to_string().as_bytes(), + &utils::millis_since_unix_epoch().to_be_bytes(), + )?; + + Ok(()) + } + + /// Returns the timestamp of the last presence update of this user in millis since the unix epoch. + pub fn last_presence_update(&self, user_id: &UserId) -> Result> { + self.userid_lastpresenceupdate + .get(&user_id.to_string().as_bytes())? + .map(|bytes| { + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Invalid timestamp in userid_lastpresenceupdate.") + }) + }) + .transpose() + } + + /// Sets all users to offline who have been quiet for too long. + pub 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(|r| r.ok()) + .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 - timestamp > 5 * 60_000) + // 5 Minutes + { + self.userid_lastpresenceupdate.remove(&user_id_bytes)?; + + // Send new presence events to set the user offline + let count = globals.next_count()?.to_be_bytes(); + let user_id = 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.to_string().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_string(&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.clone(), + }) + .expect("PresenceEvent can be serialized"), + )?; + } + } + + Ok(()) + } + + /// Returns an iterator over the most recent presence updates that happened after the event with id `since`. + pub fn presence_since( + &self, + room_id: &RoomId, + since: u64, + rooms: &super::Rooms, + globals: &super::super::globals::Globals, + ) -> Result> { + self.presence_maintain(rooms, globals)?; + + let mut prefix = room_id.to_string().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 + .range(&*first_possible_edu..) + .filter_map(|r| r.ok()) + .take_while(|(key, _)| key.starts_with(&prefix)) + { + let user_id = UserId::try_from( + 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 mut presence = serde_json::from_slice::(&value) + .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); + } + + hashmap.insert(user_id, presence); + } + + Ok(hashmap) + } } diff --git a/src/database/users.rs b/src/database/users.rs index 5030f32e..c7927677 100644 --- a/src/database/users.rs +++ b/src/database/users.rs @@ -9,7 +9,7 @@ use ruma::{ }, }, events::{AnyToDeviceEvent, EventType}, - DeviceId, Raw, UserId, + DeviceId, Raw, RoomId, UserId, }; use std::{collections::BTreeMap, convert::TryFrom, mem, time::SystemTime}; @@ -22,7 +22,8 @@ pub struct Users { pub(super) token_userdeviceid: sled::Tree, pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + AlgorithmAndDeviceId - pub(super) keychangeid_userid: sled::Tree, // KeyChangeId = Count + pub(super) userid_lastonetimekeyupdate: sled::Tree, // LastOneTimeKeyUpdate = Count + pub(super) keychangeid_userid: sled::Tree, // KeyChangeId = RoomId + Count pub(super) keyid_key: sled::Tree, // KeyId = UserId + KeyId (depends on key type) pub(super) userid_masterkeyid: sled::Tree, pub(super) userid_selfsigningkeyid: sled::Tree, @@ -270,6 +271,7 @@ impl Users { device_id: &DeviceId, one_time_key_key: &AlgorithmAndDeviceId, one_time_key_value: &OneTimeKey, + globals: &super::globals::Globals, ) -> Result<()> { let mut key = user_id.to_string().as_bytes().to_vec(); key.push(0xff); @@ -294,14 +296,32 @@ impl Users { .expect("OneTimeKey::to_string always works"), )?; + self.userid_lastonetimekeyupdate.insert( + &user_id.to_string().as_bytes(), + &globals.next_count()?.to_be_bytes(), + )?; + Ok(()) } + pub fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { + self + .userid_lastonetimekeyupdate + .get(&user_id.to_string().as_bytes())? + .map(|bytes| { + utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.") + }) + }) + .unwrap_or(Ok(0)) + } + pub fn take_one_time_key( &self, user_id: &UserId, device_id: &DeviceId, key_algorithm: &KeyAlgorithm, + globals: &super::globals::Globals, ) -> Result> { let mut prefix = user_id.to_string().as_bytes().to_vec(); prefix.push(0xff); @@ -311,6 +331,11 @@ impl Users { prefix.extend_from_slice(key_algorithm.to_string().as_bytes()); prefix.push(b':'); + self.userid_lastonetimekeyupdate.insert( + &user_id.to_string().as_bytes(), + &globals.next_count()?.to_be_bytes(), + )?; + self.onetimekeyid_onetimekeys .scan_prefix(&prefix) .next() @@ -371,6 +396,7 @@ impl Users { user_id: &UserId, device_id: &DeviceId, device_keys: &DeviceKeys, + rooms: &super::rooms::Rooms, globals: &super::globals::Globals, ) -> Result<()> { let mut userdeviceid = user_id.to_string().as_bytes().to_vec(); @@ -382,8 +408,14 @@ impl Users { &*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"), )?; - self.keychangeid_userid - .insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?; + let count = globals.next_count()?.to_be_bytes(); + for room_id in rooms.rooms_joined(&user_id) { + let mut key = room_id?.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&count); + + self.keychangeid_userid.insert(key, &*user_id.to_string())?; + } Ok(()) } @@ -394,6 +426,7 @@ impl Users { master_key: &CrossSigningKey, self_signing_key: &Option, user_signing_key: &Option, + rooms: &super::rooms::Rooms, globals: &super::globals::Globals, ) -> Result<()> { // TODO: Check signatures @@ -482,8 +515,14 @@ impl Users { .insert(&*user_id.to_string(), user_signing_key_key)?; } - self.keychangeid_userid - .insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?; + let count = globals.next_count()?.to_be_bytes(); + for room_id in rooms.rooms_joined(&user_id) { + let mut key = room_id?.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&count); + + self.keychangeid_userid.insert(key, &*user_id.to_string())?; + } Ok(()) } @@ -494,6 +533,7 @@ impl Users { key_id: &str, signature: (String, String), sender_id: &UserId, + rooms: &super::rooms::Rooms, globals: &super::globals::Globals, ) -> Result<()> { let mut key = target_id.to_string().as_bytes().to_vec(); @@ -525,19 +565,42 @@ impl Users { .expect("CrossSigningKey::to_string always works"), )?; - self.keychangeid_userid - .insert(globals.next_count()?.to_be_bytes(), &*target_id.to_string())?; + // TODO: Should we notify about this change? + let count = globals.next_count()?.to_be_bytes(); + for room_id in rooms.rooms_joined(&target_id) { + let mut key = room_id?.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&count); + + self.keychangeid_userid + .insert(key, &*target_id.to_string())?; + } Ok(()) } - pub fn keys_changed(&self, since: u64) -> impl Iterator> { + pub fn keys_changed( + &self, + room_id: &RoomId, + from: u64, + to: Option, + ) -> impl Iterator> { + let mut prefix = room_id.to_string().as_bytes().to_vec(); + prefix.push(0xff); + + let mut start = prefix.clone(); + start.extend_from_slice(&(from + 1).to_be_bytes()); + + let mut end = prefix.clone(); + end.extend_from_slice(&to.unwrap_or(u64::MAX).to_be_bytes()); + self.keychangeid_userid - .range((since + 1).to_be_bytes()..) - .values() - .map(|bytes| { + .range(start..end) + .filter_map(|r| r.ok()) + .take_while(move |(k, _)| k.starts_with(&prefix)) + .map(|(_, bytes)| { Ok( - UserId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| { + UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| { Error::bad_database( "User ID in devicekeychangeid_userid is invalid unicode.", ) diff --git a/src/main.rs b/src/main.rs index 1feee4d1..86d8446b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,7 @@ fn setup_rocket() -> rocket::Rocket { client_server::register_route, client_server::get_login_route, client_server::login_route, + client_server::whoami_route, client_server::logout_route, client_server::logout_all_route, client_server::change_password_route, @@ -86,7 +87,7 @@ fn setup_rocket() -> rocket::Rocket { client_server::get_state_events_route, client_server::get_state_events_for_key_route, client_server::get_state_events_for_empty_key_route, - client_server::sync_route, + client_server::sync_events_route, client_server::get_context_route, client_server::get_message_events_route, client_server::turn_server_route, @@ -107,6 +108,7 @@ fn setup_rocket() -> rocket::Rocket { client_server::options_route, client_server::upload_signing_keys_route, client_server::upload_signatures_route, + client_server::get_key_changes_route, client_server::pushers_route, client_server::set_pushers_route, //server_server::well_known_server, diff --git a/src/pdu.rs b/src/pdu.rs index 0cfdb639..c1492970 100644 --- a/src/pdu.rs +++ b/src/pdu.rs @@ -79,39 +79,99 @@ impl PduEvent { } pub fn to_sync_room_event(&self) -> Raw { - let json = serde_json::to_string(&self).expect("PDUs are always valid"); - serde_json::from_str::(&json) - .map(Raw::from) - .expect("AnySyncRoomEvent can always be built from a full PDU event") + 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, + "unsigned": self.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") } + pub fn to_room_event(&self) -> Raw { - let json = serde_json::to_string(&self).expect("PDUs are always valid"); - serde_json::from_str::(&json) - .map(Raw::from) - .expect("AnyRoomEvent can always be built from a full PDU event") + 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, + "unsigned": self.unsigned, + "room_id": self.room_id, + }); + + 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") } + pub fn to_state_event(&self) -> Raw { - let json = serde_json::to_string(&self).expect("PDUs are always valid"); - serde_json::from_str::(&json) - .map(Raw::from) - .expect("AnyStateEvent can always be built from a full PDU event") + let json = json!({ + "content": self.content, + "type": self.kind, + "event_id": self.event_id, + "sender": self.sender, + "origin_server_ts": self.origin_server_ts, + "unsigned": self.unsigned, + "room_id": self.room_id, + "state_key": self.state_key, + }); + + serde_json::from_value(json).expect("Raw::from_value always works") } + pub fn to_sync_state_event(&self) -> Raw { - let json = serde_json::to_string(&self).expect("PDUs are always valid"); - serde_json::from_str::(&json) - .map(Raw::from) - .expect("AnySyncStateEvent can always be built from a full PDU event") + let json = json!({ + "content": self.content, + "type": self.kind, + "event_id": self.event_id, + "sender": self.sender, + "origin_server_ts": self.origin_server_ts, + "unsigned": self.unsigned, + "state_key": self.state_key, + }); + + serde_json::from_value(json).expect("Raw::from_value always works") } + pub fn to_stripped_state_event(&self) -> Raw { - let json = serde_json::to_string(&self).expect("PDUs are always valid"); - serde_json::from_str::(&json) - .map(Raw::from) - .expect("AnyStrippedStateEvent can always be built from a full PDU event") + let json = json!({ + "content": self.content, + "type": self.kind, + "sender": self.sender, + "state_key": self.state_key, + }); + + serde_json::from_value(json).expect("Raw::from_value always works") } + pub fn to_member_event(&self) -> Raw> { - let json = serde_json::to_string(&self).expect("PDUs are always valid"); - serde_json::from_str::>(&json) - .map(Raw::from) - .expect("StateEvent can always be built from a full PDU event") + let json = json!({ + "content": self.content, + "type": self.kind, + "event_id": self.event_id, + "sender": self.sender, + "origin_server_ts": self.origin_server_ts, + "redacts": self.redacts, + "unsigned": self.unsigned, + "room_id": self.room_id, + "state_key": self.state_key, + }); + + serde_json::from_value(json).expect("Raw::from_value always works") } } diff --git a/src/ruma_wrapper.rs b/src/ruma_wrapper.rs index 2a82b10f..05934365 100644 --- a/src/ruma_wrapper.rs +++ b/src/ruma_wrapper.rs @@ -11,9 +11,9 @@ use { Data, FromDataFuture, FromTransformedData, Transform, TransformFuture, Transformed, }, http::Status, + outcome::Outcome::*, response::{self, Responder}, tokio::io::AsyncReadExt, - Outcome::*, Request, State, }, ruma::api::Endpoint, @@ -24,7 +24,7 @@ use { /// first. pub struct Ruma { pub body: T, - pub user_id: Option, + pub sender_id: Option, pub device_id: Option>, pub json_body: Option>, // This is None when body is not a valid string } @@ -94,7 +94,7 @@ impl<'a, T: Endpoint> FromTransformedData<'a> for Ruma { match T::try_from(http_request) { Ok(t) => Success(Ruma { body: t, - user_id, + sender_id: user_id, device_id, // TODO: Can we avoid parsing it again? (We only need this for append_pdu) json_body: utils::string_from_bytes(&body) diff --git a/sytest/sytest-whitelist b/sytest/sytest-whitelist index ec2aba4e..3c1095b3 100644 --- a/sytest/sytest-whitelist +++ b/sytest/sytest-whitelist @@ -36,7 +36,6 @@ Current state appears in timeline in private history Current state appears in timeline in private history with many messages before Deleted tags appear in an incremental v2 /sync Deleting a non-existent alias should return a 404 -Device messages over federation wake up /sync Device messages wake up /sync Events come down the correct room GET /device/{deviceId} @@ -120,6 +119,6 @@ User in shared private room does appear in user directory User is offline if they set_presence=offline in their sync Users with sufficient power-level can delete other's aliases Version responds 200 OK with valid structure -Wildcard device messages over federation wake up /sync +We should see our own leave event when rejecting an invite, even if history_visibility is restricted (riot-web/3462) Wildcard device messages wake up /sync query for user with no keys returns empty key dict