From 41e56baf6075a70db5e90a5d94bb67893fa46267 Mon Sep 17 00:00:00 2001 From: Matthias Ahouansou Date: Mon, 6 May 2024 17:06:25 +0100 Subject: [PATCH] refactor: all the clippy lints --- Cargo.toml | 31 + src/api/appservice_server.rs | 9 +- src/api/client_server/account.rs | 18 +- src/api/client_server/alias.rs | 15 +- src/api/client_server/backup.rs | 6 +- src/api/client_server/context.rs | 28 +- src/api/client_server/device.rs | 6 +- src/api/client_server/filter.rs | 5 +- src/api/client_server/keys.rs | 62 +- src/api/client_server/membership.rs | 717 +++++++++--------- src/api/client_server/message.rs | 2 +- src/api/client_server/profile.rs | 8 +- src/api/client_server/push.rs | 2 +- src/api/client_server/read_marker.rs | 2 +- src/api/client_server/relations.rs | 12 +- src/api/client_server/report.rs | 13 +- src/api/client_server/room.rs | 35 +- src/api/client_server/session.rs | 12 +- src/api/client_server/space.rs | 2 +- src/api/client_server/state.rs | 8 +- src/api/client_server/sync.rs | 117 ++- src/api/client_server/tag.rs | 45 +- src/api/client_server/to_device.rs | 2 +- src/api/client_server/voip.rs | 12 +- src/api/ruma_wrapper/axum.rs | 4 +- src/api/server_server.rs | 227 +++--- src/clap.rs | 4 +- src/config/mod.rs | 3 +- src/config/proxy.rs | 4 +- src/database/abstraction/rocksdb.rs | 9 +- src/database/abstraction/sqlite.rs | 7 +- src/database/key_value/account_data.rs | 6 +- src/database/key_value/globals.rs | 11 +- src/database/key_value/pusher.rs | 5 +- src/database/key_value/rooms/alias.rs | 2 +- src/database/key_value/rooms/search.rs | 7 +- src/database/key_value/rooms/short.rs | 76 +- src/database/key_value/rooms/state.rs | 2 +- .../key_value/rooms/state_accessor.rs | 7 +- src/database/key_value/rooms/user.rs | 6 +- src/database/key_value/sending.rs | 4 +- src/database/key_value/transaction_ids.rs | 4 +- src/database/key_value/uiaa.rs | 2 +- src/database/key_value/users.rs | 11 +- src/database/mod.rs | 53 +- src/lib.rs | 8 + src/main.rs | 8 +- src/service/admin/mod.rs | 48 +- src/service/media/data.rs | 2 +- src/service/media/mod.rs | 8 +- src/service/mod.rs | 2 + src/service/pusher/mod.rs | 5 +- src/service/rooms/edus/presence/data.rs | 4 +- src/service/rooms/edus/presence/mod.rs | 5 +- src/service/rooms/edus/read_receipt/data.rs | 2 +- src/service/rooms/edus/typing/mod.rs | 2 +- src/service/rooms/event_handler/mod.rs | 193 ++--- src/service/rooms/pdu_metadata/mod.rs | 4 +- src/service/rooms/short/data.rs | 2 +- src/service/rooms/short/mod.rs | 2 +- src/service/rooms/spaces/mod.rs | 15 +- src/service/rooms/state/data.rs | 4 +- src/service/rooms/state/mod.rs | 21 +- src/service/rooms/state_accessor/mod.rs | 96 +-- src/service/rooms/threads/mod.rs | 4 +- src/service/rooms/timeline/mod.rs | 48 +- src/service/sending/mod.rs | 80 +- src/service/uiaa/mod.rs | 21 +- src/service/users/data.rs | 6 +- src/service/users/mod.rs | 6 +- src/utils/error.rs | 16 +- src/utils/mod.rs | 8 +- 72 files changed, 1091 insertions(+), 1152 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a5c2647..04d73b82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,15 +3,46 @@ explicit_outlives_requirements = "warn" unused_qualifications = "warn" [workspace.lints.clippy] +# Restrictions cloned_instead_of_copied = "warn" +create_dir = "warn" dbg_macro = "warn" +get_unwrap = "warn" +rc_mutex = "warn" str_to_string = "warn" +string_lit_chars_any = "warn" + +# Groups +complexity = "warn" +perf = "warn" +style = "warn" +suspicious = "warn" + +# To allow us to override certain lints +cargo = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } + +# lints we want to allow +cast_possible_truncation = "allow" # Not really a good way to handle this issue +missing_errors_doc = "allow" # Becomes highly redundant for functions accessing the database +missing_panics_doc = "allow" # We *should* only ever use unwraps/expects where infallable +module_name_repetitions = "allow" # Many things are ex-expored, and in many cases removing repetitions just makes things more confusing +multiple_crate_versions = "allow" # Would require quite a lot of effort for minimal gain +must_use_candidate = "allow" # Basically useless, even the docs say it's "Not bad at all" +redundant_closure_for_method_calls = "allow" # `.ok()` is nicer than `std::result::Result::ok` (which needs to be done where we use our own `Result`) +struct_excessive_bools = "allow" # Very easy to spot, and the behaviour it encourages to use is much worse than not using this at all +too_many_lines = "allow" # Some functions are complicated, and won't really be re-used in other places + +# fixed in !670 +doc_markdown = "allow" [package] authors = ["timokoesters "] +categories = ["web-programming::http-server"] description = "A Matrix homeserver written in Rust" edition = "2021" homepage = "https://conduit.rs" +keywords = ["homeserver", "matrix", "selfhost"] license = "Apache-2.0" name = "conduit" readme = "README.md" diff --git a/src/api/appservice_server.rs b/src/api/appservice_server.rs index 6af31d8f..004aaa13 100644 --- a/src/api/appservice_server.rs +++ b/src/api/appservice_server.rs @@ -17,11 +17,8 @@ pub(crate) async fn send_request( where T: OutgoingRequest + Debug, { - let destination = match registration.url { - Some(url) => url, - None => { - return Ok(None); - } + let Some(destination) = registration.url else { + return Ok(None); }; let hs_token = registration.hs_token.as_str(); @@ -33,7 +30,7 @@ where &[MatrixVersion::V1_0], ) .unwrap() - .map(|body| body.freeze()); + .map(BytesMut::freeze); let mut parts = http_request.uri().clone().into_parts(); let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned(); diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs index 0226abc7..46b2e4eb 100644 --- a/src/api/client_server/account.rs +++ b/src/api/client_server/account.rs @@ -69,11 +69,11 @@ pub async fn get_register_available_route( /// to check if the user id is valid and available. /// /// - Only works if registration is enabled -/// - If type is guest: ignores all parameters except initial_device_display_name +/// - If type is guest: ignores all parameters except `initial_device_display_name` /// - If sender is not appservice: Requires UIAA (but we only use a dummy stage) /// - If type is not guest and no username is given: Always fails after UIAA check /// - Creates a new account and populates it with default account data -/// - If `inhibit_login` is false: Creates a device and returns device id and access_token +/// - If `inhibit_login` is false: Creates a device and returns device id and `access_token` pub async fn register_route(body: Ruma) -> Result { if !services().globals.allow_registration() && body.appservice_info.is_none() { return Err(Error::BadRequest( @@ -149,7 +149,7 @@ pub async fn register_route(body: Ruma) -> Result) -> Result) -> Result) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let device_id = body.sender_device.as_ref().cloned(); + let device_id = body.sender_device.clone(); Ok(whoami::v3::Response { user_id: sender_user.clone(), @@ -410,7 +410,7 @@ pub async fn deactivate_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; diff --git a/src/api/client_server/alias.rs b/src/api/client_server/alias.rs index 7cbe9fa1..1f1fbb17 100644 --- a/src/api/client_server/alias.rs +++ b/src/api/client_server/alias.rs @@ -121,7 +121,7 @@ pub(crate) async fn get_alias_helper( .send_federation_request( room_alias.server_name(), federation::query::get_room_information::v1::Request { - room_alias: room_alias.to_owned(), + room_alias: room_alias.clone(), }, ) .await?; @@ -166,14 +166,11 @@ pub(crate) async fn get_alias_helper( } }; - let room_id = match room_id { - Some(room_id) => room_id, - None => { - return Err(Error::BadRequest( - ErrorKind::NotFound, - "Room with alias not found.", - )) - } + let Some(room_id) = room_id else { + return Err(Error::BadRequest( + ErrorKind::NotFound, + "Room with alias not found.", + )); }; Ok(get_alias::v3::Response::new( diff --git a/src/api/client_server/backup.rs b/src/api/client_server/backup.rs index 115cba7c..8bbe3ef1 100644 --- a/src/api/client_server/backup.rs +++ b/src/api/client_server/backup.rs @@ -86,7 +86,7 @@ pub async fn get_backup_info_route( etag: services() .key_backups .get_etag(sender_user, &body.version)?, - version: body.version.to_owned(), + version: body.version.clone(), }) } @@ -139,7 +139,7 @@ pub async fn add_backup_keys_route( room_id, session_id, key_data, - )? + )?; } } @@ -185,7 +185,7 @@ pub async fn add_backup_keys_for_room_route( &body.room_id, session_id, key_data, - )? + )?; } Ok(add_backup_keys_for_room::v3::Response { diff --git a/src/api/client_server/context.rs b/src/api/client_server/context.rs index 8e193e6b..5a2cbfdc 100644 --- a/src/api/client_server/context.rs +++ b/src/api/client_server/context.rs @@ -11,7 +11,7 @@ use tracing::error; /// Allows loading room history around an event. /// /// - Only works if the user is joined (TODO: always allow, but only show events if the user was -/// joined, depending on history_visibility) +/// joined, depending on `history_visibility`) pub async fn get_context_route( body: Ruma, ) -> Result { @@ -22,7 +22,7 @@ pub async fn get_context_route( LazyLoadOptions::Enabled { include_redundant_members, } => (true, *include_redundant_members), - _ => (false, false), + LazyLoadOptions::Disabled => (false, false), }; let mut lazy_loaded = HashSet::new(); @@ -103,8 +103,7 @@ pub async fn get_context_route( let start_token = events_before .last() - .map(|(count, _)| count.stringify()) - .unwrap_or_else(|| base_token.stringify()); + .map_or_else(|| base_token.stringify(), |(count, _)| count.stringify()); let events_before: Vec<_> = events_before .into_iter() @@ -159,8 +158,7 @@ pub async fn get_context_route( let end_token = events_after .last() - .map(|(count, _)| count.stringify()) - .unwrap_or_else(|| base_token.stringify()); + .map_or_else(|| base_token.stringify(), |(count, _)| count.stringify()); let events_after: Vec<_> = events_after .into_iter() @@ -176,21 +174,15 @@ pub async fn get_context_route( .get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; state.push(pdu.to_state_event()); } else if !lazy_load_enabled || lazy_loaded.contains(&state_key) { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; state.push(pdu.to_state_event()); } diff --git a/src/api/client_server/device.rs b/src/api/client_server/device.rs index 9a42f048..e1cddaa4 100644 --- a/src/api/client_server/device.rs +++ b/src/api/client_server/device.rs @@ -83,7 +83,7 @@ pub async fn delete_device_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -137,7 +137,7 @@ pub async fn delete_devices_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -162,7 +162,7 @@ pub async fn delete_devices_route( } for device_id in &body.devices { - services().users.remove_device(sender_user, device_id)? + services().users.remove_device(sender_user, device_id)?; } Ok(delete_devices::v3::Response {}) diff --git a/src/api/client_server/filter.rs b/src/api/client_server/filter.rs index e9a359d6..d24e3b6f 100644 --- a/src/api/client_server/filter.rs +++ b/src/api/client_server/filter.rs @@ -13,9 +13,8 @@ pub async fn get_filter_route( body: Ruma, ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let filter = match services().users.get_filter(sender_user, &body.filter_id)? { - Some(filter) => filter, - None => return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found.")), + let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? else { + return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found.")); }; Ok(get_filter::v3::Response::new(filter)) diff --git a/src/api/client_server/keys.rs b/src/api/client_server/keys.rs index 4af8890d..e0cddb76 100644 --- a/src/api/client_server/keys.rs +++ b/src/api/client_server/keys.rs @@ -106,7 +106,7 @@ pub async fn upload_signing_keys_route( stages: vec![AuthType::Password], }], completed: Vec::new(), - params: Default::default(), + params: Box::default(), session: None, auth_error: None, }; @@ -173,7 +173,6 @@ pub async fn upload_signatures_route( "Invalid signature.", ))? .clone() - .into_iter() { // Signature validation? let signature = ( @@ -401,48 +400,45 @@ pub(crate) async fn get_keys_helper bool>( .collect(); while let Some((server, response)) = futures.next().await { - match response { - Ok(Ok(response)) => { - for (user, masterkey) in response.master_keys { - let (master_key_id, mut master_key) = - services().users.parse_master_key(&user, &masterkey)?; + if let Ok(Ok(response)) = response { + for (user, masterkey) in response.master_keys { + let (master_key_id, mut master_key) = + services().users.parse_master_key(&user, &masterkey)?; - if let Some(our_master_key) = services().users.get_key( - &master_key_id, - sender_user, - &user, - &allowed_signatures, - )? { - let (_, our_master_key) = - services().users.parse_master_key(&user, &our_master_key)?; - master_key.signatures.extend(our_master_key.signatures); - } - let json = serde_json::to_value(master_key).expect("to_value always works"); - let raw = serde_json::from_value(json).expect("Raw::from_value always works"); - services().users.add_cross_signing_keys( - &user, &raw, &None, &None, - false, // Dont notify. A notification would trigger another key request resulting in an endless loop - )?; - master_keys.insert(user, raw); + if let Some(our_master_key) = services().users.get_key( + &master_key_id, + sender_user, + &user, + &allowed_signatures, + )? { + let (_, our_master_key) = + services().users.parse_master_key(&user, &our_master_key)?; + master_key.signatures.extend(our_master_key.signatures); } - - self_signing_keys.extend(response.self_signing_keys); - device_keys.extend(response.device_keys); + let json = serde_json::to_value(master_key).expect("to_value always works"); + let raw = serde_json::from_value(json).expect("Raw::from_value always works"); + services().users.add_cross_signing_keys( + &user, &raw, &None, &None, + false, // Dont notify. A notification would trigger another key request resulting in an endless loop + )?; + master_keys.insert(user, raw); } - _ => { - back_off(server.to_owned()).await; - failures.insert(server.to_string(), json!({})); - } + self_signing_keys.extend(response.self_signing_keys); + device_keys.extend(response.device_keys); + } else { + back_off(server.to_owned()).await; + + failures.insert(server.to_string(), json!({})); } } Ok(get_keys::v3::Response { + failures, + device_keys, master_keys, self_signing_keys, user_signing_keys, - device_keys, - failures, }) } diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index cb43545e..fd3d219b 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -59,7 +59,7 @@ pub async fn join_room_by_id_route( .iter() .filter_map(|event| serde_json::from_str(event.json().get()).ok()) .filter_map(|event: serde_json::Value| event.get("sender").cloned()) - .filter_map(|sender| sender.as_str().map(|s| s.to_owned())) + .filter_map(|sender| sender.as_str().map(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()), ); @@ -105,7 +105,7 @@ pub async fn join_room_by_id_or_alias_route( .iter() .filter_map(|event| serde_json::from_str(event.json().get()).ok()) .filter_map(|event: serde_json::Value| event.get("sender").cloned()) - .filter_map(|sender| sender.as_str().map(|s| s.to_owned())) + .filter_map(|sender| sender.as_str().map(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()), ); @@ -543,313 +543,11 @@ async fn join_room_by_id_helper( let state_lock = mutex_state.lock().await; // Ask a remote server if we are not participating in this room - if !services() + if services() .rooms .state_cache .server_in_room(services().globals.server_name(), room_id)? { - info!("Joining {room_id} over federation."); - - let (make_join_response, remote_server) = - make_join_request(sender_user, room_id, servers).await?; - - info!("make_join finished"); - - let room_version_id = match make_join_response.room_version { - Some(room_version) - if services() - .globals - .supported_room_versions() - .contains(&room_version) => - { - room_version - } - _ => return Err(Error::BadServerResponse("Room version is not supported")), - }; - - let mut join_event_stub: CanonicalJsonObject = - serde_json::from_str(make_join_response.event.get()).map_err(|_| { - Error::BadServerResponse("Invalid make_join event json received from server.") - })?; - - let join_authorized_via_users_server = join_event_stub - .get("content") - .map(|s| { - s.as_object()? - .get("join_authorised_via_users_server")? - .as_str() - }) - .and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok()); - - // TODO: Is origin needed? - join_event_stub.insert( - "origin".to_owned(), - CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), - ); - join_event_stub.insert( - "origin_server_ts".to_owned(), - CanonicalJsonValue::Integer( - utils::millis_since_unix_epoch() - .try_into() - .expect("Timestamp is valid js_int value"), - ), - ); - join_event_stub.insert( - "content".to_owned(), - to_canonical_value(RoomMemberEventContent { - membership: MembershipState::Join, - displayname: services().users.displayname(sender_user)?, - avatar_url: services().users.avatar_url(sender_user)?, - is_direct: None, - third_party_invite: None, - blurhash: services().users.blurhash(sender_user)?, - reason, - join_authorized_via_users_server, - }) - .expect("event is valid, we just created it"), - ); - - // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms - join_event_stub.remove("event_id"); - - // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present - ruma::signatures::hash_and_sign_event( - services().globals.server_name().as_str(), - services().globals.keypair(), - &mut join_event_stub, - &room_version_id, - ) - .expect("event is valid, we just created it"); - - // Generate event id - let event_id = format!( - "${}", - ruma::signatures::reference_hash(&join_event_stub, &room_version_id) - .expect("ruma can calculate reference hashes") - ); - let event_id = <&EventId>::try_from(event_id.as_str()) - .expect("ruma's reference hashes are valid event ids"); - - // Add event_id back - join_event_stub.insert( - "event_id".to_owned(), - CanonicalJsonValue::String(event_id.as_str().to_owned()), - ); - - // It has enough fields to be called a proper event now - let mut join_event = join_event_stub; - - info!("Asking {remote_server} for send_join"); - let send_join_response = services() - .sending - .send_federation_request( - &remote_server, - federation::membership::create_join_event::v2::Request { - room_id: room_id.to_owned(), - event_id: event_id.to_owned(), - pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()), - omit_members: false, - }, - ) - .await?; - - info!("send_join finished"); - - if let Some(signed_raw) = &send_join_response.room_state.event { - info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event"); - let (signed_event_id, signed_value) = - match gen_event_id_canonical_json(signed_raw, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } - }; - - if signed_event_id != event_id { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Server sent event with wrong event id", - )); - } - - match signed_value["signatures"] - .as_object() - .ok_or(Error::BadRequest( - ErrorKind::InvalidParam, - "Server sent invalid signatures type", - )) - .and_then(|e| { - e.get(remote_server.as_str()).ok_or(Error::BadRequest( - ErrorKind::InvalidParam, - "Server did not send its signature", - )) - }) { - Ok(signature) => { - join_event - .get_mut("signatures") - .expect("we created a valid pdu") - .as_object_mut() - .expect("we created a valid pdu") - .insert(remote_server.to_string(), signature.clone()); - } - Err(e) => { - warn!( - "Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}", - ); - } - } - } - - services().rooms.short.get_or_create_shortroomid(room_id)?; - - info!("Parsing join event"); - let parsed_join_pdu = PduEvent::from_id_val(event_id, join_event.clone()) - .map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?; - - let mut state = HashMap::new(); - let pub_key_map = RwLock::new(BTreeMap::new()); - - info!("Fetching join signing keys"); - services() - .rooms - .event_handler - .fetch_join_signing_keys(&send_join_response, &room_version_id, &pub_key_map) - .await?; - - info!("Going through send_join response room_state"); - for result in send_join_response - .room_state - .state - .iter() - .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) - { - let (event_id, value) = match result.await { - Ok(t) => t, - Err(_) => continue, - }; - - let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| { - warn!("Invalid PDU in send_join response: {} {:?}", e, value); - Error::BadServerResponse("Invalid PDU in send_join response.") - })?; - - services() - .rooms - .outlier - .add_pdu_outlier(&event_id, &value)?; - if let Some(state_key) = &pdu.state_key { - let shortstatekey = services() - .rooms - .short - .get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?; - state.insert(shortstatekey, pdu.event_id.clone()); - } - } - - info!("Going through send_join response auth_chain"); - for result in send_join_response - .room_state - .auth_chain - .iter() - .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) - { - let (event_id, value) = match result.await { - Ok(t) => t, - Err(_) => continue, - }; - - services() - .rooms - .outlier - .add_pdu_outlier(&event_id, &value)?; - } - - info!("Running send_join auth check"); - let authenticated = state_res::event_auth::auth_check( - &state_res::RoomVersion::new(&room_version_id).expect("room version is supported"), - &parsed_join_pdu, - None::, // TODO: third party invite - |k, s| { - services() - .rooms - .timeline - .get_pdu( - state.get( - &services() - .rooms - .short - .get_or_create_shortstatekey(&k.to_string().into(), s) - .ok()?, - )?, - ) - .ok()? - }, - ) - .map_err(|e| { - warn!("Auth check failed: {e}"); - Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed") - })?; - - if !authenticated { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Auth check failed", - )); - } - - info!("Saving state from send_join"); - let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state( - room_id, - Arc::new( - state - .into_iter() - .map(|(k, id)| { - services() - .rooms - .state_compressor - .compress_state_event(k, &id) - }) - .collect::>()?, - ), - )?; - - services() - .rooms - .state - .force_state(room_id, statehash_before_join, new, removed, &state_lock) - .await?; - - info!("Updating joined counts for new room"); - services().rooms.state_cache.update_joined_count(room_id)?; - - // We append to state before appending the pdu, so we don't have a moment in time with the - // pdu without it's state. This is okay because append_pdu can't fail. - let statehash_after_join = services().rooms.state.append_to_state(&parsed_join_pdu)?; - - info!("Appending new room join event"); - services() - .rooms - .timeline - .append_pdu( - &parsed_join_pdu, - join_event, - vec![(*parsed_join_pdu.event_id).to_owned()], - &state_lock, - ) - .await?; - - info!("Setting final room state for new room"); - // We set the room state after inserting the pdu, so that we never have a moment in time - // where events in the current room state do not exist - services() - .rooms - .state - .set_room_state(room_id, statehash_after_join, &state_lock)?; - } else { info!("We can join locally"); let join_rules_event = services().rooms.state_accessor.room_state_get( @@ -870,10 +568,7 @@ async fn join_room_by_id_helper( let restriction_rooms = match join_rules_event_content { Some(RoomJoinRulesEventContent { - join_rule: JoinRule::Restricted(restricted), - }) - | Some(RoomJoinRulesEventContent { - join_rule: JoinRule::KnockRestricted(restricted), + join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted), }) => restricted .allow .into_iter() @@ -901,12 +596,12 @@ async fn join_room_by_id_helper( .collect::>() { if user.server_name() == services().globals.server_name() - && services() - .rooms - .state_accessor - .user_can_invite(room_id, &user, sender_user, &state_lock) - .await - .unwrap_or(false) + && services().rooms.state_accessor.user_can_invite( + room_id, + &user, + sender_user, + &state_lock, + ) { auth_user = Some(user); break; @@ -1056,17 +751,15 @@ async fn join_room_by_id_helper( .await?; if let Some(signed_raw) = send_join_response.room_state.event { - let (signed_event_id, signed_value) = - match gen_event_id_canonical_json(&signed_raw, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } - }; + let Ok((signed_event_id, signed_value)) = + gen_event_id_canonical_json(&signed_raw, &room_version_id) + else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); + }; if signed_event_id != event_id { return Err(Error::BadRequest( @@ -1095,6 +788,304 @@ async fn join_room_by_id_helper( } else { return Err(error); } + } else { + info!("Joining {room_id} over federation."); + + let (make_join_response, remote_server) = + make_join_request(sender_user, room_id, servers).await?; + + info!("make_join finished"); + + let room_version_id = match make_join_response.room_version { + Some(room_version) + if services() + .globals + .supported_room_versions() + .contains(&room_version) => + { + room_version + } + _ => return Err(Error::BadServerResponse("Room version is not supported")), + }; + + let mut join_event_stub: CanonicalJsonObject = + serde_json::from_str(make_join_response.event.get()).map_err(|_| { + Error::BadServerResponse("Invalid make_join event json received from server.") + })?; + + let join_authorized_via_users_server = join_event_stub + .get("content") + .map(|s| { + s.as_object()? + .get("join_authorised_via_users_server")? + .as_str() + }) + .and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok()); + + // TODO: Is origin needed? + join_event_stub.insert( + "origin".to_owned(), + CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()), + ); + join_event_stub.insert( + "origin_server_ts".to_owned(), + CanonicalJsonValue::Integer( + utils::millis_since_unix_epoch() + .try_into() + .expect("Timestamp is valid js_int value"), + ), + ); + join_event_stub.insert( + "content".to_owned(), + to_canonical_value(RoomMemberEventContent { + membership: MembershipState::Join, + displayname: services().users.displayname(sender_user)?, + avatar_url: services().users.avatar_url(sender_user)?, + is_direct: None, + third_party_invite: None, + blurhash: services().users.blurhash(sender_user)?, + reason, + join_authorized_via_users_server, + }) + .expect("event is valid, we just created it"), + ); + + // We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms + join_event_stub.remove("event_id"); + + // In order to create a compatible ref hash (EventID) the `hashes` field needs to be present + ruma::signatures::hash_and_sign_event( + services().globals.server_name().as_str(), + services().globals.keypair(), + &mut join_event_stub, + &room_version_id, + ) + .expect("event is valid, we just created it"); + + // Generate event id + let event_id = format!( + "${}", + ruma::signatures::reference_hash(&join_event_stub, &room_version_id) + .expect("ruma can calculate reference hashes") + ); + let event_id = <&EventId>::try_from(event_id.as_str()) + .expect("ruma's reference hashes are valid event ids"); + + // Add event_id back + join_event_stub.insert( + "event_id".to_owned(), + CanonicalJsonValue::String(event_id.as_str().to_owned()), + ); + + // It has enough fields to be called a proper event now + let mut join_event = join_event_stub; + + info!("Asking {remote_server} for send_join"); + let send_join_response = services() + .sending + .send_federation_request( + &remote_server, + federation::membership::create_join_event::v2::Request { + room_id: room_id.to_owned(), + event_id: event_id.to_owned(), + pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()), + omit_members: false, + }, + ) + .await?; + + info!("send_join finished"); + + if let Some(signed_raw) = &send_join_response.room_state.event { + info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event"); + let Ok((signed_event_id, signed_value)) = + gen_event_id_canonical_json(signed_raw, &room_version_id) + else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); + }; + + if signed_event_id != event_id { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Server sent event with wrong event id", + )); + } + + match signed_value["signatures"] + .as_object() + .ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Server sent invalid signatures type", + )) + .and_then(|e| { + e.get(remote_server.as_str()).ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Server did not send its signature", + )) + }) { + Ok(signature) => { + join_event + .get_mut("signatures") + .expect("we created a valid pdu") + .as_object_mut() + .expect("we created a valid pdu") + .insert(remote_server.to_string(), signature.clone()); + } + Err(e) => { + warn!( + "Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}", + ); + } + } + } + + services().rooms.short.get_or_create_shortroomid(room_id)?; + + info!("Parsing join event"); + let parsed_join_pdu = PduEvent::from_id_val(event_id, join_event.clone()) + .map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?; + + let mut state = HashMap::new(); + let pub_key_map = RwLock::new(BTreeMap::new()); + + info!("Fetching join signing keys"); + services() + .rooms + .event_handler + .fetch_join_signing_keys(&send_join_response, &room_version_id, &pub_key_map) + .await?; + + info!("Going through send_join response room_state"); + for result in send_join_response + .room_state + .state + .iter() + .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) + { + let Ok((event_id, value)) = result.await else { + continue; + }; + + let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| { + warn!("Invalid PDU in send_join response: {} {:?}", e, value); + Error::BadServerResponse("Invalid PDU in send_join response.") + })?; + + services() + .rooms + .outlier + .add_pdu_outlier(&event_id, &value)?; + if let Some(state_key) = &pdu.state_key { + let shortstatekey = services() + .rooms + .short + .get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?; + state.insert(shortstatekey, pdu.event_id.clone()); + } + } + + info!("Going through send_join response auth_chain"); + for result in send_join_response + .room_state + .auth_chain + .iter() + .map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map)) + { + let Ok((event_id, value)) = result.await else { + continue; + }; + + services() + .rooms + .outlier + .add_pdu_outlier(&event_id, &value)?; + } + + info!("Running send_join auth check"); + let authenticated = state_res::event_auth::auth_check( + &state_res::RoomVersion::new(&room_version_id).expect("room version is supported"), + &parsed_join_pdu, + None::, // TODO: third party invite + |k, s| { + services() + .rooms + .timeline + .get_pdu( + state.get( + &services() + .rooms + .short + .get_or_create_shortstatekey(&k.to_string().into(), s) + .ok()?, + )?, + ) + .ok()? + }, + ) + .map_err(|e| { + warn!("Auth check failed: {e}"); + Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed") + })?; + + if !authenticated { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Auth check failed", + )); + } + + info!("Saving state from send_join"); + let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state( + room_id, + Arc::new( + state + .into_iter() + .map(|(k, id)| { + services() + .rooms + .state_compressor + .compress_state_event(k, &id) + }) + .collect::>()?, + ), + )?; + + services() + .rooms + .state + .force_state(room_id, statehash_before_join, new, removed, &state_lock) + .await?; + + info!("Updating joined counts for new room"); + services().rooms.state_cache.update_joined_count(room_id)?; + + // We append to state before appending the pdu, so we don't have a moment in time with the + // pdu without it's state. This is okay because append_pdu can't fail. + let statehash_after_join = services().rooms.state.append_to_state(&parsed_join_pdu)?; + + info!("Appending new room join event"); + services() + .rooms + .timeline + .append_pdu( + &parsed_join_pdu, + join_event, + vec![(*parsed_join_pdu.event_id).to_owned()], + &state_lock, + ) + .await?; + + info!("Setting final room state for new room"); + // We set the room state after inserting the pdu, so that we never have a moment in time + // where events in the current room state do not exist + services() + .rooms + .state + .set_room_state(room_id, statehash_after_join, &state_lock)?; } Ok(join_room_by_id::v3::Response::new(room_id.to_owned())) @@ -1275,16 +1266,13 @@ pub(crate) async fn invite_helper<'a>( let pub_key_map = RwLock::new(BTreeMap::new()); // We do not add the event_id field to the pdu here because of signature and hashes checks - let (event_id, value) = match gen_event_id_canonical_json(&response.event, &room_version_id) - { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } + let Ok((event_id, value)) = gen_event_id_canonical_json(&response.event, &room_version_id) + else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); }; if *pdu.event_id != *event_id { @@ -1393,10 +1381,7 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> { .collect::>(); for room_id in all_rooms { - let room_id = match room_id { - Ok(room_id) => room_id, - Err(_) => continue, - }; + let Ok(room_id) = room_id else { continue }; let _ = leave_room(user_id, &room_id, None).await; } @@ -1406,35 +1391,11 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> { pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option) -> Result<()> { // Ask a remote server if we don't have this room - if !services() + if services() .rooms .state_cache .server_in_room(services().globals.server_name(), room_id)? { - if let Err(e) = remote_leave_room(user_id, room_id).await { - warn!("Failed to leave room {} remotely: {}", user_id, e); - // Don't tell the client about this error - } - - let last_state = services() - .rooms - .state_cache - .invite_state(user_id, room_id)? - .map_or_else( - || services().rooms.state_cache.left_state(user_id, room_id), - |s| Ok(Some(s)), - )?; - - // We always drop the invite, we can't rely on other servers - services().rooms.state_cache.update_membership( - room_id, - user_id, - MembershipState::Leave, - user_id, - last_state, - true, - )?; - } else { let mutex_state = Arc::clone( services() .globals @@ -1493,6 +1454,30 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option Result<()> { .iter() .filter_map(|event| serde_json::from_str(event.json().get()).ok()) .filter_map(|event: serde_json::Value| event.get("sender").cloned()) - .filter_map(|sender| sender.as_str().map(|s| s.to_owned())) + .filter_map(|sender| sender.as_str().map(ToOwned::to_owned)) .filter_map(|sender| UserId::parse(sender).ok()) .map(|user| user.server_name().to_owned()) .collect(); diff --git a/src/api/client_server/message.rs b/src/api/client_server/message.rs index 89f33591..88c618c6 100644 --- a/src/api/client_server/message.rs +++ b/src/api/client_server/message.rs @@ -110,7 +110,7 @@ pub async fn send_message_event_route( /// Allows paginating through room history. /// /// - Only works if the user is joined (TODO: always allow, but only show events where the user was -/// joined, depending on history_visibility) +/// joined, depending on `history_visibility`) pub async fn get_message_events_route( body: Ruma, ) -> Result { diff --git a/src/api/client_server/profile.rs b/src/api/client_server/profile.rs index cf1db2d7..fafb96fb 100644 --- a/src/api/client_server/profile.rs +++ b/src/api/client_server/profile.rs @@ -147,7 +147,7 @@ pub async fn get_displayname_route( /// # `PUT /_matrix/client/r0/profile/{userId}/avatar_url` /// -/// Updates the avatar_url and blurhash. +/// Updates the `avatar_url` and blurhash. /// /// - Also makes sure other users receive the update using presence EDUs pub async fn set_avatar_url_route( @@ -252,9 +252,9 @@ pub async fn set_avatar_url_route( /// # `GET /_matrix/client/r0/profile/{userId}/avatar_url` /// -/// Returns the avatar_url and blurhash of the user. +/// Returns the `avatar_url` and blurhash of the user. /// -/// - If user is on another server: Fetches avatar_url and blurhash over federation +/// - If user is on another server: Fetches `avatar_url` and blurhash over federation pub async fn get_avatar_url_route( body: Ruma, ) -> Result { @@ -284,7 +284,7 @@ pub async fn get_avatar_url_route( /// # `GET /_matrix/client/r0/profile/{userId}` /// -/// Returns the displayname, avatar_url and blurhash of the user. +/// Returns the displayname, `avatar_url` and blurhash of the user. /// /// - If user is on another server: Fetches profile over federation pub async fn get_profile_route( diff --git a/src/api/client_server/push.rs b/src/api/client_server/push.rs index 72768662..d2c35668 100644 --- a/src/api/client_server/push.rs +++ b/src/api/client_server/push.rs @@ -281,7 +281,7 @@ pub async fn get_pushrule_enabled_route( let global = account_data.content.global; let enabled = global .get(body.kind.clone(), &body.rule_id) - .map(|r| r.enabled()) + .map(ruma::push::AnyPushRuleRef::enabled) .ok_or(Error::BadRequest( ErrorKind::NotFound, "Push rule not found.", diff --git a/src/api/client_server/read_marker.rs b/src/api/client_server/read_marker.rs index a5553d25..d01a3a7f 100644 --- a/src/api/client_server/read_marker.rs +++ b/src/api/client_server/read_marker.rs @@ -140,7 +140,7 @@ pub async fn create_receipt_route( receipts.insert(ReceiptType::Read, user_receipts); let mut receipt_content = BTreeMap::new(); - receipt_content.insert(body.event_id.to_owned(), receipts); + receipt_content.insert(body.event_id.clone(), receipts); services().rooms.edus.read_receipt.readreceipt_update( sender_user, diff --git a/src/api/client_server/relations.rs b/src/api/client_server/relations.rs index 124f1310..68d06bd1 100644 --- a/src/api/client_server/relations.rs +++ b/src/api/client_server/relations.rs @@ -39,8 +39,8 @@ pub async fn get_relating_events_with_rel_type_and_event_type_route( sender_user, &body.room_id, &body.event_id, - Some(body.event_type.clone()), - Some(body.rel_type.clone()), + &Some(body.event_type.clone()), + &Some(body.rel_type.clone()), from, to, limit, @@ -89,8 +89,8 @@ pub async fn get_relating_events_with_rel_type_route( sender_user, &body.room_id, &body.event_id, - None, - Some(body.rel_type.clone()), + &None, + &Some(body.rel_type.clone()), from, to, limit, @@ -137,8 +137,8 @@ pub async fn get_relating_events_route( sender_user, &body.room_id, &body.event_id, - None, - None, + &None, + &None, from, to, limit, diff --git a/src/api/client_server/report.rs b/src/api/client_server/report.rs index ab5027cd..e41dc50e 100644 --- a/src/api/client_server/report.rs +++ b/src/api/client_server/report.rs @@ -14,14 +14,11 @@ pub async fn report_event_route( ) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - let pdu = match services().rooms.timeline.get_pdu(&body.event_id)? { - Some(pdu) => pdu, - _ => { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Invalid Event ID", - )) - } + let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid Event ID", + )); }; if let Some(true) = body.score.map(|s| s > int!(0) || s < int!(-100)) { diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index e3e8a746..3c0ece47 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -34,7 +34,7 @@ use tracing::{info, warn}; /// Creates a new room. /// /// - Room ID is randomly generated -/// - Create alias if room_alias_name is set +/// - Create alias if `room_alias_name` is set /// - Send create event /// - Join sender user /// - Send power levels event @@ -228,7 +228,7 @@ pub async fn create_room_route( event_type: TimelineEventType::RoomCreate, content: to_raw_value(&content).expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -269,9 +269,10 @@ pub async fn create_room_route( // Figure out preset. We need it for preset specific events let preset = body.preset.clone().unwrap_or(match &body.visibility { - room::Visibility::Private => RoomPreset::PrivateChat, room::Visibility::Public => RoomPreset::PublicChat, - _ => RoomPreset::PrivateChat, // Room visibility should not be custom + // Room visibility is set to private, or custom + // Room visibility should not be custom + _ => RoomPreset::PrivateChat, }); let mut users = BTreeMap::new(); @@ -309,7 +310,7 @@ pub async fn create_room_route( content: to_raw_value(&power_levels_content) .expect("to_raw_value always works on serde_json::Value"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -332,7 +333,7 @@ pub async fn create_room_route( }) .expect("We checked that alias earlier, it must be fine"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -358,7 +359,7 @@ pub async fn create_room_route( })) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -379,7 +380,7 @@ pub async fn create_room_route( )) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -401,7 +402,7 @@ pub async fn create_room_route( })) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -418,7 +419,7 @@ pub async fn create_room_route( })?; // Implicit state key defaults to "" - pdu_builder.state_key.get_or_insert_with(|| "".to_owned()); + pdu_builder.state_key.get_or_insert_with(String::new); // Silently skip encryption events if they are not allowed if pdu_builder.event_type == TimelineEventType::RoomEncryption @@ -445,7 +446,7 @@ pub async fn create_room_route( content: to_raw_value(&RoomNameEventContent::new(name.clone())) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -467,7 +468,7 @@ pub async fn create_room_route( }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -539,7 +540,7 @@ pub async fn get_room_event_route( /// /// Lists all aliases of the room. /// -/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable +/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if `history_visibility` is world readable pub async fn get_room_aliases_route( body: Ruma, ) -> Result { @@ -624,7 +625,7 @@ pub async fn upgrade_room_route( }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -725,7 +726,7 @@ pub async fn upgrade_room_route( content: to_raw_value(&create_event_content) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -795,7 +796,7 @@ pub async fn upgrade_room_route( event_type: event_type.to_string().into(), content: event_content, unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, @@ -845,7 +846,7 @@ pub async fn upgrade_room_route( content: to_raw_value(&power_levels_event_content) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, sender_user, diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index 3e583fac..e691e3fb 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -3,7 +3,13 @@ use crate::{services, utils, Error, Result, Ruma}; use ruma::{ api::client::{ error::ErrorKind, - session::{get_login_types, login, logout, logout_all}, + session::{ + get_login_types::{ + self, + v3::{ApplicationServiceLoginType, PasswordLoginType}, + }, + login, logout, logout_all, + }, uiaa::UserIdentifier, }, UserId, @@ -25,8 +31,8 @@ pub async fn get_login_types_route( _body: Ruma, ) -> Result { Ok(get_login_types::v3::Response::new(vec![ - get_login_types::v3::LoginType::Password(Default::default()), - get_login_types::v3::LoginType::ApplicationService(Default::default()), + get_login_types::v3::LoginType::Password(PasswordLoginType::default()), + get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()), ])) } diff --git a/src/api/client_server/space.rs b/src/api/client_server/space.rs index e2ea8c34..947b079e 100644 --- a/src/api/client_server/space.rs +++ b/src/api/client_server/space.rs @@ -1,7 +1,7 @@ use crate::{services, Result, Ruma}; use ruma::api::client::space::get_hierarchy; -/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`` +/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy` /// /// Paginates over the space tree in a depth-first manner to locate child rooms of a given space. pub async fn get_hierarchy_route( diff --git a/src/api/client_server/state.rs b/src/api/client_server/state.rs index e62aa013..dca6796e 100644 --- a/src/api/client_server/state.rs +++ b/src/api/client_server/state.rs @@ -20,7 +20,7 @@ use tracing::log::warn; /// /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed -/// - If event is new canonical_alias: Rejects if alias is incorrect +/// - If event is new `canonical_alias`: Rejects if alias is incorrect pub async fn send_state_event_for_key_route( body: Ruma, ) -> Result { @@ -31,7 +31,7 @@ pub async fn send_state_event_for_key_route( &body.room_id, &body.event_type, &body.body.body, // Yes, I hate it too - body.state_key.to_owned(), + body.state_key.clone(), ) .await?; @@ -45,7 +45,7 @@ pub async fn send_state_event_for_key_route( /// /// - The only requirement for the content is that it has to be valid json /// - Tries to send the event into the room, auth rules will determine if it is allowed -/// - If event is new canonical_alias: Rejects if alias is incorrect +/// - If event is new `canonical_alias`: Rejects if alias is incorrect pub async fn send_state_event_for_empty_key_route( body: Ruma, ) -> Result> { @@ -64,7 +64,7 @@ pub async fn send_state_event_for_empty_key_route( &body.room_id, &body.event_type.to_string().into(), &body.body.body, - body.state_key.to_owned(), + body.state_key.clone(), ) .await?; diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index e0c6e0b9..ad59fb4e 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -62,7 +62,7 @@ use tracing::{error, info}; /// - If the user was invited after `since`: A subset of the state of the room at the point of the invite /// /// For left rooms: -/// - If the user left after `since`: prev_batch token, empty state (TODO: subset of the state at the point of the leave) +/// - If the user left after `since`: `prev_batch` token, empty state (TODO: subset of the state at the point of the leave) /// /// - Sync is handled in an async task, multiple requests from the same device with the same /// `since` will be cached @@ -83,7 +83,7 @@ pub async fn sync_events_route( Entry::Vacant(v) => { let (tx, rx) = tokio::sync::watch::channel(None); - v.insert((body.since.to_owned(), rx.clone())); + v.insert((body.since.clone(), rx.clone())); tokio::spawn(sync_helper_wrapper( sender_user.clone(), @@ -95,7 +95,9 @@ pub async fn sync_events_route( rx } Entry::Occupied(mut o) => { - if o.get().0 != body.since { + if o.get().0 == body.since { + o.get().1.clone() + } else { let (tx, rx) = tokio::sync::watch::channel(None); o.insert((body.since.clone(), rx.clone())); @@ -110,8 +112,6 @@ pub async fn sync_events_route( )); rx - } else { - o.get().1.clone() } } }; @@ -198,7 +198,7 @@ async fn sync_helper( LazyLoadOptions::Enabled { include_redundant_members: redundant, } => (true, redundant), - _ => (false, false), + LazyLoadOptions::Disabled => (false, false), }; let full_state = body.full_state; @@ -376,28 +376,23 @@ async fn sync_helper( None => HashMap::new(), }; - let left_event_id = match services().rooms.state_accessor.room_state_get_id( + let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id( &room_id, &StateEventType::RoomMember, sender_user.as_str(), - )? { - Some(e) => e, - None => { - error!("Left room but no left state event"); - continue; - } + )? + else { + error!("Left room but no left state event"); + continue; }; - let left_shortstatehash = match services() + let Some(left_shortstatehash) = services() .rooms .state_accessor .pdu_shortstatehash(&left_event_id)? - { - Some(s) => s, - None => { - error!("Leave event has no state"); - continue; - } + else { + error!("Leave event has no state"); + continue; }; let mut left_state_ids = services() @@ -425,12 +420,9 @@ async fn sync_helper( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; left_state_events.push(pdu.to_sync_state_event()); @@ -648,13 +640,11 @@ async fn load_joined_room( // Database queries: - let current_shortstatehash = - if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? { - s - } else { - error!("Room {} has no state", room_id); - return Err(Error::BadDatabase("Room has no state")); - }; + let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? + else { + error!("Room {} has no state", room_id); + return Err(Error::BadDatabase("Room has no state")); + }; let since_shortstatehash = services() .rooms @@ -788,12 +778,9 @@ async fn load_joined_room( .get_statekey_from_short(shortstatekey)?; if event_type != StateEventType::RoomMember { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; state_events.push(pdu); @@ -807,12 +794,9 @@ async fn load_joined_room( // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 || *sender_user == state_key { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; // This check is in case a bad user ID made it into the database @@ -877,12 +861,9 @@ async fn load_joined_room( for (key, id) in current_state_ids { if full_state || since_state_ids.get(&key) != Some(&id) { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; if pdu.kind == TimelineEventType::RoomMember { @@ -1248,7 +1229,7 @@ pub async fn sync_events_v4_route( sender_user.clone(), sender_device.clone(), conn_id.clone(), - ) + ); } } @@ -1286,13 +1267,12 @@ pub async fn sync_events_v4_route( ); for room_id in &all_joined_rooms { - let current_shortstatehash = - if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? { - s - } else { - error!("Room {} has no state", room_id); - continue; - }; + let Some(current_shortstatehash) = + services().rooms.state.get_room_shortstatehash(room_id)? + else { + error!("Room {} has no state", room_id); + continue; + }; let since_shortstatehash = services() .rooms @@ -1354,12 +1334,9 @@ pub async fn sync_events_v4_route( for (key, id) in current_state_ids { if since_state_ids.get(&key) != Some(&id) { - let pdu = match services().rooms.timeline.get_pdu(&id)? { - Some(pdu) => pdu, - None => { - error!("Pdu in state not found: {}", id); - continue; - } + let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else { + error!("Pdu in state not found: {}", id); + continue; }; if pdu.kind == TimelineEventType::RoomMember { if let Some(state_key) = &pdu.state_key { @@ -1599,10 +1576,10 @@ pub async fn sync_events_v4_route( })) })? .or_else(|| { - if roomsince != &0 { - Some(roomsince.to_string()) - } else { + if roomsince == &0 { None + } else { + Some(roomsince.to_string()) } }); @@ -1613,7 +1590,7 @@ pub async fn sync_events_v4_route( let required_state = required_state_request .iter() - .flat_map(|state| { + .filter_map(|state| { services() .rooms .state_accessor @@ -1631,7 +1608,7 @@ pub async fn sync_events_v4_route( .room_members(room_id) .filter_map(|r| r.ok()) .filter(|member| member != &sender_user) - .flat_map(|member| { + .filter_map(|member| { services() .rooms .state_accessor diff --git a/src/api/client_server/tag.rs b/src/api/client_server/tag.rs index 16f1600f..acc313dc 100644 --- a/src/api/client_server/tag.rs +++ b/src/api/client_server/tag.rs @@ -24,18 +24,19 @@ pub async fn update_tag_route( RoomAccountDataEventType::Tag, )?; - let mut tags_event = event - .map(|e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) - }) - .unwrap_or_else(|| { + let mut tags_event = event.map_or_else( + || { Ok(TagEvent { content: TagEventContent { tags: BTreeMap::new(), }, }) - })?; + }, + |e| { + serde_json::from_str(e.get()) + .map_err(|_| Error::bad_database("Invalid account data event in db.")) + }, + )?; tags_event .content @@ -68,18 +69,19 @@ pub async fn delete_tag_route( RoomAccountDataEventType::Tag, )?; - let mut tags_event = event - .map(|e| { - serde_json::from_str(e.get()) - .map_err(|_| Error::bad_database("Invalid account data event in db.")) - }) - .unwrap_or_else(|| { + let mut tags_event = event.map_or_else( + || { Ok(TagEvent { content: TagEventContent { tags: BTreeMap::new(), }, }) - })?; + }, + |e| { + serde_json::from_str(e.get()) + .map_err(|_| Error::bad_database("Invalid account data event in db.")) + }, + )?; tags_event.content.tags.remove(&body.tag.clone().into()); @@ -107,18 +109,19 @@ pub async fn get_tags_route(body: Ruma) -> Result { diff --git a/src/api/client_server/voip.rs b/src/api/client_server/voip.rs index f0d91f71..dddc9023 100644 --- a/src/api/client_server/voip.rs +++ b/src/api/client_server/voip.rs @@ -17,7 +17,12 @@ pub async fn turn_server_route( let turn_secret = services().globals.turn_secret().clone(); - let (username, password) = if !turn_secret.is_empty() { + let (username, password) = if turn_secret.is_empty() { + ( + services().globals.turn_username().clone(), + services().globals.turn_password().clone(), + ) + } else { let expiry = SecondsSinceUnixEpoch::from_system_time( SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()), ) @@ -32,11 +37,6 @@ pub async fn turn_server_route( let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes()); (username, password) - } else { - ( - services().globals.turn_username().clone(), - services().globals.turn_password().clone(), - ) }; Ok(get_turn_server_info::v3::Response { diff --git a/src/api/ruma_wrapper/axum.rs b/src/api/ruma_wrapper/axum.rs index a56ee359..f211b4d7 100644 --- a/src/api/ruma_wrapper/axum.rs +++ b/src/api/ruma_wrapper/axum.rs @@ -226,7 +226,7 @@ where let keys_result = services() .rooms .event_handler - .fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.to_owned()]) + .fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.clone()]) .await; let keys = match keys_result { @@ -334,8 +334,8 @@ where sender_user, sender_device, sender_servername, - appservice_info, json_body, + appservice_info, }) } } diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 6ca352b8..ed0518d9 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -65,7 +65,7 @@ use tracing::{debug, error, warn}; /// (colon-plus-port if it was specified). /// /// Note: A `FedDest::Named` might contain an IP address in string form if there -/// was no port specified to construct a SocketAddr with. +/// was no port specified to construct a `SocketAddr` with. /// /// # Examples: /// ```rust @@ -344,7 +344,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest { FedDest::Named(host.to_owned(), port.to_owned()) } -/// Returns: actual_destination, host header +/// Returns: `actual_destination`, host header /// Implemented according to the specification at /// Numbers in comments below refer to bullet points in linked section of specification async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) { @@ -363,100 +363,91 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe FedDest::Named(host.to_owned(), port.to_owned()) } else { debug!("Requesting well known for {destination}"); - match request_well_known(destination.as_str()).await { - Some(delegated_hostname) => { - debug!("3: A .well-known file is available"); - hostname = add_port_to_hostname(&delegated_hostname).into_uri_string(); - match get_ip_with_port(&delegated_hostname) { - Some(host_and_port) => host_and_port, // 3.1: IP literal in .well-known file - None => { - if let Some(pos) = delegated_hostname.find(':') { - debug!("3.2: Hostname with port in .well-known file"); - let (host, port) = delegated_hostname.split_at(pos); - FedDest::Named(host.to_owned(), port.to_owned()) - } else { - debug!("Delegated hostname has no port in this branch"); - if let Some(hostname_override) = - query_srv_record(&delegated_hostname).await + if let Some(delegated_hostname) = request_well_known(destination.as_str()).await { + debug!("3: A .well-known file is available"); + hostname = add_port_to_hostname(&delegated_hostname).into_uri_string(); + match get_ip_with_port(&delegated_hostname) { + Some(host_and_port) => host_and_port, // 3.1: IP literal in .well-known file + None => { + if let Some(pos) = delegated_hostname.find(':') { + debug!("3.2: Hostname with port in .well-known file"); + let (host, port) = delegated_hostname.split_at(pos); + FedDest::Named(host.to_owned(), port.to_owned()) + } else { + debug!("Delegated hostname has no port in this branch"); + if let Some(hostname_override) = + query_srv_record(&delegated_hostname).await + { + debug!("3.3: SRV lookup successful"); + let force_port = hostname_override.port(); + + if let Ok(override_ip) = services() + .globals + .dns_resolver() + .lookup_ip(hostname_override.hostname()) + .await { - debug!("3.3: SRV lookup successful"); - let force_port = hostname_override.port(); - - if let Ok(override_ip) = services() + services() .globals - .dns_resolver() - .lookup_ip(hostname_override.hostname()) - .await - { - services() - .globals - .tls_name_override - .write() - .unwrap() - .insert( - delegated_hostname.clone(), - ( - override_ip.iter().collect(), - force_port.unwrap_or(8448), - ), - ); - } else { - warn!("Using SRV record, but could not resolve to IP"); - } - - if let Some(port) = force_port { - FedDest::Named(delegated_hostname, format!(":{port}")) - } else { - add_port_to_hostname(&delegated_hostname) - } + .tls_name_override + .write() + .unwrap() + .insert( + delegated_hostname.clone(), + ( + override_ip.iter().collect(), + force_port.unwrap_or(8448), + ), + ); + } else { + warn!("Using SRV record, but could not resolve to IP"); + } + + if let Some(port) = force_port { + FedDest::Named(delegated_hostname, format!(":{port}")) } else { - debug!("3.4: No SRV records, just use the hostname from .well-known"); add_port_to_hostname(&delegated_hostname) } + } else { + debug!("3.4: No SRV records, just use the hostname from .well-known"); + add_port_to_hostname(&delegated_hostname) } } } } - None => { - debug!("4: No .well-known or an error occured"); - match query_srv_record(&destination_str).await { - Some(hostname_override) => { - debug!("4: SRV record found"); - let force_port = hostname_override.port(); + } else { + debug!("4: No .well-known or an error occured"); + if let Some(hostname_override) = query_srv_record(&destination_str).await { + debug!("4: SRV record found"); + let force_port = hostname_override.port(); - if let Ok(override_ip) = services() - .globals - .dns_resolver() - .lookup_ip(hostname_override.hostname()) - .await - { - services() - .globals - .tls_name_override - .write() - .unwrap() - .insert( - hostname.clone(), - ( - override_ip.iter().collect(), - force_port.unwrap_or(8448), - ), - ); - } else { - warn!("Using SRV record, but could not resolve to IP"); - } - - if let Some(port) = force_port { - FedDest::Named(hostname.clone(), format!(":{port}")) - } else { - add_port_to_hostname(&hostname) - } - } - None => { - debug!("5: No SRV record found"); - add_port_to_hostname(&destination_str) - } + if let Ok(override_ip) = services() + .globals + .dns_resolver() + .lookup_ip(hostname_override.hostname()) + .await + { + services() + .globals + .tls_name_override + .write() + .unwrap() + .insert( + hostname.clone(), + (override_ip.iter().collect(), force_port.unwrap_or(8448)), + ); + } else { + warn!("Using SRV record, but could not resolve to IP"); } + + if let Some(port) = force_port { + FedDest::Named(hostname.clone(), format!(":{port}")) + } else { + add_port_to_hostname(&hostname) + } + } else { + debug!("5: No SRV record found"); + add_port_to_hostname(&destination_str) } } } @@ -660,15 +651,12 @@ pub fn parse_incoming_pdu( let room_version_id = services().rooms.state.get_room_version(&room_id)?; - let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } + let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); }; Ok((event_id, value, room_id)) } @@ -731,7 +719,7 @@ pub async fn send_transaction_message_route( .roomid_mutex_federation .write() .await - .entry(room_id.to_owned()) + .entry(room_id.clone()) .or_default(), ); let mutex_lock = mutex.lock().await; @@ -777,7 +765,6 @@ pub async fn send_transaction_message_route( .filter_map(|edu| serde_json::from_str::(edu.json().get()).ok()) { match edu { - Edu::Presence(_) => {} Edu::Receipt(receipt) => { for (room_id, room_updates) in receipt.receipts { for (user_id, user_updates) in room_updates.read { @@ -881,7 +868,7 @@ pub async fn send_transaction_message_route( "Event is invalid", ) })?, - )? + )?; } DeviceIdOrAllDevices::AllDevices => { @@ -929,7 +916,7 @@ pub async fn send_transaction_message_route( )?; } } - Edu::_Custom(_) => {} + Edu::_Custom(_) | Edu::Presence(_) => {} } } @@ -1278,15 +1265,14 @@ pub async fn get_room_state_route( Ok(get_room_state::v1::Response { auth_chain: auth_chain_ids - .filter_map( - |id| match services().rooms.timeline.get_pdu_json(&id).ok()? { - Some(json) => Some(PduEvent::convert_to_outgoing_federation_event(json)), - None => { - error!("Could not find event json for {id} in db."); - None - } - }, - ) + .filter_map(|id| { + if let Some(json) = services().rooms.timeline.get_pdu_json(&id).ok()? { + Some(PduEvent::convert_to_outgoing_federation_event(json)) + } else { + error!("Could not find event json for {id} in db."); + None + } + }) .collect(), pdus, }) @@ -1378,7 +1364,7 @@ pub async fn create_join_event_template_route( .roomid_mutex_state .write() .await - .entry(body.room_id.to_owned()) + .entry(body.room_id.clone()) .or_default(), ); let state_lock = mutex_state.lock().await; @@ -1518,15 +1504,12 @@ async fn create_join_event( // We do not add the event_id field to the pdu here because of signature and hashes checks let room_version_id = services().rooms.state.get_room_version(room_id)?; - let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) { - Ok(t) => t, - Err(_) => { - // Event could not be converted to canonical json - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Could not convert event to canonical json.", - )); - } + let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else { + // Event could not be converted to canonical json + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Could not convert event to canonical json.", + )); }; let origin: OwnedServerName = serde_json::from_value( @@ -1841,11 +1824,11 @@ pub async fn get_profile_information_route( match &body.field { Some(ProfileField::DisplayName) => { - displayname = services().users.displayname(&body.user_id)? + displayname = services().users.displayname(&body.user_id)?; } Some(ProfileField::AvatarUrl) => { avatar_url = services().users.avatar_url(&body.user_id)?; - blurhash = services().users.blurhash(&body.user_id)? + blurhash = services().users.blurhash(&body.user_id)?; } // TODO: what to do with custom Some(_) => {} @@ -1857,9 +1840,9 @@ pub async fn get_profile_information_route( } Ok(get_profile_information::v1::Response { - blurhash, displayname, avatar_url, + blurhash, }) } @@ -1958,7 +1941,7 @@ mod tests { assert_eq!( add_port_to_hostname("example.com"), FedDest::Named(String::from("example.com"), String::from(":8448")) - ) + ); } #[test] @@ -1966,6 +1949,6 @@ mod tests { assert_eq!( add_port_to_hostname("example.com:1337"), FedDest::Named(String::from("example.com"), String::from(":1337")) - ) + ); } } diff --git a/src/clap.rs b/src/clap.rs index 170d2a17..77229426 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -5,13 +5,13 @@ use clap::Parser; /// Returns the current version of the crate with extra info if supplied /// /// Set the environment variable `CONDUIT_VERSION_EXTRA` to any UTF-8 string to -/// include it in parenthesis after the SemVer version. A common value are git +/// include it in parenthesis after the `SemVer` version. A common value are git /// commit hashes. fn version() -> String { let cargo_pkg_version = env!("CARGO_PKG_VERSION"); match option_env!("CONDUIT_VERSION_EXTRA") { - Some(x) => format!("{} ({})", cargo_pkg_version, x), + Some(x) => format!("{cargo_pkg_version} ({x})"), None => cargo_pkg_version.to_owned(), } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 652b3a4c..76a80422 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -84,6 +84,7 @@ pub struct Config { pub emergency_password: Option, #[serde(flatten)] + #[allow(clippy::zero_sized_map_values)] pub catchall: BTreeMap, } @@ -133,7 +134,7 @@ impl Config { Some(server_name) => server_name.to_owned(), None => { if self.server_name.port().is_some() { - self.server_name.to_owned() + self.server_name.clone() } else { format!("{}:443", self.server_name.host()) .try_into() diff --git a/src/config/proxy.rs b/src/config/proxy.rs index c03463e7..d161f4ed 100644 --- a/src/config/proxy.rs +++ b/src/config/proxy.rs @@ -67,7 +67,7 @@ impl PartialProxyConfig { let mut excluded_because = None; // most specific reason it was excluded if self.include.is_empty() { // treat empty include list as `*` - included_because = Some(&WildCardedDomain::WildCard) + included_because = Some(&WildCardedDomain::WildCard); } for wc_domain in &self.include { if wc_domain.matches(domain) { @@ -127,7 +127,7 @@ impl std::str::FromStr for WildCardedDomain { Ok(if s.starts_with("*.") { WildCardedDomain::WildCarded(s[1..].to_owned()) } else if s == "*" { - WildCardedDomain::WildCarded("".to_owned()) + WildCardedDomain::WildCarded(String::new()) } else { WildCardedDomain::Exact(s.to_owned()) }) diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs index cf77e3dd..832d246e 100644 --- a/src/database/abstraction/rocksdb.rs +++ b/src/database/abstraction/rocksdb.rs @@ -32,7 +32,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O let mut db_opts = rocksdb::Options::default(); db_opts.set_block_based_table_factory(&block_based_options); db_opts.create_if_missing(true); - db_opts.increase_parallelism(num_cpus::get() as i32); + db_opts.increase_parallelism(num_cpus::get().try_into().unwrap_or(i32::MAX)); db_opts.set_max_open_files(max_open_files); db_opts.set_compression_type(rocksdb::DBCompressionType::Lz4); db_opts.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd); @@ -41,7 +41,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning db_opts.set_level_compaction_dynamic_level_bytes(true); db_opts.set_max_background_jobs(6); - db_opts.set_bytes_per_sync(1048576); + db_opts.set_bytes_per_sync(1_048_576); // https://github.com/facebook/rocksdb/issues/849 db_opts.set_keep_log_file_num(100); @@ -112,6 +112,7 @@ impl KeyValueDatabaseEngine for Arc { fn memory_usage(&self) -> Result { let stats = rocksdb::perf::get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?; + #[allow(clippy::cast_precision_loss)] Ok(format!( "Approximate memory usage of all the mem-tables: {:.3} MB\n\ Approximate memory usage of un-flushed mem-tables: {:.3} MB\n\ @@ -219,7 +220,7 @@ impl KvTree for RocksDbEngineTree<'_> { let lock = self.write_lock.write().unwrap(); let old = self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?; - let new = utils::increment(old.as_deref()).unwrap(); + let new = utils::increment(old.as_deref()); self.db .rocks .put_cf_opt(&self.cf(), key, &new, &writeoptions)?; @@ -236,7 +237,7 @@ impl KvTree for RocksDbEngineTree<'_> { for key in iter { let old = self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?; - let new = utils::increment(old.as_deref()).unwrap(); + let new = utils::increment(old.as_deref()); self.db .rocks .put_cf_opt(&self.cf(), key, new, &writeoptions)?; diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index b448c3b6..28bbcf6f 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -88,6 +88,7 @@ impl KeyValueDatabaseEngine for Arc { // 1. convert MB to KiB // 2. divide by permanent connections + permanent iter connections + write connection // 3. round down to nearest integer + #[allow(clippy::cast_precision_loss)] let cache_size_per_thread: u32 = ((config.db_cache_capacity_mb * 1024.0) / ((num_cpus::get().max(1) * 2) + 1) as f64) as u32; @@ -217,8 +218,7 @@ impl KvTree for SqliteTable { guard.execute("BEGIN", [])?; for key in iter { let old = self.get_with_guard(&guard, &key)?; - let new = crate::utils::increment(old.as_deref()) - .expect("utils::increment always returns Some"); + let new = crate::utils::increment(old.as_deref()); self.insert_with_guard(&guard, &key, &new)?; } guard.execute("COMMIT", [])?; @@ -308,8 +308,7 @@ impl KvTree for SqliteTable { let old = self.get_with_guard(&guard, key)?; - let new = - crate::utils::increment(old.as_deref()).expect("utils::increment always returns Some"); + let new = crate::utils::increment(old.as_deref()); self.insert_with_guard(&guard, key, &new)?; diff --git a/src/database/key_value/account_data.rs b/src/database/key_value/account_data.rs index 970b36b5..f08ade13 100644 --- a/src/database/key_value/account_data.rs +++ b/src/database/key_value/account_data.rs @@ -20,7 +20,7 @@ impl service::account_data::Data for KeyValueDatabase { data: &serde_json::Value, ) -> Result<()> { let mut prefix = room_id - .map(|r| r.to_string()) + .map(ToString::to_string) .unwrap_or_default() .as_bytes() .to_vec(); @@ -70,7 +70,7 @@ impl service::account_data::Data for KeyValueDatabase { kind: RoomAccountDataEventType, ) -> Result>> { let mut key = room_id - .map(|r| r.to_string()) + .map(ToString::to_string) .unwrap_or_default() .as_bytes() .to_vec(); @@ -105,7 +105,7 @@ impl service::account_data::Data for KeyValueDatabase { let mut userdata = HashMap::new(); let mut prefix = room_id - .map(|r| r.to_string()) + .map(ToString::to_string) .unwrap_or_default() .as_bytes() .to_vec(); diff --git a/src/database/key_value/globals.rs b/src/database/key_value/globals.rs index 2851ce53..2f4a5ca0 100644 --- a/src/database/key_value/globals.rs +++ b/src/database/key_value/globals.rs @@ -136,7 +136,7 @@ impl service::globals::Data for KeyValueDatabase { } fn cleanup(&self) -> Result<()> { - self._db.cleanup() + self.db.cleanup() } fn memory_usage(&self) -> String { @@ -160,7 +160,7 @@ our_real_users_cache: {our_real_users_cache} appservice_in_room_cache: {appservice_in_room_cache} lasttimelinecount_cache: {lasttimelinecount_cache}\n" ); - if let Ok(db_stats) = self._db.memory_usage() { + if let Ok(db_stats) = self.db.memory_usage() { response += &db_stats; } @@ -209,7 +209,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" self.global.insert(b"keypair", &keypair)?; Ok::<_, Error>(keypair) }, - |s| Ok(s.to_vec()), + |s| Ok(s.clone()), )?; let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff); @@ -285,7 +285,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" .server_signingkeys .get(origin.as_bytes())? .and_then(|bytes| serde_json::from_slice(&bytes).ok()) - .map(|keys: ServerSigningKeys| { + .map_or_else(BTreeMap::new, |keys: ServerSigningKeys| { let mut tree = keys.verify_keys; tree.extend( keys.old_verify_keys @@ -293,8 +293,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n" .map(|old| (old.0, VerifyKey::new(old.1.key))), ); tree - }) - .unwrap_or_else(BTreeMap::new); + }); Ok(signingkeys) } diff --git a/src/database/key_value/pusher.rs b/src/database/key_value/pusher.rs index 50a6faca..cf61a4a0 100644 --- a/src/database/key_value/pusher.rs +++ b/src/database/key_value/pusher.rs @@ -22,10 +22,7 @@ impl service::pusher::Data for KeyValueDatabase { let mut key = sender.as_bytes().to_vec(); key.push(0xff); key.extend_from_slice(ids.pushkey.as_bytes()); - self.senderkey_pusher - .remove(&key) - .map(|_| ()) - .map_err(Into::into) + self.senderkey_pusher.remove(&key).map_err(Into::into) } } } diff --git a/src/database/key_value/rooms/alias.rs b/src/database/key_value/rooms/alias.rs index 6f230323..a4666834 100644 --- a/src/database/key_value/rooms/alias.rs +++ b/src/database/key_value/rooms/alias.rs @@ -15,7 +15,7 @@ impl service::rooms::alias::Data for KeyValueDatabase { fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> { if let Some(room_id) = self.alias_roomid.get(alias.alias().as_bytes())? { - let mut prefix = room_id.to_vec(); + let mut prefix = room_id.clone(); prefix.push(0xff); for (key, _) in self.aliasid_alias.scan_prefix(prefix) { diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index ad573f06..0b3f9b0d 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -54,12 +54,11 @@ impl service::rooms::search::Data for KeyValueDatabase { .map(move |(key, _)| key[prefix3.len()..].to_vec()) }); - let common_elements = match utils::common_elements(iterators, |a, b| { + let Some(common_elements) = utils::common_elements(iterators, |a, b| { // We compare b with a because we reversed the iterator earlier b.cmp(a) - }) { - Some(it) => it, - None => return Ok(None), + }) else { + return Ok(None); }; Ok(Some((Box::new(common_elements), words))) diff --git a/src/database/key_value/rooms/short.rs b/src/database/key_value/rooms/short.rs index 98cfa48a..c0f56fed 100644 --- a/src/database/key_value/rooms/short.rs +++ b/src/database/key_value/rooms/short.rs @@ -10,18 +10,18 @@ impl service::rooms::short::Data for KeyValueDatabase { return Ok(*short); } - let short = match self.eventid_shorteventid.get(event_id.as_bytes())? { - Some(shorteventid) => utils::u64_from_bytes(&shorteventid) - .map_err(|_| Error::bad_database("Invalid shorteventid in db."))?, - None => { + let short = + if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? { + utils::u64_from_bytes(&shorteventid) + .map_err(|_| Error::bad_database("Invalid shorteventid in db."))? + } else { let shorteventid = services().globals.next_count()?; self.eventid_shorteventid .insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?; self.shorteventid_eventid .insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?; shorteventid - } - }; + }; self.eventidshort_cache .lock() @@ -45,13 +45,13 @@ impl service::rooms::short::Data for KeyValueDatabase { return Ok(Some(*short)); } - let mut statekey = event_type.to_string().as_bytes().to_vec(); - statekey.push(0xff); - statekey.extend_from_slice(state_key.as_bytes()); + let mut state_key_bytes = event_type.to_string().as_bytes().to_vec(); + state_key_bytes.push(0xff); + state_key_bytes.extend_from_slice(state_key.as_bytes()); let short = self .statekey_shortstatekey - .get(&statekey)? + .get(&state_key_bytes)? .map(|shortstatekey| { utils::u64_from_bytes(&shortstatekey) .map_err(|_| Error::bad_database("Invalid shortstatekey in db.")) @@ -82,22 +82,22 @@ impl service::rooms::short::Data for KeyValueDatabase { return Ok(*short); } - let mut statekey = event_type.to_string().as_bytes().to_vec(); - statekey.push(0xff); - statekey.extend_from_slice(state_key.as_bytes()); + let mut state_key_bytes = event_type.to_string().as_bytes().to_vec(); + state_key_bytes.push(0xff); + state_key_bytes.extend_from_slice(state_key.as_bytes()); - let short = match self.statekey_shortstatekey.get(&statekey)? { - Some(shortstatekey) => utils::u64_from_bytes(&shortstatekey) - .map_err(|_| Error::bad_database("Invalid shortstatekey in db."))?, - None => { + let short = + if let Some(shortstatekey) = self.statekey_shortstatekey.get(&state_key_bytes)? { + utils::u64_from_bytes(&shortstatekey) + .map_err(|_| Error::bad_database("Invalid shortstatekey in db."))? + } else { let shortstatekey = services().globals.next_count()?; self.statekey_shortstatekey - .insert(&statekey, &shortstatekey.to_be_bytes())?; + .insert(&state_key_bytes, &shortstatekey.to_be_bytes())?; self.shortstatekey_statekey - .insert(&shortstatekey.to_be_bytes(), &statekey)?; + .insert(&shortstatekey.to_be_bytes(), &state_key_bytes)?; shortstatekey - } - }; + }; self.statekeyshort_cache .lock() @@ -175,21 +175,22 @@ impl service::rooms::short::Data for KeyValueDatabase { Ok(result) } - /// Returns (shortstatehash, already_existed) + /// Returns (shortstatehash, `already_existed`) fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { - Ok(match self.statehash_shortstatehash.get(state_hash)? { - Some(shortstatehash) => ( - utils::u64_from_bytes(&shortstatehash) - .map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?, - true, - ), - None => { + Ok( + if let Some(shortstatehash) = self.statehash_shortstatehash.get(state_hash)? { + ( + utils::u64_from_bytes(&shortstatehash) + .map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?, + true, + ) + } else { let shortstatehash = services().globals.next_count()?; self.statehash_shortstatehash .insert(state_hash, &shortstatehash.to_be_bytes())?; (shortstatehash, false) - } - }) + }, + ) } fn get_shortroomid(&self, room_id: &RoomId) -> Result> { @@ -203,15 +204,16 @@ impl service::rooms::short::Data for KeyValueDatabase { } fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result { - Ok(match self.roomid_shortroomid.get(room_id.as_bytes())? { - Some(short) => utils::u64_from_bytes(&short) - .map_err(|_| Error::bad_database("Invalid shortroomid in db."))?, - None => { + Ok( + if let Some(short) = self.roomid_shortroomid.get(room_id.as_bytes())? { + utils::u64_from_bytes(&short) + .map_err(|_| Error::bad_database("Invalid shortroomid in db."))? + } else { let short = services().globals.next_count()?; self.roomid_shortroomid .insert(room_id.as_bytes(), &short.to_be_bytes())?; short - } - }) + }, + ) } } diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index f17d37bb..a5fc857e 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -63,7 +63,7 @@ impl service::rooms::state::Data for KeyValueDatabase { } for event_id in event_ids { - let mut key = prefix.to_owned(); + let mut key = prefix.clone(); key.extend_from_slice(event_id.as_bytes()); self.roomid_pduleaves.insert(&key, event_id.as_bytes())?; } diff --git a/src/database/key_value/rooms/state_accessor.rs b/src/database/key_value/rooms/state_accessor.rs index fe40b937..dc5a112d 100644 --- a/src/database/key_value/rooms/state_accessor.rs +++ b/src/database/key_value/rooms/state_accessor.rs @@ -79,13 +79,12 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase { event_type: &StateEventType, state_key: &str, ) -> Result>> { - let shortstatekey = match services() + let Some(shortstatekey) = services() .rooms .short .get_shortstatekey(event_type, state_key)? - { - Some(s) => s, - None => return Ok(None), + else { + return Ok(None); }; let full_state = services() .rooms diff --git a/src/database/key_value/rooms/user.rs b/src/database/key_value/rooms/user.rs index 4c435720..58b07eee 100644 --- a/src/database/key_value/rooms/user.rs +++ b/src/database/key_value/rooms/user.rs @@ -31,11 +31,10 @@ impl service::rooms::user::Data for KeyValueDatabase { self.userroomid_notificationcount .get(&userroom_id)? - .map(|bytes| { + .map_or(Ok(0), |bytes| { utils::u64_from_bytes(&bytes) .map_err(|_| Error::bad_database("Invalid notification count in db.")) }) - .unwrap_or(Ok(0)) } fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result { @@ -45,11 +44,10 @@ impl service::rooms::user::Data for KeyValueDatabase { self.userroomid_highlightcount .get(&userroom_id)? - .map(|bytes| { + .map_or(Ok(0), |bytes| { utils::u64_from_bytes(&bytes) .map_err(|_| Error::bad_database("Invalid highlight count in db.")) }) - .unwrap_or(Ok(0)) } fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result { diff --git a/src/database/key_value/sending.rs b/src/database/key_value/sending.rs index 3fc3e042..6c8e939b 100644 --- a/src/database/key_value/sending.rs +++ b/src/database/key_value/sending.rs @@ -67,9 +67,9 @@ impl service::sending::Data for KeyValueDatabase { for (outgoing_kind, event) in requests { let mut key = outgoing_kind.get_prefix(); if let SendingEventType::Pdu(value) = &event { - key.extend_from_slice(value) + key.extend_from_slice(value); } else { - key.extend_from_slice(&services().globals.next_count()?.to_be_bytes()) + key.extend_from_slice(&services().globals.next_count()?.to_be_bytes()); } let value = if let SendingEventType::Edu(value) = &event { &**value diff --git a/src/database/key_value/transaction_ids.rs b/src/database/key_value/transaction_ids.rs index 2ea6ad4a..b3bd05f4 100644 --- a/src/database/key_value/transaction_ids.rs +++ b/src/database/key_value/transaction_ids.rs @@ -12,7 +12,7 @@ impl service::transaction_ids::Data for KeyValueDatabase { ) -> Result<()> { let mut key = user_id.as_bytes().to_vec(); key.push(0xff); - key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default()); + key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default()); key.push(0xff); key.extend_from_slice(txn_id.as_bytes()); @@ -29,7 +29,7 @@ impl service::transaction_ids::Data for KeyValueDatabase { ) -> Result>> { let mut key = user_id.as_bytes().to_vec(); key.push(0xff); - key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default()); + key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default()); key.push(0xff); key.extend_from_slice(txn_id.as_bytes()); diff --git a/src/database/key_value/uiaa.rs b/src/database/key_value/uiaa.rs index 5fd91b07..20a0357d 100644 --- a/src/database/key_value/uiaa.rs +++ b/src/database/key_value/uiaa.rs @@ -34,7 +34,7 @@ impl service::uiaa::Data for KeyValueDatabase { .read() .unwrap() .get(&(user_id.to_owned(), device_id.to_owned(), session.to_owned())) - .map(|j| j.to_owned()) + .map(ToOwned::to_owned) } fn update_uiaa_session( diff --git a/src/database/key_value/users.rs b/src/database/key_value/users.rs index 0e6db83a..2c6edc38 100644 --- a/src/database/key_value/users.rs +++ b/src/database/key_value/users.rs @@ -141,7 +141,7 @@ impl service::users::Data for KeyValueDatabase { Ok(()) } - /// Get the avatar_url of a user. + /// Get the `avatar_url` of a user. fn avatar_url(&self, user_id: &UserId) -> Result> { self.userid_avatarurl .get(user_id.as_bytes())? @@ -153,7 +153,7 @@ impl service::users::Data for KeyValueDatabase { .transpose() } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is None. fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()> { if let Some(avatar_url) = avatar_url { self.userid_avatarurl @@ -178,7 +178,7 @@ impl service::users::Data for KeyValueDatabase { .transpose() } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is None. fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { if let Some(blurhash) = blurhash { self.userid_blurhash @@ -343,12 +343,11 @@ impl service::users::Data for KeyValueDatabase { fn last_one_time_keys_update(&self, user_id: &UserId) -> Result { self.userid_lastonetimekeyupdate .get(user_id.as_bytes())? - .map(|bytes| { + .map_or(Ok(0), |bytes| { utils::u64_from_bytes(&bytes).map_err(|_| { Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.") }) }) - .unwrap_or(Ok(0)) } fn take_one_time_key( @@ -949,7 +948,7 @@ impl KeyValueDatabase {} /// Will only return with Some(username) if the password was not empty and the /// username could be successfully parsed. -/// If utils::string_from_bytes(...) returns an error that username will be skipped +/// If `utils::string_from_bytes`(...) returns an error that username will be skipped /// and the error will be logged. fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option { // A valid password is not empty diff --git a/src/database/mod.rs b/src/database/mod.rs index 8d1b1913..29bec051 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -34,7 +34,7 @@ use tokio::time::interval; use tracing::{debug, error, info, warn}; pub struct KeyValueDatabase { - _db: Arc, + db: Arc, //pub globals: globals::Globals, pub(super) global: Arc, @@ -113,7 +113,7 @@ pub struct KeyValueDatabase { pub(super) roomsynctoken_shortstatehash: Arc, /// Remember the state hash at events in the past. pub(super) shorteventid_shortstatehash: Arc, - /// StateKey = EventType + StateKey, ShortStateKey = Count + /// `StateKey` = `EventType` + `StateKey`, `ShortStateKey` = Count pub(super) statekey_shortstatekey: Arc, pub(super) shortstatekey_statekey: Arc, @@ -127,14 +127,14 @@ pub struct KeyValueDatabase { pub(super) shorteventid_authchain: Arc, - /// RoomId + EventId -> outlier PDU. + /// `RoomId` + `EventId` -> outlier PDU. /// Any pdu that has passed the steps 1-8 in the incoming event /federation/send/txn. pub(super) eventid_outlierpdu: Arc, pub(super) softfailedeventids: Arc, - /// ShortEventId + ShortEventId -> (). + /// `ShortEventId` + `ShortEventId` -> (). pub(super) tofrom_relation: Arc, - /// RoomId + EventId -> Parent PDU EventId. + /// `RoomId` + `EventId` -> Parent PDU `EventId`. pub(super) referencedevents: Arc, //pub account_data: account_data::AccountData, @@ -241,6 +241,8 @@ impl KeyValueDatabase { .map_err(|_| Error::BadConfig("Database folder doesn't exists and couldn't be created (e.g. due to missing permissions). Please create the database folder yourself."))?; } + // Databases which are disabled will trigger this + #[allow(clippy::match_same_arms)] let builder: Arc = match &*config.database_backend { "sqlite" => { #[cfg(not(feature = "sqlite"))] @@ -274,7 +276,7 @@ impl KeyValueDatabase { } let db_raw = Box::new(Self { - _db: builder.clone(), + db: builder.clone(), userid_password: builder.open_tree("userid_password")?, userid_displayname: builder.open_tree("userid_displayname")?, userid_avatarurl: builder.open_tree("userid_avatarurl")?, @@ -428,12 +430,9 @@ impl KeyValueDatabase { for (roomserverid, _) in db.roomserverids.iter() { let mut parts = roomserverid.split(|&b| b == 0xff); let room_id = parts.next().expect("split always returns one element"); - let servername = match parts.next() { - Some(s) => s, - None => { - error!("Migration: Invalid roomserverid in db."); - continue; - } + let Some(servername) = parts.next() else { + error!("Migration: Invalid roomserverid in db."); + continue; }; let mut serverroomid = servername.to_vec(); serverroomid.push(0xff); @@ -620,7 +619,7 @@ impl KeyValueDatabase { Ok::<_, Error>(()) }; - for (k, seventid) in db._db.open_tree("stateid_shorteventid")?.iter() { + for (k, seventid) in db.db.open_tree("stateid_shorteventid")?.iter() { let sstatehash = utils::u64_from_bytes(&k[0..size_of::()]) .expect("number of bytes is correct"); let sstatekey = k[size_of::()..].to_vec(); @@ -793,7 +792,7 @@ impl KeyValueDatabase { } // Force E2EE device list updates so we can send them over federation - for user_id in services().users.iter().filter_map(|r| r.ok()) { + for user_id in services().users.iter().filter_map(std::result::Result::ok) { services().users.mark_device_key_update(&user_id)?; } @@ -803,7 +802,7 @@ impl KeyValueDatabase { } if services().globals.database_version()? < 11 { - db._db + db.db .open_tree("userdevicesessionid_uiaarequest")? .clear()?; services().globals.bump_database_version(11)?; @@ -979,7 +978,7 @@ impl KeyValueDatabase { error!( "Could not set the configured emergency password for the conduit user: {}", e - ) + ); } }; @@ -997,7 +996,7 @@ impl KeyValueDatabase { pub fn flush(&self) -> Result<()> { let start = std::time::Instant::now(); - let res = self._db.flush(); + let res = self.db.flush(); debug!("flush: took {:?}", start.elapsed()); @@ -1017,17 +1016,10 @@ impl KeyValueDatabase { } async fn try_handle_updates() -> Result<()> { - let response = services() - .globals - .default_client() - .get("https://conduit.rs/check-for-updates/stable") - .send() - .await?; - #[derive(Deserialize)] struct CheckForUpdatesResponseEntry { - id: u64, date: String, + id: u64, message: String, } #[derive(Deserialize)] @@ -1035,6 +1027,13 @@ impl KeyValueDatabase { updates: Vec, } + let response = services() + .globals + .default_client() + .get("https://conduit.rs/check-for-updates/stable") + .send() + .await?; + let response = serde_json::from_str::(&response.text().await?) .map_err(|_| Error::BadServerResponse("Bad version check response"))?; @@ -1048,7 +1047,7 @@ impl KeyValueDatabase { .send_message(RoomMessageEventContent::text_plain(format!( "@room: The following is a message from the Conduit developers. It was sent on '{}':\n\n{}", update.date, update.message - ))) + ))); } } services() @@ -1066,7 +1065,7 @@ impl KeyValueDatabase { use std::time::{Duration, Instant}; let timer_interval = - Duration::from_secs(services().globals.config.cleanup_second_interval as u64); + Duration::from_secs(u64::from(services().globals.config.cleanup_second_interval)); tokio::spawn(async move { let mut i = interval(timer_interval); diff --git a/src/lib.rs b/src/lib.rs index 5a89f805..80006aa7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,15 @@ +// All API endpoints must be async +#[allow(clippy::unused_async)] +// We expect request users and servers (probably shouldn't tho) +#[allow(clippy::missing_panics_doc)] pub mod api; pub mod clap; mod config; +// Results in large capacity if set to a negative number, user's fault really :P +#[allow(clippy::cast_sign_loss)] mod database; +// `self` is required for easy access to methods +#[allow(clippy::unused_self)] mod service; mod utils; diff --git a/src/main.rs b/src/main.rs index 5d60a6bf..dc24693c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -201,7 +201,7 @@ async fn run_server() -> io::Result<()> { #[cfg(feature = "systemd")] let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); - server.await? + server.await?; } None => { let server = bind(addr).handle(handle).serve(app); @@ -209,7 +209,7 @@ async fn run_server() -> io::Result<()> { #[cfg(feature = "systemd")] let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); - server.await? + server.await?; } } @@ -461,8 +461,8 @@ async fn shutdown_signal(handle: ServerHandle) { let sig: &str; tokio::select! { - _ = ctrl_c => { sig = "Ctrl+C"; }, - _ = terminate => { sig = "SIGTERM"; }, + () = ctrl_c => { sig = "Ctrl+C"; }, + () = terminate => { sig = "SIGTERM"; }, } warn!("Received {}, shutting down...", sig); diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index ab677f64..f107e0f4 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -110,7 +110,7 @@ enum AdminCommand { force: bool, }, - /// Get the auth_chain of a PDU + /// Get the `auth_chain` of a PDU GetAuthChain { /// An event ID (the $ character followed by the base64 reference hash) event_id: Box, @@ -229,7 +229,7 @@ impl Service { .roomid_mutex_state .write() .await - .entry(conduit_room.to_owned()) + .entry(conduit_room.clone()) .or_default(), ); @@ -711,11 +711,11 @@ impl Service { match <&UserId>::try_from(user) { Ok(user_id) => { if user_id.server_name() != services().globals.server_name() { - remote_ids.push(user_id) + remote_ids.push(user_id); } else if !services().users.exists(user_id)? { - non_existant_ids.push(user_id) + non_existant_ids.push(user_id); } else { - user_ids.push(user_id) + user_ids.push(user_id); } } Err(_) => { @@ -770,20 +770,21 @@ impl Service { if !force { user_ids.retain(|&user_id| match services().users.is_admin(user_id) { - Ok(is_admin) => match is_admin { - true => { + Ok(is_admin) => { + if is_admin { admins.push(user_id.localpart()); false + } else { + true } - false => true, - }, + } Err(_) => false, - }) + }); } for &user_id in &user_ids { if services().users.deactivate_account(user_id).is_ok() { - deactivation_count += 1 + deactivation_count += 1; } } @@ -846,7 +847,7 @@ impl Service { let pub_key_map = pub_key_map.read().await; match ruma::signatures::verify_json(&pub_key_map, &value) { - Ok(_) => RoomMessageEventContent::text_plain("Signature correct"), + Ok(()) => RoomMessageEventContent::text_plain("Signature correct"), Err(e) => RoomMessageEventContent::text_plain(format!( "Signature verification failed: {e}" )), @@ -909,8 +910,7 @@ impl Service { while text_lines .get(line_index) - .map(|line| line.starts_with('#')) - .unwrap_or(false) + .is_some_and(|line| line.starts_with('#')) { command_body += if text_lines[line_index].starts_with("# ") { &text_lines[line_index][2..] @@ -1001,7 +1001,7 @@ impl Service { event_type: TimelineEventType::RoomCreate, content: to_raw_value(&content).expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1054,7 +1054,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1073,7 +1073,7 @@ impl Service { content: to_raw_value(&RoomJoinRulesEventContent::new(JoinRule::Invite)) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1094,7 +1094,7 @@ impl Service { )) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1115,7 +1115,7 @@ impl Service { )) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1135,7 +1135,7 @@ impl Service { content: to_raw_value(&RoomNameEventContent::new(room_name)) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1155,7 +1155,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1181,7 +1181,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, @@ -1291,7 +1291,7 @@ impl Service { // Set power level let mut users = BTreeMap::new(); - users.insert(conduit_user.to_owned(), 100.into()); + users.insert(conduit_user.clone(), 100.into()); users.insert(user_id.to_owned(), 100.into()); services() @@ -1306,7 +1306,7 @@ impl Service { }) .expect("event is valid, we just created it"), unsigned: None, - state_key: Some("".to_owned()), + state_key: Some(String::new()), redacts: None, }, &conduit_user, diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 75a682cb..e1f946bd 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -10,7 +10,7 @@ pub trait Data: Send + Sync { content_type: Option<&str>, ) -> Result>; - /// Returns content_disposition, content_type and the metadata key. + /// Returns `content_disposition`, `content_type` and the metadata key. fn search_file_metadata( &self, mxc: String, diff --git a/src/service/media/mod.rs b/src/service/media/mod.rs index 0340ab49..2e195bb9 100644 --- a/src/service/media/mod.rs +++ b/src/service/media/mod.rs @@ -128,7 +128,7 @@ impl Service { Ok(Some(FileMeta { content_disposition, content_type, - file: file.to_vec(), + file: file.clone(), })) } else if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc.clone(), 0, 0) @@ -145,7 +145,7 @@ impl Service { return Ok(Some(FileMeta { content_disposition, content_type, - file: file.to_vec(), + file: file.clone(), })); } @@ -211,14 +211,14 @@ impl Service { Ok(Some(FileMeta { content_disposition, content_type, - file: thumbnail_bytes.to_vec(), + file: thumbnail_bytes.clone(), })) } else { // Couldn't parse file to generate thumbnail, send original Ok(Some(FileMeta { content_disposition, content_type, - file: file.to_vec(), + file: file.clone(), })) } } else { diff --git a/src/service/mod.rs b/src/service/mod.rs index 4c11bc18..57d99ae2 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -39,6 +39,8 @@ pub struct Services { } impl Services { + // Results in large capacity if set to a negative number, user's fault really :P + #[allow(clippy::cast_sign_loss)] pub fn build< D: appservice::Data + pusher::Data diff --git a/src/service/pusher/mod.rs b/src/service/pusher/mod.rs index 83127e63..41605ebc 100644 --- a/src/service/pusher/mod.rs +++ b/src/service/pusher/mod.rs @@ -64,7 +64,7 @@ impl Service { warn!("Failed to find destination {}: {}", destination, e); Error::BadServerResponse("Invalid destination") })? - .map(|body| body.freeze()); + .map(BytesMut::freeze); let reqwest_request = reqwest::Request::try_from(http_request)?; @@ -252,7 +252,7 @@ impl Service { .iter() .any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_))) { - notifi.prio = NotificationPriority::High + notifi.prio = NotificationPriority::High; } if event_id_only { @@ -279,7 +279,6 @@ impl Service { Ok(()) } // TODO: Handle email - PusherKind::Email(_) => Ok(()), _ => Ok(()), } } diff --git a/src/service/rooms/edus/presence/data.rs b/src/service/rooms/edus/presence/data.rs index 53329e08..6e8877b3 100644 --- a/src/service/rooms/edus/presence/data.rs +++ b/src/service/rooms/edus/presence/data.rs @@ -6,7 +6,7 @@ use ruma::{events::presence::PresenceEvent, OwnedUserId, RoomId, UserId}; pub trait Data: Send + Sync { /// Adds a presence event which will be saved until a new event replaces it. /// - /// Note: This method takes a RoomId because presence updates are always bound to rooms to + /// Note: This method takes a `RoomId` because presence updates are always bound to rooms to /// make sure users outside these rooms can't see them. fn update_presence( &self, @@ -21,7 +21,7 @@ pub trait Data: Send + Sync { /// Returns the timestamp of the last presence update of this user in millis since the unix epoch. fn last_presence_update(&self, user_id: &UserId) -> Result>; - /// Returns the presence event with correct last_active_ago. + /// Returns the presence event with correct `last_active_ago`. fn get_presence_event( &self, room_id: &RoomId, diff --git a/src/service/rooms/edus/presence/mod.rs b/src/service/rooms/edus/presence/mod.rs index 4b929d28..b1cb888e 100644 --- a/src/service/rooms/edus/presence/mod.rs +++ b/src/service/rooms/edus/presence/mod.rs @@ -10,10 +10,13 @@ pub struct Service { pub db: &'static dyn Data, } +// TODO: remove when presence is implemented +#[allow(clippy::unnecessary_wraps)] +#[allow(clippy::needless_pass_by_value)] impl Service { /// Adds a presence event which will be saved until a new event replaces it. /// - /// Note: This method takes a RoomId because presence updates are always bound to rooms to + /// 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, diff --git a/src/service/rooms/edus/read_receipt/data.rs b/src/service/rooms/edus/read_receipt/data.rs index 044dad82..dacd014f 100644 --- a/src/service/rooms/edus/read_receipt/data.rs +++ b/src/service/rooms/edus/read_receipt/data.rs @@ -10,7 +10,7 @@ pub trait Data: Send + Sync { event: ReceiptEvent, ) -> Result<()>; - /// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`. + /// Returns an iterator over the most recent `read_receipts` in a room that happened after the event with id `since`. #[allow(clippy::type_complexity)] fn readreceipts_since<'a>( &'a self, diff --git a/src/service/rooms/edus/typing/mod.rs b/src/service/rooms/edus/typing/mod.rs index 7546aa84..2bf5eb00 100644 --- a/src/service/rooms/edus/typing/mod.rs +++ b/src/service/rooms/edus/typing/mod.rs @@ -11,7 +11,7 @@ pub struct Service { } impl Service { - /// Sets a user as typing until the timeout timestamp is reached or roomtyping_remove is + /// Sets a user as typing until the timeout timestamp is reached or `roomtyping_remove` is /// called. pub async fn typing_add(&self, user_id: &UserId, room_id: &RoomId, timeout: u64) -> Result<()> { self.typing diff --git a/src/service/rooms/event_handler/mod.rs b/src/service/rooms/event_handler/mod.rs index b7817e50..01610061 100644 --- a/src/service/rooms/event_handler/mod.rs +++ b/src/service/rooms/event_handler/mod.rs @@ -39,7 +39,7 @@ use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; use tracing::{debug, error, info, trace, warn}; -use crate::{service::*, services, Error, PduEvent, Result}; +use crate::{service::pdu, services, Error, PduEvent, Result}; use super::state_compressor::CompressedStateEvent; @@ -99,7 +99,7 @@ impl Service { // 1. Skip the PDU if we already have it as a timeline event if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? { - return Ok(Some(pdu_id.to_vec())); + return Ok(Some(pdu_id.clone())); } let create_event = services() @@ -199,7 +199,7 @@ impl Service { e.insert((Instant::now(), 1)); } hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1) + *e.get_mut() = (Instant::now(), e.get().1 + 1); } } continue; @@ -243,7 +243,7 @@ impl Service { e.insert((Instant::now(), 1)); } hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1) + *e.get_mut() = (Instant::now(), e.get().1 + 1); } } } @@ -342,14 +342,11 @@ impl Service { Ok(ruma::signatures::Verified::Signatures) => { // Redact warn!("Calculated hash does not match: {}", event_id); - let obj = match ruma::canonical_json::redact(value, room_version_id, None) { - Ok(obj) => obj, - Err(_) => { - return Err(Error::BadRequest( - ErrorKind::InvalidParam, - "Redaction failed", - )) - } + let Ok(obj) = ruma::canonical_json::redact(value, room_version_id, None) else { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Redaction failed", + )); }; // Skip the PDU if it is redacted and we already have it as an outlier event @@ -409,12 +406,9 @@ impl Service { // Build map of auth events let mut auth_events = HashMap::new(); for id in &incoming_pdu.auth_events { - let auth_event = match services().rooms.timeline.get_pdu(id)? { - Some(e) => e, - None => { - warn!("Could not find auth event {}", id); - continue; - } + let Some(auth_event) = services().rooms.timeline.get_pdu(id)? else { + warn!("Could not find auth event {}", id); + continue; }; self.check_room_id(room_id, &auth_event)?; @@ -441,8 +435,8 @@ impl Service { // The original create event must be in the auth events if !matches!( auth_events - .get(&(StateEventType::RoomCreate, "".to_owned())) - .map(|a| a.as_ref()), + .get(&(StateEventType::RoomCreate, String::new())) + .map(AsRef::as_ref), Some(_) | None ) { return Err(Error::BadRequest( @@ -574,21 +568,16 @@ impl Service { let mut okay = true; for prev_eventid in &incoming_pdu.prev_events { - let prev_event = - if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(prev_eventid) { - pdu - } else { - okay = false; - break; - }; + let Ok(Some(prev_event)) = services().rooms.timeline.get_pdu(prev_eventid) else { + okay = false; + break; + }; - let sstatehash = if let Ok(Some(s)) = services() + let Ok(Some(sstatehash)) = services() .rooms .state_accessor .pdu_shortstatehash(prev_eventid) - { - s - } else { + else { okay = false; break; }; @@ -738,7 +727,7 @@ impl Service { .get_shortstatekey(&StateEventType::RoomCreate, "")? .expect("Room exists"); - if state.get(&create_shortstatekey).map(|id| id.as_ref()) + if state.get(&create_shortstatekey).map(AsRef::as_ref) != Some(&create_event.event_id) { return Err(Error::bad_database( @@ -1046,16 +1035,12 @@ impl Service { }; let lock = services().globals.stateres_mutex.lock(); - let state = match state_res::resolve( - room_version_id, - &fork_states, - auth_chain_sets, - fetch_event, - ) { - Ok(new_state) => new_state, - Err(_) => { - return Err(Error::bad_database("State resolution failed, either an event could not be found or deserialization")); - } + let Ok(state) = + state_res::resolve(room_version_id, &fork_states, auth_chain_sets, fetch_event) + else { + return Err(Error::bad_database( + "State resolution failed, either an event could not be found or deserialization", + )); }; drop(lock); @@ -1113,7 +1098,7 @@ impl Service { e.insert((Instant::now(), 1)); } hash_map::Entry::Occupied(mut e) => { - *e.get_mut() = (Instant::now(), e.get().1 + 1) + *e.get_mut() = (Instant::now(), e.get().1 + 1); } } }; @@ -1172,7 +1157,7 @@ impl Service { } info!("Fetching {} over federation.", next_id); - match services() + if let Ok(res) = services() .sending .send_federation_request( origin, @@ -1182,46 +1167,41 @@ impl Service { ) .await { - Ok(res) => { - info!("Got {} over federation", next_id); - let (calculated_event_id, value) = - match pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) { - Ok(t) => t, - Err(_) => { - back_off((*next_id).to_owned()).await; - continue; - } - }; - - if calculated_event_id != *next_id { - warn!("Server didn't return event id we requested: requested: {}, we got {}. Event: {:?}", - next_id, calculated_event_id, &res.pdu); - } - - if let Some(auth_events) = - value.get("auth_events").and_then(|c| c.as_array()) - { - for auth_event in auth_events { - if let Ok(auth_event) = - serde_json::from_value(auth_event.clone().into()) - { - let a: Arc = auth_event; - todo_auth_events.push(a); - } else { - warn!("Auth event id is not valid"); - } - } - } else { - warn!("Auth event list invalid"); - } - - events_in_reverse_order.push((next_id.clone(), value)); - events_all.insert(next_id); - } - Err(_) => { - warn!("Failed to fetch event: {}", next_id); + info!("Got {} over federation", next_id); + let Ok((calculated_event_id, value)) = + pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) + else { back_off((*next_id).to_owned()).await; + continue; + }; + + if calculated_event_id != *next_id { + warn!("Server didn't return event id we requested: requested: {}, we got {}. Event: {:?}", + next_id, calculated_event_id, &res.pdu); } + + if let Some(auth_events) = + value.get("auth_events").and_then(|c| c.as_array()) + { + for auth_event in auth_events { + if let Ok(auth_event) = + serde_json::from_value(auth_event.clone().into()) + { + let a: Arc = auth_event; + todo_auth_events.push(a); + } else { + warn!("Auth event id is not valid"); + } + } + } else { + warn!("Auth event list invalid"); + } + + events_in_reverse_order.push((next_id.clone(), value)); + events_all.insert(next_id); + } else { + warn!("Failed to fetch event: {}", next_id); + back_off((*next_id).to_owned()).await; } } @@ -1410,12 +1390,9 @@ impl Service { ) .await; - let keys = match fetch_res { - Ok(keys) => keys, - Err(_) => { - warn!("Signature verification failed: Could not fetch signing key.",); - continue; - } + let Ok(keys) = fetch_res else { + warn!("Signature verification failed: Could not fetch signing key.",); + continue; }; pub_key_map @@ -1637,22 +1614,21 @@ impl Service { /// Returns Ok if the acl allows the server pub fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> { - let acl_event = match services().rooms.state_accessor.room_state_get( + let Some(acl_event) = services().rooms.state_accessor.room_state_get( room_id, &StateEventType::RoomServerAcl, "", - )? { - Some(acl) => acl, - None => return Ok(()), + )? + else { + return Ok(()); }; let acl_event_content: RoomServerAclEventContent = - match serde_json::from_str(acl_event.content.get()) { - Ok(content) => content, - Err(_) => { - warn!("Invalid ACL event"); - return Ok(()); - } + if let Ok(content) = serde_json::from_str(acl_event.content.get()) { + content + } else { + warn!("Invalid ACL event"); + return Ok(()); }; if acl_event_content.allow.is_empty() { @@ -1693,18 +1669,17 @@ impl Service { .get(origin) .map(|s| Arc::clone(s).acquire_owned()); - let permit = match permit { - Some(p) => p, - None => { - let mut write = services().globals.servername_ratelimiter.write().await; - let s = Arc::clone( - write - .entry(origin.to_owned()) - .or_insert_with(|| Arc::new(Semaphore::new(1))), - ); + let permit = if let Some(permit) = permit { + permit + } else { + let mut write = services().globals.servername_ratelimiter.write().await; + let s = Arc::clone( + write + .entry(origin.to_owned()) + .or_insert_with(|| Arc::new(Semaphore::new(1))), + ); - s.acquire_owned() - } + s.acquire_owned() } .await; diff --git a/src/service/rooms/pdu_metadata/mod.rs b/src/service/rooms/pdu_metadata/mod.rs index 411f4f54..cde237a9 100644 --- a/src/service/rooms/pdu_metadata/mod.rs +++ b/src/service/rooms/pdu_metadata/mod.rs @@ -46,8 +46,8 @@ impl Service { sender_user: &UserId, room_id: &RoomId, target: &EventId, - filter_event_type: Option, - filter_rel_type: Option, + filter_event_type: &Option, + filter_rel_type: &Option, from: PduCount, to: Option, limit: usize, diff --git a/src/service/rooms/short/data.rs b/src/service/rooms/short/data.rs index 652c525b..61a251a4 100644 --- a/src/service/rooms/short/data.rs +++ b/src/service/rooms/short/data.rs @@ -22,7 +22,7 @@ pub trait Data: Send + Sync { fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)>; - /// Returns (shortstatehash, already_existed) + /// Returns (shortstatehash, `already_existed`) fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)>; fn get_shortroomid(&self, room_id: &RoomId) -> Result>; diff --git a/src/service/rooms/short/mod.rs b/src/service/rooms/short/mod.rs index 45fadd74..9869b869 100644 --- a/src/service/rooms/short/mod.rs +++ b/src/service/rooms/short/mod.rs @@ -39,7 +39,7 @@ impl Service { self.db.get_statekey_from_short(shortstatekey) } - /// Returns (shortstatehash, already_existed) + /// Returns (shortstatehash, `already_existed`) pub fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> { self.db.get_or_create_shortstatehash(state_hash) } diff --git a/src/service/rooms/spaces/mod.rs b/src/service/rooms/spaces/mod.rs index 5addc6fc..a9727b44 100644 --- a/src/service/rooms/spaces/mod.rs +++ b/src/service/rooms/spaces/mod.rs @@ -63,13 +63,13 @@ impl Service { let mut results = Vec::new(); while let Some(current_room) = { - while stack.last().map_or(false, |s| s.is_empty()) { + while stack.last().map_or(false, Vec::is_empty) { stack.pop(); } - if !stack.is_empty() { - stack.last_mut().and_then(|s| s.pop()) - } else { + if stack.is_empty() { None + } else { + stack.last_mut().and_then(Vec::pop) } } { rooms_in_path.push(current_room.clone()); @@ -81,7 +81,7 @@ impl Service { .roomid_spacechunk_cache .lock() .await - .get_mut(¤t_room.to_owned()) + .get_mut(¤t_room.clone()) .as_ref() { if let Some(cached) = cached { @@ -202,7 +202,7 @@ impl Service { .send_federation_request( server, federation::space::get_hierarchy::v1::Request { - room_id: current_room.to_owned(), + room_id: current_room.clone(), suggested_only, }, ) @@ -453,8 +453,7 @@ impl Service { room_id: &RoomId, ) -> Result { let allowed = match join_rule { - SpaceRoomJoinRule::Public => true, - SpaceRoomJoinRule::Knock => true, + SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::Public => true, SpaceRoomJoinRule::Invite => services() .rooms .state_cache diff --git a/src/service/rooms/state/data.rs b/src/service/rooms/state/data.rs index 96116b02..01176f5d 100644 --- a/src/service/rooms/state/data.rs +++ b/src/service/rooms/state/data.rs @@ -7,7 +7,7 @@ pub trait Data: Send + Sync { /// Returns the last state hash key added to the db for the given room. fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result>; - /// Set the state hash to a new version, but does not update state_cache. + /// Set the state hash to a new version, but does not update `state_cache`. fn set_room_state( &self, room_id: &RoomId, @@ -18,7 +18,7 @@ pub trait Data: Send + Sync { /// Associates a state with an event. fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()>; - /// Returns all events we would send as the prev_events of the next event. + /// Returns all events we would send as the `prev_events` of the next event. fn get_forward_extremities(&self, room_id: &RoomId) -> Result>>; /// Replace the forward extremities of the room. diff --git a/src/service/rooms/state/mod.rs b/src/service/rooms/state/mod.rs index f6581bb5..a6ba737f 100644 --- a/src/service/rooms/state/mod.rs +++ b/src/service/rooms/state/mod.rs @@ -45,9 +45,8 @@ impl Service { .ok() .map(|(_, id)| id) }) { - let pdu = match services().rooms.timeline.get_pdu_json(&event_id)? { - Some(pdu) => pdu, - None => continue, + let Some(pdu) = services().rooms.timeline.get_pdu_json(&event_id)? else { + continue; }; let pdu: PduEvent = match serde_json::from_str( @@ -70,14 +69,12 @@ impl Service { Err(_) => continue, }; - let state_key = match pdu.state_key { - Some(k) => k, - None => continue, + let Some(state_key) = pdu.state_key else { + continue; }; - let user_id = match UserId::parse(state_key) { - Ok(id) => id, - Err(_) => continue, + let Ok(user_id) = UserId::parse(state_key) else { + continue; }; services().rooms.state_cache.update_membership( @@ -374,11 +371,7 @@ impl Service { state_key: Option<&str>, content: &serde_json::value::RawValue, ) -> Result>> { - let shortstatehash = if let Some(current_shortstatehash) = - services().rooms.state.get_room_shortstatehash(room_id)? - { - current_shortstatehash - } else { + let Some(shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else { return Ok(HashMap::new()); }; diff --git a/src/service/rooms/state_accessor/mod.rs b/src/service/rooms/state_accessor/mod.rs index 53e3176f..a894b157 100644 --- a/src/service/rooms/state_accessor/mod.rs +++ b/src/service/rooms/state_accessor/mod.rs @@ -85,16 +85,15 @@ impl Service { /// The user was a joined member at this state (potentially in the past) fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) - .map(|s| s == MembershipState::Join) - .unwrap_or_default() // Return sensible default, i.e. false + .is_ok_and(|s| s == MembershipState::Join) // Return sensible default, i.e. false } /// The user was an invited or joined room member at this state (potentially /// in the past) fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool { self.user_membership(shortstatehash, user_id) - .map(|s| s == MembershipState::Join || s == MembershipState::Invite) - .unwrap_or_default() // Return sensible default, i.e. false + .is_ok_and(|s| s == MembershipState::Join || s == MembershipState::Invite) + // Return sensible default, i.e. false } /// Whether a server is allowed to see an event through federation, based on @@ -106,9 +105,8 @@ impl Service { room_id: &RoomId, event_id: &EventId, ) -> Result { - let shortstatehash = match self.pdu_shortstatehash(event_id)? { - Some(shortstatehash) => shortstatehash, - None => return Ok(true), + let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else { + return Ok(true); }; if let Some(visibility) = self @@ -170,9 +168,8 @@ impl Service { room_id: &RoomId, event_id: &EventId, ) -> Result { - let shortstatehash = match self.pdu_shortstatehash(event_id)? { - Some(shortstatehash) => shortstatehash, - None => return Ok(true), + let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else { + return Ok(true); }; if let Some(visibility) = self @@ -305,13 +302,13 @@ impl Service { }) } - pub async fn user_can_invite( + pub fn user_can_invite( &self, room_id: &RoomId, sender: &UserId, target_user: &UserId, state_lock: &MutexGuard<'_, ()>, - ) -> Result { + ) -> bool { let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite)) .expect("Event content always serializes"); @@ -323,11 +320,11 @@ impl Service { redacts: None, }; - Ok(services() + services() .rooms .timeline .create_hash_and_sign_event(new_event, sender, room_id, state_lock) - .is_ok()) + .is_ok() } pub fn get_member( @@ -358,41 +355,46 @@ impl Service { federation: bool, ) -> Result { self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")? - .map(|e| { - serde_json::from_str(e.content.get()) - .map(|c: RoomPowerLevelsEventContent| c.into()) - .map(|e: RoomPowerLevels| { - e.user_can_redact_event_of_other(sender) - || e.user_can_redact_own_event(sender) - && if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) - { - if federation { - pdu.sender().server_name() == sender.server_name() + .map_or_else( + // Falling back on m.room.create to judge power levels + || { + if let Some(pdu) = + self.room_state_get(room_id, &StateEventType::RoomCreate, "")? + { + Ok(pdu.sender == sender + || if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) { + pdu.sender == sender + } else { + false + }) + } else { + Err(Error::bad_database( + "No m.room.power_levels or m.room.create events in database for room", + )) + } + }, + |e| { + serde_json::from_str(e.content.get()) + .map(|c: RoomPowerLevelsEventContent| c.into()) + .map(|e: RoomPowerLevels| { + e.user_can_redact_event_of_other(sender) + || e.user_can_redact_own_event(sender) + && if let Ok(Some(pdu)) = + services().rooms.timeline.get_pdu(redacts) + { + if federation { + pdu.sender().server_name() == sender.server_name() + } else { + pdu.sender == sender + } } else { - pdu.sender == sender + false } - } else { - false - } - }) - .map_err(|_| { - Error::bad_database("Invalid m.room.power_levels event in database") - }) - }) - // Falling back on m.room.create to judge power levels - .unwrap_or_else(|| { - if let Some(pdu) = self.room_state_get(room_id, &StateEventType::RoomCreate, "")? { - Ok(pdu.sender == sender - || if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) { - pdu.sender == sender - } else { - false }) - } else { - Err(Error::bad_database( - "No m.room.power_levels or m.room.create events in database for room", - )) - } - }) + .map_err(|_| { + Error::bad_database("Invalid m.room.power_levels event in database") + }) + }, + ) } } diff --git a/src/service/rooms/threads/mod.rs b/src/service/rooms/threads/mod.rs index c6193bc8..ad70f2e0 100644 --- a/src/service/rooms/threads/mod.rs +++ b/src/service/rooms/threads/mod.rs @@ -1,5 +1,7 @@ mod data; +use std::collections::BTreeMap; + pub use data::Data; use ruma::{ api::client::{error::ErrorKind, threads::get_threads::v1::IncludeThreads}, @@ -56,7 +58,7 @@ impl Service { if let CanonicalJsonValue::Object(unsigned) = root_pdu_json .entry("unsigned".to_owned()) - .or_insert_with(|| CanonicalJsonValue::Object(Default::default())) + .or_insert_with(|| CanonicalJsonValue::Object(BTreeMap::default())) { if let Some(mut relations) = unsigned .get("m.relations") diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index a3b1d570..87d9c04a 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -204,6 +204,23 @@ impl Service { leaves: Vec, state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex ) -> Result> { + // Update Relationships + #[derive(Deserialize)] + struct ExtractRelatesTo { + #[serde(rename = "m.relates_to")] + relates_to: Relation, + } + + #[derive(Clone, Debug, Deserialize)] + struct ExtractEventId { + event_id: OwnedEventId, + } + #[derive(Clone, Debug, Deserialize)] + struct ExtractRelatesToEventId { + #[serde(rename = "m.relates_to")] + relates_to: ExtractEventId, + } + let shortroomid = services() .rooms .short @@ -216,7 +233,7 @@ impl Service { if let Some(state_key) = &pdu.state_key { if let CanonicalJsonValue::Object(unsigned) = pdu_json .entry("unsigned".to_owned()) - .or_insert_with(|| CanonicalJsonValue::Object(Default::default())) + .or_insert_with(|| CanonicalJsonValue::Object(BTreeMap::default())) { if let Some(shortstatehash) = services() .rooms @@ -340,8 +357,10 @@ impl Service { .map_err(|_| Error::bad_database("Invalid push rules event in db.")) }) .transpose()? - .map(|ev: PushRulesEvent| ev.content.global) - .unwrap_or_else(|| Ruleset::server_default(user)); + .map_or_else( + || Ruleset::server_default(user), + |ev: PushRulesEvent| ev.content.global, + ); let mut highlight = false; let mut notify = false; @@ -505,23 +524,6 @@ impl Service { _ => {} } - // Update Relationships - #[derive(Deserialize)] - struct ExtractRelatesTo { - #[serde(rename = "m.relates_to")] - relates_to: Relation, - } - - #[derive(Clone, Debug, Deserialize)] - struct ExtractEventId { - event_id: OwnedEventId, - } - #[derive(Clone, Debug, Deserialize)] - struct ExtractRelatesToEventId { - #[serde(rename = "m.relates_to")] - relates_to: ExtractEventId, - } - if let Ok(content) = serde_json::from_str::(pdu.content.get()) { if let Some(related_pducount) = services() .rooms @@ -786,7 +788,7 @@ impl Service { &mut pdu_json, &room_version_id, ) { - Ok(_) => {} + Ok(()) => {} Err(e) => { return match e { ruma::signatures::Error::PduSize => Err(Error::BadRequest( @@ -857,7 +859,7 @@ impl Service { .filter(|v| v.starts_with('@')) .unwrap_or(sender.as_str()); let server_name = services().globals.server_name(); - let server_user = format!("@conduit:{}", server_name); + let server_user = format!("@conduit:{server_name}"); let content = serde_json::from_str::(pdu.content.get()) .map_err(|_| Error::bad_database("Invalid content in pdu."))?; @@ -1199,7 +1201,7 @@ impl Service { .roomid_mutex_federation .write() .await - .entry(room_id.to_owned()) + .entry(room_id.clone()) .or_default(), ); let mutex_lock = mutex.lock().await; diff --git a/src/service/sending/mod.rs b/src/service/sending/mod.rs index fa14f123..b67336cb 100644 --- a/src/service/sending/mod.rs +++ b/src/service/sending/mod.rs @@ -160,7 +160,9 @@ impl Service { // Find events that have been added since starting the last request let new_events = self.db.queued_requests(&outgoing_kind).filter_map(|r| r.ok()).take(30).collect::>(); - if !new_events.is_empty() { + if new_events.is_empty() { + current_transaction_status.remove(&outgoing_kind); + } else { // Insert pdus we found self.db.mark_as_active(&new_events)?; @@ -170,8 +172,6 @@ impl Service { new_events.into_iter().map(|(event, _)| event).collect(), ) ); - } else { - current_transaction_status.remove(&outgoing_kind); } } Err((outgoing_kind, _)) => { @@ -305,41 +305,38 @@ impl Service { let event: AnySyncEphemeralRoomEvent = serde_json::from_str(read_receipt.json().get()) .map_err(|_| Error::bad_database("Invalid edu event in read_receipts."))?; - let federation_event = match event { - AnySyncEphemeralRoomEvent::Receipt(r) => { - let mut read = BTreeMap::new(); + let federation_event = if let AnySyncEphemeralRoomEvent::Receipt(r) = event { + let mut read = BTreeMap::new(); - let (event_id, mut receipt) = r - .content - .0 - .into_iter() - .next() - .expect("we only use one event per read receipt"); - let receipt = receipt - .remove(&ReceiptType::Read) - .expect("our read receipts always set this") - .remove(&user_id) - .expect("our read receipts always have the user here"); + let (event_id, mut receipt) = r + .content + .0 + .into_iter() + .next() + .expect("we only use one event per read receipt"); + let receipt = receipt + .remove(&ReceiptType::Read) + .expect("our read receipts always set this") + .remove(&user_id) + .expect("our read receipts always have the user here"); - read.insert( - user_id, - ReceiptData { - data: receipt.clone(), - event_ids: vec![event_id.clone()], - }, - ); + read.insert( + user_id, + ReceiptData { + data: receipt.clone(), + event_ids: vec![event_id.clone()], + }, + ); - let receipt_map = ReceiptMap { read }; + let receipt_map = ReceiptMap { read }; - let mut receipts = BTreeMap::new(); - receipts.insert(room_id.clone(), receipt_map); + let mut receipts = BTreeMap::new(); + receipts.insert(room_id.clone(), receipt_map); - Edu::Receipt(ReceiptContent { receipts }) - } - _ => { - Error::bad_database("Invalid event type in read_receipts"); - continue; - } + Edu::Receipt(ReceiptContent { receipts }) + } else { + Error::bad_database("Invalid event type in read_receipts"); + continue; }; events.push(serde_json::to_vec(&federation_event).expect("json can be serialized")); @@ -404,7 +401,7 @@ impl Service { )?; for ((outgoing_kind, event), key) in requests.into_iter().zip(keys) { self.sender - .send((outgoing_kind.to_owned(), event, key)) + .send((outgoing_kind.clone(), event, key)) .unwrap(); } @@ -474,7 +471,7 @@ impl Service { ), ) })? - .to_room_event()) + .to_room_event()); } SendingEventType::Edu(_) => { // Appservices don't need EDUs (?) @@ -559,13 +556,12 @@ impl Service { } } - let pusher = match services() + let Some(pusher) = services() .pusher .get_pusher(userid, pushkey) .map_err(|e| (OutgoingKind::Push(userid.clone(), pushkey.clone()), e))? - { - Some(pusher) => pusher, - None => continue, + else { + continue; }; let rules_for_user = services() @@ -577,8 +573,10 @@ impl Service { ) .unwrap_or_default() .and_then(|event| serde_json::from_str::(event.get()).ok()) - .map(|ev: PushRulesEvent| ev.content.global) - .unwrap_or_else(|| push::Ruleset::server_default(userid)); + .map_or_else( + || push::Ruleset::server_default(userid), + |ev: PushRulesEvent| ev.content.global, + ); let unread: UInt = services() .rooms diff --git a/src/service/uiaa/mod.rs b/src/service/uiaa/mod.rs index ed39af99..e51f20c4 100644 --- a/src/service/uiaa/mod.rs +++ b/src/service/uiaa/mod.rs @@ -47,10 +47,10 @@ impl Service { auth: &AuthData, uiaainfo: &UiaaInfo, ) -> Result<(bool, UiaaInfo)> { - let mut uiaainfo = auth - .session() - .map(|session| self.db.get_uiaa_session(user_id, device_id, session)) - .unwrap_or_else(|| Ok(uiaainfo.clone()))?; + let mut uiaainfo = auth.session().map_or_else( + || Ok(uiaainfo.clone()), + |session| self.db.get_uiaa_session(user_id, device_id, session), + )?; if uiaainfo.session.is_none() { uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); @@ -63,14 +63,11 @@ impl Service { password, .. }) => { - let username = match identifier { - UserIdentifier::UserIdOrLocalpart(username) => username, - _ => { - return Err(Error::BadRequest( - ErrorKind::Unrecognized, - "Identifier type not recognized.", - )) - } + let UserIdentifier::UserIdOrLocalpart(username) = identifier else { + return Err(Error::BadRequest( + ErrorKind::Unrecognized, + "Identifier type not recognized.", + )); }; let user_id = UserId::parse_with_server_name( diff --git a/src/service/users/data.rs b/src/service/users/data.rs index ddf941e3..f7d22171 100644 --- a/src/service/users/data.rs +++ b/src/service/users/data.rs @@ -42,16 +42,16 @@ pub trait Data: Send + Sync { /// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change. fn set_displayname(&self, user_id: &UserId, displayname: Option) -> Result<()>; - /// Get the avatar_url of a user. + /// Get the `avatar_url` of a user. fn avatar_url(&self, user_id: &UserId) -> Result>; - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is None. fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()>; /// Get the blurhash of a user. fn blurhash(&self, user_id: &UserId) -> Result>; - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is None. fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()>; /// Adds a new device to a user. diff --git a/src/service/users/mod.rs b/src/service/users/mod.rs index 91331667..04722aaf 100644 --- a/src/service/users/mod.rs +++ b/src/service/users/mod.rs @@ -325,12 +325,12 @@ impl Service { self.db.set_displayname(user_id, displayname) } - /// Get the avatar_url of a user. + /// Get the `avatar_url` of a user. pub fn avatar_url(&self, user_id: &UserId) -> Result> { self.db.avatar_url(user_id) } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is None. pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option) -> Result<()> { self.db.set_avatar_url(user_id, avatar_url) } @@ -340,7 +340,7 @@ impl Service { self.db.blurhash(user_id) } - /// Sets a new avatar_url or removes it if avatar_url is None. + /// Sets a new `avatar_url` or removes it if `avatar_url` is None. pub fn set_blurhash(&self, user_id: &UserId, blurhash: Option) -> Result<()> { self.db.set_blurhash(user_id, blurhash) } diff --git a/src/utils/error.rs b/src/utils/error.rs index 448f0665..cdb6bd27 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -71,7 +71,7 @@ pub enum Error { #[error("{0}")] BadConfig(&'static str), #[error("{0}")] - /// Don't create this directly. Use Error::bad_database instead. + /// Don't create this directly. Use `Error::bad_database` instead. BadDatabase(&'static str), #[error("uiaa")] Uiaa(UiaaInfo), @@ -107,6 +107,9 @@ impl Error { impl Error { pub fn to_response(&self) -> RumaResponse { + #[allow(clippy::enum_glob_use)] + use ErrorKind::*; + if let Self::Uiaa(uiaainfo) = self { return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone())); } @@ -122,20 +125,19 @@ impl Error { let message = format!("{self}"); - use ErrorKind::*; let (kind, status_code) = match self { Self::BadRequest(kind, _) => ( kind.clone(), match kind { - WrongRoomKeysVersion { .. } + Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED, + NotFound | Unrecognized => StatusCode::NOT_FOUND, + LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS, + UserDeactivated + | WrongRoomKeysVersion { .. } | Forbidden | GuestAccessForbidden | ThreepidAuthFailed | ThreepidDenied => StatusCode::FORBIDDEN, - Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED, - NotFound | Unrecognized => StatusCode::NOT_FOUND, - LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS, - UserDeactivated => StatusCode::FORBIDDEN, TooLarge => StatusCode::PAYLOAD_TOO_LARGE, _ => StatusCode::BAD_REQUEST, }, diff --git a/src/utils/mod.rs b/src/utils/mod.rs index d09a1033..e429eca7 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -18,8 +18,8 @@ pub fn millis_since_unix_epoch() -> u64 { .as_millis() as u64 } -pub fn increment(old: Option<&[u8]>) -> Option> { - let number = match old.map(|bytes| bytes.try_into()) { +pub fn increment(old: Option<&[u8]>) -> Vec { + let number = match old.map(TryInto::try_into) { Some(Ok(bytes)) => { let number = u64::from_be_bytes(bytes); number + 1 @@ -27,7 +27,7 @@ pub fn increment(old: Option<&[u8]>) -> Option> { _ => 1, // Start at one. since 0 should return the first event in the db }; - Some(number.to_be_bytes().to_vec()) + number.to_be_bytes().to_vec() } pub fn generate_keypair() -> Vec { @@ -83,7 +83,7 @@ pub fn common_elements( check_order: impl Fn(&[u8], &[u8]) -> Ordering, ) -> Option>> { let first_iterator = iterators.next()?; - let mut other_iterators = iterators.map(|i| i.peekable()).collect::>(); + let mut other_iterators = iterators.map(Iterator::peekable).collect::>(); Some(first_iterator.filter(move |target| { other_iterators.iter_mut().all(|it| {