Merge pull request 'Add displayname and avatar_url endpoints' (#5) from MTRNord/matrixserver:accountdata into master
This commit is contained in:
		
						commit
						3f2bf208a9
					
				
					 3 changed files with 230 additions and 1 deletions
				
			
		
							
								
								
									
										50
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								src/data.rs
									
									
									
									
									
								
							|  | @ -60,6 +60,56 @@ impl Data { | ||||||
|             .map(|bytes| utils::string_from_bytes(&bytes)) |             .map(|bytes| utils::string_from_bytes(&bytes)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Removes a displayname.
 | ||||||
|  |     pub fn displayname_remove(&self, user_id: &UserId) { | ||||||
|  |         self.db | ||||||
|  |             .userid_displayname | ||||||
|  |             .remove(user_id.to_string()) | ||||||
|  |             .unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Set a new displayname.
 | ||||||
|  |     pub fn displayname_set(&self, user_id: &UserId, displayname: Option<String>) { | ||||||
|  |         self.db | ||||||
|  |             .userid_displayname | ||||||
|  |             .insert(user_id.to_string(), &*displayname.unwrap_or_default()) | ||||||
|  |             .unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Get a the displayname of a user.
 | ||||||
|  |     pub fn displayname_get(&self, user_id: &UserId) -> Option<String> { | ||||||
|  |         self.db | ||||||
|  |             .userid_displayname | ||||||
|  |             .get(user_id.to_string()) | ||||||
|  |             .unwrap() | ||||||
|  |             .map(|bytes| utils::string_from_bytes(&bytes)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Removes a avatar_url.
 | ||||||
|  |     pub fn avatar_url_remove(&self, user_id: &UserId) { | ||||||
|  |         self.db | ||||||
|  |             .userid_avatarurl | ||||||
|  |             .remove(user_id.to_string()) | ||||||
|  |             .unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Set a new avatar_url.
 | ||||||
|  |     pub fn avatar_url_set(&self, user_id: &UserId, avatar_url: String) { | ||||||
|  |         self.db | ||||||
|  |             .userid_avatarurl | ||||||
|  |             .insert(user_id.to_string(), &*avatar_url) | ||||||
|  |             .unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Get a the avatar_url of a user.
 | ||||||
|  |     pub fn avatar_url_get(&self, user_id: &UserId) -> Option<String> { | ||||||
|  |         self.db | ||||||
|  |             .userid_avatarurl | ||||||
|  |             .get(user_id.to_string()) | ||||||
|  |             .unwrap() | ||||||
|  |             .map(|bytes| utils::string_from_bytes(&bytes)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Add a new device to a user.
 |     /// Add a new device to a user.
 | ||||||
|     pub fn device_add(&self, user_id: &UserId, device_id: &str) { |     pub fn device_add(&self, user_id: &UserId, device_id: &str) { | ||||||
|         if self |         if self | ||||||
|  |  | ||||||
|  | @ -52,6 +52,8 @@ impl MultiValue { | ||||||
| pub struct Database { | pub struct Database { | ||||||
|     pub userid_password: sled::Tree, |     pub userid_password: sled::Tree, | ||||||
|     pub userid_deviceids: MultiValue, |     pub userid_deviceids: MultiValue, | ||||||
|  |     pub userid_displayname: sled::Tree, | ||||||
|  |     pub userid_avatarurl: sled::Tree, | ||||||
|     pub deviceid_token: sled::Tree, |     pub deviceid_token: sled::Tree, | ||||||
|     pub token_userid: sled::Tree, |     pub token_userid: sled::Tree, | ||||||
|     pub pduid_pdus: sled::Tree, |     pub pduid_pdus: sled::Tree, | ||||||
|  | @ -75,6 +77,8 @@ impl Database { | ||||||
|         Self { |         Self { | ||||||
|             userid_password: db.open_tree("userid_password").unwrap(), |             userid_password: db.open_tree("userid_password").unwrap(), | ||||||
|             userid_deviceids: MultiValue(db.open_tree("userid_deviceids").unwrap()), |             userid_deviceids: MultiValue(db.open_tree("userid_deviceids").unwrap()), | ||||||
|  |             userid_displayname: db.open_tree("userid_displayname").unwrap(), | ||||||
|  |             userid_avatarurl: db.open_tree("userid_avatarurl").unwrap(), | ||||||
|             deviceid_token: db.open_tree("deviceid_token").unwrap(), |             deviceid_token: db.open_tree("deviceid_token").unwrap(), | ||||||
|             token_userid: db.open_tree("token_userid").unwrap(), |             token_userid: db.open_tree("token_userid").unwrap(), | ||||||
|             pduid_pdus: db.open_tree("pduid_pdus").unwrap(), |             pduid_pdus: db.open_tree("pduid_pdus").unwrap(), | ||||||
|  | @ -103,6 +107,22 @@ impl Database { | ||||||
|                 String::from_utf8_lossy(&v), |                 String::from_utf8_lossy(&v), | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|  |         println!("# UserId -> Displayname:"); | ||||||
|  |         for (k, v) in self.userid_displayname.iter().map(|r| r.unwrap()) { | ||||||
|  |             println!( | ||||||
|  |                 "{:?} -> {:?}", | ||||||
|  |                 String::from_utf8_lossy(&k), | ||||||
|  |                 String::from_utf8_lossy(&v), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         println!("# UserId -> AvatarURL:"); | ||||||
|  |         for (k, v) in self.userid_avatarurl.iter().map(|r| r.unwrap()) { | ||||||
|  |             println!( | ||||||
|  |                 "{:?} -> {:?}", | ||||||
|  |                 String::from_utf8_lossy(&k), | ||||||
|  |                 String::from_utf8_lossy(&v), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|         println!("\n# DeviceId -> Token:"); |         println!("\n# DeviceId -> Token:"); | ||||||
|         for (k, v) in self.deviceid_token.iter().map(|r| r.unwrap()) { |         for (k, v) in self.deviceid_token.iter().map(|r| r.unwrap()) { | ||||||
|             println!( |             println!( | ||||||
|  |  | ||||||
							
								
								
									
										161
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								src/main.rs
									
									
									
									
									
								
							|  | @ -1,4 +1,5 @@ | ||||||
| #![feature(proc_macro_hygiene, decl_macro)] | #![feature(proc_macro_hygiene, decl_macro)] | ||||||
|  | 
 | ||||||
| mod data; | mod data; | ||||||
| mod database; | mod database; | ||||||
| mod pdu; | mod pdu; | ||||||
|  | @ -26,6 +27,9 @@ use ruma_client_api::{ | ||||||
|         membership::{join_room_by_id, join_room_by_id_or_alias}, |         membership::{join_room_by_id, join_room_by_id_or_alias}, | ||||||
|         message::create_message_event, |         message::create_message_event, | ||||||
|         presence::set_presence, |         presence::set_presence, | ||||||
|  |         profile::{ | ||||||
|  |             get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name, | ||||||
|  |         }, | ||||||
|         push::get_pushrules_all, |         push::get_pushrules_all, | ||||||
|         room::create_room, |         room::create_room, | ||||||
|         session::{get_login_types, login}, |         session::{get_login_types, login}, | ||||||
|  | @ -39,7 +43,12 @@ use ruma_events::{collections::only::Event, EventType}; | ||||||
| use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId}; | use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId}; | ||||||
| use ruma_wrapper::{MatrixResult, Ruma}; | use ruma_wrapper::{MatrixResult, Ruma}; | ||||||
| use serde_json::json; | use serde_json::json; | ||||||
| use std::{collections::HashMap, convert::TryInto, path::PathBuf, time::Duration}; | use std::{ | ||||||
|  |     collections::HashMap, | ||||||
|  |     convert::{TryFrom, TryInto}, | ||||||
|  |     path::PathBuf, | ||||||
|  |     time::Duration, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| const GUEST_NAME_LENGTH: usize = 10; | const GUEST_NAME_LENGTH: usize = 10; | ||||||
| const DEVICE_ID_LENGTH: usize = 10; | const DEVICE_ID_LENGTH: usize = 10; | ||||||
|  | @ -282,6 +291,151 @@ fn get_global_account_data_route( | ||||||
|     })) |     })) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[put("/_matrix/client/r0/profile/<_user_id>/displayname", data = "<body>")] | ||||||
|  | fn set_displayname_route( | ||||||
|  |     data: State<Data>, | ||||||
|  |     body: Ruma<set_display_name::Request>, | ||||||
|  |     _user_id: String, | ||||||
|  | ) -> MatrixResult<set_display_name::Response> { | ||||||
|  |     let user_id = body.user_id.clone().expect("user is authenticated"); | ||||||
|  | 
 | ||||||
|  |     // Send error on None
 | ||||||
|  |     // Synapse returns a parsing error but the spec doesn't require this
 | ||||||
|  |     if body.displayname.is_none() { | ||||||
|  |         debug!("Request was missing the displayname payload."); | ||||||
|  |         return MatrixResult(Err(Error { | ||||||
|  |             kind: ErrorKind::MissingParam, | ||||||
|  |             message: "Missing displayname.".to_owned(), | ||||||
|  |             status_code: http::StatusCode::BAD_REQUEST, | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if let Some(displayname) = &body.displayname { | ||||||
|  |         // Some("") will clear the displayname
 | ||||||
|  |         if displayname == "" { | ||||||
|  |             data.displayname_remove(&user_id); | ||||||
|  |         } else { | ||||||
|  |             data.displayname_set(&user_id, body.displayname.clone()); | ||||||
|  |             // TODO send a new m.room.member join event with the updated displayname
 | ||||||
|  |             // TODO send a new m.presence event with the updated displayname
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MatrixResult(Ok(set_display_name::Response)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[get(
 | ||||||
|  |     "/_matrix/client/r0/profile/<user_id_raw>/displayname", | ||||||
|  |     data = "<body>" | ||||||
|  | )] | ||||||
|  | fn get_displayname_route( | ||||||
|  |     data: State<Data>, | ||||||
|  |     body: Ruma<get_display_name::Request>, | ||||||
|  |     user_id_raw: String, | ||||||
|  | ) -> MatrixResult<get_display_name::Response> { | ||||||
|  |     let user_id = (*body).user_id.clone(); | ||||||
|  |     if !data.user_exists(&user_id) { | ||||||
|  |         // Return 404 if we don't have a profile for this id
 | ||||||
|  |         debug!("Profile was not found."); | ||||||
|  |         return MatrixResult(Err(Error { | ||||||
|  |             kind: ErrorKind::NotFound, | ||||||
|  |             message: "Profile was not found.".to_owned(), | ||||||
|  |             status_code: http::StatusCode::NOT_FOUND, | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  |     if let Some(displayname) = data.displayname_get(&user_id) { | ||||||
|  |         return MatrixResult(Ok(get_display_name::Response { | ||||||
|  |             displayname: Some(displayname), | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // The user has no displayname
 | ||||||
|  |     MatrixResult(Ok(get_display_name::Response { displayname: None })) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[put("/_matrix/client/r0/profile/<_user_id>/avatar_url", data = "<body>")] | ||||||
|  | fn set_avatar_url_route( | ||||||
|  |     data: State<Data>, | ||||||
|  |     body: Ruma<set_avatar_url::Request>, | ||||||
|  |     _user_id: String, | ||||||
|  | ) -> MatrixResult<set_avatar_url::Response> { | ||||||
|  |     let user_id = body.user_id.clone().expect("user is authenticated"); | ||||||
|  | 
 | ||||||
|  |     if !body.avatar_url.starts_with("mxc://") { | ||||||
|  |         debug!("Request contains an invalid avatar_url."); | ||||||
|  |         return MatrixResult(Err(Error { | ||||||
|  |             kind: ErrorKind::InvalidParam, | ||||||
|  |             message: "avatar_url has to start with mxc://.".to_owned(), | ||||||
|  |             status_code: http::StatusCode::BAD_REQUEST, | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO in the future when we can handle media uploads make sure that this url is our own server
 | ||||||
|  |     // TODO also make sure this is valid mxc:// format (not only starting with it)
 | ||||||
|  | 
 | ||||||
|  |     if body.avatar_url == "" { | ||||||
|  |         data.avatar_url_remove(&user_id); | ||||||
|  |     } else { | ||||||
|  |         data.avatar_url_set(&user_id, body.avatar_url.clone()); | ||||||
|  |         // TODO send a new m.room.member join event with the updated avatar_url
 | ||||||
|  |         // TODO send a new m.presence event with the updated avatar_url
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MatrixResult(Ok(set_avatar_url::Response)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[get("/_matrix/client/r0/profile/<user_id_raw>/avatar_url", data = "<body>")] | ||||||
|  | fn get_avatar_url_route( | ||||||
|  |     data: State<Data>, | ||||||
|  |     body: Ruma<get_avatar_url::Request>, | ||||||
|  |     user_id_raw: String, | ||||||
|  | ) -> MatrixResult<get_avatar_url::Response> { | ||||||
|  |     let user_id = (*body).user_id.clone(); | ||||||
|  |     if !data.user_exists(&user_id) { | ||||||
|  |         // Return 404 if we don't have a profile for this id
 | ||||||
|  |         debug!("Profile was not found."); | ||||||
|  |         return MatrixResult(Err(Error { | ||||||
|  |             kind: ErrorKind::NotFound, | ||||||
|  |             message: "Profile was not found.".to_owned(), | ||||||
|  |             status_code: http::StatusCode::NOT_FOUND, | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  |     if let Some(avatar_url) = data.avatar_url_get(&user_id) { | ||||||
|  |         return MatrixResult(Ok(get_avatar_url::Response { | ||||||
|  |             avatar_url: Some(avatar_url), | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // The user has no avatar
 | ||||||
|  |     MatrixResult(Ok(get_avatar_url::Response { avatar_url: None })) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[get("/_matrix/client/r0/profile/<user_id_raw>", data = "<body>")] | ||||||
|  | fn get_profile_route( | ||||||
|  |     data: State<Data>, | ||||||
|  |     body: Ruma<get_profile::Request>, | ||||||
|  |     user_id_raw: String, | ||||||
|  | ) -> MatrixResult<get_profile::Response> { | ||||||
|  |     let user_id = (*body).user_id.clone(); | ||||||
|  |     let avatar_url = data.avatar_url_get(&user_id); | ||||||
|  |     let displayname = data.displayname_get(&user_id); | ||||||
|  | 
 | ||||||
|  |     if avatar_url.is_some() || displayname.is_some() { | ||||||
|  |         return MatrixResult(Ok(get_profile::Response { | ||||||
|  |             avatar_url, | ||||||
|  |             displayname, | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Return 404 if we don't have a profile for this id
 | ||||||
|  |     debug!("Profile was not found."); | ||||||
|  |     MatrixResult(Err(Error { | ||||||
|  |         kind: ErrorKind::NotFound, | ||||||
|  |         message: "Profile was not found.".to_owned(), | ||||||
|  |         status_code: http::StatusCode::NOT_FOUND, | ||||||
|  |     })) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[put("/_matrix/client/r0/presence/<_user_id>/status", data = "<body>")] | #[put("/_matrix/client/r0/presence/<_user_id>/status", data = "<body>")] | ||||||
| fn set_presence_route( | fn set_presence_route( | ||||||
|     body: Ruma<set_presence::Request>, |     body: Ruma<set_presence::Request>, | ||||||
|  | @ -634,6 +788,11 @@ fn main() { | ||||||
|                 create_filter_route, |                 create_filter_route, | ||||||
|                 set_global_account_data_route, |                 set_global_account_data_route, | ||||||
|                 get_global_account_data_route, |                 get_global_account_data_route, | ||||||
|  |                 set_displayname_route, | ||||||
|  |                 get_displayname_route, | ||||||
|  |                 set_avatar_url_route, | ||||||
|  |                 get_avatar_url_route, | ||||||
|  |                 get_profile_route, | ||||||
|                 set_presence_route, |                 set_presence_route, | ||||||
|                 get_keys_route, |                 get_keys_route, | ||||||
|                 upload_keys_route, |                 upload_keys_route, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue