Merge pull request 'Key backups and cross signing' (#132) from cross-signing into master
Reviewed-on: https://git.koesters.xyz/timo/conduit/pulls/132
This commit is contained in:
		
						commit
						e809d819ac
					
				
					 11 changed files with 1349 additions and 294 deletions
				
			
		
							
								
								
									
										263
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										263
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -1,5 +1,14 @@ | ||||||
| # This file is automatically @generated by Cargo. | # This file is automatically @generated by Cargo. | ||||||
| # It is not intended for manual editing. | # 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]] | [[package]] | ||||||
| name = "adler32" | name = "adler32" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
|  | @ -26,13 +35,13 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "async-trait" | name = "async-trait" | ||||||
| version = "0.1.35" | version = "0.1.36" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "89cb5d814ab2a47fd66d3266e9efccb53ca4c740b7451043b8ffcf9a6208f3f8" | checksum = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -43,7 +52,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "hermit-abi", |  "hermit-abi", | ||||||
|  "libc", |  "libc", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -52,6 +61,20 @@ version = "1.0.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" | 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]] | [[package]] | ||||||
| name = "base16" | name = "base16" | ||||||
| version = "0.2.1" | version = "0.2.1" | ||||||
|  | @ -75,9 +98,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "base64" | name = "base64" | ||||||
| version = "0.12.2" | version = "0.12.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67" | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "bitflags" | name = "bitflags" | ||||||
|  | @ -116,15 +139,18 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "bytes" | name = "bytes" | ||||||
| version = "0.5.4" | version = "0.5.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" | checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" | ||||||
|  | dependencies = [ | ||||||
|  |  "loom", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "cc" | name = "cc" | ||||||
| version = "1.0.54" | version = "1.0.55" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" | checksum = "b1be3409f94d7bdceeb5f5fac551039d9b3f00e25da7a74fc4d33400a0d96368" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "cfg-if" | name = "cfg-if" | ||||||
|  | @ -151,7 +177,7 @@ checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" | ||||||
| name = "conduit" | name = "conduit" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "base64 0.12.2", |  "base64 0.12.3", | ||||||
|  "directories", |  "directories", | ||||||
|  "http", |  "http", | ||||||
|  "image", |  "image", | ||||||
|  | @ -273,7 +299,7 @@ dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -294,14 +320,14 @@ checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_users", |  "redox_users", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "dtoa" | name = "dtoa" | ||||||
| version = "0.4.5" | version = "0.4.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" | checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "encoding_rs" | name = "encoding_rs" | ||||||
|  | @ -340,7 +366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -416,7 +442,7 @@ dependencies = [ | ||||||
|  "proc-macro-hack", |  "proc-macro-hack", | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -463,6 +489,19 @@ dependencies = [ | ||||||
|  "byteorder", |  "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]] | [[package]] | ||||||
| name = "getrandom" | name = "getrandom" | ||||||
| version = "0.1.14" | version = "0.1.14" | ||||||
|  | @ -484,6 +523,12 @@ dependencies = [ | ||||||
|  "lzw", |  "lzw", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "gimli" | ||||||
|  | version = "0.21.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "h2" | name = "h2" | ||||||
| version = "0.2.5" | version = "0.2.5" | ||||||
|  | @ -598,9 +643,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "image" | name = "image" | ||||||
| version = "0.23.5" | version = "0.23.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "d534e95ad8b9d5aa614322d02352b4f1bf962254adcf02ac6f2def8be18498e8" | checksum = "b5b0553fec6407d63fe2975b794dfb099f3f790bdc958823851af37b26404ab4" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bytemuck", |  "bytemuck", | ||||||
|  "byteorder", |  "byteorder", | ||||||
|  | @ -632,9 +677,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "itoa" | name = "itoa" | ||||||
| version = "0.4.5" | version = "0.4.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "jpeg-decoder" | name = "jpeg-decoder" | ||||||
|  | @ -656,9 +701,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "js_int" | name = "js_int" | ||||||
| version = "0.1.5" | version = "0.1.8" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "77ab7bb370a788ad675863e035fd9bfa56a66a030a16a88ab80aeb6b18cbdf31" | checksum = "1b2b63d60564122f2a7d6592c2f1d6c1c60e7a266b4d24715950a1ddad784f66" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "serde", |  "serde", | ||||||
| ] | ] | ||||||
|  | @ -703,6 +748,17 @@ dependencies = [ | ||||||
|  "cfg-if", |  "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]] | [[package]] | ||||||
| name = "lzw" | name = "lzw" | ||||||
| version = "0.10.0" | version = "0.10.0" | ||||||
|  | @ -829,7 +885,7 @@ checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "libc", |  "libc", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -855,9 +911,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "num-rational" | name = "num-rational" | ||||||
| version = "0.2.4" | version = "0.3.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" | checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "autocfg", |  "autocfg", | ||||||
|  "num-integer", |  "num-integer", | ||||||
|  | @ -883,6 +939,12 @@ dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "object" | ||||||
|  | version = "0.20.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "once_cell" | name = "once_cell" | ||||||
| version = "1.4.0" | version = "1.4.0" | ||||||
|  | @ -891,9 +953,9 @@ checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "openssl" | name = "openssl" | ||||||
| version = "0.10.29" | version = "0.10.30" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" | checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  | @ -943,7 +1005,7 @@ dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
|  "smallvec", |  "smallvec", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -997,7 +1059,7 @@ checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1063,7 +1125,7 @@ version = "1.0.18" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "unicode-xid 0.2.0", |  "unicode-xid 0.2.1", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1148,7 +1210,7 @@ version = "0.5.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1157,7 +1219,7 @@ version = "0.10.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" | checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "base64 0.12.2", |  "base64 0.12.3", | ||||||
|  "bytes", |  "bytes", | ||||||
|  "encoding_rs", |  "encoding_rs", | ||||||
|  "futures-core", |  "futures-core", | ||||||
|  | @ -1187,9 +1249,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ring" | name = "ring" | ||||||
| version = "0.16.14" | version = "0.16.15" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "06b3fefa4f12272808f809a0af618501fdaba41a58963c5fb72238ab0be09603" | checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cc", |  "cc", | ||||||
|  "libc", |  "libc", | ||||||
|  | @ -1197,7 +1259,7 @@ dependencies = [ | ||||||
|  "spin", |  "spin", | ||||||
|  "untrusted", |  "untrusted", | ||||||
|  "web-sys", |  "web-sys", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1255,13 +1317,13 @@ dependencies = [ | ||||||
|  "time", |  "time", | ||||||
|  "tokio", |  "tokio", | ||||||
|  "tokio-rustls", |  "tokio-rustls", | ||||||
|  "unicode-xid 0.2.0", |  "unicode-xid 0.2.1", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma" | name = "ruma" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ruma-api", |  "ruma-api", | ||||||
|  "ruma-client-api", |  "ruma-client-api", | ||||||
|  | @ -1275,7 +1337,7 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-api" | name = "ruma-api" | ||||||
| version = "0.16.1" | version = "0.16.1" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "http", |  "http", | ||||||
|  "percent-encoding 2.1.0", |  "percent-encoding 2.1.0", | ||||||
|  | @ -1290,17 +1352,17 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-api-macros" | name = "ruma-api-macros" | ||||||
| version = "0.16.1" | version = "0.16.1" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-client-api" | name = "ruma-client-api" | ||||||
| version = "0.9.0" | version = "0.9.0" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "http", |  "http", | ||||||
|  "js_int", |  "js_int", | ||||||
|  | @ -1317,7 +1379,7 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-common" | name = "ruma-common" | ||||||
| version = "0.1.3" | version = "0.1.3" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "matches", |  "matches", | ||||||
|  "ruma-serde", |  "ruma-serde", | ||||||
|  | @ -1348,13 +1410,13 @@ source = "git+https://github.com/ruma/ruma-events?rev=c1ee72d#c1ee72db0f3107a97f | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-federation-api" | name = "ruma-federation-api" | ||||||
| version = "0.0.2" | version = "0.0.2" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "js_int", |  "js_int", | ||||||
|  "matches", |  "matches", | ||||||
|  | @ -1369,7 +1431,7 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-identifiers" | name = "ruma-identifiers" | ||||||
| version = "0.16.2" | version = "0.16.2" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "rand", |  "rand", | ||||||
|  "serde", |  "serde", | ||||||
|  | @ -1379,7 +1441,7 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-serde" | name = "ruma-serde" | ||||||
| version = "0.2.2" | version = "0.2.2" | ||||||
| source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "dtoa", |  "dtoa", | ||||||
|  "itoa", |  "itoa", | ||||||
|  | @ -1392,9 +1454,9 @@ dependencies = [ | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-signatures" | name = "ruma-signatures" | ||||||
| version = "0.6.0-dev.1" | 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 = [ | dependencies = [ | ||||||
|  "base64 0.12.2", |  "base64 0.12.3", | ||||||
|  "ring", |  "ring", | ||||||
|  "serde_json", |  "serde_json", | ||||||
|  "untrusted", |  "untrusted", | ||||||
|  | @ -1418,12 +1480,27 @@ version = "0.8.2" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" | checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "base64 0.12.2", |  "base64 0.12.3", | ||||||
|  "blake2b_simd", |  "blake2b_simd", | ||||||
|  "constant_time_eq", |  "constant_time_eq", | ||||||
|  "crossbeam-utils", |  "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]] | [[package]] | ||||||
| name = "rustls" | name = "rustls" | ||||||
| version = "0.16.0" | version = "0.16.0" | ||||||
|  | @ -1450,9 +1527,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "lazy_static", |  "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]] | [[package]] | ||||||
| name = "scopeguard" | name = "scopeguard" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
|  | @ -1493,23 +1576,38 @@ dependencies = [ | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde" | name = "semver" | ||||||
| version = "1.0.112" | version = "0.9.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | 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 = [ | dependencies = [ | ||||||
|  "serde_derive", |  "serde_derive", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde_derive" | name = "serde_derive" | ||||||
| version = "1.0.112" | version = "1.0.114" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57" | checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1553,10 +1651,11 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "sled" | name = "sled" | ||||||
| version = "0.31.0" | version = "0.32.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8fb6824dde66ad33bf20c6e8476f5b82b871bc8bc3c129a10ea2f7dae5060fa3" | checksum = "cdad3dc85d888056d3bd9954ffdf22d8a22701b6cd3aca4f6df4c436111898c4" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "backtrace", | ||||||
|  "crc32fast", |  "crc32fast", | ||||||
|  "crossbeam-epoch", |  "crossbeam-epoch", | ||||||
|  "crossbeam-utils", |  "crossbeam-utils", | ||||||
|  | @ -1582,7 +1681,7 @@ dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1615,7 +1714,7 @@ dependencies = [ | ||||||
|  "heck", |  "heck", | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1631,13 +1730,13 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "syn" | name = "syn" | ||||||
| version = "1.0.31" | version = "1.0.33" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" | checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "unicode-xid 0.2.0", |  "unicode-xid 0.2.1", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1651,7 +1750,7 @@ dependencies = [ | ||||||
|  "rand", |  "rand", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
|  "remove_dir_all", |  "remove_dir_all", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1671,7 +1770,7 @@ checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1681,9 +1780,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "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]] | [[package]] | ||||||
| name = "tokio" | name = "tokio" | ||||||
| version = "0.2.21" | version = "0.2.21" | ||||||
|  | @ -1704,7 +1809,7 @@ dependencies = [ | ||||||
|  "signal-hook-registry", |  "signal-hook-registry", | ||||||
|  "slab", |  "slab", | ||||||
|  "tokio-macros", |  "tokio-macros", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1715,7 +1820,7 @@ checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1795,11 +1900,11 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-normalization" | name = "unicode-normalization" | ||||||
| version = "0.1.12" | version = "0.1.13" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "smallvec", |  "tinyvec", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1816,9 +1921,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-xid" | name = "unicode-xid" | ||||||
| version = "0.2.0" | version = "0.2.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "untrusted" | name = "untrusted" | ||||||
|  | @ -1888,7 +1993,7 @@ dependencies = [ | ||||||
|  "log", |  "log", | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
|  "wasm-bindgen-shared", |  "wasm-bindgen-shared", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | @ -1922,7 +2027,7 @@ checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.18", |  "proc-macro2 1.0.18", | ||||||
|  "quote 1.0.7", |  "quote 1.0.7", | ||||||
|  "syn 1.0.31", |  "syn 1.0.33", | ||||||
|  "wasm-bindgen-backend", |  "wasm-bindgen-backend", | ||||||
|  "wasm-bindgen-shared", |  "wasm-bindgen-shared", | ||||||
| ] | ] | ||||||
|  | @ -1961,9 +2066,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "winapi" | name = "winapi" | ||||||
| version = "0.3.8" | version = "0.3.9" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "winapi-i686-pc-windows-gnu", |  "winapi-i686-pc-windows-gnu", | ||||||
|  "winapi-x86_64-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" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.9", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Cargo.toml
									
									
									
									
									
								
							|  | @ -15,7 +15,7 @@ edition = "2018" | ||||||
| rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "4928e35ec5c4b9242f50d644282d9896d0160a10", features = ["tls"] } | rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "4928e35ec5c4b9242f50d644282d9896d0160a10", features = ["tls"] } | ||||||
| http = "0.2.1" | http = "0.2.1" | ||||||
| log = "0.4.8" | log = "0.4.8" | ||||||
| sled = "0.31.0" | sled = "0.32.0" | ||||||
| directories = "2.0.2" | directories = "2.0.2" | ||||||
| js_int = "0.1.5" | js_int = "0.1.5" | ||||||
| serde_json = { version = "1.0.53", features = ["raw_value"] } | 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"] } | image = { version = "0.23.4", default-features = false, features = ["jpeg", "png", "gif"] } | ||||||
| 
 | 
 | ||||||
| [dependencies.ruma] | [dependencies.ruma] | ||||||
| git = "https://github.com/ruma/ruma" | git = "https://github.com/timokoesters/ruma" | ||||||
| rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | #rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" | ||||||
| #path = "../ruma/ruma" | #path = "../ruma/ruma" | ||||||
| features = ["rand", "client-api", "federation-api"] | features = ["rand", "client-api", "federation-api"] | ||||||
| 
 | 
 | ||||||
| # These are required only until ruma-events and ruma-federation-api are merged into ruma/ruma | # These are required only until ruma-events and ruma-federation-api are merged into ruma/ruma | ||||||
| [patch.crates-io] | [patch.crates-io] | ||||||
| ruma-common = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" } | ruma-common = { git = "https://github.com/timokoesters/ruma" } | ||||||
| ruma-serde = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" } | ruma-serde = { git = "https://github.com/timokoesters/ruma" } | ||||||
| ruma-identifiers = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" } | ruma-identifiers = { git = "https://github.com/timokoesters/ruma" } | ||||||
| #ruma-common = { path = "../ruma/ruma-common" } | #ruma-common = { path = "../ruma/ruma-common" } | ||||||
| #ruma-serde = { path = "../ruma/ruma-serde" } | #ruma-serde = { path = "../ruma/ruma-serde" } | ||||||
|  | #ruma-identifiers = { path = "../ruma/ruma-identifiers" } | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ use std::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use crate::{utils, ConduitResult, Database, Error, Ruma}; | use crate::{utils, ConduitResult, Database, Error, Ruma}; | ||||||
|  | use keys::{upload_signatures, upload_signing_keys}; | ||||||
| use log::warn; | use log::warn; | ||||||
| use rocket::{delete, get, options, post, put, State}; | use rocket::{delete, get, options, post, put, State}; | ||||||
| use ruma::{ | use ruma::{ | ||||||
|  | @ -13,6 +14,10 @@ use ruma::{ | ||||||
|         r0::{ |         r0::{ | ||||||
|             account::{get_username_availability, register}, |             account::{get_username_availability, register}, | ||||||
|             alias::{create_alias, delete_alias, get_alias}, |             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, |             capabilities::get_capabilities, | ||||||
|             config::{get_global_account_data, set_global_account_data}, |             config::{get_global_account_data, set_global_account_data}, | ||||||
|             context::get_context, |             context::get_context, | ||||||
|  | @ -33,7 +38,7 @@ use ruma::{ | ||||||
|             profile::{ |             profile::{ | ||||||
|                 get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name, |                 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, |             read_marker::set_read_marker, | ||||||
|             redact::redact_event, |             redact::redact_event, | ||||||
|             room::{self, create_room}, |             room::{self, create_room}, | ||||||
|  | @ -71,9 +76,13 @@ const SESSION_ID_LENGTH: usize = 256; | ||||||
| 
 | 
 | ||||||
| #[get("/_matrix/client/versions")] | #[get("/_matrix/client/versions")] | ||||||
| pub fn get_supported_versions_route() -> ConduitResult<get_supported_versions::Response> { | pub fn get_supported_versions_route() -> ConduitResult<get_supported_versions::Response> { | ||||||
|  |     let mut unstable_features = BTreeMap::new(); | ||||||
|  | 
 | ||||||
|  |     unstable_features.insert("org.matrix.e2e_cross_signing".to_owned(), true); | ||||||
|  | 
 | ||||||
|     Ok(get_supported_versions::Response { |     Ok(get_supported_versions::Response { | ||||||
|         versions: vec!["r0.5.0".to_owned(), "r0.6.0".to_owned()], |         versions: vec!["r0.5.0".to_owned(), "r0.6.0".to_owned()], | ||||||
|         unstable_features: BTreeMap::new(), |         unstable_features, | ||||||
|     } |     } | ||||||
|     .into()) |     .into()) | ||||||
| } | } | ||||||
|  | @ -204,33 +213,7 @@ pub fn register_route( | ||||||
|         &EventType::PushRules, |         &EventType::PushRules, | ||||||
|         serde_json::to_value(ruma::events::push_rules::PushRulesEvent { |         serde_json::to_value(ruma::events::push_rules::PushRulesEvent { | ||||||
|             content: ruma::events::push_rules::PushRulesEventContent { |             content: ruma::events::push_rules::PushRulesEventContent { | ||||||
|                 global: ruma::events::push_rules::Ruleset { |                 global: crate::push_rules::default_pushrules(&user_id), | ||||||
|                     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(), |  | ||||||
|                         }], |  | ||||||
|                     }], |  | ||||||
|                 }, |  | ||||||
|             }, |             }, | ||||||
|         }) |         }) | ||||||
|         .expect("data is valid, we just created it") |         .expect("data is valid, we just created it") | ||||||
|  | @ -375,11 +358,11 @@ pub fn get_pushrules_all_route( | ||||||
| 
 | 
 | ||||||
| #[put(
 | #[put(
 | ||||||
|     "/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>", |     "/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>", | ||||||
|     data = "<body>" |     //data = "<body>"
 | ||||||
| )] | )] | ||||||
| pub fn set_pushrule_route( | pub fn set_pushrule_route( | ||||||
|     db: State<'_, Database>, |     //db: State<'_, Database>,
 | ||||||
|     body: Ruma<set_pushrule::Request>, |     //body: Ruma<set_pushrule::Request>,
 | ||||||
|     _scope: String, |     _scope: String, | ||||||
|     _kind: String, |     _kind: String, | ||||||
|     _rule_id: String, |     _rule_id: String, | ||||||
|  | @ -502,8 +485,7 @@ pub fn set_displayname_route( | ||||||
|                 displayname: body.displayname.clone(), |                 displayname: body.displayname.clone(), | ||||||
|                 ..serde_json::from_value::<EventJson<_>>( |                 ..serde_json::from_value::<EventJson<_>>( | ||||||
|                     db.rooms |                     db.rooms | ||||||
|                         .room_state(&room_id)? |                         .room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())? | ||||||
|                         .get(&(EventType::RoomMember, user_id.to_string())) |  | ||||||
|                         .ok_or_else(|| { |                         .ok_or_else(|| { | ||||||
|                             Error::bad_database( |                             Error::bad_database( | ||||||
|                                 "Tried to send displayname update for user not in the room.", |                                 "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(), |                 avatar_url: body.avatar_url.clone(), | ||||||
|                 ..serde_json::from_value::<EventJson<_>>( |                 ..serde_json::from_value::<EventJson<_>>( | ||||||
|                     db.rooms |                     db.rooms | ||||||
|                         .room_state(&room_id)? |                         .room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())? | ||||||
|                         .get(&(EventType::RoomMember, user_id.to_string())) |  | ||||||
|                         .ok_or_else(|| { |                         .ok_or_else(|| { | ||||||
|                             Error::bad_database( |                             Error::bad_database( | ||||||
|                                 "Tried to send avatar url update for user not in the room.", |                                 "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 { |     if let Some(device_keys) = &body.device_keys { | ||||||
|         db.users |         // This check is needed to assure that signatures are kept
 | ||||||
|             .add_device_keys(user_id, device_id, device_keys, &db.globals)?; |         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 { |     Ok(upload_keys::Response { | ||||||
|  | @ -737,33 +721,38 @@ pub fn get_keys_route( | ||||||
|     db: State<'_, Database>, |     db: State<'_, Database>, | ||||||
|     body: Ruma<get_keys::Request>, |     body: Ruma<get_keys::Request>, | ||||||
| ) -> ConduitResult<get_keys::Response> { | ) -> ConduitResult<get_keys::Response> { | ||||||
|  |     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(); |     let mut device_keys = BTreeMap::new(); | ||||||
| 
 | 
 | ||||||
|     for (user_id, device_ids) in &body.device_keys { |     for (user_id, device_ids) in &body.device_keys { | ||||||
|         if device_ids.is_empty() { |         if device_ids.is_empty() { | ||||||
|             let mut container = BTreeMap::new(); |             let mut container = BTreeMap::new(); | ||||||
|             for result in db.users.all_device_keys(&user_id.clone()) { |             for device_id in db.users.all_device_ids(user_id) { | ||||||
|                 let (device_id, mut keys) = result?; |                 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 |                     keys.unsigned = Some(keys::UnsignedDeviceInfo { | ||||||
|                     .users |                         device_display_name: metadata.display_name, | ||||||
|                     .get_device_metadata(user_id, &device_id)? |                     }); | ||||||
|                     .ok_or_else(|| { |  | ||||||
|                         Error::bad_database("all_device_keys contained nonexistent device.") |  | ||||||
|                     })?; |  | ||||||
| 
 | 
 | ||||||
|                 keys.unsigned = Some(keys::UnsignedDeviceInfo { |                     container.insert(device_id.to_owned(), keys); | ||||||
|                     device_display_name: metadata.display_name, |                 } | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|                 container.insert(device_id, keys); |  | ||||||
|             } |             } | ||||||
|             device_keys.insert(user_id.clone(), container); |             device_keys.insert(user_id.clone(), container); | ||||||
|         } else { |         } else { | ||||||
|             for device_id in device_ids { |             for device_id in device_ids { | ||||||
|                 let mut container = BTreeMap::new(); |                 let mut container = BTreeMap::new(); | ||||||
|                 for keys in db.users.get_device_keys(&user_id.clone(), &device_id) { |                 if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? { | ||||||
|                     let mut keys = keys?; |  | ||||||
|                     let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or( |                     let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or( | ||||||
|                         Error::BadRequest( |                         Error::BadRequest( | ||||||
|                             ErrorKind::InvalidParam, |                             ErrorKind::InvalidParam, | ||||||
|  | @ -780,11 +769,26 @@ pub fn get_keys_route( | ||||||
|                 device_keys.insert(user_id.clone(), container); |                 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 { |     Ok(get_keys::Response { | ||||||
|         failures: BTreeMap::new(), |         master_keys, | ||||||
|  |         self_signing_keys, | ||||||
|  |         user_signing_keys, | ||||||
|         device_keys, |         device_keys, | ||||||
|  |         failures: BTreeMap::new(), | ||||||
|     } |     } | ||||||
|     .into()) |     .into()) | ||||||
| } | } | ||||||
|  | @ -817,6 +821,125 @@ pub fn claim_keys_route( | ||||||
|     .into()) |     .into()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[post("/_matrix/client/unstable/room_keys/version", data = "<body>")] | ||||||
|  | pub fn create_backup_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<create_backup::Request>, | ||||||
|  | ) -> ConduitResult<create_backup::Response> { | ||||||
|  |     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 = "<body>" | ||||||
|  | )] | ||||||
|  | pub fn update_backup_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<update_backup::Request>, | ||||||
|  |     _version: String, | ||||||
|  | ) -> ConduitResult<update_backup::Response> { | ||||||
|  |     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 = "<body>")] | ||||||
|  | pub fn get_latest_backup_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<get_latest_backup::Request>, | ||||||
|  | ) -> ConduitResult<get_latest_backup::Response> { | ||||||
|  |     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 = "<body>" | ||||||
|  | )] | ||||||
|  | pub fn get_backup_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<get_backup::Request>, | ||||||
|  |     _version: String, | ||||||
|  | ) -> ConduitResult<get_backup::Response> { | ||||||
|  |     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 = "<body>")] | ||||||
|  | pub fn add_backup_keys_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<add_backup_keys::Request>, | ||||||
|  | ) -> ConduitResult<add_backup_keys::Response> { | ||||||
|  |     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 = "<body>")] | ||||||
|  | pub fn get_backup_keys_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<get_backup_keys::Request>, | ||||||
|  | ) -> ConduitResult<get_backup_keys::Response> { | ||||||
|  |     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 = "<body>")] | #[post("/_matrix/client/r0/rooms/<_room_id>/read_markers", data = "<body>")] | ||||||
| pub fn set_read_marker_route( | pub fn set_read_marker_route( | ||||||
|     db: State<'_, Database>, |     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
 |     // TODO: Ask a remote server if we don't have this room
 | ||||||
| 
 | 
 | ||||||
|     let event = db |     let event = member::MemberEventContent { | ||||||
|         .rooms |         membership: member::MembershipState::Join, | ||||||
|         .room_state(&body.room_id)? |         displayname: db.users.displayname(&user_id)?, | ||||||
|         .get(&(EventType::RoomMember, user_id.to_string())) |         avatar_url: db.users.avatar_url(&user_id)?, | ||||||
|         .map_or_else( |         is_direct: None, | ||||||
|             || { |         third_party_invite: None, | ||||||
|                 // 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::<EventJson<member::MemberEventContent>>( |  | ||||||
|                     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) |  | ||||||
|             }, |  | ||||||
|         )?; |  | ||||||
| 
 | 
 | ||||||
|     db.rooms.append_pdu( |     db.rooms.append_pdu( | ||||||
|         body.room_id.clone(), |         body.room_id.clone(), | ||||||
|  | @ -1348,11 +1449,10 @@ pub fn leave_room_route( | ||||||
|     _room_id: String, |     _room_id: String, | ||||||
| ) -> ConduitResult<leave_room::Response> { | ) -> ConduitResult<leave_room::Response> { | ||||||
|     let user_id = body.user_id.as_ref().expect("user is authenticated"); |     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::<EventJson<member::MemberEventContent>>( |     let mut event = serde_json::from_value::<EventJson<member::MemberEventContent>>( | ||||||
|         state |         db.rooms | ||||||
|             .get(&(EventType::RoomMember, user_id.to_string())) |             .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? | ||||||
|             .ok_or(Error::BadRequest( |             .ok_or(Error::BadRequest( | ||||||
|                 ErrorKind::BadState, |                 ErrorKind::BadState, | ||||||
|                 "Cannot leave a room you are not a member of.", |                 "Cannot leave a room you are not a member of.", | ||||||
|  | @ -1387,12 +1487,11 @@ pub fn kick_user_route( | ||||||
|     _room_id: String, |     _room_id: String, | ||||||
| ) -> ConduitResult<kick_user::Response> { | ) -> ConduitResult<kick_user::Response> { | ||||||
|     let user_id = body.user_id.as_ref().expect("user is authenticated"); |     let user_id = body.user_id.as_ref().expect("user is authenticated"); | ||||||
|     let state = db.rooms.room_state(&body.room_id)?; |  | ||||||
| 
 | 
 | ||||||
|     let mut event = |     let mut event = | ||||||
|         serde_json::from_value::<EventJson<ruma::events::room::member::MemberEventContent>>( |         serde_json::from_value::<EventJson<ruma::events::room::member::MemberEventContent>>( | ||||||
|             state |             db.rooms | ||||||
|                 .get(&(EventType::RoomMember, user_id.to_string())) |                 .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? | ||||||
|                 .ok_or(Error::BadRequest( |                 .ok_or(Error::BadRequest( | ||||||
|                     ErrorKind::BadState, |                     ErrorKind::BadState, | ||||||
|                     "Cannot kick member that's not in the room.", |                     "Cannot kick member that's not in the room.", | ||||||
|  | @ -1428,12 +1527,12 @@ pub fn ban_user_route( | ||||||
|     _room_id: String, |     _room_id: String, | ||||||
| ) -> ConduitResult<ban_user::Response> { | ) -> ConduitResult<ban_user::Response> { | ||||||
|     let user_id = body.user_id.as_ref().expect("user is authenticated"); |     let user_id = body.user_id.as_ref().expect("user is authenticated"); | ||||||
|     let state = db.rooms.room_state(&body.room_id)?; |  | ||||||
| 
 | 
 | ||||||
|     // TODO: reason
 |     // TODO: reason
 | ||||||
| 
 | 
 | ||||||
|     let event = state |     let event = db | ||||||
|         .get(&(EventType::RoomMember, user_id.to_string())) |         .rooms | ||||||
|  |         .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? | ||||||
|         .map_or( |         .map_or( | ||||||
|             Ok::<_, Error>(member::MemberEventContent { |             Ok::<_, Error>(member::MemberEventContent { | ||||||
|                 membership: member::MembershipState::Ban, |                 membership: member::MembershipState::Ban, | ||||||
|  | @ -1475,12 +1574,11 @@ pub fn unban_user_route( | ||||||
|     _room_id: String, |     _room_id: String, | ||||||
| ) -> ConduitResult<unban_user::Response> { | ) -> ConduitResult<unban_user::Response> { | ||||||
|     let user_id = body.user_id.as_ref().expect("user is authenticated"); |     let user_id = body.user_id.as_ref().expect("user is authenticated"); | ||||||
|     let state = db.rooms.room_state(&body.room_id)?; |  | ||||||
| 
 | 
 | ||||||
|     let mut event = |     let mut event = | ||||||
|         serde_json::from_value::<EventJson<ruma::events::room::member::MemberEventContent>>( |         serde_json::from_value::<EventJson<ruma::events::room::member::MemberEventContent>>( | ||||||
|             state |             db.rooms | ||||||
|                 .get(&(EventType::RoomMember, user_id.to_string())) |                 .room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())? | ||||||
|                 .ok_or(Error::BadRequest( |                 .ok_or(Error::BadRequest( | ||||||
|                     ErrorKind::BadState, |                     ErrorKind::BadState, | ||||||
|                     "Cannot unban a user who is not banned.", |                     "Cannot unban a user who is not banned.", | ||||||
|  | @ -1642,7 +1740,8 @@ pub async fn get_public_rooms_filtered_route( | ||||||
|         .map(|room_id| { |         .map(|room_id| { | ||||||
|             let room_id = 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 { |             let chunk = directory::PublicRoomsChunk { | ||||||
|                 aliases: Vec::new(), |                 aliases: Vec::new(), | ||||||
|  | @ -1774,10 +1873,30 @@ pub fn search_users_route( | ||||||
|     .into()) |     .into()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[get("/_matrix/client/r0/rooms/<_room_id>/members")] | #[get("/_matrix/client/r0/rooms/<_room_id>/members", data = "<body>")] | ||||||
| pub fn get_member_events_route(_room_id: String) -> ConduitResult<get_member_events::Response> { | pub fn get_member_events_route( | ||||||
|     warn!("TODO: get_member_events_route"); |     db: State<'_, Database>, | ||||||
|     Ok(get_member_events::Response { chunk: Vec::new() }.into()) |     body: Ruma<get_member_events::Request>, | ||||||
|  |     _room_id: String, | ||||||
|  | ) -> ConduitResult<get_member_events::Response> { | ||||||
|  |     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")] | #[get("/_matrix/client/r0/thirdparty/protocols")] | ||||||
|  | @ -1951,7 +2070,7 @@ pub fn get_state_events_route( | ||||||
|     Ok(get_state_events::Response { |     Ok(get_state_events::Response { | ||||||
|         room_state: db |         room_state: db | ||||||
|             .rooms |             .rooms | ||||||
|             .room_state(&body.room_id)? |             .room_state_full(&body.room_id)? | ||||||
|             .values() |             .values() | ||||||
|             .map(|pdu| pdu.to_state_event()) |             .map(|pdu| pdu.to_state_event()) | ||||||
|             .collect(), |             .collect(), | ||||||
|  | @ -1979,10 +2098,9 @@ pub fn get_state_events_for_key_route( | ||||||
|         )); |         )); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let state = db.rooms.room_state(&body.room_id)?; |     let event = db | ||||||
| 
 |         .rooms | ||||||
|     let event = state |         .room_state_get(&body.room_id, &body.event_type, &body.state_key)? | ||||||
|         .get(&(body.event_type.clone(), body.state_key.clone())) |  | ||||||
|         .ok_or(Error::BadRequest( |         .ok_or(Error::BadRequest( | ||||||
|             ErrorKind::NotFound, |             ErrorKind::NotFound, | ||||||
|             "State event not found.", |             "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 = db | ||||||
| 
 |         .rooms | ||||||
|     let event = state |         .room_state_get(&body.room_id, &body.event_type, "")? | ||||||
|         .get(&(body.event_type.clone(), "".to_owned())) |  | ||||||
|         .ok_or(Error::BadRequest( |         .ok_or(Error::BadRequest( | ||||||
|             ErrorKind::NotFound, |             ErrorKind::NotFound, | ||||||
|             "State event not found.", |             "State event not found.", | ||||||
|         ))?; |         ))?; | ||||||
| 
 | 
 | ||||||
|     Ok(get_state_events_for_empty_key::Response { |     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"))?, |             .map_err(|_| Error::bad_database("Invalid event content in database"))?, | ||||||
|     } |     } | ||||||
|     .into()) |     .into()) | ||||||
|  | @ -2053,7 +2170,7 @@ pub fn sync_route( | ||||||
| 
 | 
 | ||||||
|         let mut pdus = db |         let mut pdus = db | ||||||
|             .rooms |             .rooms | ||||||
|             .pdus_since(&room_id, since)? |             .pdus_since(&user_id, &room_id, since)? | ||||||
|             .filter_map(|r| r.ok()) // Filter out buggy events
 |             .filter_map(|r| r.ok()) // Filter out buggy events
 | ||||||
|             .collect::<Vec<_>>(); |             .collect::<Vec<_>>(); | ||||||
| 
 | 
 | ||||||
|  | @ -2068,7 +2185,7 @@ pub fn sync_route( | ||||||
|                     let content = serde_json::from_value::< |                     let content = serde_json::from_value::< | ||||||
|                         EventJson<ruma::events::room::member::MemberEventContent>, |                         EventJson<ruma::events::room::member::MemberEventContent>, | ||||||
|                     >(pdu.content.clone()) |                     >(pdu.content.clone()) | ||||||
|                     .map_err(|_| Error::bad_database("Invalid PDU in database."))? |                     .expect("EventJson::from_value always works") | ||||||
|                     .deserialize() |                     .deserialize() | ||||||
|                     .map_err(|_| Error::bad_database("Invalid PDU in database."))?; |                     .map_err(|_| Error::bad_database("Invalid PDU in database."))?; | ||||||
|                     if content.membership == ruma::events::room::member::MembershipState::Join { |                     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, invited_member_count, heroes) = if send_member_count { | ||||||
|             let joined_member_count = db.rooms.room_members(&room_id).count(); |             let joined_member_count = db.rooms.room_members(&room_id).count(); | ||||||
|  | @ -2096,7 +2213,7 @@ pub fn sync_route( | ||||||
| 
 | 
 | ||||||
|                 for hero in db |                 for hero in db | ||||||
|                     .rooms |                     .rooms | ||||||
|                     .all_pdus(&room_id)? |                     .all_pdus(&user_id, &room_id)? | ||||||
|                     .filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
 |                     .filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
 | ||||||
|                     .filter(|pdu| pdu.kind == EventType::RoomMember) |                     .filter(|pdu| pdu.kind == EventType::RoomMember) | ||||||
|                     .map(|pdu| { |                     .map(|pdu| { | ||||||
|  | @ -2111,8 +2228,8 @@ pub fn sync_route( | ||||||
|                             let current_content = serde_json::from_value::< |                             let current_content = serde_json::from_value::< | ||||||
|                                 EventJson<ruma::events::room::member::MemberEventContent>, |                                 EventJson<ruma::events::room::member::MemberEventContent>, | ||||||
|                             >( |                             >( | ||||||
|                                 state |                                 members | ||||||
|                                     .get(&(EventType::RoomMember, state_key.clone())) |                                     .get(state_key) | ||||||
|                                     .ok_or_else(|| { |                                     .ok_or_else(|| { | ||||||
|                                         Error::bad_database( |                                         Error::bad_database( | ||||||
|                                             "A user that joined once has no member event anymore.", |                                             "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)? { |             if let Some(last_read) = db.rooms.edus.room_read_get(&room_id, &user_id)? { | ||||||
|                 Some( |                 Some( | ||||||
|                     (db.rooms |                     (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_map(|pdu| pdu.ok()) // Filter out buggy events
 | ||||||
|                         .filter(|pdu| { |                         .filter(|pdu| { | ||||||
|                             matches!( |                             matches!( | ||||||
|  | @ -2264,7 +2381,8 @@ pub fn sync_route( | ||||||
|             // TODO: state before timeline
 |             // TODO: state before timeline
 | ||||||
|             state: sync_events::State { |             state: sync_events::State { | ||||||
|                 events: if joined_since_last_sync { |                 events: if joined_since_last_sync { | ||||||
|                     state |                     db.rooms | ||||||
|  |                         .room_state_full(&room_id)? | ||||||
|                         .into_iter() |                         .into_iter() | ||||||
|                         .map(|(_, pdu)| pdu.to_state_event()) |                         .map(|(_, pdu)| pdu.to_state_event()) | ||||||
|                         .collect() |                         .collect() | ||||||
|  | @ -2283,7 +2401,7 @@ pub fn sync_route( | ||||||
|     let mut left_rooms = BTreeMap::new(); |     let mut left_rooms = BTreeMap::new(); | ||||||
|     for room_id in db.rooms.rooms_left(&user_id) { |     for room_id in db.rooms.rooms_left(&user_id) { | ||||||
|         let room_id = room_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 |         let room_events = pdus | ||||||
|             .filter_map(|pdu| pdu.ok()) // Filter out buggy events
 |             .filter_map(|pdu| pdu.ok()) // Filter out buggy events
 | ||||||
|             .map(|pdu| pdu.to_room_event()) |             .map(|pdu| pdu.to_room_event()) | ||||||
|  | @ -2337,7 +2455,7 @@ pub fn sync_route( | ||||||
|             invite_state: sync_events::InviteState { |             invite_state: sync_events::InviteState { | ||||||
|                 events: db |                 events: db | ||||||
|                     .rooms |                     .rooms | ||||||
|                     .room_state(&room_id)? |                     .room_state_full(&room_id)? | ||||||
|                     .into_iter() |                     .into_iter() | ||||||
|                     .map(|(_, pdu)| pdu.to_stripped_state_event()) |                     .map(|(_, pdu)| pdu.to_stripped_state_event()) | ||||||
|                     .collect(), |                     .collect(), | ||||||
|  | @ -2387,7 +2505,7 @@ pub fn sync_route( | ||||||
|         device_lists: sync_events::DeviceLists { |         device_lists: sync_events::DeviceLists { | ||||||
|             changed: if since != 0 { |             changed: if since != 0 { | ||||||
|                 db.users |                 db.users | ||||||
|                     .device_keys_changed(since) |                     .keys_changed(since) | ||||||
|                     .filter_map(|u| u.ok()) |                     .filter_map(|u| u.ok()) | ||||||
|                     .collect() // Filter out buggy events
 |                     .collect() // Filter out buggy events
 | ||||||
|             } else { |             } else { | ||||||
|  | @ -2438,7 +2556,7 @@ pub fn get_context_route( | ||||||
| 
 | 
 | ||||||
|     let events_before = db |     let events_before = db | ||||||
|         .rooms |         .rooms | ||||||
|         .pdus_until(&body.room_id, base_token) |         .pdus_until(&user_id, &body.room_id, base_token) | ||||||
|         .take( |         .take( | ||||||
|             u32::try_from(body.limit).map_err(|_| { |             u32::try_from(body.limit).map_err(|_| { | ||||||
|                 Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") |                 Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") | ||||||
|  | @ -2464,7 +2582,7 @@ pub fn get_context_route( | ||||||
| 
 | 
 | ||||||
|     let events_after = db |     let events_after = db | ||||||
|         .rooms |         .rooms | ||||||
|         .pdus_after(&body.room_id, base_token) |         .pdus_after(&user_id, &body.room_id, base_token) | ||||||
|         .take( |         .take( | ||||||
|             u32::try_from(body.limit).map_err(|_| { |             u32::try_from(body.limit).map_err(|_| { | ||||||
|                 Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") |                 Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.") | ||||||
|  | @ -2496,7 +2614,7 @@ pub fn get_context_route( | ||||||
|         events_after, |         events_after, | ||||||
|         state: db // TODO: State at event
 |         state: db // TODO: State at event
 | ||||||
|             .rooms |             .rooms | ||||||
|             .room_state(&body.room_id)? |             .room_state_full(&body.room_id)? | ||||||
|             .values() |             .values() | ||||||
|             .map(|pdu| pdu.to_state_event()) |             .map(|pdu| pdu.to_state_event()) | ||||||
|             .collect(), |             .collect(), | ||||||
|  | @ -2528,7 +2646,7 @@ pub fn get_message_events_route( | ||||||
|         get_message_events::Direction::Forward => { |         get_message_events::Direction::Forward => { | ||||||
|             let events_after = db |             let events_after = db | ||||||
|                 .rooms |                 .rooms | ||||||
|                 .pdus_after(&body.room_id, from) |                 .pdus_after(&user_id, &body.room_id, from) | ||||||
|                 // Use limit or else 10
 |                 // Use limit or else 10
 | ||||||
|                 .take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| { |                 .take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| { | ||||||
|                     Ok(u32::try_from(l).map_err(|_| { |                     Ok(u32::try_from(l).map_err(|_| { | ||||||
|  | @ -2563,7 +2681,7 @@ pub fn get_message_events_route( | ||||||
|         get_message_events::Direction::Backward => { |         get_message_events::Direction::Backward => { | ||||||
|             let events_before = db |             let events_before = db | ||||||
|                 .rooms |                 .rooms | ||||||
|                 .pdus_until(&body.room_id, from) |                 .pdus_until(&user_id, &body.room_id, from) | ||||||
|                 // Use limit or else 10
 |                 // Use limit or else 10
 | ||||||
|                 .take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| { |                 .take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| { | ||||||
|                     Ok(u32::try_from(l).map_err(|_| { |                     Ok(u32::try_from(l).map_err(|_| { | ||||||
|  | @ -2883,6 +3001,122 @@ pub fn delete_devices_route( | ||||||
|     Ok(delete_devices::Response.into()) |     Ok(delete_devices::Response.into()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[post("/_matrix/client/unstable/keys/device_signing/upload", data = "<body>")] | ||||||
|  | pub fn upload_signing_keys_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<upload_signing_keys::Request>, | ||||||
|  | ) -> ConduitResult<upload_signing_keys::Response> { | ||||||
|  |     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 = "<body>")] | ||||||
|  | pub fn upload_signatures_route( | ||||||
|  |     db: State<'_, Database>, | ||||||
|  |     body: Ruma<upload_signatures::Request>, | ||||||
|  | ) -> ConduitResult<upload_signatures::Response> { | ||||||
|  |     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<get_pushers::Response> { | ||||||
|  |     Ok(get_pushers::Response { | ||||||
|  |         pushers: Vec::new(), | ||||||
|  |     } | ||||||
|  |     .into()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[post("/_matrix/client/r0/pushers/set")] | ||||||
|  | pub fn set_pushers_route() -> ConduitResult<get_pushers::Response> { | ||||||
|  |     Ok(get_pushers::Response { | ||||||
|  |         pushers: Vec::new(), | ||||||
|  |     } | ||||||
|  |     .into()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[options("/<_segments..>")] | #[options("/<_segments..>")] | ||||||
| pub fn options_route( | pub fn options_route( | ||||||
|     _segments: rocket::http::uri::Segments<'_>, |     _segments: rocket::http::uri::Segments<'_>, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| pub(self) mod account_data; | pub(self) mod account_data; | ||||||
| pub(self) mod global_edus; | pub(self) mod global_edus; | ||||||
| pub(self) mod globals; | pub(self) mod globals; | ||||||
|  | pub(self) mod key_backups; | ||||||
| pub(self) mod media; | pub(self) mod media; | ||||||
| pub(self) mod rooms; | pub(self) mod rooms; | ||||||
| pub(self) mod uiaa; | pub(self) mod uiaa; | ||||||
|  | @ -21,6 +22,7 @@ pub struct Database { | ||||||
|     pub account_data: account_data::AccountData, |     pub account_data: account_data::AccountData, | ||||||
|     pub global_edus: global_edus::GlobalEdus, |     pub global_edus: global_edus::GlobalEdus, | ||||||
|     pub media: media::Media, |     pub media: media::Media, | ||||||
|  |     pub key_backups: key_backups::KeyBackups, | ||||||
|     pub _db: sled::Db, |     pub _db: sled::Db, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -73,8 +75,11 @@ impl Database { | ||||||
|                 userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?, |                 userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?, | ||||||
|                 token_userdeviceid: db.open_tree("token_userdeviceid")?, |                 token_userdeviceid: db.open_tree("token_userdeviceid")?, | ||||||
|                 onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?, |                 onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?, | ||||||
|                 userdeviceid_devicekeys: db.open_tree("userdeviceid_devicekeys")?, |                 keychangeid_userid: db.open_tree("devicekeychangeid_userid")?, | ||||||
|                 devicekeychangeid_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")?, |                 todeviceid_events: db.open_tree("todeviceid_events")?, | ||||||
|             }, |             }, | ||||||
|             uiaa: uiaa::Uiaa { |             uiaa: uiaa::Uiaa { | ||||||
|  | @ -111,6 +116,11 @@ impl Database { | ||||||
|             media: media::Media { |             media: media::Media { | ||||||
|                 mediaid_file: db.open_tree("mediaid_file")?, |                 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, |             _db: db, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
							
								
								
									
										207
									
								
								src/database/key_backups.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/database/key_backups.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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<String> { | ||||||
|  |         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<String> { | ||||||
|  |         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<Option<(String, BackupAlgorithm)>> { | ||||||
|  |         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<Option<BackupAlgorithm>> { | ||||||
|  |         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<usize> { | ||||||
|  |         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<String> { | ||||||
|  |         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<BTreeMap<RoomId, Sessions>> { | ||||||
|  |         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::<RoomId, Sessions>::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) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -56,7 +56,10 @@ impl Rooms { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the full room state.
 |     /// Returns the full room state.
 | ||||||
|     pub fn room_state(&self, room_id: &RoomId) -> Result<HashMap<(EventType, String), PduEvent>> { |     pub fn room_state_full( | ||||||
|  |         &self, | ||||||
|  |         room_id: &RoomId, | ||||||
|  |     ) -> Result<HashMap<(EventType, String), PduEvent>> { | ||||||
|         let mut hashmap = HashMap::new(); |         let mut hashmap = HashMap::new(); | ||||||
|         for pdu in self |         for pdu in self | ||||||
|             .roomstateid_pdu |             .roomstateid_pdu | ||||||
|  | @ -78,6 +81,58 @@ impl Rooms { | ||||||
|         Ok(hashmap) |         Ok(hashmap) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Returns the full room state.
 | ||||||
|  |     pub fn room_state_type( | ||||||
|  |         &self, | ||||||
|  |         room_id: &RoomId, | ||||||
|  |         event_type: &EventType, | ||||||
|  |     ) -> Result<HashMap<String, PduEvent>> { | ||||||
|  |         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::<PduEvent>(&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<Option<PduEvent>> { | ||||||
|  |         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::<PduEvent>(&value) | ||||||
|  |                     .map_err(|_| Error::bad_database("Invalid PDU in db."))?, | ||||||
|  |             )) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Returns the `count` of this pdu's id.
 |     /// Returns the `count` of this pdu's id.
 | ||||||
|     pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> { |     pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> { | ||||||
|         self.eventid_pduid |         self.eventid_pduid | ||||||
|  | @ -212,8 +267,7 @@ impl Rooms { | ||||||
|         // Is the event authorized?
 |         // Is the event authorized?
 | ||||||
|         if let Some(state_key) = &state_key { |         if let Some(state_key) = &state_key { | ||||||
|             let power_levels = self |             let power_levels = self | ||||||
|                 .room_state(&room_id)? |                 .room_state_get(&room_id, &EventType::RoomPowerLevels, "")? | ||||||
|                 .get(&(EventType::RoomPowerLevels, "".to_owned())) |  | ||||||
|                 .map_or_else( |                 .map_or_else( | ||||||
|                     || { |                     || { | ||||||
|                         Ok::<_, Error>(power_levels::PowerLevelsEventContent { |                         Ok::<_, Error>(power_levels::PowerLevelsEventContent { | ||||||
|  | @ -244,8 +298,7 @@ impl Rooms { | ||||||
|                     }, |                     }, | ||||||
|                 )?; |                 )?; | ||||||
|             let sender_membership = self |             let sender_membership = self | ||||||
|                 .room_state(&room_id)? |                 .room_state_get(&room_id, &EventType::RoomMember, &sender.to_string())? | ||||||
|                 .get(&(EventType::RoomMember, sender.to_string())) |  | ||||||
|                 .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { |                 .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { | ||||||
|                     Ok( |                     Ok( | ||||||
|                         serde_json::from_value::<EventJson<member::MemberEventContent>>( |                         serde_json::from_value::<EventJson<member::MemberEventContent>>( | ||||||
|  | @ -280,8 +333,11 @@ impl Rooms { | ||||||
|                     })?; |                     })?; | ||||||
| 
 | 
 | ||||||
|                     let current_membership = self |                     let current_membership = self | ||||||
|                         .room_state(&room_id)? |                         .room_state_get( | ||||||
|                         .get(&(EventType::RoomMember, target_user_id.to_string())) |                             &room_id, | ||||||
|  |                             &EventType::RoomMember, | ||||||
|  |                             &target_user_id.to_string(), | ||||||
|  |                         )? | ||||||
|                         .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { |                         .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { | ||||||
|                             Ok( |                             Ok( | ||||||
|                                 serde_json::from_value::<EventJson<member::MemberEventContent>>( |                                 serde_json::from_value::<EventJson<member::MemberEventContent>>( | ||||||
|  | @ -315,8 +371,7 @@ impl Rooms { | ||||||
|                     ); |                     ); | ||||||
| 
 | 
 | ||||||
|                     let join_rules = |                     let join_rules = | ||||||
|                         self.room_state(&room_id)? |                         self.room_state_get(&room_id, &EventType::RoomJoinRules, "")? | ||||||
|                             .get(&(EventType::RoomJoinRules, "".to_owned())) |  | ||||||
|                             .map_or(Ok::<_, Error>(join_rules::JoinRule::Public), |pdu| { |                             .map_or(Ok::<_, Error>(join_rules::JoinRule::Public), |pdu| { | ||||||
|                                 Ok(serde_json::from_value::< |                                 Ok(serde_json::from_value::< | ||||||
|                                     EventJson<join_rules::JoinRulesEventContent>, |                                     EventJson<join_rules::JoinRulesEventContent>, | ||||||
|  | @ -446,13 +501,13 @@ impl Rooms { | ||||||
|             + 1; |             + 1; | ||||||
| 
 | 
 | ||||||
|         let mut unsigned = unsigned.unwrap_or_default(); |         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(state_key) = &state_key { | ||||||
|             if let Some(prev_pdu) = self |             if let Some(prev_pdu) = self.room_state_get(&room_id, &event_type, &state_key)? { | ||||||
|                 .room_state(&room_id)? |  | ||||||
|                 .get(&(event_type.clone(), state_key.to_owned())) |  | ||||||
|             { |  | ||||||
|                 unsigned.insert("prev_content".to_owned(), prev_pdu.content.clone()); |                 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.
 |     /// Returns an iterator over all PDUs in a room.
 | ||||||
|     pub fn all_pdus(&self, room_id: &RoomId) -> Result<impl Iterator<Item = Result<PduEvent>>> { |     pub fn all_pdus( | ||||||
|         self.pdus_since(room_id, 0) |         &self, | ||||||
|  |         user_id: &UserId, | ||||||
|  |         room_id: &RoomId, | ||||||
|  |     ) -> Result<impl Iterator<Item = Result<PduEvent>>> { | ||||||
|  |         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`.
 |     /// Returns an iterator over all events in a room that happened after the event with id `since`.
 | ||||||
|     pub fn pdus_since( |     pub fn pdus_since( | ||||||
|         &self, |         &self, | ||||||
|  |         user_id: &UserId, | ||||||
|         room_id: &RoomId, |         room_id: &RoomId, | ||||||
|         since: u64, |         since: u64, | ||||||
|     ) -> Result<impl Iterator<Item = Result<PduEvent>>> { |     ) -> Result<impl Iterator<Item = Result<PduEvent>>> { | ||||||
|  | @ -566,12 +626,13 @@ impl Rooms { | ||||||
|         pdu_id.push(0xff); |         pdu_id.push(0xff); | ||||||
|         pdu_id.extend_from_slice(&(since).to_be_bytes()); |         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`.
 |     /// Returns an iterator over all events in a room that happened after the event with id `since`.
 | ||||||
|     pub fn pdus_since_pduid( |     pub fn pdus_since_pduid( | ||||||
|         &self, |         &self, | ||||||
|  |         user_id: &UserId, | ||||||
|         room_id: &RoomId, |         room_id: &RoomId, | ||||||
|         pdu_id: &[u8], |         pdu_id: &[u8], | ||||||
|     ) -> Result<impl Iterator<Item = Result<PduEvent>>> { |     ) -> Result<impl Iterator<Item = Result<PduEvent>>> { | ||||||
|  | @ -579,6 +640,7 @@ impl Rooms { | ||||||
|         let mut prefix = room_id.to_string().as_bytes().to_vec(); |         let mut prefix = room_id.to_string().as_bytes().to_vec(); | ||||||
|         prefix.push(0xff); |         prefix.push(0xff); | ||||||
| 
 | 
 | ||||||
|  |         let user_id = user_id.clone(); | ||||||
|         Ok(self |         Ok(self | ||||||
|             .pduid_pdu |             .pduid_pdu | ||||||
|             .range(pdu_id..) |             .range(pdu_id..) | ||||||
|  | @ -590,9 +652,13 @@ impl Rooms { | ||||||
|             }) |             }) | ||||||
|             .filter_map(|r| r.ok()) |             .filter_map(|r| r.ok()) | ||||||
|             .take_while(move |(k, _)| k.starts_with(&prefix)) |             .take_while(move |(k, _)| k.starts_with(&prefix)) | ||||||
|             .map(|(_, v)| { |             .map(move |(_, v)| { | ||||||
|                 Ok(serde_json::from_slice(&v) |                 let mut pdu = serde_json::from_slice::<PduEvent>(&v) | ||||||
|                     .map_err(|_| Error::bad_database("PDU in db is invalid."))?) |                     .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.
 |     /// `until` in reverse-chronological order.
 | ||||||
|     pub fn pdus_until( |     pub fn pdus_until( | ||||||
|         &self, |         &self, | ||||||
|  |         user_id: &UserId, | ||||||
|         room_id: &RoomId, |         room_id: &RoomId, | ||||||
|         until: u64, |         until: u64, | ||||||
|     ) -> impl Iterator<Item = Result<PduEvent>> { |     ) -> impl Iterator<Item = Result<PduEvent>> { | ||||||
|  | @ -612,14 +679,19 @@ impl Rooms { | ||||||
| 
 | 
 | ||||||
|         let current: &[u8] = ¤t; |         let current: &[u8] = ¤t; | ||||||
| 
 | 
 | ||||||
|  |         let user_id = user_id.clone(); | ||||||
|         self.pduid_pdu |         self.pduid_pdu | ||||||
|             .range(..current) |             .range(..current) | ||||||
|             .rev() |             .rev() | ||||||
|             .filter_map(|r| r.ok()) |             .filter_map(|r| r.ok()) | ||||||
|             .take_while(move |(k, _)| k.starts_with(&prefix)) |             .take_while(move |(k, _)| k.starts_with(&prefix)) | ||||||
|             .map(|(_, v)| { |             .map(move |(_, v)| { | ||||||
|                 Ok(serde_json::from_slice(&v) |                 let mut pdu = serde_json::from_slice::<PduEvent>(&v) | ||||||
|                     .map_err(|_| Error::bad_database("PDU in db is invalid."))?) |                     .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.
 |     /// `from` in chronological order.
 | ||||||
|     pub fn pdus_after( |     pub fn pdus_after( | ||||||
|         &self, |         &self, | ||||||
|  |         user_id: &UserId, | ||||||
|         room_id: &RoomId, |         room_id: &RoomId, | ||||||
|         from: u64, |         from: u64, | ||||||
|     ) -> impl Iterator<Item = Result<PduEvent>> { |     ) -> impl Iterator<Item = Result<PduEvent>> { | ||||||
|  | @ -639,13 +712,18 @@ impl Rooms { | ||||||
| 
 | 
 | ||||||
|         let current: &[u8] = ¤t; |         let current: &[u8] = ¤t; | ||||||
| 
 | 
 | ||||||
|  |         let user_id = user_id.clone(); | ||||||
|         self.pduid_pdu |         self.pduid_pdu | ||||||
|             .range(current..) |             .range(current..) | ||||||
|             .filter_map(|r| r.ok()) |             .filter_map(|r| r.ok()) | ||||||
|             .take_while(move |(k, _)| k.starts_with(&prefix)) |             .take_while(move |(k, _)| k.starts_with(&prefix)) | ||||||
|             .map(|(_, v)| { |             .map(move |(_, v)| { | ||||||
|                 Ok(serde_json::from_slice(&v) |                 let mut pdu = serde_json::from_slice::<PduEvent>(&v) | ||||||
|                     .map_err(|_| Error::bad_database("PDU in db is invalid."))?) |                     .map_err(|_| Error::bad_database("PDU in db is invalid."))?; | ||||||
|  |                 if pdu.sender != user_id { | ||||||
|  |                     pdu.unsigned.remove("transaction_id"); | ||||||
|  |                 } | ||||||
|  |                 Ok(pdu) | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,9 +1,12 @@ | ||||||
| use crate::{utils, Error, Result}; | use crate::{utils, Error, Result}; | ||||||
| use js_int::UInt; | use js_int::UInt; | ||||||
| use ruma::{ | use ruma::{ | ||||||
|     api::client::r0::{ |     api::client::{ | ||||||
|         device::Device, |         error::ErrorKind, | ||||||
|         keys::{AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm, OneTimeKey}, |         r0::{ | ||||||
|  |             device::Device, | ||||||
|  |             keys::{AlgorithmAndDeviceId, CrossSigningKey, DeviceKeys, KeyAlgorithm, OneTimeKey}, | ||||||
|  |         }, | ||||||
|     }, |     }, | ||||||
|     events::{to_device::AnyToDeviceEvent, EventJson, EventType}, |     events::{to_device::AnyToDeviceEvent, EventJson, EventType}, | ||||||
|     identifiers::UserId, |     identifiers::UserId, | ||||||
|  | @ -19,8 +22,11 @@ pub struct Users { | ||||||
|     pub(super) token_userdeviceid: sled::Tree, |     pub(super) token_userdeviceid: sled::Tree, | ||||||
| 
 | 
 | ||||||
|     pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + AlgorithmAndDeviceId
 |     pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + AlgorithmAndDeviceId
 | ||||||
|     pub(super) userdeviceid_devicekeys: sled::Tree, |     pub(super) keychangeid_userid: sled::Tree,       // KeyChangeId = Count
 | ||||||
|     pub(super) devicekeychangeid_userid: sled::Tree, // DeviceKeyChangeId = 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
 |     pub(super) todeviceid_events: sled::Tree, // ToDeviceId = UserId + DeviceId + Count
 | ||||||
| } | } | ||||||
|  | @ -171,9 +177,6 @@ impl Users { | ||||||
|         userdeviceid.push(0xff); |         userdeviceid.push(0xff); | ||||||
|         userdeviceid.extend_from_slice(device_id.as_bytes()); |         userdeviceid.extend_from_slice(device_id.as_bytes()); | ||||||
| 
 | 
 | ||||||
|         // Remove device keys
 |  | ||||||
|         self.userdeviceid_devicekeys.remove(&userdeviceid)?; |  | ||||||
| 
 |  | ||||||
|         // Remove tokens
 |         // Remove tokens
 | ||||||
|         if let Some(old_token) = self.userdeviceid_token.remove(&userdeviceid)? { |         if let Some(old_token) = self.userdeviceid_token.remove(&userdeviceid)? { | ||||||
|             self.token_userdeviceid.remove(&old_token)?; |             self.token_userdeviceid.remove(&old_token)?; | ||||||
|  | @ -350,38 +353,163 @@ impl Users { | ||||||
|         userdeviceid.push(0xff); |         userdeviceid.push(0xff); | ||||||
|         userdeviceid.extend_from_slice(device_id.as_bytes()); |         userdeviceid.extend_from_slice(device_id.as_bytes()); | ||||||
| 
 | 
 | ||||||
|         self.userdeviceid_devicekeys.insert( |         self.keyid_key.insert( | ||||||
|             &userdeviceid, |             &userdeviceid, | ||||||
|             &*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"), |             &*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())?; |             .insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?; | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn get_device_keys( |     pub fn add_cross_signing_keys( | ||||||
|         &self, |         &self, | ||||||
|         user_id: &UserId, |         user_id: &UserId, | ||||||
|         device_id: &str, |         master_key: &CrossSigningKey, | ||||||
|     ) -> impl Iterator<Item = Result<DeviceKeys>> { |         self_signing_key: &Option<CrossSigningKey>, | ||||||
|         let mut key = user_id.to_string().as_bytes().to_vec(); |         user_signing_key: &Option<CrossSigningKey>, | ||||||
|         key.push(0xff); |         globals: &super::globals::Globals, | ||||||
|         key.extend_from_slice(device_id.as_bytes()); |     ) -> Result<()> { | ||||||
|  |         // TODO: Check signatures
 | ||||||
| 
 | 
 | ||||||
|         self.userdeviceid_devicekeys |         let mut prefix = user_id.to_string().as_bytes().to_vec(); | ||||||
|             .scan_prefix(key) |         prefix.push(0xff); | ||||||
|             .values() | 
 | ||||||
|             .map(|bytes| { |         // Master key
 | ||||||
|                 Ok(serde_json::from_slice(&bytes?) |         let mut master_key_ids = master_key.keys.values(); | ||||||
|                     .map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?) |         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<Item = Result<UserId>> { |     pub fn sign_key( | ||||||
|         self.devicekeychangeid_userid |         &self, | ||||||
|             .range(since.to_be_bytes()..) |         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::<serde_json::Value>(&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<Item = Result<UserId>> { | ||||||
|  |         self.keychangeid_userid | ||||||
|  |             .range((since + 1).to_be_bytes()..) | ||||||
|             .values() |             .values() | ||||||
|             .map(|bytes| { |             .map(|bytes| { | ||||||
|                 Ok( |                 Ok( | ||||||
|  | @ -397,29 +525,85 @@ impl Users { | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn all_device_keys( |     pub fn get_device_keys(&self, user_id: &UserId, device_id: &str) -> Result<Option<DeviceKeys>> { | ||||||
|         &self, |  | ||||||
|         user_id: &UserId, |  | ||||||
|     ) -> impl Iterator<Item = Result<(String, DeviceKeys)>> { |  | ||||||
|         let mut key = user_id.to_string().as_bytes().to_vec(); |         let mut key = user_id.to_string().as_bytes().to_vec(); | ||||||
|         key.push(0xff); |         key.push(0xff); | ||||||
|  |         key.extend_from_slice(device_id.as_bytes()); | ||||||
| 
 | 
 | ||||||
|         self.userdeviceid_devicekeys.scan_prefix(key).map(|r| { |         self.keyid_key.get(key)?.map_or(Ok(None), |bytes| { | ||||||
|             let (key, value) = r?; |             Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { | ||||||
|             let userdeviceid = utils::string_from_bytes( |                 Error::bad_database("DeviceKeys in db are invalid.") | ||||||
|                 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."))?, |  | ||||||
|             )) |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn get_master_key( | ||||||
|  |         &self, | ||||||
|  |         user_id: &UserId, | ||||||
|  |         sender_id: &UserId, | ||||||
|  |     ) -> Result<Option<CrossSigningKey>> { | ||||||
|  |         // 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::<CrossSigningKey>(&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<Option<CrossSigningKey>> { | ||||||
|  |         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::<CrossSigningKey>(&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<Option<CrossSigningKey>> { | ||||||
|  |         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( |     pub fn add_to_device_event( | ||||||
|         &self, |         &self, | ||||||
|         sender: &UserId, |         sender: &UserId, | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							|  | @ -1,6 +1,8 @@ | ||||||
| #![feature(proc_macro_hygiene, decl_macro)] | #![feature(proc_macro_hygiene, decl_macro)] | ||||||
| #![warn(rust_2018_idioms)] | #![warn(rust_2018_idioms)] | ||||||
| 
 | 
 | ||||||
|  | pub mod push_rules; | ||||||
|  | 
 | ||||||
| mod client_server; | mod client_server; | ||||||
| mod database; | mod database; | ||||||
| mod error; | mod error; | ||||||
|  | @ -44,6 +46,12 @@ fn setup_rocket() -> rocket::Rocket { | ||||||
|                 client_server::upload_keys_route, |                 client_server::upload_keys_route, | ||||||
|                 client_server::get_keys_route, |                 client_server::get_keys_route, | ||||||
|                 client_server::claim_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::set_read_marker_route, | ||||||
|                 client_server::create_typing_event_route, |                 client_server::create_typing_event_route, | ||||||
|                 client_server::create_room_route, |                 client_server::create_room_route, | ||||||
|  | @ -88,6 +96,10 @@ fn setup_rocket() -> rocket::Rocket { | ||||||
|                 client_server::delete_device_route, |                 client_server::delete_device_route, | ||||||
|                 client_server::delete_devices_route, |                 client_server::delete_devices_route, | ||||||
|                 client_server::options_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::well_known_server,
 | ||||||
|                 //server_server::get_server_version,
 |                 //server_server::get_server_version,
 | ||||||
|                 //server_server::get_server_keys,
 |                 //server_server::get_server_keys,
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ use ruma::{ | ||||||
|     api::federation::EventHash, |     api::federation::EventHash, | ||||||
|     events::{ |     events::{ | ||||||
|         collections::all::{RoomEvent, StateEvent}, |         collections::all::{RoomEvent, StateEvent}, | ||||||
|  |         room::member::MemberEvent, | ||||||
|         stripped::AnyStrippedStateEvent, |         stripped::AnyStrippedStateEvent, | ||||||
|         EventJson, EventType, |         EventJson, EventType, | ||||||
|     }, |     }, | ||||||
|  | @ -95,4 +96,9 @@ impl PduEvent { | ||||||
|         serde_json::from_str::<EventJson<AnyStrippedStateEvent>>(&json) |         serde_json::from_str::<EventJson<AnyStrippedStateEvent>>(&json) | ||||||
|             .expect("EventJson::from_str always works") |             .expect("EventJson::from_str always works") | ||||||
|     } |     } | ||||||
|  |     pub fn to_member_event(&self) -> EventJson<MemberEvent> { | ||||||
|  |         let json = serde_json::to_string(&self).expect("PDUs are always valid"); | ||||||
|  |         serde_json::from_str::<EventJson<MemberEvent>>(&json) | ||||||
|  |             .expect("EventJson::from_str always works") | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										230
									
								
								src/push_rules.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								src/push_rules.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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(), | ||||||
|  |         }], | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| # Register endpoints implemented |  | ||||||
| GET /register yields a set of flows | GET /register yields a set of flows | ||||||
| POST /register can create a user | POST /register can create a user | ||||||
| POST /register downcases capitals in usernames | 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 'é' | ||||||
| POST /register rejects registration of usernames with '\n' | POST /register rejects registration of usernames with '\n' | ||||||
| POST /register rejects registration of usernames with ''' | POST /register rejects registration of usernames with ''' | ||||||
| # Login endpoints implemented |  | ||||||
| GET /login yields a set of flows | GET /login yields a set of flows | ||||||
| POST /login can log in as a user | POST /login can log in as a user | ||||||
| POST /login returns the same device_id as that in the request | 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 can log in as a user with just the local part of the id | ||||||
| POST /login as non-existing user is rejected | POST /login as non-existing user is rejected | ||||||
| POST /login wrong password is rejected | POST /login wrong password is rejected | ||||||
| # Room creation endpoints implemented |  | ||||||
| POST /createRoom makes a private room | POST /createRoom makes a private room | ||||||
| POST /createRoom makes a private room with invites | POST /createRoom makes a private room with invites | ||||||
| GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership | 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 | 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 default rule fails with 404 | ||||||
| Changing the actions of an unknown 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 | Trying to get push rules with unknown rule_id fails with 404 | ||||||
| GET /events with non-numeric 'limit' | GET /events with non-numeric 'limit' | ||||||
| GET /events with negative 'limit' | GET /events with negative 'limit' | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue