Skip to content
Snippets Groups Projects
user_directory.rs 2.55 KiB
Newer Older
  • Learn to ignore specific revisions
  • use axum::extract::State;
    
    	api::client::user_directory::search_users,
    	events::{
    		room::join_rules::{JoinRule, RoomJoinRulesEventContent},
    		StateEventType,
    	},
    
    use crate::{Result, Ruma};
    
    /// # `POST /_matrix/client/r0/user_directory/search`
    ///
    /// Searches all known users for a match.
    ///
    
    /// - Hides any local users that aren't in any public rooms (i.e. those that
    
    Jason Volk's avatar
    Jason Volk committed
    ///   have the join rule set to public) and don't share a room with the sender
    
    pub(crate) async fn search_users_route(
    	State(services): State<crate::State>, body: Ruma<search_users::v3::Request>,
    ) -> Result<search_users::v3::Response> {
    
    	let sender_user = body.sender_user.as_ref().expect("user is authenticated");
    
    	let limit = usize::try_from(body.limit).unwrap_or(10); // default limit is 10
    
    	let mut users = services.users.iter().filter_map(|user_id| {
    
    		// Filter out buggy users (they should not exist, but you never know...)
    		let user_id = user_id.ok()?;
    
    		let user = search_users::v3::User {
    			user_id: user_id.clone(),
    
    			display_name: services.users.displayname(&user_id).ok()?,
    			avatar_url: services.users.avatar_url(&user_id).ok()?,
    
    🥺's avatar
    🥺 committed
    		let user_id_matches = user
    			.user_id
    			.to_string()
    			.to_lowercase()
    			.contains(&body.search_term.to_lowercase());
    
    		let user_displayname_matches = user
    			.display_name
    			.as_ref()
    
    🥺's avatar
    🥺 committed
    			.filter(|name| {
    				name.to_lowercase()
    					.contains(&body.search_term.to_lowercase())
    			})
    
    			.is_some();
    
    		if !user_id_matches && !user_displayname_matches {
    			return None;
    		}
    
    		// It's a matching user, but is the sender allowed to see them?
    		let mut user_visible = false;
    
    
    		let user_is_in_public_rooms = services
    
    🥺's avatar
    🥺 committed
    			.rooms
    			.state_cache
    			.rooms_joined(&user_id)
    			.filter_map(Result::ok)
    			.any(|room| {
    
    				services
    
    🥺's avatar
    🥺 committed
    					.rooms
    					.state_accessor
    					.room_state_get(&room, &StateEventType::RoomJoinRules, "")
    					.map_or(false, |event| {
    
    						event.map_or(false, |event| {
    							serde_json::from_str(event.content.get())
    								.map_or(false, |r: RoomJoinRulesEventContent| r.join_rule == JoinRule::Public)
    						})
    
    🥺's avatar
    🥺 committed
    					})
    
    		if user_is_in_public_rooms {
    
    			let user_is_in_shared_rooms = services
    
    🥺's avatar
    🥺 committed
    				.rooms
    				.user
    				.get_shared_rooms(vec![sender_user.clone(), user_id])
    				.ok()?
    				.next()
    				.is_some();
    
    			if user_is_in_shared_rooms {
    				user_visible = true;
    			}
    		}
    
    	let results = users.by_ref().take(limit).collect();
    	let limited = users.next().is_some();
    
    	Ok(search_users::v3::Response {
    		results,
    		limited,
    	})