diff --git a/Cargo.lock b/Cargo.lock index 258af13..6b18e1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c" +dependencies = [ + "gimli", +] + [[package]] name = "adler32" version = "1.1.0" @@ -26,13 +35,13 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "async-trait" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cb5d814ab2a47fd66d3266e9efccb53ca4c740b7451043b8ffcf9a6208f3f8" +checksum = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -43,7 +52,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -52,6 +61,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "backtrace" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16" version = "0.2.1" @@ -75,9 +98,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "bitflags" @@ -116,15 +139,18 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" +dependencies = [ + "loom", +] [[package]] name = "cc" -version = "1.0.54" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" +checksum = "b1be3409f94d7bdceeb5f5fac551039d9b3f00e25da7a74fc4d33400a0d96368" [[package]] name = "cfg-if" @@ -151,7 +177,7 @@ checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" name = "conduit" version = "0.1.0" dependencies = [ - "base64 0.12.2", + "base64 0.12.3", "directories", "http", "image", @@ -273,7 +299,7 @@ dependencies = [ "bitflags", "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -294,14 +320,14 @@ checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ "libc", "redox_users", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "dtoa" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" [[package]] name = "encoding_rs" @@ -340,7 +366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -416,7 +442,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -463,6 +489,19 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generator" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi 0.3.9", +] + [[package]] name = "getrandom" version = "0.1.14" @@ -484,6 +523,12 @@ dependencies = [ "lzw", ] +[[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" + [[package]] name = "h2" version = "0.2.5" @@ -598,9 +643,9 @@ dependencies = [ [[package]] name = "image" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d534e95ad8b9d5aa614322d02352b4f1bf962254adcf02ac6f2def8be18498e8" +checksum = "b5b0553fec6407d63fe2975b794dfb099f3f790bdc958823851af37b26404ab4" dependencies = [ "bytemuck", "byteorder", @@ -632,9 +677,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jpeg-decoder" @@ -656,9 +701,9 @@ dependencies = [ [[package]] name = "js_int" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ab7bb370a788ad675863e035fd9bfa56a66a030a16a88ab80aeb6b18cbdf31" +checksum = "1b2b63d60564122f2a7d6592c2f1d6c1c60e7a266b4d24715950a1ddad784f66" dependencies = [ "serde", ] @@ -703,6 +748,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "loom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", +] + [[package]] name = "lzw" version = "0.10.0" @@ -829,7 +885,7 @@ checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -855,9 +911,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" dependencies = [ "autocfg", "num-integer", @@ -883,6 +939,12 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" + [[package]] name = "once_cell" version = "1.4.0" @@ -891,9 +953,9 @@ checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" [[package]] name = "openssl" -version = "0.10.29" +version = "0.10.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" dependencies = [ "bitflags", "cfg-if", @@ -943,7 +1005,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -997,7 +1059,7 @@ checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -1063,7 +1125,7 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid 0.2.1", ] [[package]] @@ -1148,7 +1210,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1157,7 +1219,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" dependencies = [ - "base64 0.12.2", + "base64 0.12.3", "bytes", "encoding_rs", "futures-core", @@ -1187,9 +1249,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.14" +version = "0.16.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06b3fefa4f12272808f809a0af618501fdaba41a58963c5fb72238ab0be09603" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" dependencies = [ "cc", "libc", @@ -1197,7 +1259,7 @@ dependencies = [ "spin", "untrusted", "web-sys", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1255,13 +1317,13 @@ dependencies = [ "time", "tokio", "tokio-rustls", - "unicode-xid 0.2.0", + "unicode-xid 0.2.1", ] [[package]] name = "ruma" version = "0.1.0" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "ruma-api", "ruma-client-api", @@ -1275,7 +1337,7 @@ dependencies = [ [[package]] name = "ruma-api" version = "0.16.1" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "http", "percent-encoding 2.1.0", @@ -1290,17 +1352,17 @@ dependencies = [ [[package]] name = "ruma-api-macros" version = "0.16.1" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] name = "ruma-client-api" version = "0.9.0" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "http", "js_int", @@ -1317,7 +1379,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.1.3" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "matches", "ruma-serde", @@ -1348,13 +1410,13 @@ source = "git+https://github.com/ruma/ruma-events?rev=c1ee72d#c1ee72db0f3107a97f dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] name = "ruma-federation-api" version = "0.0.2" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "js_int", "matches", @@ -1369,7 +1431,7 @@ dependencies = [ [[package]] name = "ruma-identifiers" version = "0.16.2" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "rand", "serde", @@ -1379,7 +1441,7 @@ dependencies = [ [[package]] name = "ruma-serde" version = "0.2.2" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ "dtoa", "itoa", @@ -1392,9 +1454,9 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.6.0-dev.1" -source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" dependencies = [ - "base64 0.12.2", + "base64 0.12.3", "ring", "serde_json", "untrusted", @@ -1418,12 +1480,27 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" dependencies = [ - "base64 0.12.2", + "base64 0.12.3", "blake2b_simd", "constant_time_eq", "crossbeam-utils", ] +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.16.0" @@ -1450,9 +1527,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", - "winapi 0.3.8", + "winapi 0.3.9", ] +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1493,23 +1576,38 @@ dependencies = [ ] [[package]] -name = "serde" -version = "1.0.112" +name = "semver" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.112" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -1553,10 +1651,11 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "sled" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb6824dde66ad33bf20c6e8476f5b82b871bc8bc3c129a10ea2f7dae5060fa3" +checksum = "cdad3dc85d888056d3bd9954ffdf22d8a22701b6cd3aca4f6df4c436111898c4" dependencies = [ + "backtrace", "crc32fast", "crossbeam-epoch", "crossbeam-utils", @@ -1582,7 +1681,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1615,7 +1714,7 @@ dependencies = [ "heck", "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -1631,13 +1730,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "unicode-xid 0.2.0", + "unicode-xid 0.2.1", ] [[package]] @@ -1651,7 +1750,7 @@ dependencies = [ "rand", "redox_syscall", "remove_dir_all", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1671,7 +1770,7 @@ checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -1681,9 +1780,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] +[[package]] +name = "tinyvec" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" + [[package]] name = "tokio" version = "0.2.21" @@ -1704,7 +1809,7 @@ dependencies = [ "signal-hook-registry", "slab", "tokio-macros", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1715,7 +1820,7 @@ checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] @@ -1795,11 +1900,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" dependencies = [ - "smallvec", + "tinyvec", ] [[package]] @@ -1816,9 +1921,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "untrusted" @@ -1888,7 +1993,7 @@ dependencies = [ "log", "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", "wasm-bindgen-shared", ] @@ -1922,7 +2027,7 @@ checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1961,9 +2066,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -1993,7 +2098,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 62d7b7a..ba3dad8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "4928e35ec5c4b9242f50d644282d9896d0160a10", features = ["tls"] } http = "0.2.1" log = "0.4.8" -sled = "0.31.0" +sled = "0.32.0" directories = "2.0.2" js_int = "0.1.5" serde_json = { version = "1.0.53", features = ["raw_value"] } @@ -29,15 +29,16 @@ thiserror = "1.0.19" image = { version = "0.23.4", default-features = false, features = ["jpeg", "png", "gif"] } [dependencies.ruma] -git = "https://github.com/ruma/ruma" -rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" +git = "https://github.com/timokoesters/ruma" +#rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" #path = "../ruma/ruma" features = ["rand", "client-api", "federation-api"] # These are required only until ruma-events and ruma-federation-api are merged into ruma/ruma [patch.crates-io] -ruma-common = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" } -ruma-serde = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" } -ruma-identifiers = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" } +ruma-common = { git = "https://github.com/timokoesters/ruma" } +ruma-serde = { git = "https://github.com/timokoesters/ruma" } +ruma-identifiers = { git = "https://github.com/timokoesters/ruma" } #ruma-common = { path = "../ruma/ruma-common" } #ruma-serde = { path = "../ruma/ruma-serde" } +#ruma-identifiers = { path = "../ruma/ruma-identifiers" } diff --git a/src/client_server.rs b/src/client_server.rs index e49bfb4..49692a2 100644 --- a/src/client_server.rs +++ b/src/client_server.rs @@ -5,6 +5,7 @@ use std::{ }; use crate::{utils, ConduitResult, Database, Error, Ruma}; +use keys::{upload_signatures, upload_signing_keys}; use log::warn; use rocket::{delete, get, options, post, put, State}; use ruma::{ @@ -13,6 +14,10 @@ use ruma::{ r0::{ account::{get_username_availability, register}, alias::{create_alias, delete_alias, get_alias}, + backup::{ + add_backup_keys, create_backup, get_backup, get_backup_keys, get_latest_backup, + update_backup, + }, capabilities::get_capabilities, config::{get_global_account_data, set_global_account_data}, context::get_context, @@ -33,7 +38,7 @@ use ruma::{ profile::{ get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name, }, - push::{get_pushrules_all, set_pushrule, set_pushrule_enabled}, + push::{get_pushers, get_pushrules_all, set_pushrule, set_pushrule_enabled}, read_marker::set_read_marker, redact::redact_event, room::{self, create_room}, @@ -71,9 +76,13 @@ const SESSION_ID_LENGTH: usize = 256; #[get("/_matrix/client/versions")] pub fn get_supported_versions_route() -> ConduitResult { + let mut unstable_features = BTreeMap::new(); + + unstable_features.insert("org.matrix.e2e_cross_signing".to_owned(), true); + Ok(get_supported_versions::Response { versions: vec!["r0.5.0".to_owned(), "r0.6.0".to_owned()], - unstable_features: BTreeMap::new(), + unstable_features, } .into()) } @@ -204,33 +213,7 @@ pub fn register_route( &EventType::PushRules, serde_json::to_value(ruma::events::push_rules::PushRulesEvent { content: ruma::events::push_rules::PushRulesEventContent { - global: ruma::events::push_rules::Ruleset { - content: vec![], - override_: vec![ruma::events::push_rules::ConditionalPushRule { - actions: vec![ruma::events::push_rules::Action::DontNotify], - default: true, - enabled: false, - rule_id: ".m.rule.master".to_owned(), - conditions: vec![], - }], - room: vec![], - sender: vec![], - underride: vec![ruma::events::push_rules::ConditionalPushRule { - actions: vec![ - ruma::events::push_rules::Action::Notify, - ruma::events::push_rules::Action::SetTweak(ruma::push::Tweak::Sound( - "default".to_owned(), - )), - ], - default: true, - enabled: true, - rule_id: ".m.rule.message".to_owned(), - conditions: vec![ruma::events::push_rules::PushCondition::EventMatch { - key: "type".to_owned(), - pattern: "m.room.message".to_owned(), - }], - }], - }, + global: crate::push_rules::default_pushrules(&user_id), }, }) .expect("data is valid, we just created it") @@ -375,11 +358,11 @@ pub fn get_pushrules_all_route( #[put( "/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>", - data = "" + //data = "" )] pub fn set_pushrule_route( - db: State<'_, Database>, - body: Ruma, + //db: State<'_, Database>, + //body: Ruma, _scope: String, _kind: String, _rule_id: String, @@ -502,8 +485,7 @@ pub fn set_displayname_route( displayname: body.displayname.clone(), ..serde_json::from_value::>( db.rooms - .room_state(&room_id)? - .get(&(EventType::RoomMember, user_id.to_string())) + .room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())? .ok_or_else(|| { Error::bad_database( "Tried to send displayname update for user not in the room.", @@ -593,8 +575,7 @@ pub fn set_avatar_url_route( avatar_url: body.avatar_url.clone(), ..serde_json::from_value::>( db.rooms - .room_state(&room_id)? - .get(&(EventType::RoomMember, user_id.to_string())) + .room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())? .ok_or_else(|| { Error::bad_database( "Tried to send avatar url update for user not in the room.", @@ -722,8 +703,11 @@ pub fn upload_keys_route( } if let Some(device_keys) = &body.device_keys { - db.users - .add_device_keys(user_id, device_id, device_keys, &db.globals)?; + // This check is needed to assure that signatures are kept + if db.users.get_device_keys(user_id, device_id)?.is_none() { + db.users + .add_device_keys(user_id, device_id, device_keys, &db.globals)?; + } } Ok(upload_keys::Response { @@ -737,33 +721,38 @@ pub fn get_keys_route( db: State<'_, Database>, body: Ruma, ) -> ConduitResult { + let sender_id = body.user_id.as_ref().expect("user is authenticated"); + + let mut master_keys = BTreeMap::new(); + let mut self_signing_keys = BTreeMap::new(); + let mut user_signing_keys = BTreeMap::new(); let mut device_keys = BTreeMap::new(); for (user_id, device_ids) in &body.device_keys { if device_ids.is_empty() { let mut container = BTreeMap::new(); - for result in db.users.all_device_keys(&user_id.clone()) { - let (device_id, mut keys) = result?; + for device_id in db.users.all_device_ids(user_id) { + let device_id = device_id?; + if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? { + let metadata = db + .users + .get_device_metadata(user_id, &device_id)? + .ok_or_else(|| { + Error::bad_database("all_device_keys contained nonexistent device.") + })?; - let metadata = db - .users - .get_device_metadata(user_id, &device_id)? - .ok_or_else(|| { - Error::bad_database("all_device_keys contained nonexistent device.") - })?; + keys.unsigned = Some(keys::UnsignedDeviceInfo { + device_display_name: metadata.display_name, + }); - keys.unsigned = Some(keys::UnsignedDeviceInfo { - device_display_name: metadata.display_name, - }); - - container.insert(device_id, keys); + container.insert(device_id.to_owned(), keys); + } } device_keys.insert(user_id.clone(), container); } else { for device_id in device_ids { let mut container = BTreeMap::new(); - for keys in db.users.get_device_keys(&user_id.clone(), &device_id) { - let mut keys = keys?; + if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? { let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or( Error::BadRequest( ErrorKind::InvalidParam, @@ -780,11 +769,26 @@ pub fn get_keys_route( device_keys.insert(user_id.clone(), container); } } + + if let Some(master_key) = db.users.get_master_key(user_id, sender_id)? { + master_keys.insert(user_id.clone(), master_key); + } + if let Some(self_signing_key) = db.users.get_self_signing_key(user_id, sender_id)? { + self_signing_keys.insert(user_id.clone(), self_signing_key); + } + if user_id == sender_id { + if let Some(user_signing_key) = db.users.get_user_signing_key(sender_id)? { + user_signing_keys.insert(user_id.clone(), user_signing_key); + } + } } Ok(get_keys::Response { - failures: BTreeMap::new(), + master_keys, + self_signing_keys, + user_signing_keys, device_keys, + failures: BTreeMap::new(), } .into()) } @@ -817,6 +821,125 @@ pub fn claim_keys_route( .into()) } +#[post("/_matrix/client/unstable/room_keys/version", data = "")] +pub fn create_backup_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + let version = db + .key_backups + .create_backup(&user_id, &body.algorithm, &db.globals)?; + + Ok(create_backup::Response { version }.into()) +} + +#[put( + "/_matrix/client/unstable/room_keys/version/<_version>", + data = "" +)] +pub fn update_backup_route( + db: State<'_, Database>, + body: Ruma, + _version: String, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + db.key_backups + .update_backup(&user_id, &body.version, &body.algorithm, &db.globals)?; + + Ok(update_backup::Response.into()) +} + +#[get("/_matrix/client/unstable/room_keys/version", data = "")] +pub fn get_latest_backup_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + + let (version, algorithm) = + db.key_backups + .get_latest_backup(&user_id)? + .ok_or(Error::BadRequest( + ErrorKind::NotFound, + "Key backup does not exist.", + ))?; + + Ok(get_latest_backup::Response { + algorithm, + count: (db.key_backups.count_keys(user_id, &version)? as u32).into(), + etag: db.key_backups.get_etag(user_id, &version)?, + version, + } + .into()) +} + +#[get( + "/_matrix/client/unstable/room_keys/version/<_version>", + data = "" +)] +pub fn get_backup_route( + db: State<'_, Database>, + body: Ruma, + _version: String, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + let algorithm = + db.key_backups + .get_backup(&user_id, &body.version)? + .ok_or(Error::BadRequest( + ErrorKind::NotFound, + "Key backup does not exist.", + ))?; + + Ok(get_backup::Response { + algorithm, + count: (db.key_backups.count_keys(user_id, &body.version)? as u32).into(), + etag: db.key_backups.get_etag(user_id, &body.version)?, + version: body.version.clone(), + } + .into()) +} + +#[put("/_matrix/client/unstable/room_keys/keys", data = "")] +pub fn add_backup_keys_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + + for (room_id, room) in &body.rooms { + for (session_id, key_data) in &room.sessions { + db.key_backups.add_key( + &user_id, + &body.version, + &room_id, + &session_id, + &key_data, + &db.globals, + )? + } + } + + Ok(add_backup_keys::Response { + count: (db.key_backups.count_keys(user_id, &body.version)? as u32).into(), + etag: db.key_backups.get_etag(user_id, &body.version)?, + } + .into()) +} + +#[get("/_matrix/client/unstable/room_keys/keys", data = "")] +pub fn get_backup_keys_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + + let rooms = db.key_backups.get_all(&user_id, &body.version)?; + + Ok(get_backup_keys::Response { rooms }.into()) +} + #[post("/_matrix/client/r0/rooms/<_room_id>/read_markers", data = "")] pub fn set_read_marker_route( db: State<'_, Database>, @@ -1265,35 +1388,13 @@ pub fn join_room_by_id_route( // TODO: Ask a remote server if we don't have this room - let event = db - .rooms - .room_state(&body.room_id)? - .get(&(EventType::RoomMember, user_id.to_string())) - .map_or_else( - || { - // There was no existing membership event - Ok::<_, Error>(member::MemberEventContent { - membership: member::MembershipState::Join, - displayname: db.users.displayname(&user_id)?, - avatar_url: db.users.avatar_url(&user_id)?, - is_direct: None, - third_party_invite: None, - }) - }, - |pdu| { - // We change the existing membership event - let mut event = serde_json::from_value::>( - pdu.content.clone(), - ) - .map_err(|_| Error::bad_database("Invalid member event in db."))? - .deserialize() - .map_err(|_| Error::bad_database("Invalid member event in db."))?; - event.membership = member::MembershipState::Join; - event.displayname = db.users.displayname(&user_id)?; - event.avatar_url = db.users.avatar_url(&user_id)?; - Ok(event) - }, - )?; + let event = member::MemberEventContent { + membership: member::MembershipState::Join, + displayname: db.users.displayname(&user_id)?, + avatar_url: db.users.avatar_url(&user_id)?, + is_direct: None, + third_party_invite: None, + }; db.rooms.append_pdu( body.room_id.clone(), @@ -1348,11 +1449,10 @@ pub fn leave_room_route( _room_id: String, ) -> ConduitResult { let user_id = body.user_id.as_ref().expect("user is authenticated"); - let state = db.rooms.room_state(&body.room_id)?; let mut event = serde_json::from_value::>( - state - .get(&(EventType::RoomMember, user_id.to_string())) + db.rooms + .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? .ok_or(Error::BadRequest( ErrorKind::BadState, "Cannot leave a room you are not a member of.", @@ -1387,12 +1487,11 @@ pub fn kick_user_route( _room_id: String, ) -> ConduitResult { let user_id = body.user_id.as_ref().expect("user is authenticated"); - let state = db.rooms.room_state(&body.room_id)?; let mut event = serde_json::from_value::>( - state - .get(&(EventType::RoomMember, user_id.to_string())) + db.rooms + .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? .ok_or(Error::BadRequest( ErrorKind::BadState, "Cannot kick member that's not in the room.", @@ -1428,12 +1527,12 @@ pub fn ban_user_route( _room_id: String, ) -> ConduitResult { let user_id = body.user_id.as_ref().expect("user is authenticated"); - let state = db.rooms.room_state(&body.room_id)?; // TODO: reason - let event = state - .get(&(EventType::RoomMember, user_id.to_string())) + let event = db + .rooms + .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? .map_or( Ok::<_, Error>(member::MemberEventContent { membership: member::MembershipState::Ban, @@ -1475,12 +1574,11 @@ pub fn unban_user_route( _room_id: String, ) -> ConduitResult { let user_id = body.user_id.as_ref().expect("user is authenticated"); - let state = db.rooms.room_state(&body.room_id)?; let mut event = serde_json::from_value::>( - state - .get(&(EventType::RoomMember, user_id.to_string())) + db.rooms + .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? .ok_or(Error::BadRequest( ErrorKind::BadState, "Cannot unban a user who is not banned.", @@ -1642,7 +1740,8 @@ pub async fn get_public_rooms_filtered_route( .map(|room_id| { let room_id = room_id?; - let state = db.rooms.room_state(&room_id)?; + // TODO: Do not load full state? + let state = db.rooms.room_state_full(&room_id)?; let chunk = directory::PublicRoomsChunk { aliases: Vec::new(), @@ -1774,10 +1873,30 @@ pub fn search_users_route( .into()) } -#[get("/_matrix/client/r0/rooms/<_room_id>/members")] -pub fn get_member_events_route(_room_id: String) -> ConduitResult { - warn!("TODO: get_member_events_route"); - Ok(get_member_events::Response { chunk: Vec::new() }.into()) +#[get("/_matrix/client/r0/rooms/<_room_id>/members", data = "")] +pub fn get_member_events_route( + db: State<'_, Database>, + body: Ruma, + _room_id: String, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + + if !db.rooms.is_joined(user_id, &body.room_id)? { + return Err(Error::BadRequest( + ErrorKind::Forbidden, + "You don't have permission to view this room.", + )); + } + + Ok(get_member_events::Response { + chunk: db + .rooms + .room_state_type(&body.room_id, &EventType::RoomMember)? + .values() + .map(|pdu| pdu.to_member_event()) + .collect(), + } + .into()) } #[get("/_matrix/client/r0/thirdparty/protocols")] @@ -1951,7 +2070,7 @@ pub fn get_state_events_route( Ok(get_state_events::Response { room_state: db .rooms - .room_state(&body.room_id)? + .room_state_full(&body.room_id)? .values() .map(|pdu| pdu.to_state_event()) .collect(), @@ -1979,10 +2098,9 @@ pub fn get_state_events_for_key_route( )); } - let state = db.rooms.room_state(&body.room_id)?; - - let event = state - .get(&(body.event_type.clone(), body.state_key.clone())) + let event = db + .rooms + .room_state_get(&body.room_id, &body.event_type, &body.state_key)? .ok_or(Error::BadRequest( ErrorKind::NotFound, "State event not found.", @@ -2014,17 +2132,16 @@ pub fn get_state_events_for_empty_key_route( )); } - let state = db.rooms.room_state(&body.room_id)?; - - let event = state - .get(&(body.event_type.clone(), "".to_owned())) + let event = db + .rooms + .room_state_get(&body.room_id, &body.event_type, "")? .ok_or(Error::BadRequest( ErrorKind::NotFound, "State event not found.", ))?; Ok(get_state_events_for_empty_key::Response { - content: serde_json::value::to_raw_value(event) + content: serde_json::value::to_raw_value(&event) .map_err(|_| Error::bad_database("Invalid event content in database"))?, } .into()) @@ -2053,7 +2170,7 @@ pub fn sync_route( let mut pdus = db .rooms - .pdus_since(&room_id, since)? + .pdus_since(&user_id, &room_id, since)? .filter_map(|r| r.ok()) // Filter out buggy events .collect::>(); @@ -2068,7 +2185,7 @@ pub fn sync_route( let content = serde_json::from_value::< EventJson, >(pdu.content.clone()) - .map_err(|_| Error::bad_database("Invalid PDU in database."))? + .expect("EventJson::from_value always works") .deserialize() .map_err(|_| Error::bad_database("Invalid PDU in database."))?; if content.membership == ruma::events::room::member::MembershipState::Join { @@ -2081,7 +2198,7 @@ pub fn sync_route( } } - let state = db.rooms.room_state(&room_id)?; + let members = db.rooms.room_state_type(&room_id, &EventType::RoomMember)?; let (joined_member_count, invited_member_count, heroes) = if send_member_count { let joined_member_count = db.rooms.room_members(&room_id).count(); @@ -2096,7 +2213,7 @@ pub fn sync_route( for hero in db .rooms - .all_pdus(&room_id)? + .all_pdus(&user_id, &room_id)? .filter_map(|pdu| pdu.ok()) // Ignore all broken pdus .filter(|pdu| pdu.kind == EventType::RoomMember) .map(|pdu| { @@ -2111,8 +2228,8 @@ pub fn sync_route( let current_content = serde_json::from_value::< EventJson, >( - state - .get(&(EventType::RoomMember, state_key.clone())) + members + .get(state_key) .ok_or_else(|| { Error::bad_database( "A user that joined once has no member event anymore.", @@ -2170,7 +2287,7 @@ pub fn sync_route( if let Some(last_read) = db.rooms.edus.room_read_get(&room_id, &user_id)? { Some( (db.rooms - .pdus_since(&room_id, last_read)? + .pdus_since(&user_id, &room_id, last_read)? .filter_map(|pdu| pdu.ok()) // Filter out buggy events .filter(|pdu| { matches!( @@ -2264,7 +2381,8 @@ pub fn sync_route( // TODO: state before timeline state: sync_events::State { events: if joined_since_last_sync { - state + db.rooms + .room_state_full(&room_id)? .into_iter() .map(|(_, pdu)| pdu.to_state_event()) .collect() @@ -2283,7 +2401,7 @@ pub fn sync_route( let mut left_rooms = BTreeMap::new(); for room_id in db.rooms.rooms_left(&user_id) { let room_id = room_id?; - let pdus = db.rooms.pdus_since(&room_id, since)?; + let pdus = db.rooms.pdus_since(&user_id, &room_id, since)?; let room_events = pdus .filter_map(|pdu| pdu.ok()) // Filter out buggy events .map(|pdu| pdu.to_room_event()) @@ -2337,7 +2455,7 @@ pub fn sync_route( invite_state: sync_events::InviteState { events: db .rooms - .room_state(&room_id)? + .room_state_full(&room_id)? .into_iter() .map(|(_, pdu)| pdu.to_stripped_state_event()) .collect(), @@ -2387,7 +2505,7 @@ pub fn sync_route( device_lists: sync_events::DeviceLists { changed: if since != 0 { db.users - .device_keys_changed(since) + .keys_changed(since) .filter_map(|u| u.ok()) .collect() // Filter out buggy events } else { @@ -2438,7 +2556,7 @@ pub fn get_context_route( let events_before = db .rooms - .pdus_until(&body.room_id, base_token) + .pdus_until(&user_id, &body.room_id, base_token) .take( u32::try_from(body.limit).map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") @@ -2464,7 +2582,7 @@ pub fn get_context_route( let events_after = db .rooms - .pdus_after(&body.room_id, base_token) + .pdus_after(&user_id, &body.room_id, base_token) .take( u32::try_from(body.limit).map_err(|_| { Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") @@ -2496,7 +2614,7 @@ pub fn get_context_route( events_after, state: db // TODO: State at event .rooms - .room_state(&body.room_id)? + .room_state_full(&body.room_id)? .values() .map(|pdu| pdu.to_state_event()) .collect(), @@ -2528,7 +2646,7 @@ pub fn get_message_events_route( get_message_events::Direction::Forward => { let events_after = db .rooms - .pdus_after(&body.room_id, from) + .pdus_after(&user_id, &body.room_id, from) // Use limit or else 10 .take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| { Ok(u32::try_from(l).map_err(|_| { @@ -2563,7 +2681,7 @@ pub fn get_message_events_route( get_message_events::Direction::Backward => { let events_before = db .rooms - .pdus_until(&body.room_id, from) + .pdus_until(&user_id, &body.room_id, from) // Use limit or else 10 .take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| { Ok(u32::try_from(l).map_err(|_| { @@ -2883,6 +3001,122 @@ pub fn delete_devices_route( Ok(delete_devices::Response.into()) } +#[post("/_matrix/client/unstable/keys/device_signing/upload", data = "")] +pub fn upload_signing_keys_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let user_id = body.user_id.as_ref().expect("user is authenticated"); + let device_id = body.device_id.as_ref().expect("user is authenticated"); + + // UIAA + let mut uiaainfo = UiaaInfo { + flows: vec![AuthFlow { + stages: vec!["m.login.password".to_owned()], + }], + completed: Vec::new(), + params: Default::default(), + session: None, + auth_error: None, + }; + + if let Some(auth) = &body.auth { + let (worked, uiaainfo) = db.uiaa.try_auth( + &user_id, + &device_id, + auth, + &uiaainfo, + &db.users, + &db.globals, + )?; + if !worked { + return Err(Error::Uiaa(uiaainfo)); + } + // Success! + } else { + uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); + db.uiaa.create(&user_id, &device_id, &uiaainfo)?; + return Err(Error::Uiaa(uiaainfo)); + } + + if let Some(master_key) = &body.master_key { + db.users.add_cross_signing_keys( + user_id, + &master_key, + &body.self_signing_key, + &body.user_signing_key, + &db.globals, + )?; + } + + Ok(upload_signing_keys::Response.into()) +} + +#[post("/_matrix/client/unstable/keys/signatures/upload", data = "")] +pub fn upload_signatures_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let sender_id = body.user_id.as_ref().expect("user is authenticated"); + + for (user_id, signed_keys) in &body.signed_keys { + for (key_id, signed_key) in signed_keys { + for signature in signed_key + .get("signatures") + .ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Missing signatures field.", + ))? + .get(sender_id.to_string()) + .ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid user in signatures field.", + ))? + .as_object() + .ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid signature.", + ))? + .clone() + .into_iter() + { + // Signature validation? + let signature = ( + signature.0, + signature + .1 + .as_str() + .ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid signature value.", + ))? + .to_owned(), + ); + db.users + .sign_key(&user_id, &key_id, signature, &sender_id, &db.globals)?; + } + } + } + + Ok(upload_signatures::Response.into()) +} + +#[get("/_matrix/client/r0/pushers")] +pub fn pushers_route() -> ConduitResult { + Ok(get_pushers::Response { + pushers: Vec::new(), + } + .into()) +} + +#[post("/_matrix/client/r0/pushers/set")] +pub fn set_pushers_route() -> ConduitResult { + Ok(get_pushers::Response { + pushers: Vec::new(), + } + .into()) +} + #[options("/<_segments..>")] pub fn options_route( _segments: rocket::http::uri::Segments<'_>, diff --git a/src/database.rs b/src/database.rs index 2cc01ea..370fde7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,6 +1,7 @@ pub(self) mod account_data; pub(self) mod global_edus; pub(self) mod globals; +pub(self) mod key_backups; pub(self) mod media; pub(self) mod rooms; pub(self) mod uiaa; @@ -21,6 +22,7 @@ pub struct Database { pub account_data: account_data::AccountData, pub global_edus: global_edus::GlobalEdus, pub media: media::Media, + pub key_backups: key_backups::KeyBackups, pub _db: sled::Db, } @@ -73,8 +75,11 @@ impl Database { userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?, token_userdeviceid: db.open_tree("token_userdeviceid")?, onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?, - userdeviceid_devicekeys: db.open_tree("userdeviceid_devicekeys")?, - devicekeychangeid_userid: db.open_tree("devicekeychangeid_userid")?, + keychangeid_userid: db.open_tree("devicekeychangeid_userid")?, + keyid_key: db.open_tree("keyid_key")?, + userid_masterkeyid: db.open_tree("userid_masterkeyid")?, + userid_selfsigningkeyid: db.open_tree("userid_selfsigningkeyid")?, + userid_usersigningkeyid: db.open_tree("userid_usersigningkeyid")?, todeviceid_events: db.open_tree("todeviceid_events")?, }, uiaa: uiaa::Uiaa { @@ -111,6 +116,11 @@ impl Database { media: media::Media { mediaid_file: db.open_tree("mediaid_file")?, }, + key_backups: key_backups::KeyBackups { + backupid_algorithm: db.open_tree("backupid_algorithm")?, + backupid_etag: db.open_tree("backupid_etag")?, + backupkeyid_backup: db.open_tree("backupkeyid_backupmetadata")?, + }, _db: db, }) } diff --git a/src/database/key_backups.rs b/src/database/key_backups.rs new file mode 100644 index 0000000..991931b --- /dev/null +++ b/src/database/key_backups.rs @@ -0,0 +1,207 @@ +use crate::{utils, Error, Result}; +use ruma::{ + api::client::{ + error::ErrorKind, + r0::backup::{get_backup_keys::Sessions, BackupAlgorithm, KeyData}, + }, + identifiers::{RoomId, UserId}, +}; +use std::{collections::BTreeMap, convert::TryFrom}; + +pub struct KeyBackups { + pub(super) backupid_algorithm: sled::Tree, // BackupId = UserId + Version(Count) + pub(super) backupid_etag: sled::Tree, // BackupId = UserId + Version(Count) + pub(super) backupkeyid_backup: sled::Tree, // BackupKeyId = UserId + Version + RoomId + SessionId +} + +impl KeyBackups { + pub fn create_backup( + &self, + user_id: &UserId, + backup_metadata: &BackupAlgorithm, + globals: &super::globals::Globals, + ) -> Result { + let version = globals.next_count()?.to_string(); + + let mut key = user_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&version.as_bytes()); + + self.backupid_algorithm.insert( + &key, + &*serde_json::to_string(backup_metadata) + .expect("BackupAlgorithm::to_string always works"), + )?; + self.backupid_etag + .insert(&key, &globals.next_count()?.to_be_bytes())?; + Ok(version) + } + + pub fn update_backup( + &self, + user_id: &UserId, + version: &str, + backup_metadata: &BackupAlgorithm, + globals: &super::globals::Globals, + ) -> Result { + let mut key = user_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&version.as_bytes()); + + if self.backupid_algorithm.get(&key)?.is_none() { + return Err(Error::BadRequest( + ErrorKind::NotFound, + "Tried to update nonexistent backup.", + )); + } + + self.backupid_algorithm.insert( + &key, + &*serde_json::to_string(backup_metadata) + .expect("BackupAlgorithm::to_string always works"), + )?; + self.backupid_etag + .insert(&key, &globals.next_count()?.to_be_bytes())?; + Ok(version.to_string()) + } + + pub fn get_latest_backup(&self, user_id: &UserId) -> Result> { + let mut prefix = user_id.to_string().as_bytes().to_vec(); + prefix.push(0xff); + self.backupid_algorithm + .scan_prefix(&prefix) + .last() + .map_or(Ok(None), |r| { + let (key, value) = r?; + let version = utils::string_from_bytes( + key.rsplit(|&b| b == 0xff) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| Error::bad_database("backupid_algorithm key is invalid."))?; + + Ok(Some(( + version, + serde_json::from_slice(&value).map_err(|_| { + Error::bad_database("Algorithm in backupid_algorithm is invalid.") + })?, + ))) + }) + } + + pub fn get_backup(&self, user_id: &UserId, version: &str) -> Result> { + let mut key = user_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(version.as_bytes()); + + self.backupid_algorithm.get(key)?.map_or(Ok(None), |bytes| { + Ok(serde_json::from_slice(&bytes) + .map_err(|_| Error::bad_database("Algorithm in backupid_algorithm is invalid."))?) + }) + } + + pub fn add_key( + &self, + user_id: &UserId, + version: &str, + room_id: &RoomId, + session_id: &str, + key_data: &KeyData, + globals: &super::globals::Globals, + ) -> Result<()> { + let mut key = user_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(version.as_bytes()); + + if self.backupid_algorithm.get(&key)?.is_none() { + return Err(Error::BadRequest( + ErrorKind::NotFound, + "Tried to update nonexistent backup.", + )); + } + + self.backupid_etag + .insert(&key, &globals.next_count()?.to_be_bytes())?; + + key.push(0xff); + key.extend_from_slice(room_id.to_string().as_bytes()); + key.push(0xff); + key.extend_from_slice(session_id.as_bytes()); + + self.backupkeyid_backup.insert( + &key, + &*serde_json::to_string(&key_data).expect("KeyData::to_string always works"), + )?; + + Ok(()) + } + + pub fn count_keys(&self, user_id: &UserId, version: &str) -> Result { + let mut prefix = user_id.to_string().as_bytes().to_vec(); + prefix.push(0xff); + prefix.extend_from_slice(version.as_bytes()); + + Ok(self.backupkeyid_backup.scan_prefix(&prefix).count()) + } + + pub fn get_etag(&self, user_id: &UserId, version: &str) -> Result { + let mut key = user_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&version.as_bytes()); + + Ok(utils::u64_from_bytes( + &self + .backupid_etag + .get(&key)? + .ok_or_else(|| Error::bad_database("Backup has no etag."))?, + ) + .map_err(|_| Error::bad_database("etag in backupid_etag invalid."))? + .to_string()) + } + + pub fn get_all(&self, user_id: &UserId, version: &str) -> Result> { + let mut prefix = user_id.to_string().as_bytes().to_vec(); + prefix.push(0xff); + prefix.extend_from_slice(version.as_bytes()); + + let mut rooms = BTreeMap::::new(); + + for result in self.backupkeyid_backup.scan_prefix(&prefix).map(|r| { + let (key, value) = r?; + let mut parts = key.rsplit(|&b| b == 0xff); + + let session_id = utils::string_from_bytes( + &parts + .next() + .ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?, + ) + .map_err(|_| Error::bad_database("backupkeyid_backup session_id is invalid."))?; + + let room_id = RoomId::try_from( + utils::string_from_bytes( + &parts + .next() + .ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?, + ) + .map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?, + ) + .map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid room id."))?; + + let key_data = serde_json::from_slice(&value) + .map_err(|_| Error::bad_database("KeyData in backupkeyid_backup is invalid."))?; + + Ok::<_, Error>((room_id, session_id, key_data)) + }) { + let (room_id, session_id, key_data) = result?; + rooms + .entry(room_id) + .or_insert_with(|| Sessions { + sessions: BTreeMap::new(), + }) + .sessions + .insert(session_id, key_data); + } + + Ok(rooms) + } +} diff --git a/src/database/rooms.rs b/src/database/rooms.rs index ee14fc8..b7f7221 100644 --- a/src/database/rooms.rs +++ b/src/database/rooms.rs @@ -56,7 +56,10 @@ impl Rooms { } /// Returns the full room state. - pub fn room_state(&self, room_id: &RoomId) -> Result> { + pub fn room_state_full( + &self, + room_id: &RoomId, + ) -> Result> { let mut hashmap = HashMap::new(); for pdu in self .roomstateid_pdu @@ -78,6 +81,58 @@ impl Rooms { Ok(hashmap) } + /// Returns the full room state. + pub fn room_state_type( + &self, + room_id: &RoomId, + event_type: &EventType, + ) -> Result> { + let mut prefix = room_id.to_string().as_bytes().to_vec(); + prefix.push(0xff); + prefix.extend_from_slice(&event_type.to_string().as_bytes()); + + let mut hashmap = HashMap::new(); + for pdu in self + .roomstateid_pdu + .scan_prefix(&prefix) + .values() + .map(|value| { + Ok::<_, Error>( + serde_json::from_slice::(&value?) + .map_err(|_| Error::bad_database("Invalid PDU in db."))?, + ) + }) + { + let pdu = pdu?; + let state_key = pdu.state_key.clone().ok_or_else(|| { + Error::bad_database("Room state contains event without state_key.") + })?; + hashmap.insert(state_key, pdu); + } + Ok(hashmap) + } + + /// Returns the full room state. + pub fn room_state_get( + &self, + room_id: &RoomId, + event_type: &EventType, + state_key: &str, + ) -> Result> { + let mut key = room_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(&event_type.to_string().as_bytes()); + key.push(0xff); + key.extend_from_slice(&state_key.as_bytes()); + + self.roomstateid_pdu.get(&key)?.map_or(Ok(None), |value| { + Ok::<_, Error>(Some( + serde_json::from_slice::(&value) + .map_err(|_| Error::bad_database("Invalid PDU in db."))?, + )) + }) + } + /// Returns the `count` of this pdu's id. pub fn get_pdu_count(&self, event_id: &EventId) -> Result> { self.eventid_pduid @@ -212,8 +267,7 @@ impl Rooms { // Is the event authorized? if let Some(state_key) = &state_key { let power_levels = self - .room_state(&room_id)? - .get(&(EventType::RoomPowerLevels, "".to_owned())) + .room_state_get(&room_id, &EventType::RoomPowerLevels, "")? .map_or_else( || { Ok::<_, Error>(power_levels::PowerLevelsEventContent { @@ -244,8 +298,7 @@ impl Rooms { }, )?; let sender_membership = self - .room_state(&room_id)? - .get(&(EventType::RoomMember, sender.to_string())) + .room_state_get(&room_id, &EventType::RoomMember, &sender.to_string())? .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { Ok( serde_json::from_value::>( @@ -280,8 +333,11 @@ impl Rooms { })?; let current_membership = self - .room_state(&room_id)? - .get(&(EventType::RoomMember, target_user_id.to_string())) + .room_state_get( + &room_id, + &EventType::RoomMember, + &target_user_id.to_string(), + )? .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { Ok( serde_json::from_value::>( @@ -315,8 +371,7 @@ impl Rooms { ); let join_rules = - self.room_state(&room_id)? - .get(&(EventType::RoomJoinRules, "".to_owned())) + self.room_state_get(&room_id, &EventType::RoomJoinRules, "")? .map_or(Ok::<_, Error>(join_rules::JoinRule::Public), |pdu| { Ok(serde_json::from_value::< EventJson, @@ -446,13 +501,13 @@ impl Rooms { + 1; let mut unsigned = unsigned.unwrap_or_default(); - // TODO: Optimize this to not load the whole room state? if let Some(state_key) = &state_key { - if let Some(prev_pdu) = self - .room_state(&room_id)? - .get(&(event_type.clone(), state_key.to_owned())) - { + if let Some(prev_pdu) = self.room_state_get(&room_id, &event_type, &state_key)? { unsigned.insert("prev_content".to_owned(), prev_pdu.content.clone()); + unsigned.insert( + "prev_sender".to_owned(), + serde_json::to_value(prev_pdu.sender).expect("UserId::to_value always works"), + ); } } @@ -551,13 +606,18 @@ impl Rooms { } /// Returns an iterator over all PDUs in a room. - pub fn all_pdus(&self, room_id: &RoomId) -> Result>> { - self.pdus_since(room_id, 0) + pub fn all_pdus( + &self, + user_id: &UserId, + room_id: &RoomId, + ) -> Result>> { + self.pdus_since(user_id, room_id, 0) } /// Returns an iterator over all events in a room that happened after the event with id `since`. pub fn pdus_since( &self, + user_id: &UserId, room_id: &RoomId, since: u64, ) -> Result>> { @@ -566,12 +626,13 @@ impl Rooms { pdu_id.push(0xff); pdu_id.extend_from_slice(&(since).to_be_bytes()); - self.pdus_since_pduid(room_id, &pdu_id) + self.pdus_since_pduid(user_id, room_id, &pdu_id) } /// Returns an iterator over all events in a room that happened after the event with id `since`. pub fn pdus_since_pduid( &self, + user_id: &UserId, room_id: &RoomId, pdu_id: &[u8], ) -> Result>> { @@ -579,6 +640,7 @@ impl Rooms { let mut prefix = room_id.to_string().as_bytes().to_vec(); prefix.push(0xff); + let user_id = user_id.clone(); Ok(self .pduid_pdu .range(pdu_id..) @@ -590,9 +652,13 @@ impl Rooms { }) .filter_map(|r| r.ok()) .take_while(move |(k, _)| k.starts_with(&prefix)) - .map(|(_, v)| { - Ok(serde_json::from_slice(&v) - .map_err(|_| Error::bad_database("PDU in db is invalid."))?) + .map(move |(_, v)| { + let mut pdu = serde_json::from_slice::(&v) + .map_err(|_| Error::bad_database("PDU in db is invalid."))?; + if pdu.sender != user_id { + pdu.unsigned.remove("transaction_id"); + } + Ok(pdu) })) } @@ -600,6 +666,7 @@ impl Rooms { /// `until` in reverse-chronological order. pub fn pdus_until( &self, + user_id: &UserId, room_id: &RoomId, until: u64, ) -> impl Iterator> { @@ -612,14 +679,19 @@ impl Rooms { let current: &[u8] = ¤t; + let user_id = user_id.clone(); self.pduid_pdu .range(..current) .rev() .filter_map(|r| r.ok()) .take_while(move |(k, _)| k.starts_with(&prefix)) - .map(|(_, v)| { - Ok(serde_json::from_slice(&v) - .map_err(|_| Error::bad_database("PDU in db is invalid."))?) + .map(move |(_, v)| { + let mut pdu = serde_json::from_slice::(&v) + .map_err(|_| Error::bad_database("PDU in db is invalid."))?; + if pdu.sender != user_id { + pdu.unsigned.remove("transaction_id"); + } + Ok(pdu) }) } @@ -627,6 +699,7 @@ impl Rooms { /// `from` in chronological order. pub fn pdus_after( &self, + user_id: &UserId, room_id: &RoomId, from: u64, ) -> impl Iterator> { @@ -639,13 +712,18 @@ impl Rooms { let current: &[u8] = ¤t; + let user_id = user_id.clone(); self.pduid_pdu .range(current..) .filter_map(|r| r.ok()) .take_while(move |(k, _)| k.starts_with(&prefix)) - .map(|(_, v)| { - Ok(serde_json::from_slice(&v) - .map_err(|_| Error::bad_database("PDU in db is invalid."))?) + .map(move |(_, v)| { + let mut pdu = serde_json::from_slice::(&v) + .map_err(|_| Error::bad_database("PDU in db is invalid."))?; + if pdu.sender != user_id { + pdu.unsigned.remove("transaction_id"); + } + Ok(pdu) }) } diff --git a/src/database/users.rs b/src/database/users.rs index 07c6912..9f4eb40 100644 --- a/src/database/users.rs +++ b/src/database/users.rs @@ -1,9 +1,12 @@ use crate::{utils, Error, Result}; use js_int::UInt; use ruma::{ - api::client::r0::{ - device::Device, - keys::{AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm, OneTimeKey}, + api::client::{ + error::ErrorKind, + r0::{ + device::Device, + keys::{AlgorithmAndDeviceId, CrossSigningKey, DeviceKeys, KeyAlgorithm, OneTimeKey}, + }, }, events::{to_device::AnyToDeviceEvent, EventJson, EventType}, identifiers::UserId, @@ -19,8 +22,11 @@ pub struct Users { pub(super) token_userdeviceid: sled::Tree, pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + AlgorithmAndDeviceId - pub(super) userdeviceid_devicekeys: sled::Tree, - pub(super) devicekeychangeid_userid: sled::Tree, // DeviceKeyChangeId = Count + pub(super) keychangeid_userid: sled::Tree, // KeyChangeId = Count + pub(super) keyid_key: sled::Tree, // KeyId = UserId + KeyId (depends on key type) + pub(super) userid_masterkeyid: sled::Tree, + pub(super) userid_selfsigningkeyid: sled::Tree, + pub(super) userid_usersigningkeyid: sled::Tree, pub(super) todeviceid_events: sled::Tree, // ToDeviceId = UserId + DeviceId + Count } @@ -171,9 +177,6 @@ impl Users { userdeviceid.push(0xff); userdeviceid.extend_from_slice(device_id.as_bytes()); - // Remove device keys - self.userdeviceid_devicekeys.remove(&userdeviceid)?; - // Remove tokens if let Some(old_token) = self.userdeviceid_token.remove(&userdeviceid)? { self.token_userdeviceid.remove(&old_token)?; @@ -350,38 +353,163 @@ impl Users { userdeviceid.push(0xff); userdeviceid.extend_from_slice(device_id.as_bytes()); - self.userdeviceid_devicekeys.insert( + self.keyid_key.insert( &userdeviceid, &*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"), )?; - self.devicekeychangeid_userid + self.keychangeid_userid .insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?; Ok(()) } - pub fn get_device_keys( + pub fn add_cross_signing_keys( &self, user_id: &UserId, - device_id: &str, - ) -> impl Iterator> { - let mut key = user_id.to_string().as_bytes().to_vec(); - key.push(0xff); - key.extend_from_slice(device_id.as_bytes()); + master_key: &CrossSigningKey, + self_signing_key: &Option, + user_signing_key: &Option, + globals: &super::globals::Globals, + ) -> Result<()> { + // TODO: Check signatures - self.userdeviceid_devicekeys - .scan_prefix(key) - .values() - .map(|bytes| { - Ok(serde_json::from_slice(&bytes?) - .map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?) - }) + let mut prefix = user_id.to_string().as_bytes().to_vec(); + prefix.push(0xff); + + // Master key + let mut master_key_ids = master_key.keys.values(); + let master_key_id = master_key_ids.next().ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Master key contained no key.", + ))?; + + if master_key_ids.next().is_some() { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Master key contained more than one key.", + )); + } + + let mut master_key_key = prefix.clone(); + master_key_key.extend_from_slice(master_key_id.as_bytes()); + + self.keyid_key.insert( + &master_key_key, + &*serde_json::to_string(&master_key).expect("CrossSigningKey::to_string always works"), + )?; + + self.userid_masterkeyid + .insert(&*user_id.to_string(), master_key_key)?; + + // Self-signing key + if let Some(self_signing_key) = self_signing_key { + let mut self_signing_key_ids = self_signing_key.keys.values(); + let self_signing_key_id = self_signing_key_ids.next().ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "Self signing key contained no key.", + ))?; + + if self_signing_key_ids.next().is_some() { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Self signing key contained more than one key.", + )); + } + + let mut self_signing_key_key = prefix.clone(); + self_signing_key_key.extend_from_slice(self_signing_key_id.as_bytes()); + + self.keyid_key.insert( + &self_signing_key_key, + &*serde_json::to_string(&self_signing_key) + .expect("CrossSigningKey::to_string always works"), + )?; + + self.userid_selfsigningkeyid + .insert(&*user_id.to_string(), self_signing_key_key)?; + } + + // User-signing key + if let Some(user_signing_key) = user_signing_key { + let mut user_signing_key_ids = user_signing_key.keys.values(); + let user_signing_key_id = user_signing_key_ids.next().ok_or(Error::BadRequest( + ErrorKind::InvalidParam, + "User signing key contained no key.", + ))?; + + if user_signing_key_ids.next().is_some() { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "User signing key contained more than one key.", + )); + } + + let mut user_signing_key_key = prefix.clone(); + user_signing_key_key.extend_from_slice(user_signing_key_id.as_bytes()); + + self.keyid_key.insert( + &user_signing_key_key, + &*serde_json::to_string(&user_signing_key) + .expect("CrossSigningKey::to_string always works"), + )?; + + self.userid_usersigningkeyid + .insert(&*user_id.to_string(), user_signing_key_key)?; + } + + self.keychangeid_userid + .insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?; + + Ok(()) } - pub fn device_keys_changed(&self, since: u64) -> impl Iterator> { - self.devicekeychangeid_userid - .range(since.to_be_bytes()..) + pub fn sign_key( + &self, + target_id: &UserId, + key_id: &str, + signature: (String, String), + sender_id: &UserId, + globals: &super::globals::Globals, + ) -> Result<()> { + let mut key = target_id.to_string().as_bytes().to_vec(); + key.push(0xff); + key.extend_from_slice(key_id.to_string().as_bytes()); + + let mut cross_signing_key = + serde_json::from_slice::(&self.keyid_key.get(&key)?.ok_or( + Error::BadRequest(ErrorKind::InvalidParam, "Tried to sign nonexistent key."), + )?) + .map_err(|_| Error::bad_database("key in keyid_key is invalid."))?; + + let signatures = cross_signing_key + .get_mut("signatures") + .ok_or_else(|| Error::bad_database("key in keyid_key has no signatures field."))? + .as_object_mut() + .ok_or_else(|| Error::bad_database("key in keyid_key has invalid signatures field."))? + .entry(sender_id.clone()) + .or_insert_with(|| serde_json::Map::new().into()); + + signatures + .as_object_mut() + .ok_or_else(|| Error::bad_database("signatures in keyid_key for a user is invalid."))? + .insert(signature.0, signature.1.into()); + + self.keyid_key.insert( + &key, + &*serde_json::to_string(&cross_signing_key) + .expect("CrossSigningKey::to_string always works"), + )?; + + self.keychangeid_userid + .insert(globals.next_count()?.to_be_bytes(), &*target_id.to_string())?; + + Ok(()) + } + + pub fn keys_changed(&self, since: u64) -> impl Iterator> { + self.keychangeid_userid + .range((since + 1).to_be_bytes()..) .values() .map(|bytes| { Ok( @@ -397,29 +525,85 @@ impl Users { }) } - pub fn all_device_keys( - &self, - user_id: &UserId, - ) -> impl Iterator> { + pub fn get_device_keys(&self, user_id: &UserId, device_id: &str) -> Result> { let mut key = user_id.to_string().as_bytes().to_vec(); key.push(0xff); + key.extend_from_slice(device_id.as_bytes()); - self.userdeviceid_devicekeys.scan_prefix(key).map(|r| { - let (key, value) = r?; - let userdeviceid = utils::string_from_bytes( - key.rsplit(|&b| b == 0xff) - .next() - .ok_or_else(|| Error::bad_database("UserDeviceID in db is invalid."))?, - ) - .map_err(|_| Error::bad_database("UserDeviceId in db is invalid."))?; - Ok(( - userdeviceid, - serde_json::from_slice(&*value) - .map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?, - )) + self.keyid_key.get(key)?.map_or(Ok(None), |bytes| { + Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { + Error::bad_database("DeviceKeys in db are invalid.") + })?)) }) } + pub fn get_master_key( + &self, + user_id: &UserId, + sender_id: &UserId, + ) -> Result> { + // TODO: hide some signatures + self.userid_masterkeyid + .get(user_id.to_string())? + .map_or(Ok(None), |key| { + self.keyid_key.get(key)?.map_or(Ok(None), |bytes| { + let mut cross_signing_key = serde_json::from_slice::(&bytes) + .map_err(|_| { + Error::bad_database("CrossSigningKey in db is invalid.") + })?; + + // A user is not allowed to see signatures from users other than himself and + // the target user + cross_signing_key.signatures = cross_signing_key + .signatures + .into_iter() + .filter(|(user, _)| user == user_id || user == sender_id) + .collect(); + + Ok(Some(cross_signing_key)) + }) + }) + } + + pub fn get_self_signing_key( + &self, + user_id: &UserId, + sender_id: &UserId, + ) -> Result> { + self.userid_selfsigningkeyid + .get(user_id.to_string())? + .map_or(Ok(None), |key| { + self.keyid_key.get(key)?.map_or(Ok(None), |bytes| { + let mut cross_signing_key = serde_json::from_slice::(&bytes) + .map_err(|_| { + Error::bad_database("CrossSigningKey in db is invalid.") + })?; + + // A user is not allowed to see signatures from users other than himself and + // the target user + cross_signing_key.signatures = cross_signing_key + .signatures + .into_iter() + .filter(|(user, _)| user == user_id || user == sender_id) + .collect(); + + Ok(Some(cross_signing_key)) + }) + }) + } + + pub fn get_user_signing_key(&self, user_id: &UserId) -> Result> { + self.userid_usersigningkeyid + .get(user_id.to_string())? + .map_or(Ok(None), |key| { + self.keyid_key.get(key)?.map_or(Ok(None), |bytes| { + Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { + Error::bad_database("CrossSigningKey in db is invalid.") + })?)) + }) + }) + } + pub fn add_to_device_event( &self, sender: &UserId, diff --git a/src/main.rs b/src/main.rs index 8b68e36..a6c1afc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ #![feature(proc_macro_hygiene, decl_macro)] #![warn(rust_2018_idioms)] +pub mod push_rules; + mod client_server; mod database; mod error; @@ -44,6 +46,12 @@ fn setup_rocket() -> rocket::Rocket { client_server::upload_keys_route, client_server::get_keys_route, client_server::claim_keys_route, + client_server::create_backup_route, + client_server::update_backup_route, + client_server::get_latest_backup_route, + client_server::get_backup_route, + client_server::add_backup_keys_route, + client_server::get_backup_keys_route, client_server::set_read_marker_route, client_server::create_typing_event_route, client_server::create_room_route, @@ -88,6 +96,10 @@ fn setup_rocket() -> rocket::Rocket { client_server::delete_device_route, client_server::delete_devices_route, client_server::options_route, + client_server::upload_signing_keys_route, + client_server::upload_signatures_route, + client_server::pushers_route, + client_server::set_pushers_route, //server_server::well_known_server, //server_server::get_server_version, //server_server::get_server_keys, diff --git a/src/pdu.rs b/src/pdu.rs index f496933..8a5858e 100644 --- a/src/pdu.rs +++ b/src/pdu.rs @@ -4,6 +4,7 @@ use ruma::{ api::federation::EventHash, events::{ collections::all::{RoomEvent, StateEvent}, + room::member::MemberEvent, stripped::AnyStrippedStateEvent, EventJson, EventType, }, @@ -95,4 +96,9 @@ impl PduEvent { serde_json::from_str::>(&json) .expect("EventJson::from_str always works") } + pub fn to_member_event(&self) -> EventJson { + let json = serde_json::to_string(&self).expect("PDUs are always valid"); + serde_json::from_str::>(&json) + .expect("EventJson::from_str always works") + } } diff --git a/src/push_rules.rs b/src/push_rules.rs new file mode 100644 index 0000000..90e9243 --- /dev/null +++ b/src/push_rules.rs @@ -0,0 +1,230 @@ +use ruma::{ + events::push_rules::{ConditionalPushRule, PatternedPushRule, PushCondition, Ruleset}, + identifiers::UserId, + push::{Action, Tweak}, +}; + +pub fn default_pushrules(user_id: &UserId) -> Ruleset { + Ruleset { + content: vec![contains_user_name_rule(&user_id)], + override_: vec![ + master_rule(), + suppress_notices_rule(), + invite_for_me_rule(), + member_event_rule(), + contains_display_name_rule(), + tombstone_rule(), + roomnotif_rule(), + ], + room: vec![], + sender: vec![], + underride: vec![ + call_rule(), + encrypted_room_one_to_one_rule(), + room_one_to_one_rule(), + message_rule(), + encrypted_rule(), + ], + } +} + +pub fn master_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::DontNotify], + default: true, + enabled: false, + rule_id: ".m.rule.master".to_owned(), + conditions: vec![], + } +} + +pub fn suppress_notices_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::DontNotify], + default: true, + enabled: true, + rule_id: ".m.rule.suppress_notices".to_owned(), + conditions: vec![PushCondition::EventMatch { + key: "content.msgtype".to_owned(), + pattern: "m.notice".to_owned(), + }], + } +} + +pub fn invite_for_me_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![ + Action::Notify, + Action::SetTweak(Tweak::Sound("default".to_owned())), + Action::SetTweak(Tweak::Highlight(false)), + ], + default: true, + enabled: true, + rule_id: ".m.rule.invite_for_me".to_owned(), + conditions: vec![PushCondition::EventMatch { + key: "content.membership".to_owned(), + pattern: "m.invite".to_owned(), + }], + } +} + +pub fn member_event_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::DontNotify], + default: true, + enabled: true, + rule_id: ".m.rule.member_event".to_owned(), + conditions: vec![PushCondition::EventMatch { + key: "content.membership".to_owned(), + pattern: "type".to_owned(), + }], + } +} + +pub fn contains_display_name_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![ + Action::Notify, + Action::SetTweak(Tweak::Sound("default".to_owned())), + Action::SetTweak(Tweak::Highlight(true)), + ], + default: true, + enabled: true, + rule_id: ".m.rule.contains_display_name".to_owned(), + conditions: vec![PushCondition::ContainsDisplayName], + } +} + +pub fn tombstone_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(true))], + default: true, + enabled: true, + rule_id: ".m.rule.tombstone".to_owned(), + conditions: vec![ + PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.room.tombstone".to_owned(), + }, + PushCondition::EventMatch { + key: "state_key".to_owned(), + pattern: "".to_owned(), + }, + ], + } +} + +pub fn roomnotif_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(true))], + default: true, + enabled: true, + rule_id: ".m.rule.roomnotif".to_owned(), + conditions: vec![ + PushCondition::EventMatch { + key: "content.body".to_owned(), + pattern: "@room".to_owned(), + }, + PushCondition::SenderNotificationPermission { + key: "room".to_owned(), + }, + ], + } +} + +pub fn contains_user_name_rule(user_id: &UserId) -> PatternedPushRule { + PatternedPushRule { + actions: vec![ + Action::Notify, + Action::SetTweak(Tweak::Sound("default".to_owned())), + Action::SetTweak(Tweak::Highlight(true)), + ], + default: true, + enabled: true, + rule_id: ".m.rule.contains_user_name".to_owned(), + pattern: user_id.localpart().to_owned(), + } +} + +pub fn call_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![ + Action::Notify, + Action::SetTweak(Tweak::Sound("ring".to_owned())), + Action::SetTweak(Tweak::Highlight(false)), + ], + default: true, + enabled: true, + rule_id: ".m.rule.call".to_owned(), + conditions: vec![PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.call.invite".to_owned(), + }], + } +} + +pub fn encrypted_room_one_to_one_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![ + Action::Notify, + Action::SetTweak(Tweak::Sound("default".to_owned())), + Action::SetTweak(Tweak::Highlight(false)), + ], + default: true, + enabled: true, + rule_id: ".m.rule.encrypted_room_one_to_one".to_owned(), + conditions: vec![ + PushCondition::RoomMemberCount { is: "2".to_owned() }, + PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.room.encrypted".to_owned(), + }, + ], + } +} + +pub fn room_one_to_one_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![ + Action::Notify, + Action::SetTweak(Tweak::Sound("default".to_owned())), + Action::SetTweak(Tweak::Highlight(false)), + ], + default: true, + enabled: true, + rule_id: ".m.rule.room_one_to_one".to_owned(), + conditions: vec![ + PushCondition::RoomMemberCount { is: "2".to_owned() }, + PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.room.message".to_owned(), + }, + ], + } +} + +pub fn message_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(false))], + default: true, + enabled: true, + rule_id: ".m.rule.message".to_owned(), + conditions: vec![PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.room.message".to_owned(), + }], + } +} + +pub fn encrypted_rule() -> ConditionalPushRule { + ConditionalPushRule { + actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(false))], + default: true, + enabled: true, + rule_id: ".m.rule.encrypted".to_owned(), + conditions: vec![PushCondition::EventMatch { + key: "type".to_owned(), + pattern: "m.room.encrypted".to_owned(), + }], + } +} diff --git a/sytest/sytest-whitelist b/sytest/sytest-whitelist index a77c546..3642c44 100644 --- a/sytest/sytest-whitelist +++ b/sytest/sytest-whitelist @@ -1,4 +1,3 @@ -# Register endpoints implemented GET /register yields a set of flows POST /register can create a user POST /register downcases capitals in usernames @@ -17,14 +16,12 @@ POST /register rejects registration of usernames with '£' POST /register rejects registration of usernames with 'é' POST /register rejects registration of usernames with '\n' POST /register rejects registration of usernames with ''' -# Login endpoints implemented GET /login yields a set of flows POST /login can log in as a user POST /login returns the same device_id as that in the request POST /login can log in as a user with just the local part of the id POST /login as non-existing user is rejected POST /login wrong password is rejected -# Room creation endpoints implemented POST /createRoom makes a private room POST /createRoom makes a private room with invites GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership @@ -49,15 +46,6 @@ Can read configuration endpoint AS cannot create users outside its own namespace Changing the actions of an unknown default rule fails with 404 Changing the actions of an unknown rule fails with 404 -Trying to add push rule with invalid scope fails with 400 -Trying to add push rule with invalid template fails with 400 -Trying to add push rule with rule_id with slashes fails with 400 -Trying to add push rule with override rule without conditions fails with 400 -Trying to add push rule with underride rule without conditions fails with 400 -Trying to add push rule with condition without kind fails with 400 -Trying to add push rule with content rule without pattern fails with 400 -Trying to add push rule with no actions fails with 400 -Trying to add push rule with invalid action fails with 400 Trying to get push rules with unknown rule_id fails with 404 GET /events with non-numeric 'limit' GET /events with negative 'limit'