From b02c5689419548c62e038d1c022769e047cd4039 Mon Sep 17 00:00:00 2001 From: timokoesters Date: Fri, 1 May 2020 20:26:57 +0200 Subject: [PATCH] feat: notifications, simple permission systems --- Cargo.lock | 93 +++++++------- Cargo.toml | 2 +- src/client_server.rs | 226 +++++++++++++++++++++++++++++----- src/data.rs | 287 ++++++++++++++++++++++++++++++++++++------- src/database.rs | 20 +-- src/main.rs | 4 + 6 files changed, 506 insertions(+), 126 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d205cd24..3d2ef34c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,7 +34,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -247,7 +247,7 @@ version = "0.3.0" source = "git+https://github.com/SergioBenitez/Devise.git?rev=e58b3ac9a#e58b3ac9afc3b6ff10a8aaf02a3e768a8f530089" dependencies = [ "devise_core", - "quote 1.0.3", + "quote 1.0.4", ] [[package]] @@ -257,7 +257,7 @@ source = "git+https://github.com/SergioBenitez/Devise.git?rev=e58b3ac9a#e58b3ac9 dependencies = [ "bitflags", "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -414,7 +414,7 @@ checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -616,9 +616,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" +checksum = "0b823ebafcee1632403f2782d28728aab353f7881547a700043ef455c078326f" dependencies = [ "wasm-bindgen", ] @@ -796,6 +796,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + [[package]] name = "openssl" version = "0.10.29" @@ -903,7 +909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -988,9 +994,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" dependencies = [ "proc-macro2 1.0.10", ] @@ -1117,13 +1123,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.12" +version = "0.16.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196" dependencies = [ "cc", - "lazy_static", "libc", + "once_cell", "spin", "untrusted", "web-sys", @@ -1162,7 +1168,7 @@ source = "git+https://github.com/SergioBenitez/Rocket.git?branch=async#78c8ac8cc dependencies = [ "devise", "indexmap", - "quote 1.0.3", + "quote 1.0.4", "rocket_http", "version_check 0.9.1", "yansi 0.5.0", @@ -1212,14 +1218,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f6b02a6a860a96e3c2081c8aea88b37b2918b53e539856b73aadde1908b65ad" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] [[package]] name = "ruma-client-api" version = "0.8.0-rc.5" -source = "git+https://github.com/ruma/ruma-client-api.git#5a26c387646e17ba076e478d1e7b896b7e47137d" +source = "git+https://github.com/ruma/ruma-client-api.git#dbb60142cf336784d809c6c4d79bd8de4c67fb5f" dependencies = [ "http", "js_int", @@ -1254,14 +1260,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abd3cfe96c9887fe2eebfa2e5e7d3a4afff02c374874d4e718f46dab5fd3320d" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] [[package]] name = "ruma-federation-api" version = "0.0.1" -source = "git+https://github.com/ruma/ruma-federation-api.git#263f2ffc75be6542bd68161e446adf588505fb56" +source = "git+https://github.com/ruma/ruma-federation-api.git#ccbf216f39bbbaa59131cc200eae5bd18aa1947c" dependencies = [ "js_int", "ruma-api", @@ -1284,13 +1290,16 @@ dependencies = [ [[package]] name = "ruma-serde" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09901d608958f63618546957134dd4242d2ca07a885a28f794ad4574a937c22c" +checksum = "6ce9a52acce7ed3809e1b47d9cc67ee93972a2b0fedaaa76d6e794456a79858b" dependencies = [ + "dtoa", + "itoa", "js_int", "serde", "serde_json", + "url", ] [[package]] @@ -1412,7 +1421,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -1506,7 +1515,7 @@ checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck", "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -1528,7 +1537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "unicode-xid 0.2.0", ] @@ -1576,9 +1585,9 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9c43f1bb96970e153bcbae39a65e249ccb942bd9d36dbdf086024920417c9c" +checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" dependencies = [ "bytes", "fnv", @@ -1604,7 +1613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", ] @@ -1764,9 +1773,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.60" +version = "0.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" +checksum = "f56e97dbea16d5f56549d6c8ea7f36efb6be98507308650c1a5970574b3941b9" dependencies = [ "cfg-if", "serde", @@ -1776,24 +1785,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.60" +version = "0.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" +checksum = "7b75d4f3f9b81dfc7d66b955876b325b20e8affd4ce8d93e51162626fc5faadb" dependencies = [ "bumpalo", "lazy_static", "log", "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" +checksum = "736dcd8f8455458c82614f12116aabd0209d440c1a28d8824bcaed755ac3e058" dependencies = [ "cfg-if", "js-sys", @@ -1803,22 +1812,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.60" +version = "0.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" +checksum = "9dcde4b19e863521c1e78ecf100935132396291b09ae0ae2e155ff84ccbe9736" dependencies = [ - "quote 1.0.3", + "quote 1.0.4", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.60" +version = "0.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" +checksum = "13d87d2b117af2b86472402d70f7eb173bbe166beb5e727f3c0bebecdf356504" dependencies = [ "proc-macro2 1.0.10", - "quote 1.0.3", + "quote 1.0.4", "syn 1.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -1826,15 +1835,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.60" +version = "0.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" +checksum = "71f77b681efd0bca6f8ea356cdc2e497538b41d3e2a02afed18ce8f022231d29" [[package]] name = "web-sys" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" +checksum = "07c5819dc39222a788ca169a81aef7d02739019256300534f493b5747d5469c2" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 5ceb72f5..7001ada5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ directories = "2.0.2" js_int = "0.1.5" serde_json = "1.0.52" serde = "1.0.106" -tokio = { version = "0.2.19", features = ["macros"] } +tokio = { version = "0.2.20", features = ["macros"] } rand = "0.7.3" rust-argon2 = "0.8.2" reqwest = "0.10.4" diff --git a/src/client_server.rs b/src/client_server.rs index 27db78cd..6b56fb13 100644 --- a/src/client_server.rs +++ b/src/client_server.rs @@ -8,10 +8,12 @@ use ruma_client_api::{ account::register, alias::get_alias, capabilities::get_capabilities, + client_exchange::send_event_to_device, config::{get_global_account_data, set_global_account_data}, directory::{self, get_public_rooms_filtered}, filter::{self, create_filter, get_filter}, keys::{get_keys, upload_keys}, + media::get_media_config, membership::{ forget_room, get_member_events, invite_user, join_room_by_id, join_room_by_id_or_alias, leave_room, @@ -21,7 +23,7 @@ use ruma_client_api::{ profile::{ get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name, }, - push::get_pushrules_all, + push::{self, get_pushrules_all, set_pushrule, set_pushrule_enabled}, read_marker::set_read_marker, room::create_room, session::{get_login_types, login}, @@ -40,7 +42,6 @@ use serde_json::json; use std::{ collections::BTreeMap, convert::{TryFrom, TryInto}, - path::PathBuf, time::{Duration, SystemTime}, }; @@ -238,9 +239,86 @@ pub fn get_capabilities_route( #[get("/_matrix/client/r0/pushrules")] pub fn get_pushrules_all_route() -> MatrixResult { // TODO - MatrixResult(Ok(get_pushrules_all::Response { - global: BTreeMap::new(), - })) + let mut global = BTreeMap::new(); + global.insert( + push::RuleKind::Underride, + vec![push::PushRule { + actions: vec![ + push::Action::Notify, + push::Action::SetTweak { + kind: push::TweakKind::Highlight, + value: Some(false.into()), + }, + ], + default: true, + enabled: true, + rule_id: ".m.rule.message".to_owned(), + conditions: Some(vec![push::PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.room.message".to_owned(), + }]), + pattern: None, + }], + ); + MatrixResult(Ok(get_pushrules_all::Response { global })) +} + +#[put( + "/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>", + data = "" +)] +pub fn set_pushrule_route( + data: State, + body: Ruma, + _scope: String, + _kind: String, + _rule_id: String, +) -> MatrixResult { + // TODO + let user_id = body.user_id.clone().expect("user is authenticated"); + data.room_userdata_update( + None, + &user_id, + EduEvent::PushRules(ruma_events::push_rules::PushRulesEvent { + content: ruma_events::push_rules::PushRulesEventContent { + global: ruma_events::push_rules::Ruleset { + content: vec![], + override_rules: vec![], + room: vec![], + sender: vec![], + underride: vec![ruma_events::push_rules::ConditionalPushRule { + actions: vec![ + ruma_events::push_rules::Action::Notify, + ruma_events::push_rules::Action::SetTweak( + ruma_events::push_rules::Tweak::Highlight { value: false }, + ), + ], + default: true, + enabled: true, + rule_id: ".m.rule.message".to_owned(), + conditions: vec![ruma_events::push_rules::PushCondition::EventMatch( + ruma_events::push_rules::EventMatchCondition { + key: "type".to_owned(), + pattern: "m.room.message".to_owned(), + }, + )], + }], + }, + }, + }), + ); + + MatrixResult(Ok(set_pushrule::Response)) +} + +#[put("/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>/enabled")] +pub fn set_pushrule_enabled_route( + _scope: String, + _kind: String, + _rule_id: String, +) -> MatrixResult { + // TODO + MatrixResult(Ok(set_pushrule_enabled::Response)) } #[get( @@ -284,7 +362,6 @@ pub fn set_global_account_data_route( _user_id: String, _type: String, ) -> MatrixResult { - // TODO MatrixResult(Ok(set_global_account_data::Response)) } @@ -485,8 +562,20 @@ pub fn set_read_marker_route( _room_id: String, ) -> MatrixResult { let user_id = body.user_id.clone().expect("user is authenticated"); - // TODO: Fully read + data.room_userdata_update( + Some(&body.room_id), + &user_id, + EduEvent::FullyRead(ruma_events::fully_read::FullyReadEvent { + content: ruma_events::fully_read::FullyReadEventContent { + event_id: body.fully_read.clone(), + }, + room_id: Some(body.room_id.clone()), + }), + ); + if let Some(event) = &body.read_receipt { + data.room_read_set(&body.room_id, &user_id, event); + let mut user_receipts = BTreeMap::new(); user_receipts.insert( user_id.clone(), @@ -564,6 +653,8 @@ pub fn create_room_route( Some("".to_owned()), ); + data.room_join(&room_id, &user_id); + data.pdu_append( room_id.clone(), user_id.clone(), @@ -604,8 +695,6 @@ pub fn create_room_route( ); } - data.room_join(&room_id, &user_id); - for user in &body.invite { data.room_invite(&user_id, &room_id, user); } @@ -855,17 +944,22 @@ pub fn create_message_event_route( _txn_id: String, body: Ruma, ) -> MatrixResult { + let user_id = body.user_id.clone().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 = data.pdu_append( - body.room_id.clone(), - body.user_id.clone().expect("user is authenticated"), - body.event_type.clone(), - body.json_body.clone(), - Some(unsigned), - None, - ); + let event_id = data + .pdu_append( + body.room_id.clone(), + user_id.clone(), + body.event_type.clone(), + body.json_body.clone(), + Some(unsigned), + None, + ) + .expect("message events are always okay"); + MatrixResult(Ok(create_message_event::Response { event_id })) } @@ -880,16 +974,21 @@ pub fn create_state_event_for_key_route( _state_key: String, body: Ruma, ) -> MatrixResult { + let user_id = body.user_id.clone().expect("user is authenticated"); + // Reponse of with/without key is the same - let event_id = data.pdu_append( + if let Some(event_id) = data.pdu_append( body.room_id.clone(), body.user_id.clone().expect("user is authenticated"), body.event_type.clone(), body.json_body.clone(), None, Some(body.state_key.clone()), - ); - MatrixResult(Ok(create_state_event_for_key::Response { event_id })) + ) { + MatrixResult(Ok(create_state_event_for_key::Response { event_id })) + } else { + panic!("TODO: error missing permissions"); + } } #[put( @@ -902,16 +1001,21 @@ pub fn create_state_event_for_empty_key_route( _event_type: String, body: Ruma, ) -> MatrixResult { + let user_id = body.user_id.clone().expect("user is authenticated"); + // Reponse of with/without key is the same - let event_id = data.pdu_append( + if let Some(event_id) = data.pdu_append( body.room_id.clone(), body.user_id.clone().expect("user is authenticated"), body.event_type.clone(), - body.json_body, + body.json_body.clone(), None, Some("".to_owned()), - ); - MatrixResult(Ok(create_state_event_for_empty_key::Response { event_id })) + ) { + MatrixResult(Ok(create_state_event_for_empty_key::Response { event_id })) + } else { + panic!("TODO: error missing permissions"); + } } #[get("/_matrix/client/r0/sync", data = "")] @@ -919,7 +1023,7 @@ pub fn sync_route( data: State, body: Ruma, ) -> MatrixResult { - std::thread::sleep(Duration::from_millis(300)); + std::thread::sleep(Duration::from_millis(1500)); let user_id = body.user_id.clone().expect("user is authenticated"); let next_batch = data.last_pdu_index().to_string(); @@ -932,7 +1036,7 @@ pub fn sync_route( .unwrap_or(0); for room_id in joined_roomids { - let pdus = data.pdus_since(&room_id, since); + let mut pdus = data.pdus_since(&room_id, since); let mut send_member_count = false; let mut send_full_state = false; @@ -946,6 +1050,25 @@ pub fn sync_route( } } + let notification_count = if let Some(last_read) = data.room_read_get(&room_id, &user_id) { + Some((data.pdus_since(&room_id, last_read).len() as u32).into()) + } else { + 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() + .and_then(|e| data.pdu_get_count(&e.event_id)) + .map(|c| c.to_string()); + let room_events = pdus .into_iter() .map(|pdu| pdu.to_room_event()) @@ -957,7 +1080,13 @@ pub fn sync_route( joined_rooms.insert( room_id.clone().try_into().unwrap(), sync_events::JoinedRoom { - account_data: sync_events::AccountData { events: Vec::new() }, + account_data: sync_events::AccountData { + events: data + .room_userdata_since(Some(&room_id), &user_id, since) + .into_iter() + .map(|(_, v)| v) + .collect(), + }, summary: sync_events::RoomSummary { heroes: Vec::new(), joined_member_count: if send_member_count { @@ -973,11 +1102,11 @@ pub fn sync_route( }, unread_notifications: sync_events::UnreadNotificationsCount { highlight_count: None, - notification_count: None, + notification_count, }, timeline: sync_events::Timeline { - limited: None, - prev_batch: Some(since.to_string()), + limited: if limited { Some(limited) } else { None }, + prev_batch, events: room_events, }, // TODO: state before timeline @@ -1042,6 +1171,13 @@ pub fn sync_route( invite: invited_rooms, }, presence: sync_events::Presence { events: Vec::new() }, + account_data: sync_events::AccountData { + events: data + .room_userdata_since(None, &user_id, since) + .into_iter() + .map(|(_, v)| v) + .collect(), + }, device_lists: Default::default(), device_one_time_keys_count: Default::default(), to_device: sync_events::ToDevice { events: Vec::new() }, @@ -1059,14 +1195,23 @@ pub fn get_message_events_route( } if let Ok(from) = body.from.clone().parse() { - let pdus = data.pdus_until(&body.room_id, from); + let pdus = data.pdus_until( + &body.room_id, + from, + body.limit.map(|l| l.try_into().unwrap()).unwrap_or(10), + ); + let prev_batch = pdus + .last() + .and_then(|e| data.pdu_get_count(&e.event_id)) + .map(|c| c.to_string()); let room_events = pdus .into_iter() .map(|pdu| pdu.to_room_event()) .collect::>(); + MatrixResult(Ok(get_message_events::Response { start: Some(body.from.clone()), - end: None, + end: prev_batch, chunk: room_events, state: Vec::new(), })) @@ -1099,6 +1244,23 @@ pub fn publicised_groups_route() -> MatrixResult })) } +#[put("/_matrix/client/r0/sendToDevice/<_event_type>/<_txn_id>")] +pub fn send_event_to_device_route( + _event_type: String, + _txn_id: String, +) -> MatrixResult { + // TODO + MatrixResult(Ok(send_event_to_device::Response)) +} + +#[get("/_matrix/media/r0/config")] +pub fn get_media_config_route() -> MatrixResult { + // TODO + MatrixResult(Ok(get_media_config::Response { + upload_size: 0_u32.into(), + })) +} + #[options("/<_segments..>")] pub fn options_route( _segments: rocket::http::uri::Segments, diff --git a/src/data.rs b/src/data.rs index 3b652ba0..9b9c5412 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,11 +1,15 @@ -use crate::{utils, Database, PduEvent}; -use ruma_events::{collections::only::Event as EduEvent, EventJson, EventType}; +use crate::{database::COUNTER, utils, Database, PduEvent}; +use ruma_events::{ + collections::only::Event as EduEvent, room::power_levels::PowerLevelsEventContent, EventJson, + EventType, +}; use ruma_federation_api::RoomV3Pdu; use ruma_identifiers::{EventId, RoomId, UserId}; use serde_json::json; use std::{ collections::HashMap, convert::{TryFrom, TryInto}, + mem, }; pub struct Data { @@ -189,7 +193,14 @@ impl Data { } pub fn room_join(&self, room_id: &RoomId, user_id: &UserId) -> bool { - if !self.room_exists(room_id) { + if !self.room_exists(room_id) + && !self + .db + .userid_joinroomids + .get_iter(user_id.to_string().as_bytes()) + .values() + .any(|r| r.unwrap() == room_id.to_string().as_bytes()) + { return false; } @@ -249,8 +260,7 @@ impl Data { /// Check if a room exists by looking for PDUs in that room. pub fn room_exists(&self, room_id: &RoomId) -> bool { // Create the first part of the full pdu id - let mut prefix = vec![b'd']; - prefix.extend_from_slice(room_id.to_string().as_bytes()); + let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); // Add delimiter so we don't find rooms starting with the same id if let Some((key, _)) = self.db.pduid_pdu.get_gt(&prefix).unwrap() { @@ -397,13 +407,14 @@ impl Data { .collect() } - pub fn room_pdu_first(&self, room_id: &RoomId, pdu_index: u64) -> bool { - let mut pdu_id = vec![b'd']; - pdu_id.extend_from_slice(room_id.to_string().as_bytes()); - pdu_id.push(0xff); - pdu_id.extend_from_slice(&pdu_index.to_be_bytes()); - - self.db.pduid_pdu.get_lt(&pdu_id).unwrap().is_none() + pub fn pdu_get_count(&self, event_id: &EventId) -> Option { + self.db + .eventid_pduid + .get(event_id.to_string().as_bytes()) + .unwrap() + .map(|pdu_id| { + utils::u64_from_bytes(&pdu_id[pdu_id.len() - mem::size_of::()..pdu_id.len()]) + }) } pub fn pdu_get(&self, event_id: &EventId) -> Option { @@ -459,7 +470,39 @@ impl Data { content: serde_json::Value, unsigned: Option>, state_key: Option, - ) -> EventId { + ) -> Option { + // Is the event authorized? + if state_key.is_some() { + if let Some(pdu) = self + .room_state(&room_id) + .get(&(EventType::RoomPowerLevels, "".to_owned())) + { + let power_levels = serde_json::from_value::>( + pdu.content.clone(), + ) + .unwrap() + .deserialize() + .unwrap(); + + match event_type { + EventType::RoomMember => { + // Member events are okay for now (TODO) + } + _ if power_levels + .users + .get(&sender) + .unwrap_or(&power_levels.users_default) + <= &0.into() => + { + // Not authorized + return None; + } + // User has sufficient power + _ => {} + } + } + } + // prev_events are the leaves of the current graph. This method removes all leaves from the // room and replaces them with our event // TODO: Make sure this isn't called twice in parallel @@ -523,22 +566,19 @@ impl Data { self.pdu_leaves_replace(&room_id, &pdu.event_id); - // The new value will need a new index. We store the last used index in 'n' // The count will go up regardless of the room_id // This is also the next_batch/since value // Increment the last index and use that let index = utils::u64_from_bytes( &self .db - .pduid_pdu - .update_and_fetch(b"n", utils::increment) + .global + .update_and_fetch(COUNTER, utils::increment) .unwrap() .unwrap(), ); - let mut pdu_id = vec![b'd']; - pdu_id.extend_from_slice(room_id.to_string().as_bytes()); - + let mut pdu_id = room_id.to_string().as_bytes().to_vec(); pdu_id.push(0xff); // Add delimiter so we don't find rooms starting with the same id pdu_id.extend_from_slice(&index.to_be_bytes()); @@ -564,7 +604,9 @@ impl Data { .unwrap(); } - pdu.event_id + self.room_read_set(&room_id, &sender, &pdu.event_id); + + Some(pdu.event_id) } /// Returns a vector of all PDUs in a room. @@ -573,12 +615,11 @@ impl Data { } pub fn last_pdu_index(&self) -> u64 { - let count_key: Vec = vec![b'n']; utils::u64_from_bytes( &self .db - .pduid_pdu - .get(&count_key) + .global + .get(&COUNTER) .unwrap() .unwrap_or_else(|| (&0_u64.to_be_bytes()).into()), ) @@ -586,15 +627,23 @@ impl Data { /// Returns a vector of all events in a room that happened after the event with id `since`. pub fn pdus_since(&self, room_id: &RoomId, since: u64) -> Vec { + // 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); // Add delimiter so we don't find rooms starting with the same id + pdu_id.extend_from_slice(&(since).to_be_bytes()); + + self.pdus_since_pduid(room_id, pdu_id) + } + + /// Returns a vector of all events in a room that happened after the event with id `since`. + pub fn pdus_since_pduid(&self, room_id: &RoomId, pdu_id: Vec) -> Vec { let mut pdus = Vec::new(); // Create the first part of the full pdu id - let mut prefix = vec![b'd']; - prefix.extend_from_slice(room_id.to_string().as_bytes()); + let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); // Add delimiter so we don't find rooms starting with the same id - let mut current = prefix.clone(); - current.extend_from_slice(&since.to_be_bytes()); + let mut current = pdu_id; while let Some((key, value)) = self.db.pduid_pdu.get_gt(¤t).unwrap() { if key.starts_with(&prefix) { @@ -608,19 +657,18 @@ impl Data { pdus } - pub fn pdus_until(&self, room_id: &RoomId, until: u64) -> Vec { + pub fn pdus_until(&self, room_id: &RoomId, until: u64, max: u32) -> Vec { let mut pdus = Vec::new(); // Create the first part of the full pdu id - let mut prefix = vec![b'd']; - prefix.extend_from_slice(room_id.to_string().as_bytes()); + let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); // Add delimiter so we don't find rooms starting with the same id let mut current = prefix.clone(); current.extend_from_slice(&until.to_be_bytes()); while let Some((key, value)) = self.db.pduid_pdu.get_lt(¤t).unwrap() { - if key.starts_with(&prefix) { + if pdus.len() < max as usize && key.starts_with(&prefix) { current = key.to_vec(); pdus.push(serde_json::from_slice(&value).expect("pdu in db is valid")); } else { @@ -670,8 +718,8 @@ impl Data { let index = utils::u64_from_bytes( &self .db - .pduid_pdu - .update_and_fetch(b"n", utils::increment) + .global + .update_and_fetch(COUNTER, utils::increment) .unwrap() .unwrap(), ); @@ -695,17 +743,14 @@ impl Data { prefix.push(0xff); let mut current = prefix.clone(); - current.extend_from_slice(&since.to_be_bytes()); + current.extend_from_slice(&(since + 1).to_be_bytes()); while let Some((key, value)) = self.db.roomlatestid_roomlatest.get_gt(¤t).unwrap() { if key.starts_with(&prefix) { current = key.to_vec(); room_latests.push( serde_json::from_slice::>(&value) - .expect("room_latest in db is valid") - .deserialize() - .expect("room_latest in db is valid") - .into(), + .expect("room_latest in db is valid"), ); } else { break; @@ -715,6 +760,11 @@ impl Data { room_latests } + /// Returns a vector of the most recent read_receipts in a room that happened after the event with id `since`. + pub fn roomlatests_all(&self, room_id: &RoomId) -> Vec> { + self.roomlatests_since(room_id, 0) + } + pub fn roomactive_add(&self, event: EduEvent, room_id: &RoomId, timeout: u64) { let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); @@ -737,8 +787,8 @@ impl Data { let index = utils::u64_from_bytes( &self .db - .pduid_pdu - .update_and_fetch(b"n", utils::increment) + .global + .update_and_fetch(COUNTER, utils::increment) .unwrap() .unwrap(), ); @@ -790,10 +840,7 @@ impl Data { current = key.to_vec(); room_actives.push( serde_json::from_slice::>(&value) - .expect("room_active in db is valid") - .deserialize() - .expect("room_active in db is valid") - .into(), + .expect("room_active in db is valid"), ); } else { break; @@ -813,6 +860,158 @@ impl Data { } } + pub fn room_userdata_update( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + event: EduEvent, + ) { + let mut prefix = room_id + .map(|r| r.to_string()) + .unwrap_or_default() + .as_bytes() + .to_vec(); + prefix.push(0xff); + prefix.extend_from_slice(&user_id.to_string().as_bytes()); + prefix.push(0xff); + + // Start with last + if let Some(mut current) = self + .db + .roomuserdataid_accountdata + .scan_prefix(&prefix) + .keys() + .next_back() + .map(|c| c.unwrap()) + { + // Remove old entry (there should be at most one) + loop { + if !current.starts_with(&prefix) { + // We're in another room or user + break; + } + if current.rsplit(|&b| b == 0xff).nth(2).unwrap() == user_id.to_string().as_bytes() + { + // This is the old room_latest + self.db.roomuserdataid_accountdata.remove(current).unwrap(); + break; + } + // Else, try the event before that + if let Some((k, _)) = self.db.roomuserdataid_accountdata.get_lt(current).unwrap() { + current = k; + } else { + break; + } + } + } + + // Increment the last index and use that + let index = utils::u64_from_bytes( + &self + .db + .global + .update_and_fetch(COUNTER, utils::increment) + .unwrap() + .unwrap(), + ); + + let mut key = prefix; + key.extend_from_slice(&index.to_be_bytes()); + + let json = serde_json::to_value(&event).unwrap(); + key.extend_from_slice(json["type"].as_str().unwrap().as_bytes()); + + self.db + .roomuserdataid_accountdata + .insert(key, &*json.to_string()) + .unwrap(); + } + + pub fn room_userdata_get( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + kind: &str, + ) -> Option> { + self.room_userdata_all(room_id, user_id).remove(kind) + } + + pub fn room_userdata_since( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + since: u64, + ) -> HashMap> { + let mut userdata = HashMap::new(); + + let mut prefix = room_id + .map(|r| r.to_string()) + .unwrap_or_default() + .as_bytes() + .to_vec(); + prefix.push(0xff); + prefix.extend_from_slice(&user_id.to_string().as_bytes()); + prefix.push(0xff); + + let mut current = prefix.clone(); + current.extend_from_slice(&(since + 1).to_be_bytes()); + + while let Some((key, value)) = self.db.roomuserdataid_accountdata.get_gt(¤t).unwrap() + { + if key.starts_with(&prefix) { + current = key.to_vec(); + let json = serde_json::from_slice::(&value).unwrap(); + userdata.insert( + json["type"].as_str().unwrap().to_owned(), + serde_json::from_value::>(json) + .expect("userdata in db is valid"), + ); + } else { + break; + } + } + + userdata + } + + pub fn room_userdata_all( + &self, + room_id: Option<&RoomId>, + user_id: &UserId, + ) -> HashMap> { + self.room_userdata_since(room_id, user_id, 0) + } + + pub fn room_read_set( + &self, + room_id: &RoomId, + user_id: &UserId, + event_id: &EventId, + ) -> Option<()> { + let mut key = room_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&user_id.to_string().as_bytes()); + + self.db + .roomuserid_lastread + .insert(key, &self.pdu_get_count(event_id)?.to_be_bytes()) + .unwrap(); + + Some(()) + } + + pub fn room_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Option { + let mut key = room_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&user_id.to_string().as_bytes()); + + self.db + .roomuserid_lastread + .get(key) + .unwrap() + .map(|v| utils::u64_from_bytes(&v)) + } + pub fn debug(&self) { self.db.debug(); } diff --git a/src/database.rs b/src/database.rs index 3dd7564c..4551bc0a 100644 --- a/src/database.rs +++ b/src/database.rs @@ -5,6 +5,8 @@ use std::fs::remove_dir_all; pub struct MultiValue(sled::Tree); +pub const COUNTER: &str = "c"; + impl MultiValue { /// Get an iterator over all values. pub fn iter_all(&self) -> sled::Iter { @@ -67,22 +69,24 @@ pub struct Database { pub userid_deviceids: MultiValue, pub userdeviceid_token: sled::Tree, pub token_userid: sled::Tree, - pub pduid_pdu: sled::Tree, // PduId = 'd' + RoomId + Since (global since counter is at 'n') + pub pduid_pdu: sled::Tree, // PduId = RoomId + Count pub eventid_pduid: sled::Tree, pub roomid_pduleaves: MultiValue, pub roomstateid_pdu: sled::Tree, // Room + StateType + StateKey + pub roomuserdataid_accountdata: sled::Tree, // RoomUserDataId = Room + User + Count + Type + pub roomuserid_lastread: sled::Tree, // RoomUserId = Room + User pub roomid_joinuserids: MultiValue, pub roomid_inviteuserids: MultiValue, pub userid_joinroomids: MultiValue, pub userid_inviteroomids: MultiValue, pub userid_leftroomids: MultiValue, // EDUs: - pub roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Since + UserId TODO: Types - pub roomactiveid_roomactive: sled::Tree, // Typing, RoomActiveId = TimeoutTime + Since - pub globalallid_globalall: sled::Tree, // ToDevice, GlobalAllId = UserId + Since - pub globallatestid_globallatest: sled::Tree, // Presence, GlobalLatestId = Since + Type + UserId + pub roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Count + UserId TODO: Types + pub roomactiveid_roomactive: sled::Tree, // Typing, RoomActiveId = TimeoutTime + Count + pub globalallid_globalall: sled::Tree, // ToDevice, GlobalAllId = UserId + Count + pub globallatestid_globallatest: sled::Tree, // Presence, GlobalLatestId = Count + Type + UserId pub keypair: ruma_signatures::Ed25519KeyPair, - _db: sled::Db, + pub global: sled::Db, } impl Database { @@ -116,6 +120,8 @@ impl Database { eventid_pduid: db.open_tree("eventid_pduid").unwrap(), roomid_pduleaves: MultiValue(db.open_tree("roomid_pduleaves").unwrap()), roomstateid_pdu: db.open_tree("roomstateid_pdu").unwrap(), + roomuserdataid_accountdata: db.open_tree("roomuserdataid_accountdata").unwrap(), + roomuserid_lastread: db.open_tree("roomuserid_lastread").unwrap(), roomid_joinuserids: MultiValue(db.open_tree("roomid_joinuserids").unwrap()), roomid_inviteuserids: MultiValue(db.open_tree("roomid_inviteuserids").unwrap()), userid_joinroomids: MultiValue(db.open_tree("userid_joinroomids").unwrap()), @@ -132,7 +138,7 @@ impl Database { "key1".to_owned(), ) .unwrap(), - _db: db, + global: db, } } diff --git a/src/main.rs b/src/main.rs index 2f0a2f07..1b6e7aaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,8 @@ fn setup_rocket() -> rocket::Rocket { client_server::login_route, client_server::get_capabilities_route, client_server::get_pushrules_all_route, + client_server::set_pushrule_route, + client_server::set_pushrule_enabled_route, client_server::get_filter_route, client_server::create_filter_route, client_server::set_global_account_data_route, @@ -61,6 +63,8 @@ fn setup_rocket() -> rocket::Rocket { client_server::get_message_events_route, client_server::turn_server_route, client_server::publicised_groups_route, + client_server::send_event_to_device_route, + client_server::get_media_config_route, client_server::options_route, server_server::well_known_server, server_server::get_server_version,