From 80ddf80f17c57d2f29d13788e8b5424d4b5598a8 Mon Sep 17 00:00:00 2001
From: timokoesters <timo@koesters.xyz>
Date: Sun, 19 Apr 2020 14:14:47 +0200
Subject: [PATCH] work

---
 Cargo.lock           | 364 +++++++++++++++++++++++++++++++++++++------
 Cargo.toml           |  19 +--
 src/client_server.rs | 192 +++++++++++++++--------
 src/data.rs          |  62 +++++++-
 src/database.rs      |  12 +-
 src/main.rs          |   8 +-
 src/pdu.rs           |  13 +-
 src/server_server.rs |  25 +++
 src/stateres.rs      |  59 +++++++
 src/utils.rs         |  17 +-
 10 files changed, 631 insertions(+), 140 deletions(-)
 create mode 100644 src/server_server.rs
 create mode 100644 src/stateres.rs

diff --git a/Cargo.lock b/Cargo.lock
index d60665567..9d7eaf217 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -29,9 +29,9 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
 
 [[package]]
 name = "async-trait"
-version = "0.1.29"
+version = "0.1.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab5c215748dc1ad11a145359b1067107ae0f8ca5e99844fa64067ed5bf198e3"
+checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d"
 dependencies = [
  "proc-macro2 1.0.10",
  "quote 1.0.3",
@@ -148,6 +148,7 @@ dependencies = [
  "log",
  "pretty_env_logger",
  "rand",
+ "reqwest",
  "rocket",
  "ruma-api",
  "ruma-client-api",
@@ -179,6 +180,22 @@ dependencies = [
  "time",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
+
 [[package]]
 name = "crc32fast"
 version = "1.2.0"
@@ -271,6 +288,15 @@ version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
 
+[[package]]
+name = "encoding_rs"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "env_logger"
 version = "0.7.1"
@@ -290,6 +316,21 @@ version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
 
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
 [[package]]
 name = "fs2"
 version = "0.4.3"
@@ -457,9 +498,9 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.10"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
+checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
 dependencies = [
  "libc",
 ]
@@ -502,9 +543,9 @@ dependencies = [
 
 [[package]]
 name = "hyper"
-version = "0.13.4"
+version = "0.13.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed6081100e960d9d74734659ffc9cc91daf1c0fc7aceb8eaa94ee1a3f5046f2e"
+checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -516,6 +557,7 @@ dependencies = [
  "httparse",
  "itoa",
  "log",
+ "net2",
  "pin-project",
  "time",
  "tokio",
@@ -523,6 +565,19 @@ dependencies = [
  "want",
 ]
 
+[[package]]
+name = "hyper-tls"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa"
+dependencies = [
+ "bytes",
+ "hyper",
+ "native-tls",
+ "tokio",
+ "tokio-tls",
+]
+
 [[package]]
 name = "idna"
 version = "0.2.0"
@@ -594,15 +649,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
+checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
 
 [[package]]
 name = "lock_api"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
 dependencies = [
  "scopeguard",
 ]
@@ -649,6 +704,16 @@ version = "0.3.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
 
+[[package]]
+name = "mime_guess"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
 [[package]]
 name = "mio"
 version = "0.6.21"
@@ -691,6 +756,24 @@ dependencies = [
  "ws2_32-sys",
 ]
 
+[[package]]
+name = "native-tls"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
 [[package]]
 name = "net2"
 version = "0.2.33"
@@ -704,19 +787,52 @@ dependencies = [
 
 [[package]]
 name = "num_cpus"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
 dependencies = [
  "hermit-abi",
  "libc",
 ]
 
+[[package]]
+name = "openssl"
+version = "0.10.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "foreign-types",
+ "lazy_static",
+ "libc",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "parking_lot"
-version = "0.10.0"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
+checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -724,9 +840,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.7.0"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
+checksum = "0e136c1904604defe99ce5fd71a28d473fa60a12255d511aa78a9ddf11237aeb"
 dependencies = [
  "cfg-if",
  "cloudabi",
@@ -772,18 +888,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
 name = "pin-project"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c"
+checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2"
 dependencies = [
  "pin-project-internal",
 ]
 
 [[package]]
 name = "pin-project-internal"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f"
+checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a"
 dependencies = [
  "proc-macro2 1.0.10",
  "quote 1.0.3",
@@ -802,6 +918,12 @@ version = "0.1.0-alpha.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
 
+[[package]]
+name = "pkg-config"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.6"
@@ -932,9 +1054,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.3.6"
+version = "1.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3"
+checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -948,6 +1070,50 @@ version = "0.6.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
 
+[[package]]
+name = "remove_dir_all"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
+dependencies = [
+ "winapi 0.3.8",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2"
+dependencies = [
+ "base64 0.11.0",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-tls",
+ "js-sys",
+ "lazy_static",
+ "log",
+ "mime",
+ "mime_guess",
+ "native-tls",
+ "percent-encoding 2.1.0",
+ "pin-project-lite",
+ "serde",
+ "serde_urlencoded",
+ "time",
+ "tokio",
+ "tokio-tls",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
+]
+
 [[package]]
 name = "ring"
 version = "0.16.12"
@@ -1024,9 +1190,9 @@ dependencies = [
 
 [[package]]
 name = "ruma-api"
-version = "0.15.0"
+version = "0.16.0-rc.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120f0cd8625b842423ef3a63cabb8c309ca35a02de87cc4b377fb2cdd43f1fe5"
+checksum = "7769e934360383f91d68a2b9132610d02436ef3272cbdd46de239c9025198a36"
 dependencies = [
  "http",
  "percent-encoding 2.1.0",
@@ -1036,14 +1202,13 @@ dependencies = [
  "serde_json",
  "serde_urlencoded",
  "strum",
- "url",
 ]
 
 [[package]]
 name = "ruma-api-macros"
-version = "0.12.0"
+version = "0.16.0-rc.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfc523efc9c1ba7033ff17888551c1d378e12eae087cfbe4fcee938ff516759e"
+checksum = "0f8dad3311d0bee6d43da684ba03f9a84cddd39f9b3e7e88cab5d726419ec22e"
 dependencies = [
  "proc-macro2 1.0.10",
  "quote 1.0.3",
@@ -1052,14 +1217,15 @@ dependencies = [
 
 [[package]]
 name = "ruma-client-api"
-version = "0.7.2"
-source = "git+https://github.com/ruma/ruma-client-api.git?branch=uiaa-error-type#a7136c06285864dadcc0b0c6371d181002727c55"
+version = "0.8.0-rc.2"
+source = "git+https://github.com/ruma/ruma-client-api.git#1b7863dc36e6e043ae365791cc719dc190abd660"
 dependencies = [
  "http",
  "js_int",
  "ruma-api",
  "ruma-events",
  "ruma-identifiers",
+ "ruma-serde",
  "serde",
  "serde_json",
  "strum",
@@ -1068,22 +1234,23 @@ dependencies = [
 
 [[package]]
 name = "ruma-events"
-version = "0.18.0"
+version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80e34bfc20462f18d7f0beb6f1863db62d29438f2dcf390b625e9b20696cb2b3"
+checksum = "844b5d38397c945395c7a1eaf89d55714c3d22983b870085a1a67d51fb6611cf"
 dependencies = [
  "js_int",
  "ruma-events-macros",
  "ruma-identifiers",
+ "ruma-serde",
  "serde",
  "serde_json",
 ]
 
 [[package]]
 name = "ruma-events-macros"
-version = "0.3.0"
+version = "0.19.0-final"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff95b6b4480c570db471b490b35ad70add5470651654e75faf0b97052b4f29e1"
+checksum = "5477046b734fde45dd7913dbc8d7b260af3b1c31ea2bc329bd2f0b44e37368be"
 dependencies = [
  "proc-macro2 1.0.10",
  "quote 1.0.3",
@@ -1093,10 +1260,10 @@ dependencies = [
 [[package]]
 name = "ruma-federation-api"
 version = "0.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2a73a23c4d9243be91e101e1942f4d9cd913ef5156d756bafdfe2409ee23d72"
+source = "git+https://github.com/ruma/ruma-federation-api.git#5448c650f0a583382152d0f43f2dcf720d495390"
 dependencies = [
  "js_int",
+ "ruma-api",
  "ruma-events",
  "ruma-identifiers",
  "serde",
@@ -1105,19 +1272,28 @@ dependencies = [
 
 [[package]]
 name = "ruma-identifiers"
-version = "0.14.1"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e442c700a3b33fc4dd4a1c4b463ebdd252d2c2db31b83da6bb3009307039b9"
+checksum = "63db5545f38077ea141fb112df070773e6ab9b7025174d732f56c6b37525ccc0"
 dependencies = [
- "rand",
  "serde",
- "url",
+]
+
+[[package]]
+name = "ruma-serde"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09901d608958f63618546957134dd4242d2ca07a885a28f794ad4574a937c22c"
+dependencies = [
+ "js_int",
+ "serde",
+ "serde_json",
 ]
 
 [[package]]
 name = "ruma-signatures"
 version = "0.6.0-dev.1"
-source = "git+https://github.com/ruma/ruma-signatures.git#9947e94cb28daea456904197f7cd754a8e48797a"
+source = "git+https://github.com/ruma/ruma-signatures.git#c3f8399c268695464730afd6077c7ce50155b8d5"
 dependencies = [
  "base64 0.12.0",
  "ring",
@@ -1168,6 +1344,16 @@ version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
 
+[[package]]
+name = "schannel"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19"
+dependencies = [
+ "lazy_static",
+ "winapi 0.3.8",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.1.0"
@@ -1184,6 +1370,29 @@ dependencies = [
  "untrusted",
 ]
 
+[[package]]
+name = "security-framework"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "serde"
 version = "1.0.106"
@@ -1261,9 +1470,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
+checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
 
 [[package]]
 name = "spin"
@@ -1320,6 +1529,20 @@ dependencies = [
  "unicode-xid 0.2.0",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi 0.3.8",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.1.0"
@@ -1340,20 +1563,19 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.1.42"
+version = "0.1.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
+checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
 dependencies = [
  "libc",
- "redox_syscall",
  "winapi 0.3.8",
 ]
 
 [[package]]
 name = "tokio"
-version = "0.2.16"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee5a0dd887e37d37390c13ff8ac830f992307fe30a1fff0ab8427af67211ba28"
+checksum = "34ef16d072d2b6dc8b4a56c70f5c5ced1a37752116f8e7c1e80c659aa7cb6713"
 dependencies = [
  "bytes",
  "fnv",
@@ -1395,6 +1617,16 @@ dependencies = [
  "webpki",
 ]
 
+[[package]]
+name = "tokio-tls"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
 [[package]]
 name = "tokio-util"
 version = "0.3.1"
@@ -1430,6 +1662,15 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
 
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check 0.9.1",
+]
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.4"
@@ -1484,6 +1725,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "vcpkg"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
+
 [[package]]
 name = "version_check"
 version = "0.1.5"
@@ -1519,6 +1766,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f"
 dependencies = [
  "cfg-if",
+ "serde",
+ "serde_json",
  "wasm-bindgen-macro",
 ]
 
@@ -1537,6 +1786,18 @@ dependencies = [
  "wasm-bindgen-shared",
 ]
 
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "wasm-bindgen-macro"
 version = "0.2.60"
@@ -1629,6 +1890,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "winreg"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
+dependencies = [
+ "winapi 0.3.8",
+]
+
 [[package]]
 name = "ws2_32-sys"
 version = "0.2.1"
diff --git a/Cargo.toml b/Cargo.toml
index 3b8da6c64..5cf5412f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,19 +13,20 @@ 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", branch = "uiaa-error-type" }
+ruma-client-api = { git = "https://github.com/ruma/ruma-client-api.git" }
+ruma-identifiers = "0.15.1"
+ruma-api = "0.16.0-rc.1"
+ruma-events = "0.19.0"
+ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git" }
+ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git" }
 pretty_env_logger = "0.4.0"
 log = "0.4.8"
 sled = "0.31.0"
 directories = "2.0.2"
-ruma-identifiers = "0.14.1"
-ruma-api = "0.15.0"
-ruma-events = "0.18.0"
 js_int = "0.1.4"
-serde_json = "1.0.50"
-ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git" }
-ruma-federation-api = "0.0.1"
+serde_json = "1.0.51"
 serde = "1.0.106"
-tokio = { version = "0.2.16", features = ["macros"] } #rt-threaded
+tokio = { version = "0.2.18", features = ["macros"] }
 rand = "0.7.3"
-rust-argon2 = "0.8.2"
\ No newline at end of file
+rust-argon2 = "0.8.2"
+reqwest = "0.10.4"
diff --git a/src/client_server.rs b/src/client_server.rs
index 2548d2576..a0191423b 100644
--- a/src/client_server.rs
+++ b/src/client_server.rs
@@ -5,16 +5,16 @@
 use ruma_client_api::{
     error::{Error, ErrorKind},
     r0::{
-        account::{
-            register, AuthenticationFlow, UserInteractiveAuthenticationInfo,
-            UserInteractiveAuthenticationResponse,
-        },
+        account::register,
         alias::get_alias,
+        capabilities::get_capabilities,
         config::{get_global_account_data, set_global_account_data},
         directory::{self, get_public_rooms_filtered},
         filter::{self, create_filter, get_filter},
         keys::{get_keys, upload_keys},
-        membership::{invite_user, join_room_by_id, join_room_by_id_or_alias},
+        membership::{
+            get_member_events, invite_user, join_room_by_id, join_room_by_id_or_alias, leave_room,
+        },
         message::create_message_event,
         presence::set_presence,
         profile::{
@@ -28,15 +28,20 @@
         sync::sync_events,
         thirdparty::get_protocols,
         typing::create_typing_event,
+        uiaa::{AuthFlow, UiaaInfo, UiaaResponse},
         user_directory::search_users,
     },
     unversioned::get_supported_versions,
 };
 use ruma_events::{collections::only::Event as EduEvent, EventType};
-use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId};
+use ruma_identifiers::{RoomId, UserId};
 use serde_json::json;
-use std::{collections::HashMap, convert::TryInto, path::PathBuf, time::Duration};
-use argon2::{Config, Variant};
+use std::{
+    collections::{BTreeMap, HashMap},
+    convert::{TryFrom, TryInto},
+    path::PathBuf,
+    time::{Duration, SystemTime},
+};
 
 const GUEST_NAME_LENGTH: usize = 10;
 const DEVICE_ID_LENGTH: usize = 10;
@@ -46,8 +51,8 @@
 #[get("/_matrix/client/versions")]
 pub fn get_supported_versions_route() -> MatrixResult<get_supported_versions::Response> {
     MatrixResult(Ok(get_supported_versions::Response {
-        versions: vec!["r0.6.0".to_owned()],
-        unstable_features: HashMap::new(),
+        versions: vec!["r0.5.0".to_owned(), "r0.6.0".to_owned()],
+        unstable_features: BTreeMap::new(),
     }))
 }
 
@@ -55,18 +60,17 @@ pub fn get_supported_versions_route() -> MatrixResult<get_supported_versions::Re
 pub fn register_route(
     data: State<Data>,
     body: Ruma<register::Request>,
-) -> MatrixResult<register::Response, UserInteractiveAuthenticationResponse> {
+) -> MatrixResult<register::Response, UiaaResponse> {
     if body.auth.is_none() {
-        return MatrixResult(Err(UserInteractiveAuthenticationResponse::AuthResponse(
-            UserInteractiveAuthenticationInfo {
-                flows: vec![AuthenticationFlow {
-                    stages: vec!["m.login.dummy".to_owned()],
-                }],
-                completed: vec![],
-                params: json!({}),
-                session: Some(utils::random_string(SESSION_ID_LENGTH)),
-            },
-        )));
+        return MatrixResult(Err(UiaaResponse::AuthResponse(UiaaInfo {
+            flows: vec![AuthFlow {
+                stages: vec!["m.login.dummy".to_owned()],
+            }],
+            completed: vec![],
+            params: json!({}),
+            session: Some(utils::random_string(SESSION_ID_LENGTH)),
+            auth_error: None,
+        })));
     }
 
     // Validate user id
@@ -81,13 +85,11 @@ pub fn register_route(
     {
         Err(_) => {
             debug!("Username invalid");
-            return MatrixResult(Err(UserInteractiveAuthenticationResponse::MatrixError(
-                Error {
-                    kind: ErrorKind::InvalidUsername,
-                    message: "Username was invalid.".to_owned(),
-                    status_code: http::StatusCode::BAD_REQUEST,
-                },
-            )));
+            return MatrixResult(Err(UiaaResponse::MatrixError(Error {
+                kind: ErrorKind::InvalidUsername,
+                message: "Username was invalid.".to_owned(),
+                status_code: http::StatusCode::BAD_REQUEST,
+            })));
         }
         Ok(user_id) => user_id,
     };
@@ -95,13 +97,11 @@ pub fn register_route(
     // Check if username is creative enough
     if data.user_exists(&user_id) {
         debug!("ID already taken");
-        return MatrixResult(Err(UserInteractiveAuthenticationResponse::MatrixError(
-            Error {
-                kind: ErrorKind::UserInUse,
-                message: "Desired user ID is already taken.".to_owned(),
-                status_code: http::StatusCode::BAD_REQUEST,
-            },
-        )));
+        return MatrixResult(Err(UiaaResponse::MatrixError(Error {
+            kind: ErrorKind::UserInUse,
+            message: "Desired user ID is already taken.".to_owned(),
+            status_code: http::StatusCode::BAD_REQUEST,
+        })));
     }
 
     let password = body.password.clone().unwrap_or_default();
@@ -110,13 +110,11 @@ pub fn register_route(
         // Create user
         data.user_add(&user_id, &hash);
     } else {
-        return MatrixResult(Err(UserInteractiveAuthenticationResponse::MatrixError(
-            Error {
-                kind: ErrorKind::InvalidParam,
-                message: "Password did not meet requirements".to_owned(),
-                status_code: http::StatusCode::BAD_REQUEST,
-            },
-        )));
+        return MatrixResult(Err(UiaaResponse::MatrixError(Error {
+            kind: ErrorKind::InvalidParam,
+            message: "Password did not met requirements".to_owned(),
+            status_code: http::StatusCode::BAD_REQUEST,
+        })));
     }
 
     // Generate new device id if the user didn't specify one
@@ -158,8 +156,8 @@ pub fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResul
             }
             if let Ok(user_id) = (*username).try_into() {
                 if let Some(hash) = data.password_hash_get(&user_id) {
-                    let hash_matches = argon2::verify_encoded(&hash, password.as_bytes())
-                        .unwrap_or(false);
+                    let hash_matches =
+                        argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
 
                     if hash_matches {
                         // Success!
@@ -219,11 +217,25 @@ pub fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResul
     }))
 }
 
+#[get("/_matrix/client/r0/capabilities", data = "<body>")]
+pub fn get_capabilities_route(
+    body: Ruma<get_capabilities::Request>,
+) -> MatrixResult<get_capabilities::Response> {
+    // TODO
+    MatrixResult(Ok(get_capabilities::Response {
+        capabilities: get_capabilities::Capabilities {
+            change_password: None,
+            room_versions: None,
+            custom_capabilities: BTreeMap::new(),
+        },
+    }))
+}
+
 #[get("/_matrix/client/r0/pushrules")]
 pub fn get_pushrules_all_route() -> MatrixResult<get_pushrules_all::Response> {
     // TODO
     MatrixResult(Ok(get_pushrules_all::Response {
-        global: HashMap::new(),
+        global: BTreeMap::new(),
     }))
 }
 
@@ -447,8 +459,8 @@ pub fn set_presence_route(
 pub fn get_keys_route(body: Ruma<get_keys::Request>) -> MatrixResult<get_keys::Response> {
     // TODO
     MatrixResult(Ok(get_keys::Response {
-        failures: HashMap::new(),
-        device_keys: HashMap::new(),
+        failures: BTreeMap::new(),
+        device_keys: BTreeMap::new(),
     }))
 }
 
@@ -459,7 +471,7 @@ pub fn upload_keys_route(
 ) -> MatrixResult<upload_keys::Response> {
     // TODO
     MatrixResult(Ok(upload_keys::Response {
-        one_time_key_counts: HashMap::new(),
+        one_time_key_counts: BTreeMap::new(),
     }))
 }
 
@@ -472,14 +484,14 @@ pub fn set_read_marker_route(
     let user_id = body.user_id.clone().expect("user is authenticated");
     // TODO: Fully read
     if let Some(event) = &body.read_receipt {
-        let mut user_receipts = HashMap::new();
+        let mut user_receipts = BTreeMap::new();
         user_receipts.insert(
             user_id.clone(),
             ruma_events::receipt::Receipt {
-                ts: Some(utils::millis_since_unix_epoch().try_into().unwrap()),
+                ts: Some(SystemTime::now()),
             },
         );
-        let mut receipt_content = HashMap::new();
+        let mut receipt_content = BTreeMap::new();
         receipt_content.insert(
             event.clone(),
             ruma_events::receipt::Receipts {
@@ -537,7 +549,7 @@ pub fn create_room_route(
     body: Ruma<create_room::Request>,
 ) -> MatrixResult<create_room::Response> {
     // TODO: check if room is unique
-    let room_id = RoomId::new(data.hostname()).expect("host is valid");
+    let room_id = RoomId::try_from(data.hostname()).expect("host is valid");
     let user_id = body.user_id.clone().expect("user is authenticated");
 
     data.pdu_append(
@@ -589,8 +601,6 @@ pub fn create_room_route(
         );
     }
 
-    dbg!(&*body);
-
     data.room_join(&room_id, &user_id);
 
     for user in &body.invite {
@@ -651,8 +661,8 @@ pub fn join_room_by_id_or_alias_route(
     body: Ruma<join_room_by_id_or_alias::Request>,
     _room_id_or_alias: String,
 ) -> MatrixResult<join_room_by_id_or_alias::Response> {
-    let room_id = match &body.room_id_or_alias {
-        RoomIdOrAliasId::RoomAliasId(alias) => match alias.alias() {
+    let room_id = if body.room_id_or_alias.is_room_alias_id() {
+        match body.room_id_or_alias.as_ref() {
             "#room:localhost" => "!xclkjvdlfj:localhost".try_into().unwrap(),
             _ => {
                 debug!("Room not found.");
@@ -662,9 +672,9 @@ pub fn join_room_by_id_or_alias_route(
                     status_code: http::StatusCode::NOT_FOUND,
                 }));
             }
-        },
-
-        RoomIdOrAliasId::RoomId(id) => id.clone(),
+        }
+    } else {
+        body.room_id_or_alias.try_into().unwrap()
     };
 
     if data.room_join(
@@ -681,6 +691,17 @@ pub fn join_room_by_id_or_alias_route(
     }
 }
 
+#[post("/_matrix/client/r0/rooms/<_room_id>/leave", data = "<body>")]
+pub fn leave_room_route(
+    data: State<Data>,
+    body: Ruma<leave_room::Request>,
+    _room_id: String,
+) -> MatrixResult<leave_room::Response> {
+    let user_id = body.user_id.clone().expect("user is authenticated");
+    data.room_leave(&user_id, &body.room_id, &user_id);
+    MatrixResult(Ok(leave_room::Response))
+}
+
 #[post("/_matrix/client/r0/rooms/<_room_id>/invite", data = "<body>")]
 pub fn invite_user_route(
     data: State<Data>,
@@ -714,7 +735,7 @@ pub fn get_public_rooms_filtered_route(
         .map(|room_id| {
             let state = data.room_state(&room_id);
             directory::PublicRoomsChunk {
-                aliases: None,
+                aliases: Vec::new(),
                 canonical_alias: None,
                 name: state
                     .get(&(EventType::RoomName, "".to_owned()))
@@ -763,13 +784,22 @@ pub fn search_users_route(
     }))
 }
 
+#[get("/_matrix/client/r0/rooms/<_room_id>/members", data = "<body>")]
+pub fn get_member_events_route(
+    body: Ruma<get_member_events::Request>,
+    _room_id: String,
+) -> MatrixResult<get_member_events::Response> {
+    // TODO
+    MatrixResult(Ok(get_member_events::Response { chunk: Vec::new() }))
+}
+
 #[get("/_matrix/client/r0/thirdparty/protocols", data = "<body>")]
 pub fn get_protocols_route(
     body: Ruma<get_protocols::Request>,
 ) -> MatrixResult<get_protocols::Response> {
     // TODO
     MatrixResult(Ok(get_protocols::Response {
-        protocols: HashMap::new(),
+        protocols: BTreeMap::new(),
     }))
 }
 
@@ -851,7 +881,7 @@ pub fn sync_route(
     std::thread::sleep(Duration::from_millis(200));
     let next_batch = data.last_pdu_index().to_string();
 
-    let mut joined_rooms = HashMap::new();
+    let mut joined_rooms = BTreeMap::new();
     let joined_roomids = data.rooms_joined(body.user_id.as_ref().expect("user is authenticated"));
     let since = body
         .since
@@ -860,7 +890,11 @@ pub fn sync_route(
         .unwrap_or(0);
     for room_id in joined_roomids {
         let pdus = data.pdus_since(&room_id, since);
-        let room_events = pdus.into_iter().map(|pdu| pdu.to_room_event()).collect();
+        let room_events = pdus
+            .into_iter()
+            .map(|pdu| pdu.to_room_event())
+            .filter_map(|e| e)
+            .collect();
         let mut edus = data.roomlatests_since(&room_id, since);
         edus.extend_from_slice(&data.roomactives_in(&room_id));
 
@@ -888,7 +922,33 @@ pub fn sync_route(
         );
     }
 
-    let mut invited_rooms = HashMap::new();
+    let mut left_rooms = BTreeMap::new();
+    let left_roomids = data.rooms_left(body.user_id.as_ref().expect("user is authenticated"));
+    for room_id in left_roomids {
+        let pdus = data.pdus_since(&room_id, since);
+        let room_events = pdus
+            .into_iter()
+            .map(|pdu| pdu.to_room_event())
+            .filter_map(|e| e)
+            .collect();
+        let mut edus = data.roomlatests_since(&room_id, since);
+        edus.extend_from_slice(&data.roomactives_in(&room_id));
+
+        left_rooms.insert(
+            room_id.clone().try_into().unwrap(),
+            sync_events::LeftRoom {
+                account_data: sync_events::AccountData { events: Vec::new() },
+                timeline: sync_events::Timeline {
+                    limited: Some(false),
+                    prev_batch: Some("".to_owned()),
+                    events: room_events,
+                },
+                state: sync_events::State { events: Vec::new() },
+            },
+        );
+    }
+
+    let mut invited_rooms = BTreeMap::new();
     for room_id in data.rooms_invited(body.user_id.as_ref().expect("user is authenticated")) {
         let events = data
             .pdus_since(&room_id, since)
@@ -907,7 +967,7 @@ pub fn sync_route(
     MatrixResult(Ok(sync_events::Response {
         next_batch,
         rooms: sync_events::Rooms {
-            leave: Default::default(),
+            leave: left_rooms,
             join: joined_rooms,
             invite: invited_rooms,
         },
@@ -945,4 +1005,4 @@ pub fn options_route(_segments: PathBuf) -> MatrixResult<create_message_event::R
         message: "This is the options route.".to_owned(),
         status_code: http::StatusCode::OK,
     }))
-}
\ No newline at end of file
+}
diff --git a/src/data.rs b/src/data.rs
index f73731211..2aa003a55 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -10,15 +10,18 @@
 
 pub struct Data {
     hostname: String,
+    reqwest_client: reqwest::Client,
     db: Database,
 }
 
 impl Data {
     /// Load an existing database or create a new one.
     pub fn load_or_create(hostname: &str) -> Self {
+        let db = Database::load_or_create(hostname);
         Self {
             hostname: hostname.to_owned(),
-            db: Database::load_or_create(hostname),
+            reqwest_client: reqwest::Client::new(),
+            db,
         }
     }
 
@@ -27,6 +30,15 @@ pub fn hostname(&self) -> &str {
         &self.hostname
     }
 
+    /// Get the hostname of the server.
+    pub fn reqwest_client(&self) -> &reqwest::Client {
+        &self.reqwest_client
+    }
+
+    pub fn keypair(&self) -> &ruma_signatures::Ed25519KeyPair {
+        &self.db.keypair
+    }
+
     /// Check if a user has an account by looking for an assigned password.
     pub fn user_exists(&self, user_id: &UserId) -> bool {
         self.db
@@ -183,6 +195,10 @@ pub fn room_join(&self, room_id: &RoomId, user_id: &UserId) -> bool {
             user_id.to_string().as_bytes(),
             room_id.to_string().as_bytes(),
         );
+        self.db.userid_leftroomids.remove_value(
+            user_id.to_string().as_bytes(),
+            room_id.to_string().as_bytes().into(),
+        );
 
         self.pdu_append(
             room_id.clone(),
@@ -277,6 +293,33 @@ pub fn room_state(&self, room_id: &RoomId) -> HashMap<(EventType, String), PduEv
         hashmap
     }
 
+    pub fn room_leave(&self, sender: &UserId, room_id: &RoomId, user_id: &UserId) {
+        self.pdu_append(
+            room_id.clone(),
+            sender.clone(),
+            EventType::RoomMember,
+            json!({"membership": "leave"}),
+            None,
+            Some(user_id.to_string()),
+        );
+        self.db.userid_inviteroomids.remove_value(
+            user_id.to_string().as_bytes(),
+            room_id.to_string().as_bytes().into(),
+        );
+        self.db.userid_roomids.remove_value(
+            user_id.to_string().as_bytes(),
+            room_id.to_string().as_bytes().into(),
+        );
+        self.db.roomid_userids.remove_value(
+            room_id.to_string().as_bytes(),
+            user_id.to_string().as_bytes().into(),
+        );
+        self.db.userid_leftroomids.add(
+            user_id.to_string().as_bytes(),
+            room_id.to_string().as_bytes().into(),
+        );
+    }
+
     pub fn room_invite(&self, sender: &UserId, room_id: &RoomId, user_id: &UserId) {
         self.pdu_append(
             room_id.clone(),
@@ -287,9 +330,13 @@ pub fn room_invite(&self, sender: &UserId, room_id: &RoomId, user_id: &UserId) {
             Some(user_id.to_string()),
         );
         self.db.userid_inviteroomids.add(
-            &user_id.to_string().as_bytes(),
+            user_id.to_string().as_bytes(),
             room_id.to_string().as_bytes().into(),
         );
+        self.db.roomid_userids.add(
+            room_id.to_string().as_bytes(),
+            user_id.to_string().as_bytes().into(),
+        );
     }
 
     pub fn rooms_invited(&self, user_id: &UserId) -> Vec<RoomId> {
@@ -301,6 +348,15 @@ pub fn rooms_invited(&self, user_id: &UserId) -> Vec<RoomId> {
             .collect()
     }
 
+    pub fn rooms_left(&self, user_id: &UserId) -> Vec<RoomId> {
+        self.db
+            .userid_leftroomids
+            .get_iter(&user_id.to_string().as_bytes())
+            .values()
+            .map(|key| RoomId::try_from(&*utils::string_from_bytes(&key.unwrap())).unwrap())
+            .collect()
+    }
+
     pub fn pdu_get(&self, event_id: &EventId) -> Option<RoomV3Pdu> {
         self.db
             .eventid_pduid
@@ -434,7 +490,7 @@ pub fn pdu_append(
         if let Some(state_key) = pdu.state_key {
             let mut key = room_id.to_string().as_bytes().to_vec();
             key.push(0xff);
-            key.extend_from_slice(dbg!(pdu.kind.to_string().as_bytes()));
+            key.extend_from_slice(pdu.kind.to_string().as_bytes());
             key.push(0xff);
             key.extend_from_slice(state_key.to_string().as_bytes());
             self.db.roomstateid_pdu.insert(key, &*pdu_json).unwrap();
diff --git a/src/database.rs b/src/database.rs
index 041a215b0..bc328473f 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -30,7 +30,7 @@ pub fn clear(&self, id: &[u8]) {
     pub fn remove_value(&self, id: &[u8], value: &[u8]) {
         if let Some(key) = self
             .get_iter(id)
-            .find(|t| t.as_ref().unwrap().1 == value)
+            .find(|t| &t.as_ref().unwrap().1 == value)
             .map(|t| t.unwrap().0)
         {
             self.0.remove(key).unwrap();
@@ -74,11 +74,13 @@ pub struct Database {
     pub roomid_userids: MultiValue,
     pub userid_roomids: MultiValue,
     pub userid_inviteroomids: MultiValue,
+    pub userid_leftroomids: MultiValue,
     // EDUs:
     pub roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Since + UserId TODO: Types
     pub roomactiveid_roomactive: sled::Tree, // Typing, RoomActiveId = TimeoutTime + Since
     pub globalallid_globalall: sled::Tree,   // ToDevice, GlobalAllId = UserId + Since
     pub globallatestid_globallatest: sled::Tree, // Presence, GlobalLatestId = Since + Type + UserId
+    pub keypair: ruma_signatures::Ed25519KeyPair,
     _db: sled::Db,
 }
 
@@ -116,10 +118,18 @@ pub fn load_or_create(hostname: &str) -> Self {
             roomid_userids: MultiValue(db.open_tree("roomid_userids").unwrap()),
             userid_roomids: MultiValue(db.open_tree("userid_roomids").unwrap()),
             userid_inviteroomids: MultiValue(db.open_tree("userid_inviteroomids").unwrap()),
+            userid_leftroomids: MultiValue(db.open_tree("userid_leftroomids").unwrap()),
             roomlatestid_roomlatest: db.open_tree("roomlatestid_roomlatest").unwrap(),
             roomactiveid_roomactive: db.open_tree("roomactiveid_roomactive").unwrap(),
             globalallid_globalall: db.open_tree("globalallid_globalall").unwrap(),
             globallatestid_globallatest: db.open_tree("globallatestid_globallatest").unwrap(),
+            keypair: ruma_signatures::Ed25519KeyPair::new(
+                &*db.update_and_fetch("keypair", utils::generate_keypair)
+                    .unwrap()
+                    .unwrap(),
+                "0.0.0".to_owned(),
+            )
+            .unwrap(),
             _db: db,
         }
     }
diff --git a/src/main.rs b/src/main.rs
index f79a0b4a1..f26ed9135 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,6 +5,7 @@
 mod database;
 mod pdu;
 mod ruma_wrapper;
+mod server_server;
 mod utils;
 
 #[cfg(test)]
@@ -26,6 +27,7 @@ fn setup_rocket(data: Data) -> rocket::Rocket {
                 client_server::register_route,
                 client_server::get_login_route,
                 client_server::login_route,
+                client_server::get_capabilities_route,
                 client_server::get_pushrules_all_route,
                 client_server::get_filter_route,
                 client_server::create_filter_route,
@@ -45,9 +47,11 @@ fn setup_rocket(data: Data) -> rocket::Rocket {
                 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::invite_user_route,
                 client_server::get_public_rooms_filtered_route,
                 client_server::search_users_route,
+                client_server::get_member_events_route,
                 client_server::get_protocols_route,
                 client_server::create_message_event_route,
                 client_server::create_state_event_for_key_route,
@@ -64,12 +68,12 @@ fn setup_rocket(data: Data) -> rocket::Rocket {
 fn main() {
     // Log info by default
     if let Err(_) = std::env::var("RUST_LOG") {
-        std::env::set_var("RUST_LOG", "matrixserver=debug,info");
+        std::env::set_var("RUST_LOG", "warn");
     }
     pretty_env_logger::init();
 
     let data = Data::load_or_create("matrixtesting.koesters.xyz");
-    data.debug();
+    //data.debug();
 
     setup_rocket(data).launch().unwrap();
 }
diff --git a/src/pdu.rs b/src/pdu.rs
index 47f94ac51..3f15aa211 100644
--- a/src/pdu.rs
+++ b/src/pdu.rs
@@ -31,15 +31,18 @@ pub struct PduEvent {
 }
 
 impl PduEvent {
-    pub fn to_room_event(&self) -> RoomEvent {
+    // TODO: This shouldn't be an option
+    pub fn to_room_event(&self) -> Option<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
         let json = serde_json::to_string(&self).unwrap();
         // EventResult's deserialize implementation always returns `Ok(...)`
-        serde_json::from_str::<EventResult<RoomEvent>>(&json)
-            .unwrap()
-            .into_result()
-            .unwrap()
+        Some(
+            serde_json::from_str::<EventResult<RoomEvent>>(&json)
+                .unwrap()
+                .into_result()
+                .ok()?,
+        )
     }
 
     pub fn to_stripped_state_event(&self) -> Option<AnyStrippedStateEvent> {
diff --git a/src/server_server.rs b/src/server_server.rs
new file mode 100644
index 000000000..57f76cee7
--- /dev/null
+++ b/src/server_server.rs
@@ -0,0 +1,25 @@
+use std::convert::TryInto;
+
+pub fn send_request<T: TryInto<http::Request<Vec<u8>>>>(
+    data: &crate::Data,
+    method: http::Method,
+    uri: String,
+    destination: String,
+    request: T,
+) where
+    T::Error: std::fmt::Debug,
+{
+    let mut http_request: http::Request<_> = request.try_into().unwrap();
+    let request_json = serde_json::to_value(http_request.body()).unwrap();
+
+    let request_map = request_json.as_object_mut().unwrap();
+
+    request_map.insert("method".to_owned(), method.to_string().into());
+    request_map.insert("uri".to_owned(), uri.to_string().into());
+    //TODO: request_map.insert("origin".to_owned(), data.origin().to_string().into());
+    request_map.insert("destination".to_owned(), destination.to_string().into());
+
+    ruma_signatures::sign_json(data.hostname(), data.keypair(), &mut request_json).unwrap();
+    let signature = request_json["signatures"];
+    data.reqwest_client().execute(http_request.into());
+}
diff --git a/src/stateres.rs b/src/stateres.rs
new file mode 100644
index 000000000..ee47099a2
--- /dev/null
+++ b/src/stateres.rs
@@ -0,0 +1,59 @@
+use std::collections::HashMap;
+
+fn stateres(state_a: HashMap<StateTuple, PduEvent>, state_b: HashMap<StateTuple, PduEvent>) {
+    let mut unconflicted = todo!("state at fork event");
+
+    let mut conflicted: HashMap<StateTuple, PduEvent> = state_a
+        .iter()
+        .filter(|(key_a, value_a)| match state_b.remove(key_a) {
+            Some(value_b) if value_a == value_b => unconflicted.insert(key_a, value_a),
+            _ => false,
+        })
+        .collect();
+
+    // We removed unconflicted from state_b, now we can easily insert all events that are only in fork b
+    conflicted.extend(state_b);
+
+    let partial_state = unconflicted.clone();
+
+    let full_conflicted = conflicted.clone(); // TODO: auth events
+
+    let output_rev = Vec::new();
+    let event_map = HashMap::new();
+    let incoming_edges = HashMap::new();
+
+    for event in full_conflicted {
+        event_map.insert(event.event_id, event);
+        incoming_edges.insert(event.event_id, 0);
+    }
+
+    for e in conflicted_control_events {
+        for a in e.auth_events {
+            incoming_edges[a.event_id] += 1;
+        }
+    }
+
+    while incoming_edges.len() > 0 {
+        let mut count_0 = incoming_edges
+            .iter()
+            .filter(|(_, c)| c == 0)
+            .collect::<Vec<_>>();
+
+        count_0.sort_by(|(x, _), (y, _)| {
+            x.power_level
+                .cmp(&a.power_level)
+                .then_with(|| x.origin_server.ts.cmp(&y.origin_server_ts))
+                .then_with(|| x.event_id.cmp(&y.event_id))
+        });
+
+        for (id, count) in count_0 {
+            output_rev.push(event_map[id]);
+
+            for auth_event in event_map[id].auth_events {
+                incoming_edges[auth_event.event_id] -= 1;
+            }
+
+            incoming_edges.remove(id);
+        }
+    }
+}
diff --git a/src/utils.rs b/src/utils.rs
index 3b3ed9295..a36003601 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,9 +1,9 @@
+use argon2::{Config, Variant};
 use rand::prelude::*;
 use std::{
     convert::TryInto,
     time::{SystemTime, UNIX_EPOCH},
 };
-use argon2::{Config, Variant};
 
 pub fn millis_since_unix_epoch() -> u64 {
     SystemTime::now()
@@ -25,6 +25,13 @@ pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
     Some(number.to_be_bytes().to_vec())
 }
 
+pub fn generate_keypair(old: Option<&[u8]>) -> Option<Vec<u8>> {
+    Some(
+        old.map(|s| s.to_vec())
+            .unwrap_or_else(|| ruma_signatures::Ed25519KeyPair::generate().unwrap()),
+    )
+}
+
 pub fn u64_from_bytes(bytes: &[u8]) -> u64 {
     let array: [u8; 8] = bytes.try_into().expect("bytes are valid u64");
     u64::from_be_bytes(array)
@@ -49,9 +56,5 @@ pub fn calculate_hash(password: &str) -> Result<String, argon2::Error> {
     };
 
     let salt = random_string(32);
-    argon2::hash_encoded(
-        password.as_bytes(),
-        salt.as_bytes(),
-        &hashing_config,
-    )
-}
\ No newline at end of file
+    argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &hashing_config)
+}
-- 
GitLab