From 18bf67748c14c2ded3bd7112e38317153d93a577 Mon Sep 17 00:00:00 2001
From: timokoesters <timo@koesters.xyz>
Date: Tue, 26 May 2020 10:27:51 +0200
Subject: [PATCH 1/3] feat: redaction

---
 Cargo.lock            | 126 ++++++++++++++++++------------------------
 Cargo.toml            |  11 ++--
 src/client_server.rs  |  82 ++++++++++++++++++++-------
 src/database/rooms.rs |  55 +++++++++++++++---
 src/pdu.rs            |  33 ++++++++++-
 src/server_server.rs  |   4 +-
 6 files changed, 204 insertions(+), 107 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index dc478160..fc9840f7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -30,9 +30,9 @@ version = "0.1.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -277,9 +277,9 @@ version = "0.3.0"
 source = "git+https://github.com/SergioBenitez/Devise.git?rev=e58b3ac9a#e58b3ac9afc3b6ff10a8aaf02a3e768a8f530089"
 dependencies = [
  "bitflags",
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -421,9 +421,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
 dependencies = [
  "proc-macro-hack",
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -918,9 +918,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.56"
+version = "0.9.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e"
+checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990"
 dependencies = [
  "autocfg",
  "cc",
@@ -1002,9 +1002,9 @@ version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -1045,9 +1045,9 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
 
 [[package]]
 name = "proc-macro-hack"
-version = "0.5.15"
+version = "0.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
+checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
 
 [[package]]
 name = "proc-macro-nested"
@@ -1066,9 +1066,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.13"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639"
+checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
 dependencies = [
  "unicode-xid 0.2.0",
 ]
@@ -1088,7 +1088,7 @@ version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
 ]
 
 [[package]]
@@ -1229,7 +1229,7 @@ dependencies = [
  "time",
  "tokio",
  "toml",
- "version_check 0.9.1",
+ "version_check 0.9.2",
  "yansi 0.5.0",
 ]
 
@@ -1242,7 +1242,7 @@ dependencies = [
  "indexmap",
  "quote 1.0.6",
  "rocket_http",
- "version_check 0.9.1",
+ "version_check 0.9.2",
  "yansi 0.5.0",
 ]
 
@@ -1277,7 +1277,7 @@ dependencies = [
  "percent-encoding 2.1.0",
  "ruma-api-macros",
  "ruma-identifiers",
- "ruma-serde 0.2.1",
+ "ruma-serde",
  "serde",
  "serde_json",
  "strum",
@@ -1289,16 +1289,15 @@ version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "52b82b4567b9af9b40a86f7778821c016ea961f55e4fee255f8f24bb28ee7452"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
 name = "ruma-client-api"
 version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "082913ad135ca55ee06a55d295bea954982f2ac5e0150adc09024f5cbb8cb6cf"
+source = "git+https://github.com/ruma/ruma-client-api.git#b064daf23dbf970933e83ce3b84a2563c5e646e7"
 dependencies = [
  "http",
  "js_int",
@@ -1306,7 +1305,7 @@ dependencies = [
  "ruma-common",
  "ruma-events",
  "ruma-identifiers",
- "ruma-serde 0.2.1",
+ "ruma-serde",
  "serde",
  "serde_json",
  "strum",
@@ -1319,7 +1318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "253416d67b4bde281f2781424232a58a946a4f1c451d5f857a8d0705d58eaf2a"
 dependencies = [
  "matches",
- "ruma-serde 0.2.1",
+ "ruma-serde",
  "serde",
  "serde_json",
 ]
@@ -1327,14 +1326,13 @@ dependencies = [
 [[package]]
 name = "ruma-events"
 version = "0.21.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0afc6d4da07d10213478d32dc42b6222df0a1ea319e9ced9f8a341617952d909"
+source = "git+https://github.com/ruma/ruma-events.git#a17714669da1db4aa7bf10948463bb964cf5058a"
 dependencies = [
  "js_int",
  "ruma-common",
  "ruma-events-macros",
  "ruma-identifiers",
- "ruma-serde 0.2.1",
+ "ruma-serde",
  "serde",
  "serde_json",
 ]
@@ -1342,24 +1340,24 @@ dependencies = [
 [[package]]
 name = "ruma-events-macros"
 version = "0.21.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc706c4a53cc54c3a198cfbcd7dfff20448599d84f90e636d96034d0df5a9ac9"
+source = "git+https://github.com/ruma/ruma-events.git#a17714669da1db4aa7bf10948463bb964cf5058a"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
 name = "ruma-federation-api"
 version = "0.0.1"
-source = "git+https://github.com/ruma/ruma-federation-api.git?rev=ccbf216f39bbbaa59131cc200eae5bd18aa1947c#ccbf216f39bbbaa59131cc200eae5bd18aa1947c"
+source = "git+https://github.com/ruma/ruma-federation-api.git?rev=4cf4aa6ef74b25ad8c14d99d7774129f023df163#4cf4aa6ef74b25ad8c14d99d7774129f023df163"
 dependencies = [
  "js_int",
+ "matches",
  "ruma-api",
  "ruma-events",
  "ruma-identifiers",
- "ruma-serde 0.1.3",
+ "ruma-serde",
  "serde",
  "serde_json",
 ]
@@ -1376,23 +1374,9 @@ dependencies = [
 
 [[package]]
 name = "ruma-serde"
-version = "0.1.3"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14edc0e2f5177c419e3b89060b1e94fb3af81b2f253783ac6967f14a7ec3911"
-dependencies = [
- "dtoa",
- "itoa",
- "js_int",
- "serde",
- "serde_json",
- "url",
-]
-
-[[package]]
-name = "ruma-serde"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dd3d04c6755bae23101dec7426d044b773ef517932f23d5a6254c4caa1cfce3"
+checksum = "8c71dabb8e2709ca4f59201cb72d7fe8d590e7e3f55feb348e851c18354938af"
 dependencies = [
  "dtoa",
  "itoa",
@@ -1520,9 +1504,9 @@ version = "1.0.110"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -1614,9 +1598,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c"
 dependencies = [
  "heck",
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -1632,11 +1616,11 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.23"
+version = "1.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
+checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
  "unicode-xid 0.2.0",
 ]
@@ -1657,22 +1641,22 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.18"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5976891d6950b4f68477850b5b9e5aa64d955961466f9e174363f573e54e8ca7"
+checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.18"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab81dbd1cd69cd2ce22ecfbdd3bdb73334ba25350649408cc6c085f46d89573d"
+checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -1714,9 +1698,9 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
 ]
 
 [[package]]
@@ -1782,7 +1766,7 @@ version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
 dependencies = [
- "version_check 0.9.1",
+ "version_check 0.9.2",
 ]
 
 [[package]]
@@ -1852,9 +1836,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
 
 [[package]]
 name = "version_check"
-version = "0.9.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
 
 [[package]]
 name = "want"
@@ -1893,9 +1877,9 @@ dependencies = [
  "bumpalo",
  "lazy_static",
  "log",
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
  "wasm-bindgen-shared",
 ]
 
@@ -1927,9 +1911,9 @@ version = "0.2.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
 dependencies = [
- "proc-macro2 1.0.13",
+ "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.23",
+ "syn 1.0.25",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 5a068f59..c864508e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,12 +14,12 @@ edition = "2018"
 [dependencies]
 rocket = { git = "https://github.com/SergioBenitez/Rocket.git", branch = "async", features = ["tls"] }
 http = "0.2.1"
-ruma-client-api = "0.9.0"
+ruma-client-api = { git = "https://github.com/ruma/ruma-client-api.git" }
 ruma-identifiers = { version = "0.16.1", features = ["rand"] }
 ruma-api = "0.16.1"
-ruma-events = "0.21.2"
+ruma-events = { git = "https://github.com/ruma/ruma-events.git" }
 ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git", rev = "1ca545cba8dfd43e0fc8e3c18e1311fb73390a97" }
-ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git", rev = "ccbf216f39bbbaa59131cc200eae5bd18aa1947c" }
+ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git", rev = "4cf4aa6ef74b25ad8c14d99d7774129f023df163" }
 log = "0.4.8"
 sled = "0.31.0"
 directories = "2.0.2"
@@ -31,6 +31,9 @@ rand = "0.7.3"
 rust-argon2 = "0.8.2"
 reqwest = "0.10.4"
 base64 = "0.12.1"
-thiserror = "1.0.18"
+thiserror = "1.0.19"
 ruma-common = "0.1.2"
 image = { version = "0.23.4", default-features = false, features = ["jpeg", "png", "gif"] }
+
+[patch.crates-io]
+ruma-events = { git = "https://github.com/ruma/ruma-events.git" }
diff --git a/src/client_server.rs b/src/client_server.rs
index 6812fdde..0701be1a 100644
--- a/src/client_server.rs
+++ b/src/client_server.rs
@@ -39,13 +39,13 @@ use ruma_client_api::{
         to_device::{self, send_event_to_device},
         typing::create_typing_event,
         uiaa::{AuthFlow, UiaaInfo, UiaaResponse},
-        user_directory::search_users,
+        user_directory::search_users, redact::redact_event,
     },
     unversioned::get_supported_versions,
 };
 use ruma_events::{
     collections::only::Event as EduEvent,
-    room::{guest_access, history_visibility, join_rules, member},
+    room::{guest_access, history_visibility, join_rules, member, redaction},
     EventJson, EventType,
 };
 use ruma_identifiers::{RoomId, RoomVersionId, UserId};
@@ -517,6 +517,7 @@ pub fn set_displayname_route(
                 .unwrap(),
                 None,
                 Some(user_id.to_string()),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -564,7 +565,7 @@ pub fn set_avatar_url_route(
 ) -> MatrixResult<set_avatar_url::Response> {
     let user_id = body.user_id.as_ref().expect("user is authenticated");
 
-    if let avatar_url = &body.avatar_url {
+    if let Some(avatar_url) = &body.avatar_url {
         if !avatar_url.starts_with("mxc://") {
             debug!("Request contains an invalid avatar_url.");
             return MatrixResult(Err(Error {
@@ -579,7 +580,7 @@ pub fn set_avatar_url_route(
     }
 
     db.users
-        .set_avatar_url(&user_id, Some(body.avatar_url.clone()))
+        .set_avatar_url(&user_id, body.avatar_url.clone())
         .unwrap();
 
     // Send a new membership event into all joined rooms
@@ -591,7 +592,7 @@ pub fn set_avatar_url_route(
                 user_id.clone(),
                 EventType::RoomMember,
                 serde_json::to_value(ruma_events::room::member::MemberEventContent {
-                    avatar_url: Some(body.avatar_url.clone()),
+                    avatar_url: body.avatar_url.clone(),
                     ..serde_json::from_value::<EventJson<_>>(
                         db.rooms
                             .room_state(&room_id)
@@ -608,6 +609,7 @@ pub fn set_avatar_url_route(
                 .unwrap(),
                 None,
                 Some(user_id.to_string()),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -917,14 +919,15 @@ pub fn create_room_route(
                 creator: user_id.clone(),
                 federate: body
                     .creation_content
-                    .and_then(|c| c.federate)
-                    .unwrap_or(true),
-                predecessor: None, // TODO: Check creation_content.predecessor once ruma has that
+                    .as_ref()
+                    .map_or(true, |c| c.federate),
+                predecessor: body.creation_content.as_ref().and_then(|c| c.predecessor.clone()),
                 room_version: RoomVersionId::version_5(),
             })
             .unwrap(),
             None,
             Some("".to_owned()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -945,6 +948,7 @@ pub fn create_room_route(
             .unwrap(),
             None,
             Some(user_id.to_string()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -968,18 +972,8 @@ pub fn create_room_route(
             .expect("TODO: handle. we hope the client sends a valid power levels json")
     } else {
         serde_json::to_value(ruma_events::room::power_levels::PowerLevelsEventContent {
-            ban: 50.into(),
-            events: BTreeMap::new(),
-            events_default: 0.into(),
-            invite: 50.into(),
-            kick: 50.into(),
-            redact: 50.into(),
-            state_default: 50.into(),
             users,
-            users_default: 0.into(),
-            notifications: ruma_events::room::power_levels::NotificationPowerLevels {
-                room: 50.into(),
-            },
+            ..Default::default()
         })
         .unwrap()
     };
@@ -991,6 +985,7 @@ pub fn create_room_route(
             power_levels_content,
             None,
             Some("".to_owned()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -1016,6 +1011,7 @@ pub fn create_room_route(
             },
             None,
             Some("".to_owned()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -1032,6 +1028,7 @@ pub fn create_room_route(
             .unwrap(),
             None,
             Some("".to_owned()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -1056,6 +1053,7 @@ pub fn create_room_route(
             },
             None,
             Some("".to_owned()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -1071,10 +1069,11 @@ pub fn create_room_route(
             .append_pdu(
                 room_id.clone(),
                 user_id.clone(),
-                EventType::from(event_type),
+                event_type.clone(),
                 serde_json::from_str(content.get()).unwrap(),
                 None,
                 state_key.clone(),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -1093,6 +1092,7 @@ pub fn create_room_route(
                 .unwrap(),
                 None,
                 Some("".to_owned()),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -1110,6 +1110,7 @@ pub fn create_room_route(
                 .unwrap(),
                 None,
                 Some("".to_owned()),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -1132,6 +1133,7 @@ pub fn create_room_route(
                 .unwrap(),
                 None,
                 Some(user.to_string()),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -1140,6 +1142,38 @@ pub fn create_room_route(
     MatrixResult(Ok(create_room::Response { room_id }))
 }
 
+#[put("/_matrix/client/r0/rooms/<_room_id>/redact/<_event_id>/<_txn_id>", data = "<body>")]
+pub fn redact_event_route(
+    db: State<'_, Database>,
+    body: Ruma<redact_event::Request>,
+    _room_id: String,
+    _event_id: String,
+    _txn_id: String,
+) -> MatrixResult<redact_event::Response> {
+    let user_id = body.user_id.as_ref().expect("user is authenticated");
+
+    if let Ok(event_id) = db.rooms.append_pdu(
+        body.room_id.clone(),
+        user_id.clone(),
+        EventType::RoomRedaction,
+        serde_json::to_value(redaction::RedactionEventContent {
+            reason: body.reason.clone(),
+        }).unwrap(),
+        None,
+        None,
+        Some(body.event_id.clone()),
+        &db.globals,
+    ) {
+        MatrixResult(Ok(redact_event::Response { event_id }))
+    } else {
+        MatrixResult(Err(Error {
+            kind: ErrorKind::Unknown,
+            message: "Failed to redact event.".to_owned(),
+            status_code: http::StatusCode::BAD_REQUEST,
+        }))
+    }
+}
+
 #[get("/_matrix/client/r0/directory/room/<_room_alias>", data = "<body>")]
 pub fn get_alias_route(
     db: State<'_, Database>,
@@ -1220,6 +1254,7 @@ pub fn join_room_by_id_route(
             serde_json::to_value(event).unwrap(),
             None,
             Some(user_id.to_string()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -1285,6 +1320,7 @@ pub fn leave_room_route(
             json!({"membership": "leave"}),
             None,
             Some(user_id.to_string()),
+            None,
             &db.globals,
         )
         .unwrap();
@@ -1328,6 +1364,7 @@ pub fn invite_user_route(
                 serde_json::to_value(event).unwrap(),
                 None,
                 Some(user_id.to_string()),
+                None,
                 &db.globals,
             )
             .unwrap();
@@ -1414,7 +1451,7 @@ pub async fn get_public_rooms_filtered_route(
                         .deserialize()
                         .unwrap()
                         .alias
-                }).map(|a| a.to_string()),
+                }),
                 name: state.get(&(EventType::RoomName, "".to_owned())).map(|s| {
                     serde_json::from_value::<EventJson<ruma_events::room::name::NameEventContent>>(
                         s.content.clone(),
@@ -1565,6 +1602,7 @@ pub fn create_message_event_route(
         serde_json::from_str(body.json_body.unwrap().get()).unwrap(),
         Some(unsigned),
         None,
+        None,
         &db.globals,
     ) {
         MatrixResult(Ok(create_message_event::Response { event_id }))
@@ -1598,6 +1636,7 @@ pub fn create_state_event_for_key_route(
         serde_json::from_str(body.json_body.clone().unwrap().get()).unwrap(),
         None,
         Some(body.state_key.clone()),
+        None,
         &db.globals,
     ) {
         MatrixResult(Ok(create_state_event_for_key::Response { event_id }))
@@ -1630,6 +1669,7 @@ pub fn create_state_event_for_empty_key_route(
         serde_json::from_str(body.json_body.unwrap().get()).unwrap(),
         None,
         Some("".to_owned()),
+        None,
         &db.globals,
     ) {
         MatrixResult(Ok(create_state_event_for_empty_key::Response { event_id }))
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 87f3429e..8e626cb0 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -12,7 +12,7 @@ use ruma_events::{
     EventJson, EventType,
 };
 use ruma_identifiers::{EventId, RoomId, UserId};
-
+use sled::IVec;
 use std::{
     collections::{BTreeMap, HashMap},
     convert::{TryFrom, TryInto},
@@ -110,28 +110,50 @@ impl Rooms {
         self.eventid_pduid
             .get(event_id.to_string().as_bytes())?
             .map_or(Ok(None), |pdu_id| {
-                Ok(serde_json::from_slice(
+                Ok(Some(serde_json::from_slice(
                     &self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
                         "eventid_pduid points to nonexistent pdu",
                     ))?,
-                )?)
-                .map(Some)
+                )?))
             })
     }
 
+    /// Returns the pdu's id.
+    pub fn get_pdu_id(&self, event_id: &EventId) -> Result<Option<IVec>> {
+        self.eventid_pduid
+            .get(event_id.to_string().as_bytes())?
+            .map_or(Ok(None), |pdu_id| Ok(Some(pdu_id)))
+    }
+
     /// Returns the pdu.
     pub fn get_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>> {
         self.eventid_pduid
             .get(event_id.to_string().as_bytes())?
             .map_or(Ok(None), |pdu_id| {
-                Ok(serde_json::from_slice(
+                Ok(Some(serde_json::from_slice(
                     &self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
                         "eventid_pduid points to nonexistent pdu",
                     ))?,
-                )?)
-                .map(Some)
+                )?))
             })
     }
+    /// Returns the pdu.
+    pub fn get_pdu_from_id(&self, pdu_id: &IVec) -> Result<Option<PduEvent>> {
+        self.pduid_pdu
+            .get(pdu_id)?
+            .map_or(Ok(None), |pdu| Ok(Some(serde_json::from_slice(&pdu)?)))
+    }
+
+    /// Returns the pdu.
+    pub fn replace_pdu(&self, pdu_id: &IVec, pdu: &PduEvent) -> Result<()> {
+        if self.pduid_pdu.get(&pdu_id)?.is_some() {
+            self.pduid_pdu
+                .insert(&pdu_id, &*serde_json::to_string(pdu)?)?;
+            Ok(())
+        } else {
+            Err(Error::BadRequest("pdu does not exist"))
+        }
+    }
 
     /// Returns the leaf pdus of a room.
     pub fn get_pdu_leaves(&self, room_id: &RoomId) -> Result<Vec<EventId>> {
@@ -177,6 +199,7 @@ impl Rooms {
         content: serde_json::Value,
         unsigned: Option<serde_json::Map<String, serde_json::Value>>,
         state_key: Option<String>,
+        redacts: Option<EventId>,
         globals: &super::globals::Globals,
     ) -> Result<EventId> {
         // TODO: Make sure this isn't called twice in parallel
@@ -435,7 +458,7 @@ impl Rooms {
                 .try_into()
                 .expect("depth can overflow and should be deprecated..."),
             auth_events: Vec::new(),
-            redacts: None,
+            redacts,
             unsigned,
             hashes: ruma_federation_api::EventHash {
                 sha256: "aaa".to_owned(),
@@ -555,7 +578,21 @@ impl Rooms {
             .map(|(_, v)| Ok(serde_json::from_slice(&v)?))
     }
 
-    /// Makes a user join a room. Only call this if the membership is Join already
+    /// Replace a PDU with the redacted form.
+    pub fn redact_pdu(&self, event_id: &EventId) -> Result<()> {
+        if let Some(pdu_id) = self.get_pdu_id(event_id)? {
+            let mut pdu = self
+                .get_pdu_from_id(&pdu_id)?
+                .ok_or(Error::BadDatabase("pduid points to invalid pdu"))?;
+            pdu.redact();
+            self.replace_pdu(&pdu_id, &pdu)?;
+            Ok(())
+        } else {
+            Err(Error::BadRequest("eventid does not exist"))
+        }
+    }
+
+    /// Update current membership data.
     fn update_membership(
         &self,
         room_id: &RoomId,
diff --git a/src/pdu.rs b/src/pdu.rs
index 0e1b3de6..3e1ac0ac 100644
--- a/src/pdu.rs
+++ b/src/pdu.rs
@@ -33,6 +33,38 @@ pub struct PduEvent {
 }
 
 impl PduEvent {
+    pub fn redact(&mut self) {
+        self.unsigned.clear();
+        let allowed = match self.kind {
+            EventType::RoomMember => vec!["membership"],
+            EventType::RoomCreate => vec!["creator"],
+            EventType::RoomJoinRules => vec!["join_rule"],
+            EventType::RoomPowerLevels => vec![
+                "ban",
+                "events",
+                "events_default",
+                "kick",
+                "redact",
+                "state_default",
+                "users",
+                "users_default",
+            ],
+            EventType::RoomHistoryVisibility => vec!["history_visibility"],
+            _ => vec![],
+        };
+
+        let old_content = self.content.as_object_mut().unwrap(); // TODO error
+        let mut new_content = serde_json::Map::new();
+
+        for key in allowed {
+            if let Some(value) = old_content.remove(key) {
+                new_content.insert(key.to_owned(), value);
+            }
+        }
+
+        self.content = new_content.into();
+    }
+
     pub fn to_room_event(&self) -> EventJson<RoomEvent> {
         // Can only fail in rare circumstances that won't ever happen here, see
         // https://docs.rs/serde_json/1.0.50/serde_json/fn.to_string.html
@@ -40,7 +72,6 @@ impl PduEvent {
         // EventJson's deserialize implementation always returns `Ok(...)`
         serde_json::from_str::<EventJson<RoomEvent>>(&json).unwrap()
     }
-
     pub fn to_state_event(&self) -> EventJson<StateEvent> {
         let json = serde_json::to_string(&self).unwrap();
         serde_json::from_str::<EventJson<StateEvent>>(&json).unwrap()
diff --git a/src/server_server.rs b/src/server_server.rs
index 84ca5cc3..6aa1e99b 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -4,7 +4,9 @@ use log::error;
 use rocket::{get, response::content::Json, State};
 use ruma_api::Endpoint;
 use ruma_client_api::error::Error;
-use ruma_federation_api::{v1::get_server_version, v2::get_server_keys};
+use ruma_federation_api::discovery::{
+    get_server_keys::v2 as get_server_keys, get_server_version::v1 as get_server_version,
+};
 use serde_json::json;
 use std::{
     collections::BTreeMap,

From 9c26e22ad7d1d821feba6253703c074bcb3c1bf6 Mon Sep 17 00:00:00 2001
From: timokoesters <timo@koesters.xyz>
Date: Sun, 24 May 2020 08:30:57 +0200
Subject: [PATCH 2/3] improvement: load aliases from database

---
 src/client_server.rs  | 12 ++++++------
 src/database/rooms.rs | 12 ++++++++++++
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/src/client_server.rs b/src/client_server.rs
index 0701be1a..36cdb38b 100644
--- a/src/client_server.rs
+++ b/src/client_server.rs
@@ -1272,17 +1272,17 @@ pub fn join_room_by_id_or_alias_route(
 ) -> MatrixResult<join_room_by_id_or_alias::Response> {
     let room_id = match RoomId::try_from(body.room_id_or_alias.clone()) {
         Ok(room_id) => room_id,
-        Err(room_alias) => {
-            if room_alias.server_name() == db.globals.server_name() {
+        Err(_) => {
+            if let Some(room_id) = db.rooms.id_from_alias(body.room_id_or_alias.as_ref()).unwrap() {
+                room_id
+            } else {
+                // Ask creator server of the room to join TODO ask someone else when not available
+                //server_server::send_request(data, destination, request)
                 return MatrixResult(Err(Error {
                     kind: ErrorKind::NotFound,
                     message: "Room alias not found.".to_owned(),
                     status_code: http::StatusCode::BAD_REQUEST,
                 }));
-            } else {
-                // Ask creator server of the room to join TODO ask someone else when not available
-                //server_server::send_request(data, destination, request)
-                todo!();
             }
         }
     };
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 8e626cb0..8969da11 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -26,6 +26,8 @@ pub struct Rooms {
     pub(super) roomid_pduleaves: sled::Tree,
     pub(super) roomstateid_pdu: sled::Tree, // RoomStateId = Room + StateType + StateKey
 
+    pub(super) alias_roomid: sled::Tree,
+
     pub(super) userroomid_joined: sled::Tree,
     pub(super) roomuserid_joined: sled::Tree,
     pub(super) userroomid_invited: sled::Tree,
@@ -646,6 +648,16 @@ impl Rooms {
         Ok(())
     }
 
+    pub fn id_from_alias(&self, alias: &str) -> Result<Option<RoomId>> {
+        if !alias.starts_with('#') {
+            return Err(Error::BadRequest("room alias does not start with #"));
+        }
+
+        self.alias_roomid.get(alias)?.map_or(Ok(None), |bytes| {
+            Ok(Some(RoomId::try_from(utils::string_from_bytes(&bytes)?)?))
+        })
+    }
+
     /// Returns an iterator over all rooms a user joined.
     pub fn room_members(&self, room_id: &RoomId) -> impl Iterator<Item = Result<UserId>> {
         self.roomuserid_joined

From 4e507ef70624fcaf3386bf3ee71c9bfb65801754 Mon Sep 17 00:00:00 2001
From: timokoesters <timo@koesters.xyz>
Date: Mon, 25 May 2020 23:24:13 +0200
Subject: [PATCH 3/3] feat: room visibility and aliases

---
 Cargo.lock            | 109 +++++++-------
 Cargo.toml            |  10 +-
 README.md             |   2 +-
 Rocket-example.toml   |   3 +
 src/client_server.rs  | 321 ++++++++++++++++++++++++++++++++----------
 src/database.rs       |   4 +
 src/database/rooms.rs |  98 +++++++++----
 src/database/users.rs |   5 +-
 src/main.rs           |   5 +
 9 files changed, 399 insertions(+), 158 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index fc9840f7..6b41ec36 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -32,7 +32,7 @@ checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -279,7 +279,7 @@ dependencies = [
  "bitflags",
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -423,7 +423,7 @@ dependencies = [
  "proc-macro-hack",
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -557,9 +557,9 @@ checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
 
 [[package]]
 name = "hyper"
-version = "0.13.5"
+version = "0.13.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14"
+checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -571,8 +571,8 @@ dependencies = [
  "httparse",
  "itoa",
  "log",
- "net2",
  "pin-project",
+ "socket2",
  "time",
  "tokio",
  "tower-service",
@@ -663,9 +663,9 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.39"
+version = "0.3.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
+checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -697,9 +697,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
+checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
 
 [[package]]
 name = "lock_api"
@@ -1004,7 +1004,7 @@ checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -1195,9 +1195,9 @@ dependencies = [
 
 [[package]]
 name = "ring"
-version = "0.16.13"
+version = "0.16.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196"
+checksum = "06b3fefa4f12272808f809a0af618501fdaba41a58963c5fb72238ab0be09603"
 dependencies = [
  "cc",
  "libc",
@@ -1291,13 +1291,13 @@ checksum = "52b82b4567b9af9b40a86f7778821c016ea961f55e4fee255f8f24bb28ee7452"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
 name = "ruma-client-api"
 version = "0.9.0"
-source = "git+https://github.com/ruma/ruma-client-api.git#b064daf23dbf970933e83ce3b84a2563c5e646e7"
+source = "git+https://github.com/ruma/ruma-client-api.git?rev=c725288cd099690c1d13f1a9b9e57228bc860a62#c725288cd099690c1d13f1a9b9e57228bc860a62"
 dependencies = [
  "http",
  "js_int",
@@ -1325,8 +1325,8 @@ dependencies = [
 
 [[package]]
 name = "ruma-events"
-version = "0.21.2"
-source = "git+https://github.com/ruma/ruma-events.git#a17714669da1db4aa7bf10948463bb964cf5058a"
+version = "0.21.3"
+source = "git+https://github.com/ruma/ruma-events.git?rev=4d09416cd1663d63c22153705c9e1fd77910797f#4d09416cd1663d63c22153705c9e1fd77910797f"
 dependencies = [
  "js_int",
  "ruma-common",
@@ -1335,16 +1335,17 @@ dependencies = [
  "ruma-serde",
  "serde",
  "serde_json",
+ "strum",
 ]
 
 [[package]]
 name = "ruma-events-macros"
-version = "0.21.2"
-source = "git+https://github.com/ruma/ruma-events.git#a17714669da1db4aa7bf10948463bb964cf5058a"
+version = "0.21.3"
+source = "git+https://github.com/ruma/ruma-events.git?rev=4d09416cd1663d63c22153705c9e1fd77910797f#4d09416cd1663d63c22153705c9e1fd77910797f"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -1491,22 +1492,22 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.110"
+version = "1.0.111"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"
+checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.110"
+version = "1.0.111"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
+checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -1570,6 +1571,18 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
 
+[[package]]
+name = "socket2"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "winapi 0.3.8",
+]
+
 [[package]]
 name = "spin"
 version = "0.5.2"
@@ -1600,7 +1613,7 @@ dependencies = [
  "heck",
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -1616,9 +1629,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.25"
+version = "1.0.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f"
+checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
@@ -1656,7 +1669,7 @@ checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -1700,7 +1713,7 @@ checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
 ]
 
 [[package]]
@@ -1858,9 +1871,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.62"
+version = "0.2.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
+checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0"
 dependencies = [
  "cfg-if",
  "serde",
@@ -1870,24 +1883,24 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.62"
+version = "0.2.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
+checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101"
 dependencies = [
  "bumpalo",
  "lazy_static",
  "log",
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04"
+checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -1897,9 +1910,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.62"
+version = "0.2.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
+checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3"
 dependencies = [
  "quote 1.0.6",
  "wasm-bindgen-macro-support",
@@ -1907,28 +1920,28 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.62"
+version = "0.2.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
+checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92"
 dependencies = [
  "proc-macro2 1.0.17",
  "quote 1.0.6",
- "syn 1.0.25",
+ "syn 1.0.29",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.62"
+version = "0.2.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"
+checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd"
 
 [[package]]
 name = "web-sys"
-version = "0.3.39"
+version = "0.3.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
+checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -1936,9 +1949,9 @@ dependencies = [
 
 [[package]]
 name = "webpki"
-version = "0.21.2"
+version = "0.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef"
+checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
 dependencies = [
  "ring",
  "untrusted",
diff --git a/Cargo.toml b/Cargo.toml
index c864508e..198521e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,10 +14,10 @@ edition = "2018"
 [dependencies]
 rocket = { git = "https://github.com/SergioBenitez/Rocket.git", branch = "async", features = ["tls"] }
 http = "0.2.1"
-ruma-client-api = { git = "https://github.com/ruma/ruma-client-api.git" }
+ruma-client-api = { git = "https://github.com/ruma/ruma-client-api.git", rev = "c725288cd099690c1d13f1a9b9e57228bc860a62" }
 ruma-identifiers = { version = "0.16.1", features = ["rand"] }
 ruma-api = "0.16.1"
-ruma-events = { git = "https://github.com/ruma/ruma-events.git" }
+ruma-events = { git = "https://github.com/ruma/ruma-events.git", rev = "4d09416cd1663d63c22153705c9e1fd77910797f" }
 ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git", rev = "1ca545cba8dfd43e0fc8e3c18e1311fb73390a97" }
 ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git", rev = "4cf4aa6ef74b25ad8c14d99d7774129f023df163" }
 log = "0.4.8"
@@ -25,15 +25,15 @@ sled = "0.31.0"
 directories = "2.0.2"
 js_int = "0.1.5"
 serde_json = { version = "1.0.53", features = ["raw_value"] }
-serde = "1.0.110"
+serde = "1.0.111"
 tokio = { version = "0.2.21", features = ["macros"] }
 rand = "0.7.3"
 rust-argon2 = "0.8.2"
-reqwest = "0.10.4"
+reqwest = "=0.10.4"
 base64 = "0.12.1"
 thiserror = "1.0.19"
 ruma-common = "0.1.2"
 image = { version = "0.23.4", default-features = false, features = ["jpeg", "png", "gif"] }
 
 [patch.crates-io]
-ruma-events = { git = "https://github.com/ruma/ruma-events.git" }
+ruma-events = { git = "https://github.com/ruma/ruma-events.git", rev = "4d09416cd1663d63c22153705c9e1fd77910797f" }
diff --git a/README.md b/README.md
index 94061a87..772fb96c 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ Also check out the [milestones](https://git.koesters.xyz/timo/conduit/milestones
 
 #### How can I contribute?
 
-If you want to help, you may be able to find something in the issue tracker. If you do, comment on the issue, so others know. You can also join #conduit:matrix.org and ask there. 
+If you want to help, you may be able to find something in the issue tracker. If you do, comment on the issue, so others know. You can also join #conduit:matrix.org and ask there.
 
 #### Donate
 
diff --git a/Rocket-example.toml b/Rocket-example.toml
index fb9f6d65..924b540e 100644
--- a/Rocket-example.toml
+++ b/Rocket-example.toml
@@ -3,6 +3,9 @@ server_name = "your.server.name"
 port = 8448
 address = "0.0.0.0"
 
+# Default path is in this user's data
+#database_path = "/home/timo/MyConduitServer"
+
 #[global.tls]
 #certs = "/etc/letsencrypt/live/your.server.name/fullchain.pem"
 #key = "/etc/letsencrypt/live/your.server.name/privkey.pem"
diff --git a/src/client_server.rs b/src/client_server.rs
index 36cdb38b..150555c7 100644
--- a/src/client_server.rs
+++ b/src/client_server.rs
@@ -5,15 +5,18 @@ use std::{
 };
 
 use log::{debug, warn};
-use rocket::{get, options, post, put, State};
+use rocket::{delete, get, options, post, put, State};
 use ruma_client_api::{
     error::{Error, ErrorKind},
     r0::{
         account::{get_username_availability, register},
-        alias::get_alias,
+        alias::{create_alias, delete_alias, get_alias},
         capabilities::get_capabilities,
         config::{get_global_account_data, set_global_account_data},
-        directory::{self, get_public_rooms, get_public_rooms_filtered},
+        directory::{
+            self, get_public_rooms, get_public_rooms_filtered, get_room_visibility,
+            set_room_visibility,
+        },
         filter::{self, create_filter, get_filter},
         keys::{claim_keys, get_keys, upload_keys},
         media::{create_content, get_content, get_content_thumbnail, get_media_config},
@@ -28,6 +31,7 @@ use ruma_client_api::{
         },
         push::{get_pushrules_all, set_pushrule, set_pushrule_enabled},
         read_marker::set_read_marker,
+        redact::redact_event,
         room::{self, create_room},
         session::{get_login_types, login, logout},
         state::{
@@ -39,16 +43,16 @@ use ruma_client_api::{
         to_device::{self, send_event_to_device},
         typing::create_typing_event,
         uiaa::{AuthFlow, UiaaInfo, UiaaResponse},
-        user_directory::search_users, redact::redact_event,
+        user_directory::search_users,
     },
     unversioned::get_supported_versions,
 };
 use ruma_events::{
     collections::only::Event as EduEvent,
-    room::{guest_access, history_visibility, join_rules, member, redaction},
+    room::{canonical_alias, guest_access, history_visibility, join_rules, member, redaction},
     EventJson, EventType,
 };
-use ruma_identifiers::{RoomId, RoomVersionId, UserId};
+use ruma_identifiers::{RoomAliasId, RoomId, RoomVersionId, UserId};
 use serde_json::{json, value::RawValue};
 
 use crate::{server_server, utils, Database, MatrixResult, Ruma};
@@ -671,7 +675,7 @@ pub fn get_profile_route(
     MatrixResult(Err(Error {
         kind: ErrorKind::NotFound,
         message: "Profile was not found.".to_owned(),
-        status_code: http::StatusCode::BAD_REQUEST,
+        status_code: http::StatusCode::NOT_FOUND,
     }))
 }
 
@@ -908,8 +912,32 @@ pub fn create_room_route(
     let room_id = RoomId::new(db.globals.server_name()).expect("host is valid");
     let user_id = body.user_id.as_ref().expect("user is authenticated");
 
-    // TODO: Create alias and check if it already exists
+    let alias = if let Some(localpart) = &body.room_alias_name {
+        // TODO: Check for invalid characters and maximum length
+        if let Ok(alias) =
+            RoomAliasId::try_from(format!("#{}:{}", localpart, db.globals.server_name()))
+        {
+            if db.rooms.id_from_alias(&alias).unwrap().is_some() {
+                return MatrixResult(Err(Error {
+                    kind: ErrorKind::Unknown,
+                    message: "Alias already exists.".to_owned(),
+                    status_code: http::StatusCode::BAD_REQUEST,
+                }));
+            }
 
+            Some(alias)
+        } else {
+            return MatrixResult(Err(Error {
+                kind: ErrorKind::Unknown,
+                message: "Invalid alias.".to_owned(),
+                status_code: http::StatusCode::BAD_REQUEST,
+            }));
+        }
+    } else {
+        None
+    };
+
+    // 1. The room create event
     db.rooms
         .append_pdu(
             room_id.clone(),
@@ -917,11 +945,11 @@ pub fn create_room_route(
             EventType::RoomCreate,
             serde_json::to_value(ruma_events::room::create::CreateEventContent {
                 creator: user_id.clone(),
-                federate: body
+                federate: body.creation_content.as_ref().map_or(true, |c| c.federate),
+                predecessor: body
                     .creation_content
                     .as_ref()
-                    .map_or(true, |c| c.federate),
-                predecessor: body.creation_content.as_ref().and_then(|c| c.predecessor.clone()),
+                    .and_then(|c| c.predecessor.clone()),
                 room_version: RoomVersionId::version_5(),
             })
             .unwrap(),
@@ -932,7 +960,7 @@ pub fn create_room_route(
         )
         .unwrap();
 
-    // Join room
+    // 2. Let the room creator join
     db.rooms
         .append_pdu(
             room_id.clone(),
@@ -960,7 +988,7 @@ pub fn create_room_route(
         room::Visibility::Public => create_room::RoomPreset::PublicChat,
     });
 
-    // 0. Power levels
+    // 3. Power levels
     let mut users = BTreeMap::new();
     users.insert(user_id.clone(), 100.into());
     for invite_user_id in &body.invite {
@@ -972,8 +1000,18 @@ pub fn create_room_route(
             .expect("TODO: handle. we hope the client sends a valid power levels json")
     } else {
         serde_json::to_value(ruma_events::room::power_levels::PowerLevelsEventContent {
+            ban: 50.into(),
+            events: BTreeMap::new(),
+            events_default: 0.into(),
+            invite: 50.into(),
+            kick: 50.into(),
+            redact: 50.into(),
+            state_default: 50.into(),
             users,
-            ..Default::default()
+            users_default: 0.into(),
+            notifications: ruma_events::room::power_levels::NotificationPowerLevels {
+                room: 50.into(),
+            },
         })
         .unwrap()
     };
@@ -990,8 +1028,8 @@ pub fn create_room_route(
         )
         .unwrap();
 
-    // 1. Events set by preset
-    // 1.1 Join Rules
+    // 4. Events set by preset
+    // 4.1 Join Rules
     db.rooms
         .append_pdu(
             room_id.clone(),
@@ -1016,7 +1054,7 @@ pub fn create_room_route(
         )
         .unwrap();
 
-    // 1.2 History Visibility
+    // 4.2 History Visibility
     db.rooms
         .append_pdu(
             room_id.clone(),
@@ -1033,7 +1071,7 @@ pub fn create_room_route(
         )
         .unwrap();
 
-    // 1.3 Guest Access
+    // 4.3 Guest Access
     db.rooms
         .append_pdu(
             room_id.clone(),
@@ -1058,7 +1096,7 @@ pub fn create_room_route(
         )
         .unwrap();
 
-    // 2. Events listed in initial_state
+    // 5. Events listed in initial_state
     for create_room::InitialStateEvent {
         event_type,
         state_key,
@@ -1079,7 +1117,7 @@ pub fn create_room_route(
             .unwrap();
     }
 
-    // 3. Events implied by name and topic
+    // 6. Events implied by name and topic
     if let Some(name) = &body.name {
         db.rooms
             .append_pdu(
@@ -1116,7 +1154,7 @@ pub fn create_room_route(
             .unwrap();
     }
 
-    // 4. Events implied by invite (and TODO: invite_3pid)
+    // 7. Events implied by invite (and TODO: invite_3pid)
     for user in &body.invite {
         db.rooms
             .append_pdu(
@@ -1139,10 +1177,24 @@ pub fn create_room_route(
             .unwrap();
     }
 
+    // Homeserver specific stuff
+    if let Some(alias) = alias {
+        db.rooms
+            .set_alias(&alias, Some(&room_id), &db.globals)
+            .unwrap();
+    }
+
+    if let Some(room::Visibility::Public) = body.visibility {
+        db.rooms.set_public(&room_id, true).unwrap();
+    }
+
     MatrixResult(Ok(create_room::Response { room_id }))
 }
 
-#[put("/_matrix/client/r0/rooms/<_room_id>/redact/<_event_id>/<_txn_id>", data = "<body>")]
+#[put(
+    "/_matrix/client/r0/rooms/<_room_id>/redact/<_event_id>/<_txn_id>",
+    data = "<body>"
+)]
 pub fn redact_event_route(
     db: State<'_, Database>,
     body: Ruma<redact_event::Request>,
@@ -1158,7 +1210,8 @@ pub fn redact_event_route(
         EventType::RoomRedaction,
         serde_json::to_value(redaction::RedactionEventContent {
             reason: body.reason.clone(),
-        }).unwrap(),
+        })
+        .unwrap(),
         None,
         None,
         Some(body.event_id.clone()),
@@ -1174,35 +1227,63 @@ pub fn redact_event_route(
     }
 }
 
+#[put("/_matrix/client/r0/directory/room/<_room_alias>", data = "<body>")]
+pub fn create_alias_route(
+    db: State<'_, Database>,
+    body: Ruma<create_alias::Request>,
+    _room_alias: String,
+) -> MatrixResult<create_alias::Response> {
+    if db.rooms.id_from_alias(&body.room_alias).unwrap().is_some() {
+        return MatrixResult(Err(Error {
+            kind: ErrorKind::Unknown,
+            message: "Alias already exists".to_owned(),
+            status_code: http::StatusCode::BAD_REQUEST,
+        }));
+    }
+
+    db.rooms
+        .set_alias(&body.room_alias, Some(&body.room_id), &db.globals)
+        .unwrap();
+
+    MatrixResult(Ok(create_alias::Response))
+}
+
+#[delete("/_matrix/client/r0/directory/room/<_room_alias>", data = "<body>")]
+pub fn delete_alias_route(
+    db: State<'_, Database>,
+    body: Ruma<delete_alias::Request>,
+    _room_alias: String,
+) -> MatrixResult<delete_alias::Response> {
+    db.rooms
+        .set_alias(&body.room_alias, None, &db.globals)
+        .unwrap();
+
+    MatrixResult(Ok(delete_alias::Response))
+}
+
 #[get("/_matrix/client/r0/directory/room/<_room_alias>", data = "<body>")]
 pub fn get_alias_route(
     db: State<'_, Database>,
     body: Ruma<get_alias::Request>,
     _room_alias: String,
 ) -> MatrixResult<get_alias::Response> {
-    warn!("TODO: get_alias_route");
-    let room_id = if body.room_alias.server_name() == db.globals.server_name() {
-        match body.room_alias.alias() {
-            "conduit" => "!lgOCCXQKtXOAPlAlG5:conduit.rs",
-            _ => {
-                debug!("Room alias not found.");
-                return MatrixResult(Err(Error {
-                    kind: ErrorKind::NotFound,
-                    message: "Room not found.".to_owned(),
-                    status_code: http::StatusCode::BAD_REQUEST,
-                }));
-            }
+    if body.room_alias.server_name() == db.globals.server_name() {
+        if let Some(room_id) = db.rooms.id_from_alias(&body.room_alias).unwrap() {
+            MatrixResult(Ok(get_alias::Response {
+                room_id,
+                servers: vec![db.globals.server_name().to_owned()],
+            }))
+        } else {
+            debug!("Room alias not found.");
+            return MatrixResult(Err(Error {
+                kind: ErrorKind::NotFound,
+                message: "Room with alias not found.".to_owned(),
+                status_code: http::StatusCode::BAD_REQUEST,
+            }));
         }
     } else {
         todo!("ask remote server");
     }
-    .try_into()
-    .unwrap();
-
-    MatrixResult(Ok(get_alias::Response {
-        room_id,
-        servers: vec!["conduit.rs".to_owned()],
-    }))
 }
 
 #[post("/_matrix/client/r0/rooms/<_room_id>/join", data = "<body>")]
@@ -1273,7 +1354,11 @@ pub fn join_room_by_id_or_alias_route(
     let room_id = match RoomId::try_from(body.room_id_or_alias.clone()) {
         Ok(room_id) => room_id,
         Err(_) => {
-            if let Some(room_id) = db.rooms.id_from_alias(body.room_id_or_alias.as_ref()).unwrap() {
+            if let Some(room_id) = db
+                .rooms
+                .id_from_alias(&body.room_id_or_alias.clone().try_into().unwrap())
+                .unwrap()
+            {
                 room_id
             } else {
                 // Ask creator server of the room to join TODO ask someone else when not available
@@ -1379,6 +1464,35 @@ pub fn invite_user_route(
     }
 }
 
+#[put("/_matrix/client/r0/directory/list/room/<_room_id>", data = "<body>")]
+pub async fn set_room_visibility_route(
+    db: State<'_, Database>,
+    body: Ruma<set_room_visibility::Request>,
+    _room_id: String,
+) -> MatrixResult<set_room_visibility::Response> {
+    match body.visibility {
+        room::Visibility::Public => db.rooms.set_public(&body.room_id, true).unwrap(),
+        room::Visibility::Private => db.rooms.set_public(&body.room_id, false).unwrap(),
+    }
+
+    MatrixResult(Ok(set_room_visibility::Response))
+}
+
+#[get("/_matrix/client/r0/directory/list/room/<_room_id>", data = "<body>")]
+pub async fn get_room_visibility_route(
+    db: State<'_, Database>,
+    body: Ruma<get_room_visibility::Request>,
+    _room_id: String,
+) -> MatrixResult<get_room_visibility::Response> {
+    MatrixResult(Ok(get_room_visibility::Response {
+        visibility: if db.rooms.is_public_room(&body.room_id).unwrap() {
+            room::Visibility::Public
+        } else {
+            room::Visibility::Private
+        },
+    }))
+}
+
 #[get("/_matrix/client/r0/publicRooms", data = "<body>")]
 pub async fn get_public_rooms_route(
     db: State<'_, Database>,
@@ -1436,9 +1550,10 @@ pub async fn get_public_rooms_filtered_route(
 ) -> MatrixResult<get_public_rooms_filtered::Response> {
     let mut chunk = db
         .rooms
-        .all_rooms()
-        .into_iter()
+        .public_rooms()
         .map(|room_id| {
+            let room_id = room_id.unwrap();
+
             let state = db.rooms.room_state(&room_id).unwrap();
 
             directory::PublicRoomsChunk {
@@ -1628,12 +1743,46 @@ pub fn create_state_event_for_key_route(
 ) -> MatrixResult<create_state_event_for_key::Response> {
     let user_id = body.user_id.as_ref().expect("user is authenticated");
 
-    // Reponse of with/without key is the same
+    let content =
+        serde_json::from_str::<serde_json::Value>(body.json_body.clone().unwrap().get()).unwrap();
+
+    if body.event_type == EventType::RoomCanonicalAlias {
+        let canonical_alias = serde_json::from_value::<
+            EventJson<canonical_alias::CanonicalAliasEventContent>,
+        >(content.clone())
+        .unwrap()
+        .deserialize()
+        .unwrap();
+
+        let mut aliases = canonical_alias.alt_aliases;
+
+        if let Some(alias) = canonical_alias.alias {
+            aliases.push(alias);
+        }
+
+        for alias in aliases {
+            if alias.server_name() != db.globals.server_name()
+                || db
+                    .rooms
+                    .id_from_alias(&alias)
+                    .unwrap()
+                    .filter(|room| room == &body.room_id) // Make sure it's the right room
+                    .is_none()
+            {
+                return MatrixResult(Err(Error {
+                    kind: ErrorKind::Unknown,
+                    message: "You are only allowed to send canonical_alias events when it's aliases already exists".to_owned(),
+                    status_code: http::StatusCode::BAD_REQUEST,
+                }));
+            }
+        }
+    }
+
     if let Ok(event_id) = db.rooms.append_pdu(
         body.room_id.clone(),
         user_id.clone(),
         body.event_type.clone(),
-        serde_json::from_str(body.json_body.clone().unwrap().get()).unwrap(),
+        content,
         None,
         Some(body.state_key.clone()),
         None,
@@ -1659,27 +1808,43 @@ pub fn create_state_event_for_empty_key_route(
     _room_id: String,
     _event_type: String,
 ) -> MatrixResult<create_state_event_for_empty_key::Response> {
-    let user_id = body.user_id.as_ref().expect("user is authenticated");
+    // This just calls create_state_event_for_key_route
+    let Ruma {
+        body:
+            create_state_event_for_empty_key::Request {
+                room_id,
+                event_type,
+                data,
+            },
+        user_id,
+        device_id,
+        json_body,
+    } = body;
 
-    // Reponse of with/without key is the same
-    if let Ok(event_id) = db.rooms.append_pdu(
-        body.room_id.clone(),
-        user_id.clone(),
-        body.event_type.clone(),
-        serde_json::from_str(body.json_body.unwrap().get()).unwrap(),
-        None,
-        Some("".to_owned()),
-        None,
-        &db.globals,
-    ) {
-        MatrixResult(Ok(create_state_event_for_empty_key::Response { event_id }))
-    } else {
-        MatrixResult(Err(Error {
-            kind: ErrorKind::Unknown,
-            message: "Failed to send event.".to_owned(),
-            status_code: http::StatusCode::BAD_REQUEST,
-        }))
-    }
+    let response = create_state_event_for_key_route(
+        db,
+        Ruma {
+            body: create_state_event_for_key::Request {
+                room_id,
+                event_type,
+                data,
+                state_key: "".to_owned(),
+            },
+            user_id,
+            device_id,
+            json_body,
+        },
+        _room_id,
+        _event_type,
+        "".to_owned(),
+    );
+
+    MatrixResult(match response.0 {
+        Ok(create_state_event_for_key::Response { event_id }) => {
+            Ok(create_state_event_for_empty_key::Response { event_id })
+        }
+        Err(e) => Err(e),
+    })
 }
 
 #[get("/_matrix/client/r0/rooms/<_room_id>/state", data = "<body>")]
@@ -1973,17 +2138,19 @@ pub fn sync_route(
     let mut invited_rooms = BTreeMap::new();
     for room_id in db.rooms.rooms_invited(&user_id) {
         let room_id = room_id.unwrap();
-        let events = db
-            .rooms
-            .pdus_since(&room_id, since)
-            .unwrap()
-            .map(|pdu| pdu.unwrap().to_stripped_state_event())
-            .collect();
 
         invited_rooms.insert(
-            room_id,
+            room_id.clone(),
             sync_events::InvitedRoom {
-                invite_state: sync_events::InviteState { events },
+                invite_state: sync_events::InviteState {
+                    events: db
+                        .rooms
+                        .room_state(&room_id)
+                        .unwrap()
+                        .into_iter()
+                        .map(|(_, pdu)| pdu.to_stripped_state_event())
+                        .collect(),
+                },
             },
         );
     }
@@ -2086,12 +2253,12 @@ pub fn get_message_events_route(
             .map(|pdu| pdu.to_room_event())
             .collect::<Vec<_>>();
 
-        MatrixResult(Ok(get_message_events::Response {
+        MatrixResult(Ok(dbg!(get_message_events::Response {
             start: Some(body.from.clone()),
             end: prev_batch,
             chunk: room_events,
             state: Vec::new(),
-        }))
+        })))
     } else {
         MatrixResult(Err(Error {
             kind: ErrorKind::Unknown,
diff --git a/src/database.rs b/src/database.rs
index 9c08a22e..de14805b 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -77,6 +77,10 @@ impl Database {
                 roomid_pduleaves: db.open_tree("roomid_pduleaves").unwrap(),
                 roomstateid_pdu: db.open_tree("roomstateid_pdu").unwrap(),
 
+                alias_roomid: db.open_tree("alias_roomid").unwrap(),
+                aliasid_alias: db.open_tree("alias_roomid").unwrap(),
+                publicroomids: db.open_tree("publicroomids").unwrap(),
+
                 userroomid_joined: db.open_tree("userroomid_joined").unwrap(),
                 roomuserid_joined: db.open_tree("roomuserid_joined").unwrap(),
                 userroomid_invited: db.open_tree("userroomid_invited").unwrap(),
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 8969da11..1c25c259 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -11,7 +11,7 @@ use ruma_events::{
     },
     EventJson, EventType,
 };
-use ruma_identifiers::{EventId, RoomId, UserId};
+use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
 use sled::IVec;
 use std::{
     collections::{BTreeMap, HashMap},
@@ -27,6 +27,8 @@ pub struct Rooms {
     pub(super) roomstateid_pdu: sled::Tree, // RoomStateId = Room + StateType + StateKey
 
     pub(super) alias_roomid: sled::Tree,
+    pub(super) aliasid_alias: sled::Tree, // AliasId = RoomId + Count
+    pub(super) publicroomids: sled::Tree,
 
     pub(super) userroomid_joined: sled::Tree,
     pub(super) roomuserid_joined: sled::Tree,
@@ -312,7 +314,7 @@ impl Rooms {
                                 .join_rule
                             });
 
-                        if target_membership == member::MembershipState::Join {
+                        let authorized = if target_membership == member::MembershipState::Join {
                             let mut prev_events = prev_events.iter();
                             let prev_event = self
                                 .get_pdu(prev_events.next().ok_or(Error::BadRequest(
@@ -392,34 +394,30 @@ impl Rooms {
                             }
                         } else {
                             false
+                        };
+
+                        if authorized {
+                            // Update our membership info
+                            self.update_membership(&room_id, &target_user_id, &target_membership)?;
                         }
+
+                        authorized
                     }
                     EventType::RoomCreate => prev_events.is_empty(),
-                    _ if sender_membership == member::MembershipState::Join => {
+
+                    // Not allow any of the following events if the sender is not joined.
+                    _ if sender_membership != member::MembershipState::Join => false,
+
+                    _ => {
                         // TODO
                         sender_power.unwrap_or(&power_levels.users_default)
                             >= &power_levels.state_default
                     }
-
-                    _ => false,
                 } {
                     error!("Unauthorized");
                     // Not authorized
                     return Err(Error::BadRequest("event not authorized"));
                 }
-                if event_type == EventType::RoomMember {
-                    // TODO: Don't get this twice
-                    let target_user_id = UserId::try_from(&**state_key)?;
-                    self.update_membership(
-                        &room_id,
-                        &target_user_id,
-                        &serde_json::from_value::<EventJson<member::MemberEventContent>>(
-                            content.clone(),
-                        )?
-                        .deserialize()?
-                        .membership,
-                    )?;
-                }
             }
         } else if !self.is_joined(&sender, &room_id)? {
             return Err(Error::BadRequest("event not authorized"));
@@ -648,14 +646,66 @@ impl Rooms {
         Ok(())
     }
 
-    pub fn id_from_alias(&self, alias: &str) -> Result<Option<RoomId>> {
-        if !alias.starts_with('#') {
-            return Err(Error::BadRequest("room alias does not start with #"));
+    pub fn set_alias(
+        &self,
+        alias: &RoomAliasId,
+        room_id: Option<&RoomId>,
+        globals: &super::globals::Globals,
+    ) -> Result<()> {
+        if let Some(room_id) = room_id {
+            self.alias_roomid
+                .insert(alias.alias(), &*room_id.to_string())?;
+            let mut aliasid = room_id.to_string().as_bytes().to_vec();
+            aliasid.extend_from_slice(&globals.next_count()?.to_be_bytes());
+            self.aliasid_alias.insert(aliasid, &*alias.alias())?;
+        } else {
+            if let Some(room_id) = self.alias_roomid.remove(alias.alias())? {
+                for key in self.aliasid_alias.scan_prefix(room_id).keys() {
+                    self.aliasid_alias.remove(key?)?;
+                }
+            }
         }
 
-        self.alias_roomid.get(alias)?.map_or(Ok(None), |bytes| {
-            Ok(Some(RoomId::try_from(utils::string_from_bytes(&bytes)?)?))
-        })
+        Ok(())
+    }
+
+    pub fn id_from_alias(&self, alias: &RoomAliasId) -> Result<Option<RoomId>> {
+        self.alias_roomid
+            .get(alias.alias())?
+            .map_or(Ok(None), |bytes| {
+                Ok(Some(RoomId::try_from(utils::string_from_bytes(&bytes)?)?))
+            })
+    }
+
+    pub fn room_aliases(&self, room_id: &RoomId) -> impl Iterator<Item = Result<RoomAliasId>> {
+        let mut prefix = room_id.to_string().as_bytes().to_vec();
+        prefix.push(0xff);
+
+        self.aliasid_alias
+            .scan_prefix(prefix)
+            .values()
+            .map(|bytes| Ok(RoomAliasId::try_from(utils::string_from_bytes(&bytes?)?)?))
+    }
+
+    pub fn set_public(&self, room_id: &RoomId, public: bool) -> Result<()> {
+        if public {
+            self.publicroomids.insert(room_id.to_string(), &[])?;
+        } else {
+            self.publicroomids.remove(room_id.to_string())?;
+        }
+
+        Ok(())
+    }
+
+    pub fn is_public_room(&self, room_id: &RoomId) -> Result<bool> {
+        Ok(self.publicroomids.contains_key(room_id.to_string())?)
+    }
+
+    pub fn public_rooms(&self) -> impl Iterator<Item = Result<RoomId>> {
+        self.publicroomids
+            .iter()
+            .keys()
+            .map(|bytes| Ok(RoomId::try_from(utils::string_from_bytes(&bytes?)?)?))
     }
 
     /// Returns an iterator over all rooms a user joined.
diff --git a/src/database/users.rs b/src/database/users.rs
index 81a7bb8a..6540a70a 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -141,9 +141,8 @@ impl Users {
         let mut prefix = userdeviceid.clone();
         prefix.push(0xff);
 
-        for result in self.todeviceid_events.scan_prefix(&prefix) {
-            let (key, value) = result?;
-            self.todeviceid_events.remove(key)?;
+        for key in self.todeviceid_events.scan_prefix(&prefix).keys() {
+            self.todeviceid_events.remove(key?)?;
         }
 
         // TODO: Remove onetimekeys
diff --git a/src/main.rs b/src/main.rs
index 3e34ded3..41462824 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -50,12 +50,17 @@ fn setup_rocket() -> rocket::Rocket {
                 client_server::set_read_marker_route,
                 client_server::create_typing_event_route,
                 client_server::create_room_route,
+                client_server::redact_event_route,
+                client_server::create_alias_route,
+                client_server::delete_alias_route,
                 client_server::get_alias_route,
                 client_server::join_room_by_id_route,
                 client_server::join_room_by_id_or_alias_route,
                 client_server::leave_room_route,
                 client_server::forget_room_route,
                 client_server::invite_user_route,
+                client_server::set_room_visibility_route,
+                client_server::get_room_visibility_route,
                 client_server::get_public_rooms_route,
                 client_server::get_public_rooms_filtered_route,
                 client_server::search_users_route,