docs: documentation for every endpoint
This commit is contained in:
		
							parent
							
								
									364820aae4
								
							
						
					
					
						commit
						b9eb39a9c6
					
				
					 35 changed files with 847 additions and 93 deletions
				
			
		|  | @ -9,7 +9,7 @@ use std::{ | |||
| }; | ||||
| use tracing::warn; | ||||
| 
 | ||||
| pub async fn send_request<T: OutgoingRequest>( | ||||
| pub(crate) async fn send_request<T: OutgoingRequest>( | ||||
|     globals: &crate::database::globals::Globals, | ||||
|     registration: serde_yaml::Value, | ||||
|     request: T, | ||||
|  |  | |||
|  | @ -40,8 +40,12 @@ const GUEST_NAME_LENGTH: usize = 10; | |||
| ///
 | ||||
| /// Checks if a username is valid and available on this server.
 | ||||
| ///
 | ||||
| /// - Returns true if no user or appservice on this server claimed this username
 | ||||
| /// - This will not reserve the username, so the username might become invalid when trying to register
 | ||||
| /// Conditions for returning true:
 | ||||
| /// - The user id is not historical
 | ||||
| /// - The server name of the user id matches this server
 | ||||
| /// - No user or appservice on this server already claimed this username
 | ||||
| ///
 | ||||
| /// Note: This will not reserve the username, so the username might become invalid when trying to register
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/register/available", data = "<body>") | ||||
|  | @ -80,11 +84,15 @@ pub async fn get_register_available_route( | |||
| ///
 | ||||
| /// Register an account on this homeserver.
 | ||||
| ///
 | ||||
| /// - Returns the device id and access_token unless `inhibit_login` is true
 | ||||
| /// - When registering a guest account, all parameters except initial_device_display_name will be
 | ||||
| /// ignored
 | ||||
| /// - Creates a new account and a device for it
 | ||||
| /// - The account will be populated with default account data
 | ||||
| /// You can use [`GET /_matrix/client/r0/register/available`](fn.get_register_available_route.html)
 | ||||
| /// 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 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
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/register", data = "<body>") | ||||
|  | @ -129,7 +137,7 @@ pub async fn register_route( | |||
|     ))?; | ||||
| 
 | ||||
|     // Check if username is creative enough
 | ||||
|     if !missing_username && db.users.exists(&user_id)? { | ||||
|     if db.users.exists(&user_id)? { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::UserInUse, | ||||
|             "Desired user ID is already taken.", | ||||
|  | @ -193,12 +201,12 @@ pub async fn register_route( | |||
|     // Create user
 | ||||
|     db.users.create(&user_id, password)?; | ||||
| 
 | ||||
|     // Default to pretty displayname
 | ||||
|     let displayname = format!("{} ⚡️", user_id.localpart()); | ||||
| 
 | ||||
|     db.users | ||||
|         .set_displayname(&user_id, Some(displayname.clone()))?; | ||||
| 
 | ||||
|     // Initial data
 | ||||
|     // Initial account data
 | ||||
|     db.account_data.update( | ||||
|         None, | ||||
|         &user_id, | ||||
|  | @ -211,6 +219,7 @@ pub async fn register_route( | |||
|         &db.globals, | ||||
|     )?; | ||||
| 
 | ||||
|     // Inhibit login does not work for guests
 | ||||
|     if !is_guest && body.inhibit_login { | ||||
|         return Ok(register::Response { | ||||
|             access_token: None, | ||||
|  | @ -231,7 +240,7 @@ pub async fn register_route( | |||
|     // Generate new token for the device
 | ||||
|     let token = utils::random_string(TOKEN_LENGTH); | ||||
| 
 | ||||
|     // Add device
 | ||||
|     // Create device for this account
 | ||||
|     db.users.create_device( | ||||
|         &user_id, | ||||
|         &device_id, | ||||
|  | @ -239,7 +248,7 @@ pub async fn register_route( | |||
|         body.initial_device_display_name.clone(), | ||||
|     )?; | ||||
| 
 | ||||
|     // If this is the first user on this server, create the admins room
 | ||||
|     // If this is the first user on this server, create the admin room
 | ||||
|     if db.users.count()? == 1 { | ||||
|         // Create a user for the server
 | ||||
|         let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name()) | ||||
|  | @ -529,9 +538,16 @@ pub async fn register_route( | |||
| ///
 | ||||
| /// Changes the password of this account.
 | ||||
| ///
 | ||||
| /// - Invalidates all other access tokens if logout_devices is true
 | ||||
| /// - Deletes all other devices and most of their data (to-device events, last seen, etc.) if
 | ||||
| /// logout_devices is true
 | ||||
| /// - Requires UIAA to verify user password
 | ||||
| /// - Changes the password of the sender user
 | ||||
| /// - The password hash is calculated using argon2 with 32 character salt, the plain password is
 | ||||
| /// not saved
 | ||||
| ///
 | ||||
| /// If logout_devices is true it does the following for each device except the sender device:
 | ||||
| /// - Invalidates access token
 | ||||
| /// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
 | ||||
| /// - Forgets to-device events
 | ||||
| /// - Triggers device list updates
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/account/password", data = "<body>") | ||||
|  | @ -598,9 +614,9 @@ pub async fn change_password_route( | |||
| 
 | ||||
| /// # `GET _matrix/client/r0/account/whoami`
 | ||||
| ///
 | ||||
| /// Get user_id of this account.
 | ||||
| /// Get user_id of the sender user.
 | ||||
| ///
 | ||||
| /// - Also works for Application Services
 | ||||
| /// Note: Also works for Application Services
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/account/whoami", data = "<body>") | ||||
|  | @ -616,11 +632,13 @@ pub async fn whoami_route(body: Ruma<whoami::Request>) -> ConduitResult<whoami:: | |||
| 
 | ||||
| /// # `POST /_matrix/client/r0/account/deactivate`
 | ||||
| ///
 | ||||
| /// Deactivate this user's account
 | ||||
| /// Deactivate sender user account.
 | ||||
| ///
 | ||||
| /// - Leaves all rooms and rejects all invitations
 | ||||
| /// - Invalidates all access tokens
 | ||||
| /// - Deletes all devices
 | ||||
| /// - Deletes all device metadata (device id, device display name, last seen ip, last seen ts)
 | ||||
| /// - Forgets all to-device events
 | ||||
| /// - Triggers device list updates
 | ||||
| /// - Removes ability to log in again
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|  | @ -667,6 +685,7 @@ pub async fn deactivate_route( | |||
|     } | ||||
| 
 | ||||
|     // Leave all joined rooms and reject all invitations
 | ||||
|     // TODO: work over federation invites
 | ||||
|     let all_rooms = db | ||||
|         .rooms | ||||
|         .rooms_joined(&sender_user) | ||||
|  | @ -730,6 +749,8 @@ pub async fn deactivate_route( | |||
| /// # `GET _matrix/client/r0/account/3pid`
 | ||||
| ///
 | ||||
| /// Get a list of third party identifiers associated with this account.
 | ||||
| ///
 | ||||
| /// - Currently always returns empty list
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/account/3pid", data = "<body>") | ||||
|  |  | |||
|  | @ -15,6 +15,9 @@ use ruma::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{delete, get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/directory/room/{roomAlias}`
 | ||||
| ///
 | ||||
| /// Creates a new room alias on this server.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/directory/room/<_>", data = "<body>") | ||||
|  | @ -24,6 +27,13 @@ pub async fn create_alias_route( | |||
|     db: DatabaseGuard, | ||||
|     body: Ruma<create_alias::Request<'_>>, | ||||
| ) -> ConduitResult<create_alias::Response> { | ||||
|     if body.room_alias.server_name() != db.globals.server_name() { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::InvalidParam, | ||||
|             "Alias is from another server.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     if db.rooms.id_from_alias(&body.room_alias)?.is_some() { | ||||
|         return Err(Error::Conflict("Alias already exists.")); | ||||
|     } | ||||
|  | @ -36,6 +46,12 @@ pub async fn create_alias_route( | |||
|     Ok(create_alias::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/directory/room/{roomAlias}`
 | ||||
| ///
 | ||||
| /// Deletes a room alias from this server.
 | ||||
| ///
 | ||||
| /// - TODO: additional access control checks
 | ||||
| /// - TODO: Update canonical alias event
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/r0/directory/room/<_>", data = "<body>") | ||||
|  | @ -45,13 +61,27 @@ pub async fn delete_alias_route( | |||
|     db: DatabaseGuard, | ||||
|     body: Ruma<delete_alias::Request<'_>>, | ||||
| ) -> ConduitResult<delete_alias::Response> { | ||||
|     if body.room_alias.server_name() != db.globals.server_name() { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::InvalidParam, | ||||
|             "Alias is from another server.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     db.rooms.set_alias(&body.room_alias, None, &db.globals)?; | ||||
| 
 | ||||
|     // TODO: update alt_aliases?
 | ||||
| 
 | ||||
|     db.flush()?; | ||||
| 
 | ||||
|     Ok(delete_alias::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/directory/room/{roomAlias}`
 | ||||
| ///
 | ||||
| /// Resolve an alias locally or over federation.
 | ||||
| ///
 | ||||
| /// - TODO: Suggest more servers to join via
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/directory/room/<_>", data = "<body>") | ||||
|  | @ -64,7 +94,7 @@ pub async fn get_alias_route( | |||
|     get_alias_helper(&db, &body.room_alias).await | ||||
| } | ||||
| 
 | ||||
| pub async fn get_alias_helper( | ||||
| pub(crate) async fn get_alias_helper( | ||||
|     db: &Database, | ||||
|     room_alias: &RoomAliasId, | ||||
| ) -> ConduitResult<get_alias::Response> { | ||||
|  |  | |||
|  | @ -12,6 +12,9 @@ use ruma::api::client::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{delete, get, post, put}; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/room_keys/version`
 | ||||
| ///
 | ||||
| /// Creates a new backup.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/unstable/room_keys/version", data = "<body>") | ||||
|  | @ -31,6 +34,9 @@ pub async fn create_backup_route( | |||
|     Ok(create_backup::Response { version }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/room_keys/version/{version}`
 | ||||
| ///
 | ||||
| /// Update information about an existing backup. Only `auth_data` can be modified.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/unstable/room_keys/version/<_>", data = "<body>") | ||||
|  | @ -49,6 +55,9 @@ pub async fn update_backup_route( | |||
|     Ok(update_backup::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/room_keys/version`
 | ||||
| ///
 | ||||
| /// Get information about the latest backup version.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/unstable/room_keys/version", data = "<body>") | ||||
|  | @ -77,6 +86,9 @@ pub async fn get_latest_backup_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/room_keys/version`
 | ||||
| ///
 | ||||
| /// Get information about an existing backup.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/unstable/room_keys/version/<_>", data = "<body>") | ||||
|  | @ -104,6 +116,11 @@ pub async fn get_backup_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/room_keys/version/{version}`
 | ||||
| ///
 | ||||
| /// Delete an existing key backup.
 | ||||
| ///
 | ||||
| /// - Deletes both information about the backup, as well as all key data related to the backup
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/unstable/room_keys/version/<_>", data = "<body>") | ||||
|  | @ -122,7 +139,13 @@ pub async fn delete_backup_route( | |||
|     Ok(delete_backup::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/room_keys/keys`
 | ||||
| ///
 | ||||
| /// Add the received backup keys to the database.
 | ||||
| ///
 | ||||
| /// - Only manipulating the most recently created version of the backup is allowed
 | ||||
| /// - Adds the keys to the backup
 | ||||
| /// - Returns the new number of keys in this backup and the etag
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/unstable/room_keys/keys", data = "<body>") | ||||
|  | @ -134,6 +157,18 @@ pub async fn add_backup_keys_route( | |||
| ) -> ConduitResult<add_backup_keys::Response> { | ||||
|     let sender_user = body.sender_user.as_ref().expect("user is authenticated"); | ||||
| 
 | ||||
|     if Some(&body.version) | ||||
|         != db | ||||
|             .key_backups | ||||
|             .get_latest_backup_version(sender_user)? | ||||
|             .as_ref() | ||||
|     { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::InvalidParam, | ||||
|             "You may only manipulate the most recently created version of the backup.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     for (room_id, room) in &body.rooms { | ||||
|         for (session_id, key_data) in &room.sessions { | ||||
|             db.key_backups.add_key( | ||||
|  | @ -156,7 +191,13 @@ pub async fn add_backup_keys_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/room_keys/keys/{roomId}`
 | ||||
| ///
 | ||||
| /// Add the received backup keys to the database.
 | ||||
| ///
 | ||||
| /// - Only manipulating the most recently created version of the backup is allowed
 | ||||
| /// - Adds the keys to the backup
 | ||||
| /// - Returns the new number of keys in this backup and the etag
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/unstable/room_keys/keys/<_>", data = "<body>") | ||||
|  | @ -168,6 +209,18 @@ pub async fn add_backup_key_sessions_route( | |||
| ) -> ConduitResult<add_backup_key_sessions::Response> { | ||||
|     let sender_user = body.sender_user.as_ref().expect("user is authenticated"); | ||||
| 
 | ||||
|     if Some(&body.version) | ||||
|         != db | ||||
|             .key_backups | ||||
|             .get_latest_backup_version(sender_user)? | ||||
|             .as_ref() | ||||
|     { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::InvalidParam, | ||||
|             "You may only manipulate the most recently created version of the backup.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     for (session_id, key_data) in &body.sessions { | ||||
|         db.key_backups.add_key( | ||||
|             &sender_user, | ||||
|  | @ -188,7 +241,13 @@ pub async fn add_backup_key_sessions_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
 | ||||
| ///
 | ||||
| /// Add the received backup key to the database.
 | ||||
| ///
 | ||||
| /// - Only manipulating the most recently created version of the backup is allowed
 | ||||
| /// - Adds the keys to the backup
 | ||||
| /// - Returns the new number of keys in this backup and the etag
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/unstable/room_keys/keys/<_>/<_>", data = "<body>") | ||||
|  | @ -200,6 +259,18 @@ pub async fn add_backup_key_session_route( | |||
| ) -> ConduitResult<add_backup_key_session::Response> { | ||||
|     let sender_user = body.sender_user.as_ref().expect("user is authenticated"); | ||||
| 
 | ||||
|     if Some(&body.version) | ||||
|         != db | ||||
|             .key_backups | ||||
|             .get_latest_backup_version(sender_user)? | ||||
|             .as_ref() | ||||
|     { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::InvalidParam, | ||||
|             "You may only manipulate the most recently created version of the backup.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     db.key_backups.add_key( | ||||
|         &sender_user, | ||||
|         &body.version, | ||||
|  | @ -218,6 +289,9 @@ pub async fn add_backup_key_session_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/room_keys/keys`
 | ||||
| ///
 | ||||
| /// Retrieves all keys from the backup.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/unstable/room_keys/keys", data = "<body>") | ||||
|  | @ -234,6 +308,9 @@ pub async fn get_backup_keys_route( | |||
|     Ok(get_backup_keys::Response { rooms }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}`
 | ||||
| ///
 | ||||
| /// Retrieves all keys from the backup for a given room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/unstable/room_keys/keys/<_>", data = "<body>") | ||||
|  | @ -252,6 +329,9 @@ pub async fn get_backup_key_sessions_route( | |||
|     Ok(get_backup_key_sessions::Response { sessions }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
 | ||||
| ///
 | ||||
| /// Retrieves a key from the backup.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/unstable/room_keys/keys/<_>/<_>", data = "<body>") | ||||
|  | @ -274,6 +354,9 @@ pub async fn get_backup_key_session_route( | |||
|     Ok(get_backup_key_session::Response { key_data }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/room_keys/keys`
 | ||||
| ///
 | ||||
| /// Delete the keys from the backup.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/unstable/room_keys/keys", data = "<body>") | ||||
|  | @ -297,6 +380,9 @@ pub async fn delete_backup_keys_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}`
 | ||||
| ///
 | ||||
| /// Delete the keys from the backup for a given room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/unstable/room_keys/keys/<_>", data = "<body>") | ||||
|  | @ -320,6 +406,9 @@ pub async fn delete_backup_key_sessions_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
 | ||||
| ///
 | ||||
| /// Delete a key from the backup.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/unstable/room_keys/keys/<_>/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ use rocket::get; | |||
| 
 | ||||
| /// # `GET /_matrix/client/r0/capabilities`
 | ||||
| ///
 | ||||
| /// Get information on this server's supported feature set and other relevent capabilities.
 | ||||
| /// Get information on the supported feature set and other relevent capabilities of this server.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/capabilities", data = "<_body>") | ||||
|  |  | |||
|  | @ -16,6 +16,9 @@ use serde_json::{json, value::RawValue as RawJsonValue}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}`
 | ||||
| ///
 | ||||
| /// Sets some account data for the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/user/<_>/account_data/<_>", data = "<body>") | ||||
|  | @ -48,6 +51,9 @@ pub async fn set_global_account_data_route( | |||
|     Ok(set_global_account_data::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
 | ||||
| ///
 | ||||
| /// Sets some room account data for the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put( | ||||
|  | @ -83,6 +89,9 @@ pub async fn set_room_account_data_route( | |||
|     Ok(set_room_account_data::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/user/{userId}/account_data/{type}`
 | ||||
| ///
 | ||||
| /// Gets some account data for the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/user/<_>/account_data/<_>", data = "<body>") | ||||
|  | @ -98,7 +107,6 @@ pub async fn get_global_account_data_route( | |||
|         .account_data | ||||
|         .get::<Box<RawJsonValue>>(None, sender_user, body.event_type.clone().into())? | ||||
|         .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; | ||||
|     db.flush()?; | ||||
| 
 | ||||
|     let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get()) | ||||
|         .map_err(|_| Error::bad_database("Invalid account data event in db."))? | ||||
|  | @ -107,6 +115,9 @@ pub async fn get_global_account_data_route( | |||
|     Ok(get_global_account_data::Response { account_data }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
 | ||||
| ///
 | ||||
| /// Gets some room account data for the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get( | ||||
|  | @ -129,7 +140,6 @@ pub async fn get_room_account_data_route( | |||
|             body.event_type.clone().into(), | ||||
|         )? | ||||
|         .ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?; | ||||
|     db.flush()?; | ||||
| 
 | ||||
|     let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get()) | ||||
|         .map_err(|_| Error::bad_database("Invalid account data event in db."))? | ||||
|  |  | |||
|  | @ -5,6 +5,12 @@ use std::convert::TryFrom; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::get; | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomId}/context`
 | ||||
| ///
 | ||||
| /// Allows loading room history around an event.
 | ||||
| ///
 | ||||
| /// - Only works if the user is joined (TODO: always allow, but only show events if the user was
 | ||||
| /// joined, depending on history_visibility)
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/context/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -11,6 +11,9 @@ use super::SESSION_ID_LENGTH; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{delete, get, post, put}; | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/devices`
 | ||||
| ///
 | ||||
| /// Get metadata on all devices of the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/devices", data = "<body>") | ||||
|  | @ -31,6 +34,9 @@ pub async fn get_devices_route( | |||
|     Ok(get_devices::Response { devices }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/devices/{deviceId}`
 | ||||
| ///
 | ||||
| /// Get metadata on a single device of the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/devices/<_>", data = "<body>") | ||||
|  | @ -50,6 +56,9 @@ pub async fn get_device_route( | |||
|     Ok(get_device::Response { device }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/devices/{deviceId}`
 | ||||
| ///
 | ||||
| /// Updates the metadata on a given device of the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/devices/<_>", data = "<body>") | ||||
|  | @ -76,6 +85,15 @@ pub async fn update_device_route( | |||
|     Ok(update_device::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/devices/{deviceId}`
 | ||||
| ///
 | ||||
| /// Deletes the given device.
 | ||||
| ///
 | ||||
| /// - Requires UIAA to verify user password
 | ||||
| /// - Invalidates access token
 | ||||
| /// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
 | ||||
| /// - Forgets to-device events
 | ||||
| /// - Triggers device list updates
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/r0/devices/<_>", data = "<body>") | ||||
|  | @ -128,6 +146,17 @@ pub async fn delete_device_route( | |||
|     Ok(delete_device::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/devices/{deviceId}`
 | ||||
| ///
 | ||||
| /// Deletes the given device.
 | ||||
| ///
 | ||||
| /// - Requires UIAA to verify user password
 | ||||
| ///
 | ||||
| /// For each device:
 | ||||
| /// - Invalidates access token
 | ||||
| /// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
 | ||||
| /// - Forgets to-device events
 | ||||
| /// - Triggers device list updates
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/delete_devices", data = "<body>") | ||||
|  |  | |||
|  | @ -28,6 +28,11 @@ use tracing::{info, warn}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, post, put}; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/publicRooms`
 | ||||
| ///
 | ||||
| /// Lists the public rooms on this server.
 | ||||
| ///
 | ||||
| /// - Rooms are ordered by the number of joined members
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/publicRooms", data = "<body>") | ||||
|  | @ -48,6 +53,11 @@ pub async fn get_public_rooms_filtered_route( | |||
|     .await | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/publicRooms`
 | ||||
| ///
 | ||||
| /// Lists the public rooms on this server.
 | ||||
| ///
 | ||||
| /// - Rooms are ordered by the number of joined members
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/publicRooms", data = "<body>") | ||||
|  | @ -77,6 +87,11 @@ pub async fn get_public_rooms_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/directory/list/room/{roomId}`
 | ||||
| ///
 | ||||
| /// Sets the visibility of a given room in the room directory.
 | ||||
| ///
 | ||||
| /// - TODO: Access control checks
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/directory/list/room/<_>", data = "<body>") | ||||
|  | @ -107,6 +122,9 @@ pub async fn set_room_visibility_route( | |||
|     Ok(set_room_visibility::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/directory/list/room/{roomId}`
 | ||||
| ///
 | ||||
| /// Gets the visibility of a given room in the room directory.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/directory/list/room/<_>", data = "<body>") | ||||
|  | @ -126,7 +144,7 @@ pub async fn get_room_visibility_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| pub async fn get_public_rooms_filtered_helper( | ||||
| pub(crate) async fn get_public_rooms_filtered_helper( | ||||
|     db: &Database, | ||||
|     server: Option<&ServerName>, | ||||
|     limit: Option<UInt>, | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ use ruma::api::client::r0::filter::{self, create_filter, get_filter}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, post}; | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}`
 | ||||
| ///
 | ||||
| /// TODO: Loads a filter that was previously created.
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/user/<_>/filter/<_>"))] | ||||
| #[tracing::instrument] | ||||
| pub async fn get_filter_route() -> ConduitResult<get_filter::Response> { | ||||
|  | @ -18,6 +21,9 @@ pub async fn get_filter_route() -> ConduitResult<get_filter::Response> { | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/user/{userId}/filter`
 | ||||
| ///
 | ||||
| /// TODO: Creates a new filter to be used by other endpoints.
 | ||||
| #[cfg_attr(feature = "conduit_bin", post("/_matrix/client/r0/user/<_>/filter"))] | ||||
| #[tracing::instrument] | ||||
| pub async fn create_filter_route() -> ConduitResult<create_filter::Response> { | ||||
|  |  | |||
|  | @ -24,6 +24,12 @@ use std::collections::{BTreeMap, HashMap, HashSet}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, post}; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/keys/upload`
 | ||||
| ///
 | ||||
| /// Publish end-to-end encryption keys for the sender device.
 | ||||
| ///
 | ||||
| /// - Adds one time keys
 | ||||
| /// - If there are no device keys yet: Adds device keys (TODO: merge with existing keys?)
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/keys/upload", data = "<body>") | ||||
|  | @ -49,6 +55,7 @@ pub async fn upload_keys_route( | |||
|     } | ||||
| 
 | ||||
|     if let Some(device_keys) = &body.device_keys { | ||||
|         // TODO: merge this and the existing event?
 | ||||
|         // This check is needed to assure that signatures are kept
 | ||||
|         if db | ||||
|             .users | ||||
|  | @ -73,6 +80,13 @@ pub async fn upload_keys_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/keys/query`
 | ||||
| ///
 | ||||
| /// Get end-to-end encryption keys for the given users.
 | ||||
| ///
 | ||||
| /// - Always fetches users from other servers over federation
 | ||||
| /// - Gets master keys, self-signing keys, user signing keys and device keys.
 | ||||
| /// - The master and self-signing keys contain signatures that the user is allowed to see
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/keys/query", data = "<body>") | ||||
|  | @ -95,6 +109,9 @@ pub async fn get_keys_route( | |||
|     Ok(response.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/keys/claim`
 | ||||
| ///
 | ||||
| /// Claims one-time keys
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/keys/claim", data = "<body>") | ||||
|  | @ -111,6 +128,11 @@ pub async fn claim_keys_route( | |||
|     Ok(response.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/keys/device_signing/upload`
 | ||||
| ///
 | ||||
| /// Uploads end-to-end key information for the sender user.
 | ||||
| ///
 | ||||
| /// - Requires UIAA to verify password
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/unstable/keys/device_signing/upload", data = "<body>") | ||||
|  | @ -172,6 +194,9 @@ pub async fn upload_signing_keys_route( | |||
|     Ok(upload_signing_keys::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/keys/signatures/upload`
 | ||||
| ///
 | ||||
| /// Uploads end-to-end key signatures from the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/unstable/keys/signatures/upload", data = "<body>") | ||||
|  | @ -233,6 +258,11 @@ pub async fn upload_signatures_route( | |||
|     Ok(upload_signatures::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/keys/changes`
 | ||||
| ///
 | ||||
| /// Gets a list of users who have updated their device identity keys since the previous sync token.
 | ||||
| ///
 | ||||
| /// - TODO: left users
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/keys/changes", data = "<body>") | ||||
|  | @ -284,7 +314,7 @@ pub async fn get_key_changes_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| pub async fn get_keys_helper<F: Fn(&UserId) -> bool>( | ||||
| pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>( | ||||
|     sender_user: Option<&UserId>, | ||||
|     device_keys_input: &BTreeMap<UserId, Vec<Box<DeviceId>>>, | ||||
|     allowed_signatures: F, | ||||
|  | @ -409,7 +439,7 @@ pub async fn get_keys_helper<F: Fn(&UserId) -> bool>( | |||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub async fn claim_keys_helper( | ||||
| pub(crate) async fn claim_keys_helper( | ||||
|     one_time_keys_input: &BTreeMap<UserId, BTreeMap<Box<DeviceId>, DeviceKeyAlgorithm>>, | ||||
|     db: &Database, | ||||
| ) -> Result<claim_keys::Response> { | ||||
|  |  | |||
|  | @ -12,6 +12,9 @@ use rocket::{get, post}; | |||
| 
 | ||||
| const MXC_LENGTH: usize = 32; | ||||
| 
 | ||||
| /// # `GET /_matrix/media/r0/config`
 | ||||
| ///
 | ||||
| /// Returns max upload size.
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/media/r0/config"))] | ||||
| #[tracing::instrument(skip(db))] | ||||
| pub async fn get_media_config_route( | ||||
|  | @ -23,6 +26,12 @@ pub async fn get_media_config_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/media/r0/upload`
 | ||||
| ///
 | ||||
| /// Permanently save media in the server.
 | ||||
| ///
 | ||||
| /// - Some metadata will be saved in the database
 | ||||
| /// - Media will be saved in the media/ directory
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/media/r0/upload", data = "<body>") | ||||
|  | @ -61,6 +70,11 @@ pub async fn create_content_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/media/r0/download/{serverName}/{mediaId}`
 | ||||
| ///
 | ||||
| /// Load media from our server or over federation.
 | ||||
| ///
 | ||||
| /// - Only allows federation if `allow_remote` is true
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/media/r0/download/<_>/<_>", data = "<body>") | ||||
|  | @ -114,6 +128,11 @@ pub async fn get_content_route( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/media/r0/thumbnail/{serverName}/{mediaId}`
 | ||||
| ///
 | ||||
| /// Load media thumbnail from our server or over federation.
 | ||||
| ///
 | ||||
| /// - Only allows federation if `allow_remote` is true
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/media/r0/thumbnail/<_>/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -38,6 +38,12 @@ use tracing::{debug, error, warn}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, post}; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/join`
 | ||||
| ///
 | ||||
| /// Tries to join the sender user into a room.
 | ||||
| ///
 | ||||
| /// - If the server knowns about this room: creates the join event and does auth rules locally
 | ||||
| /// - If the server does not know about the room: asks other servers over federation
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/join", data = "<body>") | ||||
|  | @ -79,6 +85,12 @@ pub async fn join_room_by_id_route( | |||
|     ret | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/join/{roomIdOrAlias}`
 | ||||
| ///
 | ||||
| /// Tries to join the sender user into a room.
 | ||||
| ///
 | ||||
| /// - If the server knowns about this room: creates the join event and does auth rules locally
 | ||||
| /// - If the server does not know about the room: asks other servers over federation
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/join/<_>", data = "<body>") | ||||
|  | @ -133,6 +145,11 @@ pub async fn join_room_by_id_or_alias_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/leave`
 | ||||
| ///
 | ||||
| /// Tries to leave the sender user from a room.
 | ||||
| ///
 | ||||
| /// - This should always work if the user is currently joined.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/leave", data = "<body>") | ||||
|  | @ -151,6 +168,9 @@ pub async fn leave_room_route( | |||
|     Ok(leave_room::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
 | ||||
| ///
 | ||||
| /// Tries to send an invite event into the room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/invite", data = "<body>") | ||||
|  | @ -171,6 +191,9 @@ pub async fn invite_user_route( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/kick`
 | ||||
| ///
 | ||||
| /// Tries to send a kick event into the room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/kick", data = "<body>") | ||||
|  | @ -234,6 +257,9 @@ pub async fn kick_user_route( | |||
|     Ok(kick_user::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/ban`
 | ||||
| ///
 | ||||
| /// Tries to send a ban event into the room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/ban", data = "<body>") | ||||
|  | @ -307,6 +333,9 @@ pub async fn ban_user_route( | |||
|     Ok(ban_user::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/unban`
 | ||||
| ///
 | ||||
| /// Tries to send an unban event into the room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/unban", data = "<body>") | ||||
|  | @ -369,6 +398,14 @@ pub async fn unban_user_route( | |||
|     Ok(unban_user::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/forget`
 | ||||
| ///
 | ||||
| /// Forgets about a room.
 | ||||
| ///
 | ||||
| /// - If the sender user currently left the room: Stops sender user from receiving information about the room
 | ||||
| ///
 | ||||
| /// Note: Other devices of the user have no way of knowing the room was forgotten, so this has to
 | ||||
| /// be called from every device
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/forget", data = "<body>") | ||||
|  | @ -387,6 +424,9 @@ pub async fn forget_room_route( | |||
|     Ok(forget_room::Response::new().into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/joined_rooms`
 | ||||
| ///
 | ||||
| /// Lists all rooms the user has joined.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/joined_rooms", data = "<body>") | ||||
|  | @ -408,6 +448,11 @@ pub async fn joined_rooms_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/members`
 | ||||
| ///
 | ||||
| /// Lists all joined users in a room (TODO: at a specific point in time, with a specific membership).
 | ||||
| ///
 | ||||
| /// - Only works if the user is currently joined
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/members", data = "<body>") | ||||
|  | @ -419,6 +464,7 @@ pub async fn get_member_events_route( | |||
| ) -> ConduitResult<get_member_events::Response> { | ||||
|     let sender_user = body.sender_user.as_ref().expect("user is authenticated"); | ||||
| 
 | ||||
|     // TODO: check history visibility?
 | ||||
|     if !db.rooms.is_joined(sender_user, &body.room_id)? { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::Forbidden, | ||||
|  | @ -438,6 +484,12 @@ pub async fn get_member_events_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/joined_members`
 | ||||
| ///
 | ||||
| /// Lists all members of a room.
 | ||||
| ///
 | ||||
| /// - The sender user must be in the room
 | ||||
| /// - TODO: An appservice just needs a puppet joined
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/joined_members", data = "<body>") | ||||
|  | @ -449,11 +501,7 @@ pub async fn joined_members_route( | |||
| ) -> ConduitResult<joined_members::Response> { | ||||
|     let sender_user = body.sender_user.as_ref().expect("user is authenticated"); | ||||
| 
 | ||||
|     if !db | ||||
|         .rooms | ||||
|         .is_joined(&sender_user, &body.room_id) | ||||
|         .unwrap_or(false) | ||||
|     { | ||||
|     if !db.rooms.is_joined(&sender_user, &body.room_id)? { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::Forbidden, | ||||
|             "You aren't a member of the room.", | ||||
|  | @ -803,7 +851,7 @@ async fn validate_and_add_event_id( | |||
|     Ok((event_id, value)) | ||||
| } | ||||
| 
 | ||||
| pub async fn invite_helper<'a>( | ||||
| pub(crate) async fn invite_helper<'a>( | ||||
|     sender_user: &UserId, | ||||
|     user_id: &UserId, | ||||
|     room_id: &RoomId, | ||||
|  |  | |||
|  | @ -16,6 +16,13 @@ use std::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}`
 | ||||
| ///
 | ||||
| /// Send a message event into the room.
 | ||||
| ///
 | ||||
| /// - Is a NOOP if the txn id was already used before and returns the same event id again
 | ||||
| /// - 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
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/rooms/<_>/send/<_>/<_>", data = "<body>") | ||||
|  | @ -92,6 +99,12 @@ pub async fn send_message_event_route( | |||
|     Ok(send_message_event::Response::new(event_id).into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomId}/messages`
 | ||||
| ///
 | ||||
| /// Allows paginating through room history.
 | ||||
| ///
 | ||||
| /// - Only works if the user is joined (TODO: always allow, but only show events where the user was
 | ||||
| /// joined, depending on history_visibility)
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/messages", data = "<body>") | ||||
|  |  | |||
|  | @ -71,6 +71,9 @@ pub const DEVICE_ID_LENGTH: usize = 10; | |||
| pub const TOKEN_LENGTH: usize = 256; | ||||
| pub const SESSION_ID_LENGTH: usize = 256; | ||||
| 
 | ||||
| /// # `OPTIONS`
 | ||||
| ///
 | ||||
| /// Web clients use this to get CORS headers.
 | ||||
| #[cfg(feature = "conduit_bin")] | ||||
| #[options("/<_..>")] | ||||
| #[tracing::instrument] | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ use std::{convert::TryInto, time::Duration}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/presence/{userId}/status`
 | ||||
| ///
 | ||||
| /// Sets the presence state of the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/presence/<_>/status", data = "<body>") | ||||
|  | @ -46,6 +49,11 @@ pub async fn set_presence_route( | |||
|     Ok(set_presence::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/presence/{userId}/status`
 | ||||
| ///
 | ||||
| /// Gets the presence state of the given user.
 | ||||
| ///
 | ||||
| /// - Only works if you share a room with the user
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/presence/<_>/status", data = "<body>") | ||||
|  | @ -71,6 +79,7 @@ pub async fn get_presence_route( | |||
|             .get_last_presence_event(&sender_user, &room_id)? | ||||
|         { | ||||
|             presence_event = Some(presence); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,6 +17,11 @@ use std::{convert::TryInto, sync::Arc}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/profile/{userId}/displayname`
 | ||||
| ///
 | ||||
| /// Updates the displayname.
 | ||||
| ///
 | ||||
| /// - Also makes sure other users receive the update using presence EDUs
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/profile/<_>/displayname", data = "<body>") | ||||
|  | @ -115,6 +120,11 @@ pub async fn set_displayname_route( | |||
|     Ok(set_display_name::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/profile/{userId}/displayname`
 | ||||
| ///
 | ||||
| /// Returns the displayname of the user.
 | ||||
| ///
 | ||||
| /// - If user is on another server: Fetches displayname over federation
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/profile/<_>/displayname", data = "<body>") | ||||
|  | @ -149,6 +159,11 @@ pub async fn get_displayname_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/profile/{userId}/avatar_url`
 | ||||
| ///
 | ||||
| /// Updates the avatar_url and blurhash.
 | ||||
| ///
 | ||||
| /// - Also makes sure other users receive the update using presence EDUs
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/profile/<_>/avatar_url", data = "<body>") | ||||
|  | @ -249,6 +264,11 @@ pub async fn set_avatar_url_route( | |||
|     Ok(set_avatar_url::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/profile/{userId}/avatar_url`
 | ||||
| ///
 | ||||
| /// Returns the avatar_url and blurhash of the user.
 | ||||
| ///
 | ||||
| /// - If user is on another server: Fetches avatar_url and blurhash over federation
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/profile/<_>/avatar_url", data = "<body>") | ||||
|  | @ -285,6 +305,11 @@ pub async fn get_avatar_url_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/profile/{userId}`
 | ||||
| ///
 | ||||
| /// Returns the displayname, avatar_url and blurhash of the user.
 | ||||
| ///
 | ||||
| /// - If user is on another server: Fetches profile over federation
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/profile/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -15,6 +15,9 @@ use ruma::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{delete, get, post, put}; | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/pushrules`
 | ||||
| ///
 | ||||
| /// Retrieves the push rules event for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/pushrules", data = "<body>") | ||||
|  | @ -40,6 +43,9 @@ pub async fn get_pushrules_all_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
 | ||||
| ///
 | ||||
| /// Retrieves a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/pushrules/<_>/<_>/<_>", data = "<body>") | ||||
|  | @ -94,6 +100,9 @@ pub async fn get_pushrule_route( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
 | ||||
| ///
 | ||||
| /// Creates a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/pushrules/<_>/<_>/<_>", data = "<req>") | ||||
|  | @ -197,6 +206,9 @@ pub async fn set_pushrule_route( | |||
|     Ok(set_pushrule::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions`
 | ||||
| ///
 | ||||
| /// Gets the actions of a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/pushrules/<_>/<_>/<_>/actions", data = "<body>") | ||||
|  | @ -256,6 +268,9 @@ pub async fn get_pushrule_actions_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions`
 | ||||
| ///
 | ||||
| /// Sets the actions of a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/pushrules/<_>/<_>/<_>/actions", data = "<body>") | ||||
|  | @ -330,6 +345,9 @@ pub async fn set_pushrule_actions_route( | |||
|     Ok(set_pushrule_actions::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled`
 | ||||
| ///
 | ||||
| /// Gets the enabled status of a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/pushrules/<_>/<_>/<_>/enabled", data = "<body>") | ||||
|  | @ -391,6 +409,9 @@ pub async fn get_pushrule_enabled_route( | |||
|     Ok(get_pushrule_enabled::Response { enabled }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled`
 | ||||
| ///
 | ||||
| /// Sets the enabled status of a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/pushrules/<_>/<_>/<_>/enabled", data = "<body>") | ||||
|  | @ -470,6 +491,9 @@ pub async fn set_pushrule_enabled_route( | |||
|     Ok(set_pushrule_enabled::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
 | ||||
| ///
 | ||||
| /// Deletes a single specified push rule for this user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/r0/pushrules/<_>/<_>/<_>", data = "<body>") | ||||
|  | @ -539,6 +563,9 @@ pub async fn delete_pushrule_route( | |||
|     Ok(delete_pushrule::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/pushers`
 | ||||
| ///
 | ||||
| /// Gets all currently active pushers for the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/pushers", data = "<body>") | ||||
|  | @ -556,6 +583,11 @@ pub async fn get_pushers_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/pushers/set`
 | ||||
| ///
 | ||||
| /// Adds a pusher for the sender user.
 | ||||
| ///
 | ||||
| /// - TODO: Handle `append`
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/pushers/set", data = "<body>") | ||||
|  |  | |||
|  | @ -13,6 +13,12 @@ use std::collections::BTreeMap; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::post; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
 | ||||
| ///
 | ||||
| /// Sets different types of read markers.
 | ||||
| ///
 | ||||
| /// - Updates fully-read account data event to `fully_read`
 | ||||
| /// - If `read_receipt` is set: Update private marker and public read receipt EDU
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/read_markers", data = "<body>") | ||||
|  | @ -80,6 +86,9 @@ pub async fn set_read_marker_route( | |||
|     Ok(set_read_marker::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}`
 | ||||
| ///
 | ||||
| /// Sets private read marker and public read receipt EDU.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/receipt/<_>/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -9,6 +9,11 @@ use ruma::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::put; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
 | ||||
| ///
 | ||||
| /// Tries to send a redaction event into the room.
 | ||||
| ///
 | ||||
| /// - TODO: Handle txn id
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/rooms/<_>/redact/<_>/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -20,6 +20,22 @@ use tracing::{info, warn}; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, post}; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/createRoom`
 | ||||
| ///
 | ||||
| /// Creates a new room.
 | ||||
| ///
 | ||||
| /// - Room ID is randomly generated
 | ||||
| /// - Create alias if room_alias_name is set
 | ||||
| /// - Send create event
 | ||||
| /// - Join sender user
 | ||||
| /// - Send power levels event
 | ||||
| /// - Send canonical room alias
 | ||||
| /// - Send join rules
 | ||||
| /// - Send history visibility
 | ||||
| /// - Send guest access
 | ||||
| /// - Send events listed in initial state
 | ||||
| /// - Send events implied by `name` and `topic`
 | ||||
| /// - Send invite events
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/createRoom", data = "<body>") | ||||
|  | @ -344,6 +360,11 @@ pub async fn create_room_route( | |||
|     Ok(create_room::Response::new(room_id).into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}`
 | ||||
| ///
 | ||||
| /// Gets a single event.
 | ||||
| ///
 | ||||
| /// - You have to currently be joined to the room (TODO: Respect history visibility)
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/event/<_>", data = "<body>") | ||||
|  | @ -372,6 +393,11 @@ pub async fn get_room_event_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomId}/aliases`
 | ||||
| ///
 | ||||
| /// 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
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/aliases", data = "<body>") | ||||
|  | @ -400,6 +426,16 @@ pub async fn get_room_aliases_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomId}/upgrade`
 | ||||
| ///
 | ||||
| /// Upgrades the room.
 | ||||
| ///
 | ||||
| /// - Creates a replacement room
 | ||||
| /// - Sends a tombstone event into the current room
 | ||||
| /// - Sender user joins the room
 | ||||
| /// - Transfers some state events
 | ||||
| /// - Moves local aliases
 | ||||
| /// - Modifies old room power levels to prevent users from speaking
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/rooms/<_>/upgrade", data = "<body>") | ||||
|  |  | |||
|  | @ -6,6 +6,11 @@ use rocket::post; | |||
| use search_events::{EventContextResult, ResultCategories, ResultRoomEvents, SearchResult}; | ||||
| use std::collections::BTreeMap; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/search`
 | ||||
| ///
 | ||||
| /// Searches rooms for messages.
 | ||||
| ///
 | ||||
| /// - Only works if the user is currently joined to the room (TODO: Respect history visibility)
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/search", data = "<body>") | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ use rocket::{get, post}; | |||
| 
 | ||||
| /// # `GET /_matrix/client/r0/login`
 | ||||
| ///
 | ||||
| /// Get the homeserver's supported login types. One of these should be used as the `type` field
 | ||||
| /// Get the supported login types of this server. One of these should be used as the `type` field
 | ||||
| /// when logging in.
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/login"))] | ||||
| #[tracing::instrument] | ||||
|  | @ -41,9 +41,10 @@ pub async fn get_login_types_route() -> ConduitResult<get_login_types::Response> | |||
| ///
 | ||||
| /// Authenticates the user and returns an access token it can use in subsequent requests.
 | ||||
| ///
 | ||||
| /// - The returned access token is associated with the user and device
 | ||||
| /// - Old access tokens of that device should be invalidated
 | ||||
| /// - If `device_id` is unknown, a new device will be created
 | ||||
| /// - The user needs to authenticate using their password (or if enabled using a json web token)
 | ||||
| /// - If `device_id` is known: invalidates old access token of that device
 | ||||
| /// - If `device_id` is unknown: creates a new device
 | ||||
| /// - Returns access token that is associated with the user and device
 | ||||
| ///
 | ||||
| /// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
 | ||||
| /// supported login types.
 | ||||
|  | @ -162,8 +163,10 @@ pub async fn login_route( | |||
| ///
 | ||||
| /// Log out the current device.
 | ||||
| ///
 | ||||
| /// - Invalidates the access token
 | ||||
| /// - Deletes the device and most of it's data (to-device events, last seen, etc.)
 | ||||
| /// - Invalidates access token
 | ||||
| /// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
 | ||||
| /// - Forgets to-device events
 | ||||
| /// - Triggers device list updates
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/logout", data = "<body>") | ||||
|  | @ -188,7 +191,9 @@ pub async fn logout_route( | |||
| /// Log out all devices of this user.
 | ||||
| ///
 | ||||
| /// - Invalidates all access tokens
 | ||||
| /// - Deletes devices and most of their data (to-device events, last seen, etc.)
 | ||||
| /// - Deletes all device metadata (device id, device display name, last seen ip, last seen ts)
 | ||||
| /// - Forgets all to-device events
 | ||||
| /// - Triggers device list updates
 | ||||
| ///
 | ||||
| /// Note: This is equivalent to calling [`GET /_matrix/client/r0/logout`](fn.logout_route.html)
 | ||||
| /// from each device of this user.
 | ||||
|  |  | |||
|  | @ -22,6 +22,13 @@ use ruma::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}`
 | ||||
| ///
 | ||||
| /// Sends a state event into the room.
 | ||||
| ///
 | ||||
| /// - The only requirement for the content is that it has to be valid json
 | ||||
| /// - Tries to send the event into the room, auth rules will determine if it is allowed
 | ||||
| /// - If event is new canonical_alias: Rejects if alias is incorrect
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/rooms/<_>/state/<_>/<_>", data = "<body>") | ||||
|  | @ -48,6 +55,13 @@ pub async fn send_state_event_for_key_route( | |||
|     Ok(send_state_event::Response { event_id }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}`
 | ||||
| ///
 | ||||
| /// Sends a state event into the room.
 | ||||
| ///
 | ||||
| /// - The only requirement for the content is that it has to be valid json
 | ||||
| /// - Tries to send the event into the room, auth rules will determine if it is allowed
 | ||||
| /// - If event is new canonical_alias: Rejects if alias is incorrect
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/rooms/<_>/state/<_>", data = "<body>") | ||||
|  | @ -74,6 +88,11 @@ pub async fn send_state_event_for_empty_key_route( | |||
|     Ok(send_state_event::Response { event_id }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomid}/state`
 | ||||
| ///
 | ||||
| /// Get all state events for a room.
 | ||||
| ///
 | ||||
| /// - If not joined: Only works if current room history visibility is world readable
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/state", data = "<body>") | ||||
|  | @ -121,6 +140,11 @@ pub async fn get_state_events_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomid}/state/{eventType}/{stateKey}`
 | ||||
| ///
 | ||||
| /// Get single state event of a room.
 | ||||
| ///
 | ||||
| /// - If not joined: Only works if current room history visibility is world readable
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/state/<_>/<_>", data = "<body>") | ||||
|  | @ -172,6 +196,11 @@ pub async fn get_state_events_for_key_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/rooms/{roomid}/state/{eventType}`
 | ||||
| ///
 | ||||
| /// Get single state event of a room.
 | ||||
| ///
 | ||||
| /// - If not joined: Only works if current room history visibility is world readable
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/rooms/<_>/state/<_>", data = "<body>") | ||||
|  | @ -223,7 +252,7 @@ pub async fn get_state_events_for_empty_key_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| pub async fn send_state_event_for_key_helper( | ||||
| async fn send_state_event_for_key_helper( | ||||
|     db: &Database, | ||||
|     sender: &UserId, | ||||
|     room_id: &RoomId, | ||||
|  | @ -233,6 +262,8 @@ pub async fn send_state_event_for_key_helper( | |||
| ) -> Result<EventId> { | ||||
|     let sender_user = sender; | ||||
| 
 | ||||
|     // TODO: Review this check, error if event is unparsable, use event type, allow alias if it
 | ||||
|     // previously existed
 | ||||
|     if let Ok(canonical_alias) = | ||||
|         serde_json::from_str::<CanonicalAliasEventContent>(json.json().get()) | ||||
|     { | ||||
|  |  | |||
|  | @ -22,12 +22,33 @@ use rocket::{get, tokio}; | |||
| /// Synchronize the client's state with the latest state on the server.
 | ||||
| ///
 | ||||
| /// - This endpoint takes a `since` parameter which should be the `next_batch` value from a
 | ||||
| /// previous request.
 | ||||
| /// - Calling this endpoint without a `since` parameter will return all recent events, the state
 | ||||
| /// of all rooms and more data. This should only be called on the initial login of the device.
 | ||||
| /// - To get incremental updates, you can call this endpoint with a `since` parameter. This will
 | ||||
| /// return all recent events, state updates and more data that happened since the last /sync
 | ||||
| /// request.
 | ||||
| /// previous request for incremental syncs.
 | ||||
| ///
 | ||||
| /// Calling this endpoint without a `since` parameter returns:
 | ||||
| /// - Some of the most recent events of each timeline
 | ||||
| /// - Notification counts for each room
 | ||||
| /// - Joined and invited member counts, heroes
 | ||||
| /// - All state events
 | ||||
| ///
 | ||||
| /// Calling this endpoint with a `since` parameter from a previous `next_batch` returns:
 | ||||
| /// For joined rooms:
 | ||||
| /// - Some of the most recent events of each timeline that happened after since
 | ||||
| /// - If user joined the room after since: All state events and device list updates in that room
 | ||||
| /// - If the user was already in the room: A list of all events that are in the state now, but were
 | ||||
| /// not in the state at `since`
 | ||||
| /// - If the state we send contains a member event: Joined and invited member counts, heroes
 | ||||
| /// - Device list updates that happened after `since`
 | ||||
| /// - If there are events in the timeline we send or the user send updated his read mark: Notification counts
 | ||||
| /// - EDUs that are active now (read receipts, typing updates, presence)
 | ||||
| ///
 | ||||
| /// For invited rooms:
 | ||||
| /// - If the user was invited after `since`: A subset of the state of the room at the point of the invite
 | ||||
| ///
 | ||||
| /// For left rooms:
 | ||||
| /// - If the user left after `since`: prev_batch token, empty state (TODO: subset of the state at the point of the leave)
 | ||||
| ///
 | ||||
| /// - Sync is handled in an async task, multiple requests from the same device with the same
 | ||||
| /// `since` will be cached
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/sync", data = "<body>") | ||||
|  | @ -106,7 +127,7 @@ pub async fn sync_events_route( | |||
|     result | ||||
| } | ||||
| 
 | ||||
| pub async fn sync_helper_wrapper( | ||||
| async fn sync_helper_wrapper( | ||||
|     db: Arc<DatabaseGuard>, | ||||
|     sender_user: UserId, | ||||
|     sender_device: Box<DeviceId>, | ||||
|  |  | |||
|  | @ -8,6 +8,11 @@ use std::collections::BTreeMap; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::{delete, get, put}; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}`
 | ||||
| ///
 | ||||
| /// Adds a tag to the room.
 | ||||
| ///
 | ||||
| /// - Inserts the tag into the tag event of the room account data.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/user/<_>/rooms/<_>/tags/<_>", data = "<body>") | ||||
|  | @ -45,6 +50,11 @@ pub async fn update_tag_route( | |||
|     Ok(create_tag::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `DELETE /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}`
 | ||||
| ///
 | ||||
| /// Deletes a tag from the room.
 | ||||
| ///
 | ||||
| /// - Removes the tag from the tag event of the room account data.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     delete("/_matrix/client/r0/user/<_>/rooms/<_>/tags/<_>", data = "<body>") | ||||
|  | @ -79,6 +89,11 @@ pub async fn delete_tag_route( | |||
|     Ok(delete_tag::Response {}.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags`
 | ||||
| ///
 | ||||
| /// Returns tags on the room.
 | ||||
| ///
 | ||||
| /// - Gets the tag event of the room account data.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/user/<_>/rooms/<_>/tags", data = "<body>") | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ use ruma::api::client::r0::thirdparty::get_protocols; | |||
| use rocket::get; | ||||
| use std::collections::BTreeMap; | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/thirdparty/protocols`
 | ||||
| ///
 | ||||
| /// TODO: Fetches all metadata about protocols supported by the homeserver.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/client/r0/thirdparty/protocols") | ||||
|  |  | |||
|  | @ -13,6 +13,9 @@ use ruma::{ | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::put; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}`
 | ||||
| ///
 | ||||
| /// Send a to-device event to a set of client devices.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/sendToDevice/<_>/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ use ruma::api::client::r0::typing::create_typing_event; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::put; | ||||
| 
 | ||||
| /// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
 | ||||
| ///
 | ||||
| /// Sets the typing state of the sender user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/client/r0/rooms/<_>/typing/<_>", data = "<body>") | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ use rocket::get; | |||
| ///
 | ||||
| /// - Versions take the form MAJOR.MINOR.PATCH
 | ||||
| /// - Only the latest PATCH release will be reported for each MAJOR.MINOR value
 | ||||
| /// - Unstable features should be namespaced and may include version information in their name
 | ||||
| /// - Unstable features are namespaced and may include version information in their name
 | ||||
| ///
 | ||||
| /// Note: Unstable features are used while developing new features. Clients should avoid using
 | ||||
| /// unstable features in their stable releases
 | ||||
|  |  | |||
|  | @ -4,6 +4,11 @@ use ruma::api::client::r0::user_directory::search_users; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::post; | ||||
| 
 | ||||
| /// # `POST /_matrix/client/r0/user_directory/search`
 | ||||
| ///
 | ||||
| /// Searches all known users for a match.
 | ||||
| ///
 | ||||
| /// - TODO: Hide users that are not in any public rooms?
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/client/r0/user_directory/search", data = "<body>") | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ use std::time::Duration; | |||
| #[cfg(feature = "conduit_bin")] | ||||
| use rocket::get; | ||||
| 
 | ||||
| /// # `GET /_matrix/client/r0/voip/turnServer`
 | ||||
| ///
 | ||||
| /// TODO: Returns information about the recommended turn server.
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/voip/turnServer"))] | ||||
| #[tracing::instrument] | ||||
| pub async fn turn_server_route() -> ConduitResult<get_turn_server_info::Response> { | ||||
|  |  | |||
|  | @ -84,6 +84,27 @@ impl KeyBackups { | |||
|         Ok(version.to_string()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_latest_backup_version(&self, user_id: &UserId) -> Result<Option<String>> { | ||||
|         let mut prefix = user_id.as_bytes().to_vec(); | ||||
|         prefix.push(0xff); | ||||
|         let mut last_possible_key = prefix.clone(); | ||||
|         last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes()); | ||||
| 
 | ||||
|         self.backupid_algorithm | ||||
|             .iter_from(&last_possible_key, true) | ||||
|             .take_while(move |(k, _)| k.starts_with(&prefix)) | ||||
|             .next() | ||||
|             .map_or(Ok(None), |(key, _)| { | ||||
|                 utils::string_from_bytes( | ||||
|                     key.rsplit(|&b| b == 0xff) | ||||
|                         .next() | ||||
|                         .expect("rsplit always returns an element"), | ||||
|                 ) | ||||
|                 .map_err(|_| Error::bad_database("backupid_algorithm key is invalid.")) | ||||
|                 .map(Some) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_latest_backup(&self, user_id: &UserId) -> Result<Option<(String, BackupAlgorithm)>> { | ||||
|         let mut prefix = user_id.as_bytes().to_vec(); | ||||
|         prefix.push(0xff); | ||||
|  |  | |||
|  | @ -1529,19 +1529,35 @@ impl Rooms { | |||
|                                 "get_auth_chain" => { | ||||
|                                     if args.len() == 1 { | ||||
|                                         if let Ok(event_id) = EventId::try_from(args[0]) { | ||||
|                                             let start = Instant::now(); | ||||
|                                             let count = server_server::get_auth_chain( | ||||
|                                                 vec![Arc::new(event_id)], | ||||
|                                                 db, | ||||
|                                             )? | ||||
|                                             .count(); | ||||
|                                             let elapsed = start.elapsed(); | ||||
|                                             db.admin.send(AdminCommand::SendMessage( | ||||
|                                                 message::MessageEventContent::text_plain(format!( | ||||
|                                             if let Some(event) = db.rooms.get_pdu_json(&event_id)? { | ||||
|                                                 let room_id_str = event | ||||
|                                                     .get("room_id") | ||||
|                                                     .and_then(|val| val.as_str()) | ||||
|                                                     .ok_or_else(|| { | ||||
|                                                         Error::bad_database( | ||||
|                                                             "Invalid event in database", | ||||
|                                                         ) | ||||
|                                                     })?; | ||||
| 
 | ||||
|                                                 let room_id = RoomId::try_from(room_id_str) | ||||
|                                                 .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; | ||||
|                                                 let start = Instant::now(); | ||||
|                                                 let count = server_server::get_auth_chain( | ||||
|                                                     &room_id, | ||||
|                                                     vec![Arc::new(event_id)], | ||||
|                                                     db, | ||||
|                                                 )? | ||||
|                                                 .count(); | ||||
|                                                 let elapsed = start.elapsed(); | ||||
|                                                 db.admin.send(AdminCommand::SendMessage( | ||||
|                                                     message::MessageEventContent::text_plain( | ||||
|                                                         format!( | ||||
|                                                     "Loaded auth chain with length {} in {:?}", | ||||
|                                                     count, elapsed | ||||
|                                                 )), | ||||
|                                             )); | ||||
|                                                 ), | ||||
|                                                     ), | ||||
|                                                 )); | ||||
|                                             } | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|  | @ -3083,6 +3099,15 @@ impl Rooms { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     #[tracing::instrument(skip(self))] | ||||
|     pub fn server_in_room<'a>(&'a self, server: &ServerName, room_id: &RoomId) -> Result<bool> { | ||||
|         let mut key = server.as_bytes().to_vec(); | ||||
|         key.push(0xff); | ||||
|         key.extend_from_slice(room_id.as_bytes()); | ||||
| 
 | ||||
|         self.serverroomids.get(&key).map(|o| o.is_some()) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns an iterator of all rooms a server participates in (as far as we know).
 | ||||
|     #[tracing::instrument(skip(self))] | ||||
|     pub fn server_rooms<'a>( | ||||
|  |  | |||
|  | @ -119,7 +119,7 @@ impl FedDest { | |||
| } | ||||
| 
 | ||||
| #[tracing::instrument(skip(globals, request))] | ||||
| pub async fn send_request<T: OutgoingRequest>( | ||||
| pub(crate) async fn send_request<T: OutgoingRequest>( | ||||
|     globals: &crate::database::globals::Globals, | ||||
|     destination: &ServerName, | ||||
|     request: T, | ||||
|  | @ -487,7 +487,7 @@ async fn query_srv_record( | |||
| } | ||||
| 
 | ||||
| #[tracing::instrument(skip(globals))] | ||||
| pub async fn request_well_known( | ||||
| async fn request_well_known( | ||||
|     globals: &crate::database::globals::Globals, | ||||
|     destination: &str, | ||||
| ) -> Option<String> { | ||||
|  | @ -512,6 +512,9 @@ pub async fn request_well_known( | |||
|     Some(body.get("m.server")?.as_str()?.to_owned()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/version`
 | ||||
| ///
 | ||||
| /// Get version information on this server.
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/federation/v1/version"))] | ||||
| #[tracing::instrument(skip(db))] | ||||
| pub fn get_server_version_route( | ||||
|  | @ -530,6 +533,12 @@ pub fn get_server_version_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/key/v2/server`
 | ||||
| ///
 | ||||
| /// Gets the public signing keys of this server.
 | ||||
| ///
 | ||||
| /// - Matrix does not support invalidating public keys, so the key returned by this will be valid
 | ||||
| /// forever.
 | ||||
| // Response type for this endpoint is Json because we need to calculate a signature for the response
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/key/v2/server"))] | ||||
| #[tracing::instrument(skip(db))] | ||||
|  | @ -578,12 +587,21 @@ pub fn get_server_keys_route(db: DatabaseGuard) -> Json<String> { | |||
|     Json(serde_json::to_string(&response).expect("JSON is canonical")) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/key/v2/server/{keyId}`
 | ||||
| ///
 | ||||
| /// Gets the public signing keys of this server.
 | ||||
| ///
 | ||||
| /// - Matrix does not support invalidating public keys, so the key returned by this will be valid
 | ||||
| /// forever.
 | ||||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/key/v2/server/<_>"))] | ||||
| #[tracing::instrument(skip(db))] | ||||
| pub fn get_server_keys_deprecated_route(db: DatabaseGuard) -> Json<String> { | ||||
|     get_server_keys_route(db) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/federation/v1/publicRooms`
 | ||||
| ///
 | ||||
| /// Lists the public rooms on this server.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/federation/v1/publicRooms", data = "<body>") | ||||
|  | @ -628,6 +646,9 @@ pub async fn get_public_rooms_filtered_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/publicRooms`
 | ||||
| ///
 | ||||
| /// Lists the public rooms on this server.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/publicRooms", data = "<body>") | ||||
|  | @ -672,6 +693,9 @@ pub async fn get_public_rooms_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/federation/v1/send/{txnId}`
 | ||||
| ///
 | ||||
| /// Push EDUs and PDUs to this server.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/federation/v1/send/<_>", data = "<body>") | ||||
|  | @ -921,7 +945,7 @@ type AsyncRecursiveType<'a, T> = Pin<Box<dyn Future<Output = T> + 'a + Send>>; | |||
| /// 14. Use state resolution to find new room state
 | ||||
| // We use some AsyncRecursiveType hacks here so we can call this async funtion recursively
 | ||||
| #[tracing::instrument(skip(value, is_timeline_event, db, pub_key_map))] | ||||
| pub async fn handle_incoming_pdu<'a>( | ||||
| pub(crate) async fn handle_incoming_pdu<'a>( | ||||
|     origin: &'a ServerName, | ||||
|     event_id: &'a EventId, | ||||
|     room_id: &'a RoomId, | ||||
|  | @ -1397,9 +1421,13 @@ async fn upgrade_outlier_to_timeline_pdu( | |||
|             let mut auth_chain_sets = Vec::new(); | ||||
|             for state in fork_states { | ||||
|                 auth_chain_sets.push( | ||||
|                     get_auth_chain(state.iter().map(|(_, id)| id.clone()).collect(), db) | ||||
|                         .map_err(|_| "Failed to load auth chain.".to_owned())? | ||||
|                         .collect(), | ||||
|                     get_auth_chain( | ||||
|                         &room_id, | ||||
|                         state.iter().map(|(_, id)| id.clone()).collect(), | ||||
|                         db, | ||||
|                     ) | ||||
|                     .map_err(|_| "Failed to load auth chain.".to_owned())? | ||||
|                     .collect(), | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|  | @ -1745,9 +1773,13 @@ async fn upgrade_outlier_to_timeline_pdu( | |||
|             let mut auth_chain_sets = Vec::new(); | ||||
|             for state in fork_states { | ||||
|                 auth_chain_sets.push( | ||||
|                     get_auth_chain(state.iter().map(|(_, id)| id.clone()).collect(), db) | ||||
|                         .map_err(|_| "Failed to load auth chain.".to_owned())? | ||||
|                         .collect(), | ||||
|                     get_auth_chain( | ||||
|                         &room_id, | ||||
|                         state.iter().map(|(_, id)| id.clone()).collect(), | ||||
|                         db, | ||||
|                     ) | ||||
|                     .map_err(|_| "Failed to load auth chain.".to_owned())? | ||||
|                     .collect(), | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|  | @ -2187,10 +2219,11 @@ fn append_incoming_pdu( | |||
| } | ||||
| 
 | ||||
| #[tracing::instrument(skip(starting_events, db))] | ||||
| pub fn get_auth_chain( | ||||
| pub(crate) fn get_auth_chain<'a>( | ||||
|     room_id: &RoomId, | ||||
|     starting_events: Vec<Arc<EventId>>, | ||||
|     db: &Database, | ||||
| ) -> Result<impl Iterator<Item = Arc<EventId>> + '_> { | ||||
|     db: &'a Database, | ||||
| ) -> Result<impl Iterator<Item = Arc<EventId>> + 'a> { | ||||
|     const NUM_BUCKETS: usize = 50; | ||||
| 
 | ||||
|     let mut buckets = vec![BTreeSet::new(); NUM_BUCKETS]; | ||||
|  | @ -2231,7 +2264,7 @@ pub fn get_auth_chain( | |||
|                 chunk_cache.extend(cached.iter().cloned()); | ||||
|             } else { | ||||
|                 misses2 += 1; | ||||
|                 let auth_chain = Arc::new(get_auth_chain_inner(&event_id, db)?); | ||||
|                 let auth_chain = Arc::new(get_auth_chain_inner(&room_id, &event_id, db)?); | ||||
|                 db.rooms | ||||
|                     .cache_auth_chain(vec![sevent_id], Arc::clone(&auth_chain))?; | ||||
|                 println!( | ||||
|  | @ -2267,13 +2300,20 @@ pub fn get_auth_chain( | |||
| } | ||||
| 
 | ||||
| #[tracing::instrument(skip(event_id, db))] | ||||
| fn get_auth_chain_inner(event_id: &EventId, db: &Database) -> Result<HashSet<u64>> { | ||||
| fn get_auth_chain_inner( | ||||
|     room_id: &RoomId, | ||||
|     event_id: &EventId, | ||||
|     db: &Database, | ||||
| ) -> Result<HashSet<u64>> { | ||||
|     let mut todo = vec![event_id.clone()]; | ||||
|     let mut found = HashSet::new(); | ||||
| 
 | ||||
|     while let Some(event_id) = todo.pop() { | ||||
|         match db.rooms.get_pdu(&event_id) { | ||||
|             Ok(Some(pdu)) => { | ||||
|                 if &pdu.room_id != room_id { | ||||
|                     return Err(Error::BadRequest(ErrorKind::Forbidden, "Evil event in db")); | ||||
|                 } | ||||
|                 for auth_event in &pdu.auth_events { | ||||
|                     let sauthevent = db | ||||
|                         .rooms | ||||
|  | @ -2297,6 +2337,11 @@ fn get_auth_chain_inner(event_id: &EventId, db: &Database) -> Result<HashSet<u64 | |||
|     Ok(found) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/event/{eventId}`
 | ||||
| ///
 | ||||
| /// Retrieves a single event from the server.
 | ||||
| ///
 | ||||
| /// - Only works if a user of this server is currently invited or joined the room
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/event/<_>", data = "<body>") | ||||
|  | @ -2310,18 +2355,39 @@ pub fn get_event_route( | |||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let sender_servername = body | ||||
|         .sender_servername | ||||
|         .as_ref() | ||||
|         .expect("server is authenticated"); | ||||
| 
 | ||||
|     let event = db | ||||
|         .rooms | ||||
|         .get_pdu_json(&body.event_id)? | ||||
|         .ok_or(Error::BadRequest(ErrorKind::NotFound, "Event not found."))?; | ||||
| 
 | ||||
|     let room_id_str = event | ||||
|         .get("room_id") | ||||
|         .and_then(|val| val.as_str()) | ||||
|         .ok_or_else(|| Error::bad_database("Invalid event in database"))?; | ||||
| 
 | ||||
|     let room_id = RoomId::try_from(room_id_str) | ||||
|         .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; | ||||
| 
 | ||||
|     if !db.rooms.server_in_room(sender_servername, &room_id)? { | ||||
|         return Err(Error::BadRequest(ErrorKind::NotFound, "Event not found.")); | ||||
|     } | ||||
| 
 | ||||
|     Ok(get_event::v1::Response { | ||||
|         origin: db.globals.server_name().to_owned(), | ||||
|         origin_server_ts: MilliSecondsSinceUnixEpoch::now(), | ||||
|         pdu: PduEvent::convert_to_outgoing_federation_event( | ||||
|             db.rooms | ||||
|                 .get_pdu_json(&body.event_id)? | ||||
|                 .ok_or(Error::BadRequest(ErrorKind::NotFound, "Event not found."))?, | ||||
|         ), | ||||
|         pdu: PduEvent::convert_to_outgoing_federation_event(event), | ||||
|     } | ||||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/federation/v1/get_missing_events/{roomId}`
 | ||||
| ///
 | ||||
| /// Retrieves events that the sender is missing.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/federation/v1/get_missing_events/<_>", data = "<body>") | ||||
|  | @ -2335,22 +2401,44 @@ pub fn get_missing_events_route( | |||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let sender_servername = body | ||||
|         .sender_servername | ||||
|         .as_ref() | ||||
|         .expect("server is authenticated"); | ||||
| 
 | ||||
|     if !db.rooms.server_in_room(sender_servername, &body.room_id)? { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::Forbidden, | ||||
|             "Server is not in room", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     let mut queued_events = body.latest_events.clone(); | ||||
|     let mut events = Vec::new(); | ||||
| 
 | ||||
|     let mut i = 0; | ||||
|     while i < queued_events.len() && events.len() < u64::from(body.limit) as usize { | ||||
|         if let Some(pdu) = db.rooms.get_pdu_json(&queued_events[i])? { | ||||
|             let event_id = | ||||
|                 serde_json::from_value( | ||||
|                     serde_json::to_value(pdu.get("event_id").cloned().ok_or_else(|| { | ||||
|                         Error::bad_database("Event in db has no event_id field.") | ||||
|                     })?) | ||||
|                     .expect("canonical json is valid json value"), | ||||
|                 ) | ||||
|                 .map_err(|_| Error::bad_database("Invalid event_id field in pdu in db."))?; | ||||
|             let room_id_str = pdu | ||||
|                 .get("room_id") | ||||
|                 .and_then(|val| val.as_str()) | ||||
|                 .ok_or_else(|| Error::bad_database("Invalid event in database"))?; | ||||
| 
 | ||||
|             if body.earliest_events.contains(&event_id) { | ||||
|             let event_room_id = RoomId::try_from(room_id_str) | ||||
|                 .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; | ||||
| 
 | ||||
|             if event_room_id != body.room_id { | ||||
|                 warn!( | ||||
|                     "Evil event detected: Event {} found while searching in room {}", | ||||
|                     queued_events[i], body.room_id | ||||
|                 ); | ||||
|                 return Err(Error::BadRequest( | ||||
|                     ErrorKind::InvalidParam, | ||||
|                     "Evil event detected", | ||||
|                 )); | ||||
|             } | ||||
| 
 | ||||
|             if body.earliest_events.contains(&queued_events[i]) { | ||||
|                 i += 1; | ||||
|                 continue; | ||||
|             } | ||||
|  | @ -2371,6 +2459,11 @@ pub fn get_missing_events_route( | |||
|     Ok(get_missing_events::v1::Response { events }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/event_auth/{roomId}/{eventId}`
 | ||||
| ///
 | ||||
| /// Retrieves the auth chain for a given event.
 | ||||
| ///
 | ||||
| /// - This does not include the event itself
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/event_auth/<_>/<_>", data = "<body>") | ||||
|  | @ -2384,7 +2477,29 @@ pub fn get_event_authorization_route( | |||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let auth_chain_ids = get_auth_chain(vec![Arc::new(body.event_id.clone())], &db)?; | ||||
|     let sender_servername = body | ||||
|         .sender_servername | ||||
|         .as_ref() | ||||
|         .expect("server is authenticated"); | ||||
| 
 | ||||
|     let event = db | ||||
|         .rooms | ||||
|         .get_pdu_json(&body.event_id)? | ||||
|         .ok_or(Error::BadRequest(ErrorKind::NotFound, "Event not found."))?; | ||||
| 
 | ||||
|     let room_id_str = event | ||||
|         .get("room_id") | ||||
|         .and_then(|val| val.as_str()) | ||||
|         .ok_or_else(|| Error::bad_database("Invalid event in database"))?; | ||||
| 
 | ||||
|     let room_id = RoomId::try_from(room_id_str) | ||||
|         .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?; | ||||
| 
 | ||||
|     if !db.rooms.server_in_room(sender_servername, &room_id)? { | ||||
|         return Err(Error::BadRequest(ErrorKind::NotFound, "Event not found.")); | ||||
|     } | ||||
| 
 | ||||
|     let auth_chain_ids = get_auth_chain(&room_id, vec![Arc::new(body.event_id.clone())], &db)?; | ||||
| 
 | ||||
|     Ok(get_event_authorization::v1::Response { | ||||
|         auth_chain: auth_chain_ids | ||||
|  | @ -2395,6 +2510,9 @@ pub fn get_event_authorization_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/state/{roomId}`
 | ||||
| ///
 | ||||
| /// Retrieves the current state of the room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/state/<_>", data = "<body>") | ||||
|  | @ -2408,6 +2526,18 @@ pub fn get_room_state_route( | |||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let sender_servername = body | ||||
|         .sender_servername | ||||
|         .as_ref() | ||||
|         .expect("server is authenticated"); | ||||
| 
 | ||||
|     if !db.rooms.server_in_room(sender_servername, &body.room_id)? { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::Forbidden, | ||||
|             "Server is not in room.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     let shortstatehash = db | ||||
|         .rooms | ||||
|         .pdu_shortstatehash(&body.event_id)? | ||||
|  | @ -2427,7 +2557,7 @@ pub fn get_room_state_route( | |||
|         }) | ||||
|         .collect(); | ||||
| 
 | ||||
|     let auth_chain_ids = get_auth_chain(vec![Arc::new(body.event_id.clone())], &db)?; | ||||
|     let auth_chain_ids = get_auth_chain(&body.room_id, vec![Arc::new(body.event_id.clone())], &db)?; | ||||
| 
 | ||||
|     Ok(get_room_state::v1::Response { | ||||
|         auth_chain: auth_chain_ids | ||||
|  | @ -2443,6 +2573,9 @@ pub fn get_room_state_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/state_ids/{roomId}`
 | ||||
| ///
 | ||||
| /// Retrieves the current state of the room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/state_ids/<_>", data = "<body>") | ||||
|  | @ -2456,6 +2589,18 @@ pub fn get_room_state_ids_route( | |||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let sender_servername = body | ||||
|         .sender_servername | ||||
|         .as_ref() | ||||
|         .expect("server is authenticated"); | ||||
| 
 | ||||
|     if !db.rooms.server_in_room(sender_servername, &body.room_id)? { | ||||
|         return Err(Error::BadRequest( | ||||
|             ErrorKind::Forbidden, | ||||
|             "Server is not in room.", | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     let shortstatehash = db | ||||
|         .rooms | ||||
|         .pdu_shortstatehash(&body.event_id)? | ||||
|  | @ -2471,7 +2616,7 @@ pub fn get_room_state_ids_route( | |||
|         .map(|(_, id)| (*id).clone()) | ||||
|         .collect(); | ||||
| 
 | ||||
|     let auth_chain_ids = get_auth_chain(vec![Arc::new(body.event_id.clone())], &db)?; | ||||
|     let auth_chain_ids = get_auth_chain(&body.room_id, vec![Arc::new(body.event_id.clone())], &db)?; | ||||
| 
 | ||||
|     Ok(get_room_state_ids::v1::Response { | ||||
|         auth_chain_ids: auth_chain_ids.map(|id| (*id).clone()).collect(), | ||||
|  | @ -2480,6 +2625,9 @@ pub fn get_room_state_ids_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
 | ||||
| ///
 | ||||
| /// Creates a join template.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/make_join/<_>/<_>", data = "<body>") | ||||
|  | @ -2719,7 +2867,11 @@ async fn create_join_event( | |||
|     drop(mutex_lock); | ||||
| 
 | ||||
|     let state_ids = db.rooms.state_full_ids(shortstatehash)?; | ||||
|     let auth_chain_ids = get_auth_chain(state_ids.iter().map(|(_, id)| id.clone()).collect(), &db)?; | ||||
|     let auth_chain_ids = get_auth_chain( | ||||
|         &room_id, | ||||
|         state_ids.iter().map(|(_, id)| id.clone()).collect(), | ||||
|         &db, | ||||
|     )?; | ||||
| 
 | ||||
|     for server in db | ||||
|         .rooms | ||||
|  | @ -2745,6 +2897,9 @@ async fn create_join_event( | |||
|     }) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}`
 | ||||
| ///
 | ||||
| /// Submits a signed join event.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/federation/v1/send_join/<_>/<_>", data = "<body>") | ||||
|  | @ -2759,6 +2914,9 @@ pub async fn create_join_event_v1_route( | |||
|     Ok(create_join_event::v1::Response { room_state }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}`
 | ||||
| ///
 | ||||
| /// Submits a signed join event.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/federation/v2/send_join/<_>/<_>", data = "<body>") | ||||
|  | @ -2773,6 +2931,9 @@ pub async fn create_join_event_v2_route( | |||
|     Ok(create_join_event::v2::Response { room_state }.into()) | ||||
| } | ||||
| 
 | ||||
| /// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
 | ||||
| ///
 | ||||
| /// Invites a remote user to a room.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     put("/_matrix/federation/v2/invite/<_>/<_>", data = "<body>") | ||||
|  | @ -2882,6 +3043,9 @@ pub async fn create_invite_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/user/devices/{userId}`
 | ||||
| ///
 | ||||
| /// Gets information on all devices of the user.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/user/devices/<_>", data = "<body>") | ||||
|  | @ -2922,6 +3086,9 @@ pub fn get_devices_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/query/directory`
 | ||||
| ///
 | ||||
| /// Resolve a room alias to a room id.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/query/directory", data = "<body>") | ||||
|  | @ -2950,6 +3117,9 @@ pub fn get_room_information_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `GET /_matrix/federation/v1/query/profile`
 | ||||
| ///
 | ||||
| /// Gets information on a profile.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     get("/_matrix/federation/v1/query/profile", data = "<body>") | ||||
|  | @ -2990,6 +3160,9 @@ pub fn get_profile_information_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/federation/v1/user/keys/query`
 | ||||
| ///
 | ||||
| /// Gets devices and identity keys for the given users.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/federation/v1/user/keys/query", data = "<body>") | ||||
|  | @ -3021,6 +3194,9 @@ pub async fn get_keys_route( | |||
|     .into()) | ||||
| } | ||||
| 
 | ||||
| /// # `POST /_matrix/federation/v1/user/keys/claim`
 | ||||
| ///
 | ||||
| /// Claims one-time keys.
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "conduit_bin", | ||||
|     post("/_matrix/federation/v1/user/keys/claim", data = "<body>") | ||||
|  | @ -3045,7 +3221,7 @@ pub async fn claim_keys_route( | |||
| } | ||||
| 
 | ||||
| #[tracing::instrument(skip(event, pub_key_map, db))] | ||||
| pub async fn fetch_required_signing_keys( | ||||
| pub(crate) async fn fetch_required_signing_keys( | ||||
|     event: &BTreeMap<String, CanonicalJsonValue>, | ||||
|     pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, String>>>, | ||||
|     db: &Database, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue