From 062c5521f026ac98d899ded4137297476dad8988 Mon Sep 17 00:00:00 2001
From: Marcel <mtrnord1@gmail.com>
Date: Thu, 9 Apr 2020 18:49:27 +0200
Subject: [PATCH 1/5] Add displayname and avatar_url endpoints

Add PUT and GET /_matrix/client/r0/profile/{userId}/displayname Endpoint
Add PUT and GET /_matrix/client/r0/profile/{userId}/avatar_url Endpoint
Add GET /_matrix/client/r0/profile/{userId} Endpoint

Took 2 hours 16 minutes
---
 src/data.rs     |  34 ++++++++++++
 src/database.rs |  20 +++++++
 src/main.rs     | 136 +++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 189 insertions(+), 1 deletion(-)

diff --git a/src/data.rs b/src/data.rs
index dffb1e04..3ec3b44a 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<String>) {
+        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<String> {
+        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<String> {
+        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 dee2e944..a6ddd552 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 92cb6d1f..41c7e6c4 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 = "<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");
+    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/<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 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 = "<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 == "" {
+        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/<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 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/<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>")]
 fn set_presence_route(
     body: Ruma<set_presence::Request>,
@@ -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,

From b1284fd50966058f388fa672be79ba0412491eee Mon Sep 17 00:00:00 2001
From: Marcel <mtrnord1@gmail.com>
Date: Thu, 9 Apr 2020 20:47:03 +0200
Subject: [PATCH 2/5] Replace profile_* with userid_* Add a missing dot in the
 errormessage Require mxc:// to be present at the start of an avatar_url
 Update mxc check TODO Show displayname or avatar_url if either is available
 when getting the profile Endpoint Return the correct data in case of a empty
 displayname or an empty avatar_url

Took 50 minutes

Took 34 seconds
---
 src/data.rs     |  18 ++++++--
 src/database.rs |  12 ++---
 src/main.rs     | 113 ++++++++++++++++++++++++++++++------------------
 3 files changed, 91 insertions(+), 52 deletions(-)

diff --git a/src/data.rs b/src/data.rs
index 3ec3b44a..ef30e273 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -60,10 +60,15 @@ impl Data {
             .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).unwrap();
+    }
+
     /// Set a new displayname.
     pub fn displayname_set(&self, user_id: &UserId, displayname: Option<String>) {
         self.db
-            .profile_displayname
+            .userid_displayname
             .insert(user_id.to_string(), &*displayname.unwrap_or_default())
             .unwrap();
     }
@@ -71,16 +76,21 @@ impl Data {
     /// Get a the displayname of a user.
     pub fn displayname_get(&self, user_id: &UserId) -> Option<String> {
         self.db
-            .profile_displayname
+            .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_avatar_url.remove(user_id).unwrap();
+    }
+
     /// Set a new avatar_url.
     pub fn avatar_url_set(&self, user_id: &UserId, avatar_url: String) {
         self.db
-            .profile_avatar_url
+            .userid_avatar_url
             .insert(user_id.to_string(), &*avatar_url)
             .unwrap();
     }
@@ -88,7 +98,7 @@ impl Data {
     /// Get a the avatar_url of a user.
     pub fn avatar_url_get(&self, user_id: &UserId) -> Option<String> {
         self.db
-            .profile_avatar_url
+            .userid_avatar_url
             .get(user_id.to_string())
             .unwrap()
             .map(|bytes| utils::string_from_bytes(&bytes))
diff --git a/src/database.rs b/src/database.rs
index a6ddd552..bdfab5b1 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -52,8 +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 userid_displayname: sled::Tree,
+    pub userid_avatar_url: sled::Tree,
     pub deviceid_token: sled::Tree,
     pub token_userid: sled::Tree,
     pub pduid_pdus: sled::Tree,
@@ -77,8 +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(),
+            userid_displayname: db.open_tree("userid_displayname").unwrap(),
+            userid_avatar_url: db.open_tree("userid_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(),
@@ -108,7 +108,7 @@ impl Database {
             );
         }
         println!("# AccountData -> Displayname:");
-        for (k, v) in self.profile_displayname.iter().map(|r| r.unwrap()) {
+        for (k, v) in self.userid_displayname.iter().map(|r| r.unwrap()) {
             println!(
                 "{:?} -> {:?}",
                 String::from_utf8_lossy(&k),
@@ -116,7 +116,7 @@ impl Database {
             );
         }
         println!("# AccountData -> AvatarURL:");
-        for (k, v) in self.profile_avatar_url.iter().map(|r| r.unwrap()) {
+        for (k, v) in self.userid_avatar_url.iter().map(|r| r.unwrap()) {
             println!(
                 "{:?} -> {:?}",
                 String::from_utf8_lossy(&k),
diff --git a/src/main.rs b/src/main.rs
index 41c7e6c4..3bc1e4bf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,5 @@
 #![feature(proc_macro_hygiene, decl_macro)]
+
 mod data;
 mod database;
 mod pdu;
@@ -88,7 +89,7 @@ fn register_route(
             .unwrap_or_else(|| utils::random_string(GUEST_NAME_LENGTH)),
         data.hostname()
     ))
-    .try_into()
+        .try_into()
     {
         Err(_) => {
             debug!("Username invalid");
@@ -152,7 +153,7 @@ fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResult<lo
     // Validate login method
     let user_id =
         if let (login::UserInfo::MatrixId(mut username), login::LoginInfo::Password { password }) =
-            (body.user.clone(), body.login_info.clone())
+        (body.user.clone(), body.login_info.clone())
         {
             if !username.contains(':') {
                 username = format!("@{}:{}", username, data.hostname());
@@ -229,8 +230,8 @@ fn get_pushrules_all_route() -> MatrixResult<get_pushrules_all::Response> {
 }
 
 #[get(
-    "/_matrix/client/r0/user/<_user_id>/filter/<_filter_id>",
-    data = "<body>"
+"/_matrix/client/r0/user/<_user_id>/filter/<_filter_id>",
+data = "<body>"
 )]
 fn get_filter_route(
     body: Ruma<get_filter::Request>,
@@ -261,8 +262,8 @@ fn create_filter_route(
 }
 
 #[put(
-    "/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
-    data = "<body>"
+"/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
+data = "<body>"
 )]
 fn set_global_account_data_route(
     body: Ruma<set_global_account_data::Request>,
@@ -274,8 +275,8 @@ fn set_global_account_data_route(
 }
 
 #[get(
-    "/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
-    data = "<body>"
+"/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
+data = "<body>"
 )]
 fn get_global_account_data_route(
     body: Ruma<get_global_account_data::Request>,
@@ -297,25 +298,35 @@ fn set_displayname_route(
     _user_id: String,
 ) -> MatrixResult<set_display_name::Response> {
     let user_id = body.user_id.clone().expect("user is authenticated");
+
+    // Send error on None and accept Some("") as valid username
+    // 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(),
+            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
+    if let Some(displayname) = body.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>"
+"/_matrix/client/r0/profile/<user_id_raw>/displayname",
+data = "<body>"
 )]
 fn get_displayname_route(
     data: State<Data>,
@@ -323,20 +334,26 @@ fn get_displayname_route(
     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.");
+        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),
         }));
     }
 
-    // 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,
+    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>,
@@ -344,21 +361,28 @@ fn set_avatar_url_route(
     _user_id: String,
 ) -> MatrixResult<set_avatar_url::Response> {
     let user_id = body.user_id.clone().expect("user is authenticated");
-    if body.avatar_url == "" {
-        debug!("Request was missing the avatar_url payload.");
+
+    if !body.avatar_url.starts_with("mxc://") {
+        debug!("Request contains an invalid avatar_url.");
         return MatrixResult(Err(Error {
-            kind: ErrorKind::MissingParam,
+            kind: ErrorKind::InvalidParam,
             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
+    // TODO also make sure this is valid mxc:// format (not only starting with it)
 
-    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
+
+    if body.avatar_url == "" {
+        data.avatar_url_remove(&user_id);
+    } else {
+        data.avatar_url_set(&user_id, body.displayname.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))
 }
@@ -370,18 +394,23 @@ fn get_avatar_url_route(
     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.");
+        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),
         }));
     }
 
-    // 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,
+    MatrixResult(Ok(get_avatar_url::Response {
+        avatar_url: None,
     }))
 }
 
@@ -395,7 +424,7 @@ fn get_profile_route(
     let avatar_url = data.avatar_url_get(&user_id);
     let displayname = data.displayname_get(&user_id);
 
-    if avatar_url.is_some() && displayname.is_some() {
+    if avatar_url.is_some() || displayname.is_some() {
         return MatrixResult(Ok(get_profile::Response {
             avatar_url,
             displayname,
@@ -498,8 +527,8 @@ fn get_alias_route(room_alias: String) -> MatrixResult<get_alias::Response> {
             }));
         }
     }
-    .try_into()
-    .unwrap();
+        .try_into()
+        .unwrap();
 
     MatrixResult(Ok(get_alias::Response {
         room_id,
@@ -606,8 +635,8 @@ fn get_protocols_route(
 }
 
 #[put(
-    "/_matrix/client/r0/rooms/<_room_id>/send/<_event_type>/<_txn_id>",
-    data = "<body>"
+"/_matrix/client/r0/rooms/<_room_id>/send/<_event_type>/<_txn_id>",
+data = "<body>"
 )]
 fn create_message_event_route(
     data: State<Data>,
@@ -631,8 +660,8 @@ fn create_message_event_route(
 }
 
 #[put(
-    "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>/<_state_key>",
-    data = "<body>"
+"/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>/<_state_key>",
+data = "<body>"
 )]
 fn create_state_event_for_key_route(
     data: State<Data>,
@@ -654,8 +683,8 @@ fn create_state_event_for_key_route(
 }
 
 #[put(
-    "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>",
-    data = "<body>"
+"/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>",
+data = "<body>"
 )]
 fn create_state_event_for_empty_key_route(
     data: State<Data>,

From 49fe5e32710ee502bd10da843571c6e7cba2390a Mon Sep 17 00:00:00 2001
From: Marcel <mtrnord1@gmail.com>
Date: Thu, 9 Apr 2020 21:01:39 +0200
Subject: [PATCH 3/5] Rename userid_avatar_url to userid_avatarurl and fix
 debug print Run cargo fmt

Took 5 minutes
---
 src/data.rs     |  6 +++---
 src/database.rs | 10 +++++-----
 src/main.rs     | 47 ++++++++++++++++++++---------------------------
 3 files changed, 28 insertions(+), 35 deletions(-)

diff --git a/src/data.rs b/src/data.rs
index ef30e273..70cd2f88 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -84,13 +84,13 @@ impl Data {
 
     /// Removes a avatar_url.
     pub fn avatar_url_remove(&self, user_id: &UserId) {
-        self.db.userid_avatar_url.remove(user_id).unwrap();
+        self.db.userid_avatarurl.remove(user_id).unwrap();
     }
 
     /// Set a new avatar_url.
     pub fn avatar_url_set(&self, user_id: &UserId, avatar_url: String) {
         self.db
-            .userid_avatar_url
+            .userid_avatarurl
             .insert(user_id.to_string(), &*avatar_url)
             .unwrap();
     }
@@ -98,7 +98,7 @@ impl Data {
     /// Get a the avatar_url of a user.
     pub fn avatar_url_get(&self, user_id: &UserId) -> Option<String> {
         self.db
-            .userid_avatar_url
+            .userid_avatarurl
             .get(user_id.to_string())
             .unwrap()
             .map(|bytes| utils::string_from_bytes(&bytes))
diff --git a/src/database.rs b/src/database.rs
index bdfab5b1..b8a41519 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -53,7 +53,7 @@ pub struct Database {
     pub userid_password: sled::Tree,
     pub userid_deviceids: MultiValue,
     pub userid_displayname: sled::Tree,
-    pub userid_avatar_url: sled::Tree,
+    pub userid_avatarurl: sled::Tree,
     pub deviceid_token: sled::Tree,
     pub token_userid: sled::Tree,
     pub pduid_pdus: sled::Tree,
@@ -78,7 +78,7 @@ impl Database {
             userid_password: db.open_tree("userid_password").unwrap(),
             userid_deviceids: MultiValue(db.open_tree("userid_deviceids").unwrap()),
             userid_displayname: db.open_tree("userid_displayname").unwrap(),
-            userid_avatar_url: db.open_tree("userid_avatar_url").unwrap(),
+            userid_avatarurl: db.open_tree("userid_avatarurl").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(),
@@ -107,7 +107,7 @@ impl Database {
                 String::from_utf8_lossy(&v),
             );
         }
-        println!("# AccountData -> Displayname:");
+        println!("# UserId -> Displayname:");
         for (k, v) in self.userid_displayname.iter().map(|r| r.unwrap()) {
             println!(
                 "{:?} -> {:?}",
@@ -115,8 +115,8 @@ impl Database {
                 String::from_utf8_lossy(&v),
             );
         }
-        println!("# AccountData -> AvatarURL:");
-        for (k, v) in self.userid_avatar_url.iter().map(|r| r.unwrap()) {
+        println!("# UserId -> AvatarURL:");
+        for (k, v) in self.userid_avatarurl.iter().map(|r| r.unwrap()) {
             println!(
                 "{:?} -> {:?}",
                 String::from_utf8_lossy(&k),
diff --git a/src/main.rs b/src/main.rs
index 3bc1e4bf..aee2d47c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -89,7 +89,7 @@ fn register_route(
             .unwrap_or_else(|| utils::random_string(GUEST_NAME_LENGTH)),
         data.hostname()
     ))
-        .try_into()
+    .try_into()
     {
         Err(_) => {
             debug!("Username invalid");
@@ -153,7 +153,7 @@ fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResult<lo
     // Validate login method
     let user_id =
         if let (login::UserInfo::MatrixId(mut username), login::LoginInfo::Password { password }) =
-        (body.user.clone(), body.login_info.clone())
+            (body.user.clone(), body.login_info.clone())
         {
             if !username.contains(':') {
                 username = format!("@{}:{}", username, data.hostname());
@@ -230,8 +230,8 @@ fn get_pushrules_all_route() -> MatrixResult<get_pushrules_all::Response> {
 }
 
 #[get(
-"/_matrix/client/r0/user/<_user_id>/filter/<_filter_id>",
-data = "<body>"
+    "/_matrix/client/r0/user/<_user_id>/filter/<_filter_id>",
+    data = "<body>"
 )]
 fn get_filter_route(
     body: Ruma<get_filter::Request>,
@@ -262,8 +262,8 @@ fn create_filter_route(
 }
 
 #[put(
-"/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
-data = "<body>"
+    "/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
+    data = "<body>"
 )]
 fn set_global_account_data_route(
     body: Ruma<set_global_account_data::Request>,
@@ -275,8 +275,8 @@ fn set_global_account_data_route(
 }
 
 #[get(
-"/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
-data = "<body>"
+    "/_matrix/client/r0/user/<_user_id>/account_data/<_type>",
+    data = "<body>"
 )]
 fn get_global_account_data_route(
     body: Ruma<get_global_account_data::Request>,
@@ -317,7 +317,6 @@ fn set_displayname_route(
             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
-
         }
     }
 
@@ -325,8 +324,8 @@ fn set_displayname_route(
 }
 
 #[get(
-"/_matrix/client/r0/profile/<user_id_raw>/displayname",
-data = "<body>"
+    "/_matrix/client/r0/profile/<user_id_raw>/displayname",
+    data = "<body>"
 )]
 fn get_displayname_route(
     data: State<Data>,
@@ -349,9 +348,7 @@ fn get_displayname_route(
         }));
     }
 
-    MatrixResult(Ok(get_display_name::Response {
-        displayname: None,
-    }))
+    MatrixResult(Ok(get_display_name::Response { displayname: None }))
 }
 
 #[put("/_matrix/client/r0/profile/<_user_id>/avatar_url", data = "<body>")]
@@ -374,14 +371,12 @@ fn set_avatar_url_route(
     // 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.displayname.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))
@@ -409,9 +404,7 @@ fn get_avatar_url_route(
         }));
     }
 
-    MatrixResult(Ok(get_avatar_url::Response {
-        avatar_url: None,
-    }))
+    MatrixResult(Ok(get_avatar_url::Response { avatar_url: None }))
 }
 
 #[get("/_matrix/client/r0/profile/<user_id_raw>", data = "<body>")]
@@ -527,8 +520,8 @@ fn get_alias_route(room_alias: String) -> MatrixResult<get_alias::Response> {
             }));
         }
     }
-        .try_into()
-        .unwrap();
+    .try_into()
+    .unwrap();
 
     MatrixResult(Ok(get_alias::Response {
         room_id,
@@ -635,8 +628,8 @@ fn get_protocols_route(
 }
 
 #[put(
-"/_matrix/client/r0/rooms/<_room_id>/send/<_event_type>/<_txn_id>",
-data = "<body>"
+    "/_matrix/client/r0/rooms/<_room_id>/send/<_event_type>/<_txn_id>",
+    data = "<body>"
 )]
 fn create_message_event_route(
     data: State<Data>,
@@ -660,8 +653,8 @@ fn create_message_event_route(
 }
 
 #[put(
-"/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>/<_state_key>",
-data = "<body>"
+    "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>/<_state_key>",
+    data = "<body>"
 )]
 fn create_state_event_for_key_route(
     data: State<Data>,
@@ -683,8 +676,8 @@ fn create_state_event_for_key_route(
 }
 
 #[put(
-"/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>",
-data = "<body>"
+    "/_matrix/client/r0/rooms/<_room_id>/state/<_event_type>",
+    data = "<body>"
 )]
 fn create_state_event_for_empty_key_route(
     data: State<Data>,

From 8bcbc983c381d8318aeb85fabfacd29563332f14 Mon Sep 17 00:00:00 2001
From: Marcel <mtrnord1@gmail.com>
Date: Thu, 9 Apr 2020 21:11:21 +0200
Subject: [PATCH 4/5] Change and add Comments to the profile endpoints Add
 missing docs

Took 9 minutes
---
 src/main.rs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index aee2d47c..e85d90b3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -299,7 +299,7 @@ fn set_displayname_route(
 ) -> MatrixResult<set_display_name::Response> {
     let user_id = body.user_id.clone().expect("user is authenticated");
 
-    // Send error on None and accept Some("") as valid username
+    // 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.");
@@ -311,6 +311,7 @@ fn set_displayname_route(
     }
 
     if let Some(displayname) = body.displayname {
+        // Some("") will clear the displayname
         if displayname == "" {
             data.displayname_remove(&user_id);
         } else {
@@ -338,7 +339,7 @@ fn get_displayname_route(
         debug!("Profile was not found.");
         MatrixResult(Err(Error {
             kind: ErrorKind::NotFound,
-            message: "Profile was not found".to_owned(),
+            message: "Profile was not found.".to_owned(),
             status_code: http::StatusCode::NOT_FOUND,
         }))
     }
@@ -348,6 +349,7 @@ fn get_displayname_route(
         }));
     }
 
+    // The user has no displayname
     MatrixResult(Ok(get_display_name::Response { displayname: None }))
 }
 
@@ -394,7 +396,7 @@ fn get_avatar_url_route(
         debug!("Profile was not found.");
         MatrixResult(Err(Error {
             kind: ErrorKind::NotFound,
-            message: "Profile was not found".to_owned(),
+            message: "Profile was not found.".to_owned(),
             status_code: http::StatusCode::NOT_FOUND,
         }))
     }
@@ -404,6 +406,7 @@ fn get_avatar_url_route(
         }));
     }
 
+    // The user has no avatar
     MatrixResult(Ok(get_avatar_url::Response { avatar_url: None }))
 }
 
@@ -428,7 +431,7 @@ fn get_profile_route(
     debug!("Profile was not found.");
     MatrixResult(Err(Error {
         kind: ErrorKind::NotFound,
-        message: "Profile was not found".to_owned(),
+        message: "Profile was not found.".to_owned(),
         status_code: http::StatusCode::NOT_FOUND,
     }))
 }

From 57264f72e91f2fef41345e6166e9b1adebd5fc8a Mon Sep 17 00:00:00 2001
From: Marcel <mtrnord1@gmail.com>
Date: Thu, 9 Apr 2020 21:19:27 +0200
Subject: [PATCH 5/5] Fix avatar_url error message if not starting with mxc://

Took 8 minutes
---
 src/main.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index e85d90b3..17d01f51 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -365,7 +365,7 @@ fn set_avatar_url_route(
         debug!("Request contains an invalid avatar_url.");
         return MatrixResult(Err(Error {
             kind: ErrorKind::InvalidParam,
-            message: "Missing avatar_url".to_owned(),
+            message: "avatar_url has to start with mxc://.".to_owned(),
             status_code: http::StatusCode::BAD_REQUEST,
         }));
     }