diff --git a/src/data.rs b/src/data.rs index dffb1e0..3ec3b44 100644 --- a/src/data.rs +++ b/src/data.rs @@ -60,6 +60,40 @@ impl Data { .map(|bytes| utils::string_from_bytes(&bytes)) } + /// Set a new displayname. + pub fn displayname_set(&self, user_id: &UserId, displayname: Option) { + self.db + .profile_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 { + self.db + .profile_displayname + .get(user_id.to_string()) + .unwrap() + .map(|bytes| utils::string_from_bytes(&bytes)) + } + + /// Set a new avatar_url. + pub fn avatar_url_set(&self, user_id: &UserId, avatar_url: String) { + self.db + .profile_avatar_url + .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 { + self.db + .profile_avatar_url + .get(user_id.to_string()) + .unwrap() + .map(|bytes| utils::string_from_bytes(&bytes)) + } + /// Add a new device to a user. pub fn device_add(&self, user_id: &UserId, device_id: &str) { if self diff --git a/src/database.rs b/src/database.rs index dee2e94..a6ddd55 100644 --- a/src/database.rs +++ b/src/database.rs @@ -52,6 +52,8 @@ impl MultiValue { pub struct Database { pub userid_password: sled::Tree, pub userid_deviceids: MultiValue, + pub profile_displayname: sled::Tree, + pub profile_avatar_url: sled::Tree, pub deviceid_token: sled::Tree, pub token_userid: sled::Tree, pub pduid_pdus: sled::Tree, @@ -75,6 +77,8 @@ impl Database { Self { userid_password: db.open_tree("userid_password").unwrap(), userid_deviceids: MultiValue(db.open_tree("userid_deviceids").unwrap()), + profile_displayname: db.open_tree("profile_displayname").unwrap(), + profile_avatar_url: db.open_tree("profile_avatar_url").unwrap(), deviceid_token: db.open_tree("deviceid_token").unwrap(), token_userid: db.open_tree("token_userid").unwrap(), pduid_pdus: db.open_tree("pduid_pdus").unwrap(), @@ -103,6 +107,22 @@ impl Database { String::from_utf8_lossy(&v), ); } + println!("# AccountData -> Displayname:"); + for (k, v) in self.profile_displayname.iter().map(|r| r.unwrap()) { + println!( + "{:?} -> {:?}", + String::from_utf8_lossy(&k), + String::from_utf8_lossy(&v), + ); + } + println!("# AccountData -> AvatarURL:"); + for (k, v) in self.profile_avatar_url.iter().map(|r| r.unwrap()) { + println!( + "{:?} -> {:?}", + String::from_utf8_lossy(&k), + String::from_utf8_lossy(&v), + ); + } println!("\n# DeviceId -> Token:"); for (k, v) in self.deviceid_token.iter().map(|r| r.unwrap()) { println!( diff --git a/src/main.rs b/src/main.rs index 92cb6d1..41c7e6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,9 @@ use ruma_client_api::{ membership::{join_room_by_id, join_room_by_id_or_alias}, message::create_message_event, presence::set_presence, + profile::{ + get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name, + }, push::get_pushrules_all, room::create_room, session::{get_login_types, login}, @@ -39,7 +42,12 @@ use ruma_events::{collections::only::Event, EventType}; use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId}; use ruma_wrapper::{MatrixResult, Ruma}; 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 DEVICE_ID_LENGTH: usize = 10; @@ -282,6 +290,127 @@ fn get_global_account_data_route( })) } +#[put("/_matrix/client/r0/profile/<_user_id>/displayname", data = "")] +fn set_displayname_route( + data: State, + body: Ruma, + _user_id: String, +) -> MatrixResult { + let user_id = body.user_id.clone().expect("user is authenticated"); + 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, + })); + } + + 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//displayname", + data = "" +)] +fn get_displayname_route( + data: State, + body: Ruma, + user_id_raw: String, +) -> MatrixResult { + let user_id = (*body).user_id.clone(); + if let Some(displayname) = data.displayname_get(&user_id) { + return MatrixResult(Ok(get_display_name::Response { + displayname: Some(displayname), + })); + } + + // Return 404 if we don't have any + 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/profile/<_user_id>/avatar_url", data = "")] +fn set_avatar_url_route( + data: State, + body: Ruma, + _user_id: String, +) -> MatrixResult { + let user_id = body.user_id.clone().expect("user is authenticated"); + if body.avatar_url == "" { + debug!("Request was missing the avatar_url payload."); + return MatrixResult(Err(Error { + kind: ErrorKind::MissingParam, + message: "Missing avatar_url".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 mxc:// format + + 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//avatar_url", data = "")] +fn get_avatar_url_route( + data: State, + body: Ruma, + user_id_raw: String, +) -> MatrixResult { + let user_id = (*body).user_id.clone(); + if let Some(avatar_url) = data.avatar_url_get(&user_id) { + return MatrixResult(Ok(get_avatar_url::Response { + avatar_url: Some(avatar_url), + })); + } + + // 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, + })) +} + +#[get("/_matrix/client/r0/profile/", data = "")] +fn get_profile_route( + data: State, + body: Ruma, + user_id_raw: String, +) -> MatrixResult { + 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 = "")] fn set_presence_route( body: Ruma, @@ -634,6 +763,11 @@ fn main() { create_filter_route, set_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, get_keys_route, upload_keys_route,