Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42a7a9f795 | ||
|
|
54a0f21347 | ||
|
|
54f4fd2cc8 | ||
|
|
ac5dd5e251 | ||
|
|
6510ffa0fa | ||
|
|
caa186ad22 |
@@ -1,28 +1,13 @@
|
||||
image: "rust:latest"
|
||||
|
||||
default:
|
||||
tags: [docker]
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- target
|
||||
- cargohome
|
||||
|
||||
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CARGO_HOME: "cargohome"
|
||||
FF_USE_FASTZIP: 1
|
||||
|
||||
before_script:
|
||||
- mkdir -p $CARGO_HOME && echo "using $CARGO_HOME to cache cargo deps"
|
||||
- apt-get update -yqq
|
||||
- apt-get install -yqq --no-install-recommends build-essential libssl-dev pkg-config
|
||||
- rustup component add clippy rustfmt
|
||||
|
||||
test:cargo:
|
||||
script:
|
||||
- rustc --version && cargo --version # Print version info for debugging
|
||||
- cargo test --workspace --verbose --locked
|
||||
- cargo fmt --all -- --check
|
||||
- cargo clippy
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Setting up Appservices
|
||||
|
||||
## Getting help
|
||||
|
||||
If you run into any problems while setting up an Appservice, write an email to `timo@koesters.xyz`, ask us in `#conduit:matrix.org` or [open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new).
|
||||
|
||||
## Tested appservices
|
||||
|
||||
Here are some appservices we tested and that work with Conduit:
|
||||
- matrix-appservice-discord
|
||||
- mautrix-hangouts
|
||||
- mautrix-telegram
|
||||
|
||||
## Set up the appservice
|
||||
|
||||
Follow whatever instructions are given by the appservice. This usually includes
|
||||
downloading, changing its config (setting domain, homeserver url, port etc.)
|
||||
and later starting it.
|
||||
|
||||
At some point the appservice guide should ask you to add a registration yaml
|
||||
file to the homeserver. In Synapse you would do this by adding the path to the
|
||||
homeserver.yaml, but in Conduit you can do this from within Matrix:
|
||||
|
||||
First, go into the #admins room of your homeserver. The first person that
|
||||
registered on the homeserver automatically joins it. Then send a message into
|
||||
the room like this:
|
||||
|
||||
@conduit:your.server.name: register_appservice
|
||||
```
|
||||
paste
|
||||
the
|
||||
contents
|
||||
of
|
||||
the
|
||||
yaml
|
||||
registration
|
||||
here
|
||||
```
|
||||
|
||||
You can confirm it worked by sending a message like this:
|
||||
`@conduit:your.server.name: list_appservices`
|
||||
|
||||
The @conduit bot should answer with `Appservices (1): your-bridge`
|
||||
|
||||
Then you are done. Conduit will send messages to the appservices and the
|
||||
appservice can send requests to the homeserver. You don't need to restart
|
||||
Conduit, but if it doesn't work, restarting while the appservice is running
|
||||
could help.
|
||||
Generated
+115
-421
@@ -42,6 +42,27 @@ version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a26cb53174ddd320edfff199a853f93d571f48eeb4dde75e67a9a3dbb7b7e5e"
|
||||
dependencies = [
|
||||
"async-stream-impl",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream-impl"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.50"
|
||||
@@ -103,25 +124,6 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd4865004a46a0aafb2a0a5eb19d3c9fc46ee5f063a6cfc605c69ac9ecf5263d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
@@ -139,15 +141,6 @@ dependencies = [
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.6.1"
|
||||
@@ -181,15 +174,6 @@ dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
@@ -215,17 +199,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
@@ -243,16 +216,14 @@ dependencies = [
|
||||
"image",
|
||||
"jsonwebtoken",
|
||||
"log",
|
||||
"lru-cache",
|
||||
"opentelemetry",
|
||||
"opentelemetry-jaeger",
|
||||
"pretty_env_logger",
|
||||
"rand 0.8.3",
|
||||
"rand",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"rocket",
|
||||
"rocksdb",
|
||||
"ruma",
|
||||
"rust-argon2",
|
||||
"rustls",
|
||||
@@ -261,6 +232,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"sled",
|
||||
"state-res",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -270,12 +242,6 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "279bc8fc53f788a75c7804af68237d1fce02cde1e275a886a4b320604dc2aeda"
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.7"
|
||||
@@ -321,15 +287,6 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
@@ -363,19 +320,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "639891fde0dbea823fc3d798a0fdf9d2f9440a42d64a78ab3488b0ca025117b3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
"rand_core 0.5.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.3.2"
|
||||
@@ -392,15 +336,6 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eeb9d92785d1facb50567852ce75d0858630630e7eabea59cf7eb7474051087"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.13"
|
||||
@@ -443,15 +378,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "3.0.1"
|
||||
@@ -484,29 +410,6 @@ version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d0860415b12243916284c67a9be413e044ee6668247b99ba26d94b2bc06c8f6"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sha2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
@@ -701,16 +604,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
@@ -924,15 +817,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136"
|
||||
dependencies = [
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.14"
|
||||
@@ -1049,40 +933,12 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librocksdb-sys"
|
||||
version = "6.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5da125e1c0f22c7cae785982115523a0738728498547f415c9054cb17c7e89f9"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"glob",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
@@ -1227,16 +1083,6 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
@@ -1314,12 +1160,6 @@ version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
@@ -1338,7 +1178,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"rand 0.8.3",
|
||||
"rand",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -1418,12 +1258,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "0.8.3"
|
||||
@@ -1473,17 +1307,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9c2f795bc591cb3384cb64082a578b89207ac92bb89c9d98c1ea2ace7cd8110"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.8"
|
||||
@@ -1571,19 +1394,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.3"
|
||||
@@ -1591,19 +1401,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.0",
|
||||
"rand_core 0.6.2",
|
||||
"rand_hc 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1613,16 +1413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1634,22 +1425,13 @@ dependencies = [
|
||||
"getrandom 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core 0.6.2",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1814,7 +1596,7 @@ dependencies = [
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"rand 0.8.3",
|
||||
"rand",
|
||||
"ref-cast",
|
||||
"rocket_codegen",
|
||||
"rocket_http",
|
||||
@@ -1870,25 +1652,16 @@ dependencies = [
|
||||
"uncased",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c749134fda8bfc90d0de643d59bfc841dcb3ac8a1062e12b6754bd60235c48b3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"librocksdb-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
"ruma-api",
|
||||
"ruma-appservice-api",
|
||||
"ruma-client",
|
||||
"ruma-client-api",
|
||||
"ruma-common",
|
||||
"ruma-events",
|
||||
@@ -1898,13 +1671,12 @@ dependencies = [
|
||||
"ruma-push-gateway-api",
|
||||
"ruma-serde",
|
||||
"ruma-signatures",
|
||||
"ruma-state-res",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-api"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.17.0-alpha.4"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
@@ -1919,8 +1691,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-api-macros"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.17.0-alpha.4"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -1930,8 +1702,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.2.0-alpha.3"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"ruma-api",
|
||||
"ruma-common",
|
||||
@@ -1942,10 +1714,31 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-client"
|
||||
version = "0.5.0-alpha.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"reqwest",
|
||||
"ruma-api",
|
||||
"ruma-common",
|
||||
"ruma-events",
|
||||
"ruma-identifiers",
|
||||
"ruma-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.10.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.10.0-alpha.3"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"bytes",
|
||||
@@ -1964,11 +1757,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"js_int",
|
||||
"maplit",
|
||||
"ruma-identifiers",
|
||||
"ruma-serde",
|
||||
"serde",
|
||||
@@ -1979,10 +1773,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.22.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.22.0-alpha.3"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"indoc",
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
"ruma-events-macros",
|
||||
@@ -1994,8 +1787,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-events-macros"
|
||||
version = "0.22.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.22.0-alpha.3"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -2005,8 +1798,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.1.0-alpha.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-api",
|
||||
@@ -2020,11 +1813,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers"
|
||||
version = "0.19.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.19.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"paste",
|
||||
"rand 0.8.3",
|
||||
"rand",
|
||||
"ruma-identifiers-macros",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-serde",
|
||||
@@ -2034,9 +1827,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-macros"
|
||||
version = "0.19.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.19.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruma-identifiers-validation",
|
||||
"syn",
|
||||
@@ -2044,13 +1838,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.1.0-alpha.1"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-api",
|
||||
@@ -2058,12 +1852,13 @@ dependencies = [
|
||||
"ruma-identifiers",
|
||||
"ruma-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.1.0-alpha.1"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-api",
|
||||
@@ -2077,8 +1872,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-serde"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
@@ -2091,8 +1886,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-serde-macros"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -2102,41 +1897,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.7.2"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"ed25519-dalek",
|
||||
"pkcs8",
|
||||
"rand 0.7.3",
|
||||
"ring",
|
||||
"ruma-identifiers",
|
||||
"ruma-serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
||||
dependencies = [
|
||||
"itertools 0.10.0",
|
||||
"js_int",
|
||||
"maplit",
|
||||
"ruma-common",
|
||||
"ruma-events",
|
||||
"ruma-identifiers",
|
||||
"ruma-serde",
|
||||
"ruma-signatures",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
@@ -2149,12 +1920,6 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
@@ -2273,18 +2038,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2332,19 +2097,6 @@ version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.1"
|
||||
@@ -2354,12 +2106,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.3.0"
|
||||
@@ -2369,12 +2115,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f0242b8e50dd9accdd56170e94ca1ebd223b098eb9c83539a6e367d0f36ae68"
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.4.1"
|
||||
@@ -2442,15 +2182,6 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dae7e047abc519c96350e9484a96c6bf1492348af912fd3446dd2dc323f6268"
|
||||
dependencies = [
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable-pattern"
|
||||
version = "0.1.0"
|
||||
@@ -2477,6 +2208,20 @@ dependencies = [
|
||||
"loom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "state-res"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ruma/state-res?rev=bca91bcc17df4d16942ff8b8c3be622b1795e3f0#bca91bcc17df4d16942ff8b8c3be622b1795e3f0"
|
||||
dependencies = [
|
||||
"itertools 0.10.0",
|
||||
"log",
|
||||
"maplit",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.4.20"
|
||||
@@ -2526,12 +2271,6 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.70"
|
||||
@@ -2543,18 +2282,6 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.2.0"
|
||||
@@ -2563,7 +2290,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"rand 0.8.3",
|
||||
"rand",
|
||||
"redox_syscall 0.2.6",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
@@ -2764,9 +2491,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.26"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
|
||||
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"pin-project-lite",
|
||||
@@ -2787,9 +2514,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.18"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
|
||||
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
@@ -2867,7 +2594,7 @@ dependencies = [
|
||||
"ipnet",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand 0.8.3",
|
||||
"rand",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
@@ -2911,12 +2638,6 @@ dependencies = [
|
||||
"unchecked-index",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "ubyte"
|
||||
version = "0.10.1"
|
||||
@@ -2972,12 +2693,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
@@ -3194,27 +2909,6 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.5.4+zstd.1.4.7"
|
||||
|
||||
+12
-14
@@ -8,7 +8,6 @@ repository = "https://gitlab.com/famedly/conduit"
|
||||
readme = "README.md"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
rust = "1.50"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -16,16 +15,21 @@ rust = "1.50"
|
||||
# Used to handle requests
|
||||
# TODO: This can become optional as soon as proper configs are supported
|
||||
rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "801e04bd5369eb39e126c75f6d11e1e9597304d8", features = ["tls"] } # Used to handle requests
|
||||
#rocket = { git = "https://github.com/timokoesters/Rocket.git", branch = "empty_parameters", default-features = false, features = ["tls"] }
|
||||
|
||||
# Used for matrix spec type definitions and helpers
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "5a7e2cddcf257e367465cced51442c91e8f557c9", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||
#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2", features = ["client-reqwest", "compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||
#ruma = { git = "https://github.com/timokoesters/ruma", rev = "220d5b4a76b3b781f7f8297fbe6b14473b04214b", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||
#ruma = { path = "../ruma/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||
|
||||
# Used when doing state resolution
|
||||
state-res = { git = "https://github.com/ruma/state-res", rev = "bca91bcc17df4d16942ff8b8c3be622b1795e3f0", features = ["unstable-pre-spec"] }
|
||||
#state-res = { path = "../state-res", features = ["unstable-pre-spec"] }
|
||||
|
||||
# Used for long polling and federation sender, should be the same as rocket::tokio
|
||||
tokio = "1.2.0"
|
||||
# Used for storing data permanently
|
||||
sled = { version = "0.34.6", features = ["compression", "no_metrics"], optional = true }
|
||||
rocksdb = { version = "0.16.0", features = ["multi-threaded-cf"], optional = true }
|
||||
sled = { version = "0.34.6", features = ["compression", "no_metrics"] }
|
||||
#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] }
|
||||
|
||||
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
||||
@@ -73,12 +77,9 @@ tracing-subscriber = "0.2.16"
|
||||
tracing-opentelemetry = "0.11.0"
|
||||
opentelemetry-jaeger = "0.11.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
lru-cache = "0.1.2"
|
||||
|
||||
[features]
|
||||
default = ["conduit_bin", "backend_sled"]
|
||||
backend_sled = ["sled"]
|
||||
backend_rocksdb = ["rocksdb"]
|
||||
default = ["conduit_bin"]
|
||||
conduit_bin = [] # TODO: add rocket to this when it is optional
|
||||
|
||||
[[bin]]
|
||||
@@ -102,16 +103,13 @@ instead of a server that has high scalability."""
|
||||
section = "net"
|
||||
priority = "optional"
|
||||
assets = [
|
||||
["debian/env.local", "etc/matrix-conduit/local", "644"],
|
||||
["debian/README.Debian", "usr/share/doc/matrix-conduit/", "644"],
|
||||
["README.md", "usr/share/doc/matrix-conduit/", "644"],
|
||||
["target/release/conduit", "usr/sbin/matrix-conduit", "755"],
|
||||
]
|
||||
conf-files = [
|
||||
"/etc/matrix-conduit/conduit.toml"
|
||||
"/etc/matrix-conduit/local"
|
||||
]
|
||||
maintainer-scripts = "debian/"
|
||||
systemd-units = { unit-name = "matrix-conduit" }
|
||||
|
||||
# For flamegraphs:
|
||||
#[profile.release]
|
||||
#debug = true
|
||||
|
||||
@@ -6,7 +6,7 @@ If you run into any problems while setting up Conduit, write an email to `timo@k
|
||||
|
||||
## Installing Conduit
|
||||
|
||||
You may simply download the binary that fits your machine. Run `uname -m` to see
|
||||
You have to download the binary that fits your machine. Run `uname -m` to see
|
||||
what you need. Now copy the right url:
|
||||
- x84_64: `https://conduit.rs/master/x86_64/conduit-bin`
|
||||
- armv7: `https://conduit.rs/master/armv7/conduit-bin`
|
||||
@@ -18,14 +18,6 @@ $ sudo wget -O /usr/local/bin/matrix-conduit <url>
|
||||
$ sudo chmod +x /usr/local/bin/matrix-conduit
|
||||
```
|
||||
|
||||
Alternatively, you may compile the binary yourself using
|
||||
```bash
|
||||
$ cargo build --release
|
||||
```
|
||||
Note that this currently requires Rust 1.50.
|
||||
|
||||
If you want to cross compile Conduit to another architecture, read the [Cross-Compile Guide](CROSS_COMPILE.md).
|
||||
|
||||
## Adding a Conduit user
|
||||
|
||||
While Conduit can run as any user it is usually better to use dedicated users for different services.
|
||||
@@ -104,10 +96,8 @@ allow_registration = false
|
||||
allow_encryption = true
|
||||
allow_federation = true
|
||||
|
||||
trusted_servers = ["matrix.org"]
|
||||
|
||||
#cache_capacity = 1073741824 # in bytes, 1024 * 1024 * 1024
|
||||
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
||||
#max_concurrent_requests = 4 # How many requests Conduit sends to other servers at the same time
|
||||
#workers = 4 # default: cpu core count * 2
|
||||
|
||||
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
||||
@@ -217,5 +207,3 @@ Set it to start automatically when your system boots with:
|
||||
```bash
|
||||
$ sudo systemctl enable conduit
|
||||
```
|
||||
|
||||
If you want to set up an appservice, take a look at the [Appservice Guide](APPSERVICES.md).
|
||||
|
||||
+3
-5
@@ -30,7 +30,7 @@ COPY . .
|
||||
RUN if [[ $LOCAL == "true" ]]; then \
|
||||
cargo install --path . ; \
|
||||
else \
|
||||
cargo install --git "https://gitlab.com/famedly/conduit.git" --rev ${GIT_REF}; \
|
||||
cargo install --git "https://github.com/timokoesters/conduit.git" --rev ${GIT_REF}; \
|
||||
fi
|
||||
|
||||
########################## RUNTIME IMAGE ##########################
|
||||
@@ -40,9 +40,7 @@ FROM alpine:3.12
|
||||
|
||||
ARG CREATED
|
||||
ARG VERSION
|
||||
ARG GIT_REF=origin/master
|
||||
|
||||
ENV CONDUIT_CONFIG="/srv/conduit/conduit.toml"
|
||||
ARG GIT_REF=HEAD
|
||||
|
||||
# Labels according to https://github.com/opencontainers/image-spec/blob/master/annotations.md
|
||||
# including a custom label specifying the build command
|
||||
@@ -54,7 +52,7 @@ LABEL org.opencontainers.image.created=${CREATED} \
|
||||
org.opencontainers.image.description="A Matrix homeserver written in Rust" \
|
||||
org.opencontainers.image.url="https://conduit.rs/" \
|
||||
org.opencontainers.image.revision=${GIT_REF} \
|
||||
org.opencontainers.image.source="https://gitlab.com/famedly/conduit.git" \
|
||||
org.opencontainers.image.source="https://git.koesters.xyz/timo/conduit.git" \
|
||||
org.opencontainers.image.licenses="Apache-2.0" \
|
||||
org.opencontainers.image.documentation="" \
|
||||
org.opencontainers.image.ref.name="" \
|
||||
|
||||
@@ -14,7 +14,7 @@ Yes! Just open a Matrix client (<https://app.element.io> or Element Android for
|
||||
example) and register on the `https://conduit.koesters.xyz` homeserver.
|
||||
|
||||
|
||||
#### What is it built on?
|
||||
#### What is it build on?
|
||||
|
||||
- [Ruma](https://www.ruma.io): Useful structures for endpoint requests and
|
||||
responses that can be (de)serialized
|
||||
@@ -52,40 +52,15 @@ Check out the [Conduit 1.0 Release Milestone](https://gitlab.com/famedly/conduit
|
||||
Download or compile a Conduit binary, set up the config and call it from somewhere like a systemd script. [Read
|
||||
more](DEPLOY.md)
|
||||
|
||||
If you want to connect an Appservice to Conduit, take a look at the [Appservice Guide](APPSERVICES.md).
|
||||
|
||||
##### Deploy using a Debian package
|
||||
|
||||
You need to have the `deb` helper command installed that creates Debian packages from Cargo projects (see [cargo-deb](https://github.com/mmstick/cargo-deb/) for more info):
|
||||
|
||||
```shell
|
||||
$ cargo install cargo-deb
|
||||
```
|
||||
|
||||
Then, you can create and install a Debian package at a whim:
|
||||
|
||||
```shell
|
||||
$ cargo deb
|
||||
$ dpkg -i target/debian/matrix-conduit_0.1.0_amd64.deb
|
||||
```
|
||||
|
||||
This will build, package, install, configure and start Conduit. [Read more](debian/README.Debian).
|
||||
|
||||
Note that `cargo deb` supports [cross-compilation](https://github.com/mmstick/cargo-deb/#cross-compilation) too!
|
||||
Official Debian packages will follow once Conduit starts to have stable releases.
|
||||
|
||||
##### Deploy using Docker
|
||||
|
||||
Pull and run the docker image with
|
||||
|
||||
``` bash
|
||||
docker pull matrixconduit/matrix-conduit:latest
|
||||
docker run -d -p 8448:8000 -v ~/conduit.toml:/srv/conduit/conduit.toml -v db:/srv/conduit/.local/share/conduit matrixconduit/matrix-conduit:latest
|
||||
docker run -d -p 8448:8000 -v db:/srv/conduit/.local/share/conduit matrixconduit/matrix-conduit:latest
|
||||
```
|
||||
|
||||
> <b>Note:</b> You also need to supply a `conduit.toml` config file, you can find an example [here](./conduit-example.toml).
|
||||
> Or you can pass in `-e CONDUIT_CONFIG=""` and configure Conduit purely with env vars.
|
||||
|
||||
Or build and run it with docker or docker-compose. [Read more](docker/README.md)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# YOU NEED TO EDIT THIS
|
||||
#server_name = "your.server.name"
|
||||
|
||||
# This is the only directory where Conduit will save its data
|
||||
# This is the only directly where Conduit will save its data
|
||||
database_path = "/var/lib/conduit/conduit.db"
|
||||
|
||||
# The port Conduit will be running on. You need to set up a reverse proxy in
|
||||
@@ -33,10 +33,8 @@ max_request_size = 20_000_000 # in bytes
|
||||
# Enable jaeger to support monitoring and troubleshooting through jaeger
|
||||
#allow_jaeger = false
|
||||
|
||||
trusted_servers = ["matrix.org"]
|
||||
|
||||
#cache_capacity = 1073741824 # in bytes, 1024 * 1024 * 1024
|
||||
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
||||
#max_concurrent_requests = 4 # How many requests Conduit sends to other servers at the same time
|
||||
#log = "info,state_res=warn,rocket=off,_=off,sled=off"
|
||||
#workers = 4 # default: cpu core count * 2
|
||||
|
||||
|
||||
Vendored
+10
-10
@@ -4,25 +4,25 @@ Conduit for Debian
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
When installed, Debconf generates the configuration of the homeserver
|
||||
(host)name, the address and port it listens on. This configuration ends up in
|
||||
/etc/matrix-conduit/conduit.toml.
|
||||
When installed, Debconf handles the configuration of the homeserver (host)name,
|
||||
the address and port it listens on. These configuration variables end up in
|
||||
/etc/matrix-conduit/debian.
|
||||
|
||||
You can tweak more detailed settings by uncommenting and setting the variables
|
||||
in /etc/matrix-conduit/conduit.toml. This involves settings such as the maximum
|
||||
file size for download/upload, enabling federation, etc.
|
||||
in /etc/matrix-conduit/local. This involves settings such as the maximum file
|
||||
size for download/upload, enabling federation, etc.
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The package uses the matrix-conduit.service systemd unit file to start and
|
||||
stop Conduit. It loads the configuration file mentioned above to set up the
|
||||
stop Conduit. It loads the configuration files mentioned above to set up the
|
||||
environment before running the server.
|
||||
|
||||
This package assumes by default that Conduit will be placed behind a reverse
|
||||
proxy such as Apache or nginx. This default deployment entails just listening
|
||||
on 127.0.0.1 and the free port 6167 and is reachable via a client using the URL
|
||||
http://localhost:6167.
|
||||
This package assumes by default that Conduit is placed behind a reverse proxy
|
||||
such as Apache or nginx. This default deployment entails just listening on
|
||||
127.0.0.1 and the free port 14004 and is reachable via a client using the URL
|
||||
http://localhost:14004.
|
||||
|
||||
At a later stage this packaging may support also setting up TLS and running
|
||||
stand-alone. In this case, however, you need to set up some certificates and
|
||||
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
# Conduit homeserver local configuration
|
||||
#
|
||||
# Conduit is an application based on the Rocket web framework.
|
||||
# Configuration of Conduit happens via Debconf (see the resulting config in
|
||||
# `/etc/matrix-conduit/debian`) and optionally by uncommenting and tweaking the
|
||||
# variables in this file below.
|
||||
|
||||
# The maximum size of a Matrix HTTP requests in bytes.
|
||||
#
|
||||
# This mostly affects the size of files that can be downloaded/uploaded.
|
||||
# It defaults to 20971520 (20MB).
|
||||
#ROCKET_MAX_REQUEST_SIZE=20971520
|
||||
|
||||
# Whether user registration is allowed.
|
||||
#
|
||||
# User registration is not disabled by default.
|
||||
#ROCKET_REGISTRATION_DISABLED=false
|
||||
|
||||
# Whether encryption is enabled.
|
||||
#
|
||||
# (End-to-end) encryption is not disabled by default.
|
||||
#ROCKET_ENCRYPTION_DISABLED=false
|
||||
|
||||
# Whether federation with other Matrix servers is enabled.
|
||||
#
|
||||
# Federation is not enabled by default; it is still experimental.
|
||||
#ROCKET_FEDERATION_ENABLED=false
|
||||
|
||||
# The log level of the homeserver.
|
||||
#
|
||||
# The log level is "critical" by default.
|
||||
# Allowed values are: "off", "normal", "debug", "critical"
|
||||
#ROCKET_LOG="critical"
|
||||
Vendored
+4
-1
@@ -34,7 +34,10 @@ SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
StateDirectory=matrix-conduit
|
||||
|
||||
Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml"
|
||||
Environment="ROCKET_ENV=production"
|
||||
Environment="ROCKET_DATABASE_PATH=/var/lib/matrix-conduit"
|
||||
EnvironmentFile=/etc/matrix-conduit/debian
|
||||
EnvironmentFile=/etc/matrix-conduit/local
|
||||
|
||||
ExecStart=/usr/sbin/matrix-conduit
|
||||
Restart=on-failure
|
||||
|
||||
Vendored
+33
-45
@@ -4,8 +4,8 @@ set -e
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
||||
CONDUIT_CONFIG_FILE="${CONDUIT_CONFIG_PATH}/conduit.toml"
|
||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit/conduit_db
|
||||
CONDUIT_CONFIG_FILE="$CONDUIT_CONFIG_PATH/debian"
|
||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
@@ -13,7 +13,7 @@ case "$1" in
|
||||
if ! getent passwd _matrix-conduit > /dev/null ; then
|
||||
echo 'Adding system user for the Conduit Matrix homeserver' 1>&2
|
||||
adduser --system --group --quiet \
|
||||
--home "$CONDUIT_DATABASE_PATH" \
|
||||
--home $CONDUIT_DATABASE_PATH \
|
||||
--disabled-login \
|
||||
--force-badname \
|
||||
_matrix-conduit
|
||||
@@ -25,60 +25,48 @@ case "$1" in
|
||||
chown _matrix-conduit "$CONDUIT_DATABASE_PATH"
|
||||
fi
|
||||
|
||||
if [ ! -e "$CONDUIT_CONFIG_FILE" ]; then
|
||||
# Write the debconf values in the config.
|
||||
db_get matrix-conduit/hostname
|
||||
CONDUIT_SERVER_NAME="$RET"
|
||||
ROCKET_SERVER_NAME="$RET"
|
||||
db_get matrix-conduit/address
|
||||
CONDUIT_ADDRESS="$RET"
|
||||
ROCKET_ADDRESS="$RET"
|
||||
db_get matrix-conduit/port
|
||||
CONDUIT_PORT="$RET"
|
||||
mkdir -p "$CONDUIT_CONFIG_PATH"
|
||||
ROCKET_PORT="$RET"
|
||||
cat >"$CONDUIT_CONFIG_FILE" << EOF
|
||||
[global]
|
||||
# The server_name is the name of this server. It is used as a suffix for user
|
||||
# and room ids. Examples: matrix.org, conduit.rs
|
||||
# The Conduit server needs to be reachable at https://your.server.name/ on port
|
||||
# 443 (client-server) and 8448 (federation) OR you can create /.well-known
|
||||
# files to redirect requests. See
|
||||
# https://matrix.org/docs/spec/client_server/latest#get-well-known-matrix-client
|
||||
# and https://matrix.org/docs/spec/server_server/r0.1.4#get-well-known-matrix-server
|
||||
# for more information.
|
||||
server_name = "${CONDUIT_SERVER_NAME}"
|
||||
# Conduit homeserver Debian configuration
|
||||
#
|
||||
# Conduit is an application based on the Rocket web framework.
|
||||
# Configuration of Conduit happens via Debconf (of which the resulting config
|
||||
# is in this file) and optionally by uncommenting and tweaking the variables in
|
||||
# /etc/matrix-conduit/local.
|
||||
|
||||
# This is the only directory where Conduit will save its data.
|
||||
database_path = "${CONDUIT_DATABASE_PATH}"
|
||||
# THIS FILE IS GENERATED BY DEBCONF AND WILL BE OVERRIDDEN!
|
||||
#
|
||||
# Please make changes by running:
|
||||
#
|
||||
# \$ dpkg-reconfigure matrix-conduit
|
||||
#
|
||||
# or by providing overriding changes in /etc/matrix-conduit/local.
|
||||
|
||||
# The address Conduit will be listening on.
|
||||
# The server (host)name of the Matrix homeserver.
|
||||
#
|
||||
# This is the hostname the homeserver will be reachable at via a client.
|
||||
ROCKET_SERVER_NAME="$ROCKET_SERVER_NAME"
|
||||
|
||||
# The address the Matrix homeserver listens on.
|
||||
#
|
||||
# By default the server listens on address 0.0.0.0. Change this to 127.0.0.1 to
|
||||
# only listen on the localhost when using a reverse proxy.
|
||||
address = "${CONDUIT_ADDRESS}"
|
||||
ROCKET_ADDRESS="$ROCKET_ADDRESS"
|
||||
|
||||
# The port Conduit will be running on. You need to set up a reverse proxy in
|
||||
# your web server (e.g. apache or nginx), so all requests to /_matrix on port
|
||||
# 443 and 8448 will be forwarded to the Conduit instance running on this port.
|
||||
port = ${CONDUIT_PORT}
|
||||
# The port of the Matrix homeserver.
|
||||
#
|
||||
# This port is could be any available port if accessed by a reverse proxy.
|
||||
# By default the server listens on port 8000.
|
||||
ROCKET_PORT="$ROCKET_PORT"
|
||||
|
||||
# Max size for uploads
|
||||
max_request_size = 20_000_000 # in bytes
|
||||
|
||||
# Disable registration. No new users will be able to register on this server.
|
||||
#allow_registration = false
|
||||
|
||||
# Disable encryption, so no new encrypted rooms can be created.
|
||||
# Note: Existing rooms will continue to work.
|
||||
#allow_encryption = false
|
||||
#allow_federation = false
|
||||
|
||||
# Enable jaeger to support monitoring and troubleshooting through jaeger.
|
||||
#allow_jaeger = false
|
||||
|
||||
#cache_capacity = 1073741824 # in bytes, 1024 * 1024 * 1024
|
||||
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
||||
#log = "info,state_res=warn,rocket=off,_=off,sled=off"
|
||||
#workers = 4 # default: cpu core count * 2
|
||||
# THIS FILE IS GENERATED BY DEBCONF AND WILL BE OVERRIDDEN!
|
||||
EOF
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
Vendored
-5
@@ -1,16 +1,11 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit
|
||||
|
||||
case $1 in
|
||||
purge)
|
||||
# Remove debconf changes from the db
|
||||
db_purge
|
||||
|
||||
# Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior
|
||||
# "configuration files must be preserved when the package is removed, and
|
||||
# only deleted when the package is purged."
|
||||
|
||||
Vendored
+1
-1
@@ -16,6 +16,6 @@ Description: The listen address of the Matrix homeserver
|
||||
|
||||
Template: matrix-conduit/port
|
||||
Type: string
|
||||
Default: 6167
|
||||
Default: 14004
|
||||
Description: The port of the Matrix homeserver
|
||||
This port is most often just accessed by a reverse proxy.
|
||||
|
||||
+21
-26
@@ -5,48 +5,43 @@ services:
|
||||
homeserver:
|
||||
### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image,
|
||||
### then you are ready to go.
|
||||
#image: matrixconduit/matrix-conduit:latest
|
||||
image: matrixconduit/matrix-conduit:latest
|
||||
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
CREATED: '2021-03-16T08:18:27Z'
|
||||
VERSION: '0.1.0'
|
||||
LOCAL: 'false'
|
||||
GIT_REF: origin/master
|
||||
# build:
|
||||
# context: .
|
||||
# args:
|
||||
# CREATED:
|
||||
# VERSION:
|
||||
# LOCAL: 'false'
|
||||
# GIT_REF: HEAD
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8448:8000
|
||||
volumes:
|
||||
- db:/srv/conduit/.local/share/conduit
|
||||
### Uncomment if you want to use conduit.toml to configure Conduit
|
||||
### Note: Set env vars will override conduit.toml values
|
||||
# - ./conduit.toml:/srv/conduit/conduit.toml
|
||||
### Uncomment if you want to use Rocket.toml to configure Conduit
|
||||
### Note: Set env vars will override Rocket.toml values
|
||||
# - ./Rocket.toml:/srv/conduit/Rocket.toml
|
||||
environment:
|
||||
CONDUIT_SERVER_NAME: localhost:8000 # replace with your own name
|
||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
ROCKET_SERVER_NAME: localhost:8000 # replace with your own name
|
||||
### Uncomment and change values as desired
|
||||
# CONDUIT_ADDRESS: 127.0.0.1
|
||||
# CONDUIT_PORT: 8000
|
||||
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if you want to configure purely by env vars, set this to an empty string ''
|
||||
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
||||
# CONDUIT_LOG: info # default is: "info,rocket=off,_=off,sled=off"
|
||||
# CONDUIT_ALLOW_JAEGER: 'false'
|
||||
# CONDUIT_ALLOW_REGISTRATION : 'false'
|
||||
# CONDUIT_ALLOW_ENCRYPTION: 'false'
|
||||
# CONDUIT_ALLOW_FEDERATION: 'false'
|
||||
# CONDUIT_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||
# CONDUIT_WORKERS: 10
|
||||
# CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||
# ROCKET_LOG: normal # Available levels are: off, debug, normal, critical
|
||||
# ROCKET_PORT: 8000
|
||||
# ROCKET_REGISTRATION_DISABLED: 'true'
|
||||
# ROCKET_ENCRYPTION_DISABLED: 'true'
|
||||
# ROCKET_FEDERATION_ENABLED: 'true'
|
||||
# ROCKET_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||
# ROCKET_WORKERS: 10
|
||||
# ROCKET_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||
|
||||
### Uncomment if you want to use your own Element-Web App.
|
||||
### Note: You need to provide a config.json for Element and you also need a second
|
||||
### Domain or Subdomain for the communication between Element and Conduit
|
||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||
# element-web:
|
||||
# image: vectorim/element-web:latest
|
||||
# image: vectorim/riot-web:latest
|
||||
# restart: unless-stopped
|
||||
# ports:
|
||||
# - 8009:80
|
||||
|
||||
+4
-6
@@ -17,13 +17,13 @@ The Dockerfile includes a few build arguments that should be supplied when build
|
||||
ARG LOCAL=false
|
||||
ARG CREATED
|
||||
ARG VERSION
|
||||
ARG GIT_REF=origin/master
|
||||
ARG GIT_REF=HEAD
|
||||
```
|
||||
|
||||
- **CREATED:** Date and time as string (date-time as defined by RFC 3339). Will be used to create the Open Container Initiative compliant label `org.opencontainers.image.created`. Supply by it like this `$(date -u +'%Y-%m-%dT%H:%M:%SZ')`
|
||||
- **VERSION:** The SemVer version of Conduit, which is in the image. Will be used to create the Open Container Initiative compliant label `org.opencontainers.image.version`. If you have a `Cargo.toml` in your build context, you can get it with `$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml)`
|
||||
- **LOCAL:** *(Optional)* A boolean value, specifies if the local build context should be used, or if the official repository will be cloned. If not supplied with the build command, it will default to `false`.
|
||||
- **GIT_REF:** *(Optional)* A git ref, like `HEAD` or a commit ID. The supplied ref will be used to create the Open Container Initiative compliant label `org.opencontainers.image.revision` and will be the ref that is cloned from the repository when not building from the local context. If not supplied with the build command, it will default to `origin/master`.
|
||||
- **GIT_REF:** *(Optional)* A git ref, like `HEAD` or a commit ID. The supplied ref will be used to create the Open Container Initiative compliant label `org.opencontainers.image.revision` and will be the ref that is cloned from the repository when not building from the local context. If not supplied with the build command, it will default to `HEAD`.
|
||||
|
||||
To build the image you can use the following command
|
||||
|
||||
@@ -40,12 +40,10 @@ which also will tag the resulting image as `matrixconduit/matrix-conduit:latest`
|
||||
After building the image you can simply run it with
|
||||
|
||||
``` bash
|
||||
docker run -d -p 8448:8000 -v ~/conduit.toml:/srv/conduit/conduit.toml -v db:/srv/conduit/.local/share/conduit matrixconduit/matrix-conduit:latest
|
||||
docker run -d -p 8448:8000 -v db:/srv/conduit/.local/share/conduit -e ROCKET_SERVER_NAME="localhost:8000" matrixconduit/matrix-conduit:latest
|
||||
```
|
||||
|
||||
For detached mode, you also need to use the `-d` flag. You also need to supply a `conduit.toml` config file, you can find an example [here](../conduit-example.toml).
|
||||
You can pass in different env vars to change config values on the fly. You can even configure Conduit completely by using env vars, but for that you need
|
||||
too pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible values, please take a look at the `docker-compose.yml` file.
|
||||
For detached mode, you also need to use the `-d` flag. You can pass in more env vars as are shown here, for an overview of possible values, you can take a look at the `docker-compose.yml` file.
|
||||
If you just want to test Conduit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
|
||||
|
||||
|
||||
|
||||
@@ -15,38 +15,32 @@ services:
|
||||
# CREATED:
|
||||
# VERSION:
|
||||
# LOCAL: 'false'
|
||||
# GIT_REF: origin/master
|
||||
# GIT_REF: HEAD
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db:/srv/conduit/.local/share/conduit
|
||||
### Uncomment if you want to use conduit.toml to configure Conduit
|
||||
### Note: Set env vars will override conduit.toml values
|
||||
# - ./conduit.toml:/srv/conduit/conduit.toml
|
||||
### Uncomment if you want to use Rocket.toml to configure Conduit
|
||||
### Note: Set env vars will override Rocket.toml values
|
||||
# - ./Rocket.toml:/srv/conduit/Rocket.toml
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
CONDUIT_SERVER_NAME: localhost:8000 # replace with your own name
|
||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
ROCKET_SERVER_NAME: localhost:8000 # replace with your own name
|
||||
### Uncomment and change values as desired
|
||||
# CONDUIT_ADDRESS: 127.0.0.1
|
||||
# CONDUIT_PORT: 8000
|
||||
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if want to configure purely by env vars, set this to an empty string ''
|
||||
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
||||
# CONDUIT_LOG: info # default is: "info,rocket=off,_=off,sled=off"
|
||||
# CONDUIT_ALLOW_JAEGER: 'false'
|
||||
# CONDUIT_ALLOW_REGISTRATION : 'false'
|
||||
# CONDUIT_ALLOW_ENCRYPTION: 'false'
|
||||
# CONDUIT_ALLOW_FEDERATION: 'false'
|
||||
# CONDUIT_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||
# CONDUIT_WORKERS: 10
|
||||
# CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||
# ROCKET_LOG: normal # Available levels are: off, debug, normal, critical
|
||||
# ROCKET_PORT: 8000
|
||||
# ROCKET_REGISTRATION_DISABLED: 'true'
|
||||
# ROCKET_ENCRYPTION_DISABLED: 'true'
|
||||
# ROCKET_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||
# ROCKET_WORKERS: 10
|
||||
# ROCKET_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||
|
||||
### Uncomment if you want to use your own Element-Web App.
|
||||
### Note: You need to provide a config.json for Element and you also need a second
|
||||
### Domain or Subdomain for the communication between Element and Conduit
|
||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||
# element-web:
|
||||
# image: vectorim/element-web:latest
|
||||
# image: vectorim/riot-web:latest
|
||||
# restart: unless-stopped
|
||||
# volumes:
|
||||
# - ./element_config.json:/app/config.json
|
||||
|
||||
+37
-29
@@ -1,11 +1,13 @@
|
||||
use crate::{utils, Error, Result};
|
||||
use bytes::BytesMut;
|
||||
use log::warn;
|
||||
use ruma::api::{IncomingResponse, OutgoingRequest, SendAccessToken};
|
||||
use ruma::{
|
||||
api::{IncomingResponse, OutgoingRequest, SendAccessToken},
|
||||
client::HttpClientExt,
|
||||
};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::Debug,
|
||||
mem,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@@ -20,11 +22,13 @@ where
|
||||
let destination = registration.get("url").unwrap().as_str().unwrap();
|
||||
let hs_token = registration.get("hs_token").unwrap().as_str().unwrap();
|
||||
|
||||
let mut http_request = request
|
||||
.try_into_http_request::<BytesMut>(&destination, SendAccessToken::IfRequired(""))
|
||||
.unwrap()
|
||||
.map(|body| body.freeze());
|
||||
|
||||
globals
|
||||
.reqwest_client()
|
||||
.send_customized_matrix_request(
|
||||
&destination,
|
||||
SendAccessToken::IfRequired(""),
|
||||
request,
|
||||
|http_request| {
|
||||
let mut parts = http_request.uri().clone().into_parts();
|
||||
let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned();
|
||||
let symbol = if old_path_and_query.contains('?') {
|
||||
@@ -33,34 +37,38 @@ where
|
||||
"?"
|
||||
};
|
||||
|
||||
parts.path_and_query = Some(
|
||||
(old_path_and_query + symbol + "access_token=" + hs_token)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
*http_request.uri_mut() = parts.try_into().expect("our manipulation is always valid");
|
||||
parts.path_and_query =
|
||||
Some((old_path_and_query + symbol + "access_token=" + hs_token).parse()?);
|
||||
*http_request.uri_mut() = parts.try_into()?;
|
||||
|
||||
let mut reqwest_request = reqwest::Request::try_from(http_request)
|
||||
.expect("all http requests are valid reqwest requests");
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Appservice error: {}", e);
|
||||
Error::BadServerResponse("Server returned an error.")
|
||||
})?;
|
||||
|
||||
*reqwest_request.timeout_mut() = Some(Duration::from_secs(30));
|
||||
|
||||
let url = reqwest_request.url().clone();
|
||||
let mut response = globals.reqwest_client().execute(reqwest_request).await?;
|
||||
let mut reqwest_response = globals.reqwest_client().execute(reqwest_request).await?;
|
||||
|
||||
// reqwest::Response -> http::Response conversion
|
||||
let status = response.status();
|
||||
let mut http_response_builder = http::Response::builder()
|
||||
.status(status)
|
||||
.version(response.version());
|
||||
mem::swap(
|
||||
response.headers_mut(),
|
||||
http_response_builder
|
||||
.headers_mut()
|
||||
.expect("http::response::Builder is usable"),
|
||||
);
|
||||
// Because reqwest::Response -> http::Response is complicated:
|
||||
let status = reqwest_response.status();
|
||||
let mut http_response = http::Response::builder().status(status);
|
||||
let headers = http_response.headers_mut().unwrap();
|
||||
|
||||
let body = response.bytes().await.unwrap_or_else(|e| {
|
||||
for (k, v) in reqwest_response.headers_mut().drain() {
|
||||
if let Some(key) = k {
|
||||
headers.insert(key, v);
|
||||
}
|
||||
}
|
||||
|
||||
let status = reqwest_response.status();
|
||||
|
||||
let body = reqwest_response.bytes().await.unwrap_or_else(|e| {
|
||||
warn!("server error: {}", e);
|
||||
Vec::new().into()
|
||||
}); // TODO: handle timeout
|
||||
@@ -76,7 +84,7 @@ where
|
||||
}
|
||||
|
||||
let response = T::IncomingResponse::try_from_http_response(
|
||||
http_response_builder
|
||||
http_response
|
||||
.body(body)
|
||||
.expect("reqwest body is valid http body"),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{collections::BTreeMap, convert::TryInto, sync::Arc};
|
||||
use std::{collections::BTreeMap, convert::TryInto};
|
||||
|
||||
use super::{State, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::{pdu::PduBuilder, utils, ConduitResult, Database, Error, Ruma};
|
||||
@@ -42,7 +42,7 @@ const GUEST_NAME_LENGTH: usize = 10;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_register_available_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_username_availability::Request<'_>>,
|
||||
) -> ConduitResult<get_username_availability::Response> {
|
||||
// Validate user id
|
||||
@@ -85,10 +85,10 @@ pub async fn get_register_available_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn register_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<register::Request<'_>>,
|
||||
) -> ConduitResult<register::Response> {
|
||||
if !db.globals.allow_registration() && !body.from_appservice {
|
||||
if !db.globals.allow_registration() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
"Registration has been disabled.",
|
||||
@@ -143,28 +143,16 @@ pub async fn register_route(
|
||||
|
||||
if !body.from_appservice {
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = db.uiaa.try_auth(
|
||||
&UserId::parse_with_server_name("", db.globals.server_name())
|
||||
.expect("we know this is valid"),
|
||||
"".into(),
|
||||
auth,
|
||||
&uiaainfo,
|
||||
&db.users,
|
||||
&db.globals,
|
||||
)?;
|
||||
let (worked, uiaainfo) =
|
||||
db.uiaa
|
||||
.try_auth(&user_id, "".into(), 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(
|
||||
&UserId::parse_with_server_name("", db.globals.server_name())
|
||||
.expect("we know this is valid"),
|
||||
"".into(),
|
||||
&uiaainfo,
|
||||
&body.json_body.expect("body is json"),
|
||||
)?;
|
||||
db.uiaa.create(&user_id, "".into(), &uiaainfo)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
}
|
||||
@@ -179,11 +167,12 @@ pub async fn register_route(
|
||||
let password = if is_guest {
|
||||
None
|
||||
} else {
|
||||
body.password.as_deref()
|
||||
};
|
||||
body.password.clone()
|
||||
}
|
||||
.unwrap_or_default();
|
||||
|
||||
// Create user
|
||||
db.users.create(&user_id, password)?;
|
||||
db.users.create(&user_id, &password)?;
|
||||
|
||||
// Initial data
|
||||
db.account_data.update(
|
||||
@@ -227,12 +216,12 @@ pub async fn register_route(
|
||||
)?;
|
||||
|
||||
// If this is the first user on this server, create the admins room
|
||||
if db.users.count()? == 1 {
|
||||
if db.users.count() == 1 {
|
||||
// Create a user for the server
|
||||
let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name())
|
||||
.expect("@conduit:server_name is valid");
|
||||
|
||||
db.users.create(&conduit_user, None)?;
|
||||
db.users.create(&conduit_user, "")?;
|
||||
|
||||
let room_id = RoomId::new(db.globals.server_name());
|
||||
|
||||
@@ -286,8 +275,18 @@ pub async fn register_route(
|
||||
event_type: EventType::RoomPowerLevels,
|
||||
content: serde_json::to_value(
|
||||
ruma::events::room::power_levels::PowerLevelsEventContent {
|
||||
ban: 50.into(),
|
||||
events: BTreeMap::new(),
|
||||
events_default: 0.into(),
|
||||
invite: 50.into(),
|
||||
kick: 50.into(),
|
||||
redact: 50.into(),
|
||||
state_default: 50.into(),
|
||||
users,
|
||||
..Default::default()
|
||||
users_default: 0.into(),
|
||||
notifications: ruma::events::room::power_levels::NotificationPowerLevels {
|
||||
room: 50.into(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.expect("event is valid, we just created it"),
|
||||
@@ -496,7 +495,7 @@ pub async fn register_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn change_password_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<change_password::Request<'_>>,
|
||||
) -> ConduitResult<change_password::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -527,17 +526,11 @@ pub async fn change_password_route(
|
||||
// Success!
|
||||
} else {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
db.uiaa.create(
|
||||
&sender_user,
|
||||
&sender_device,
|
||||
&uiaainfo,
|
||||
&body.json_body.expect("body is json"),
|
||||
)?;
|
||||
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
db.users
|
||||
.set_password(&sender_user, Some(&body.new_password))?;
|
||||
db.users.set_password(&sender_user, &body.new_password)?;
|
||||
|
||||
if body.logout_devices {
|
||||
// Logout all devices except the current one
|
||||
@@ -588,7 +581,7 @@ pub async fn whoami_route(body: Ruma<whoami::Request>) -> ConduitResult<whoami::
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn deactivate_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<deactivate::Request<'_>>,
|
||||
) -> ConduitResult<deactivate::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -619,12 +612,7 @@ pub async fn deactivate_route(
|
||||
// Success!
|
||||
} else {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
db.uiaa.create(
|
||||
&sender_user,
|
||||
&sender_device,
|
||||
&uiaainfo,
|
||||
&body.json_body.expect("body is json"),
|
||||
)?;
|
||||
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use regex::Regex;
|
||||
@@ -24,7 +22,7 @@ use rocket::{delete, get, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn create_alias_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_alias::Request<'_>>,
|
||||
) -> ConduitResult<create_alias::Response> {
|
||||
if db.rooms.id_from_alias(&body.room_alias)?.is_some() {
|
||||
@@ -45,7 +43,7 @@ pub async fn create_alias_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_alias_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_alias::Request<'_>>,
|
||||
) -> ConduitResult<delete_alias::Response> {
|
||||
db.rooms.set_alias(&body.room_alias, None, &db.globals)?;
|
||||
@@ -61,7 +59,7 @@ pub async fn delete_alias_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_alias_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_alias::Request<'_>>,
|
||||
) -> ConduitResult<get_alias::Response> {
|
||||
get_alias_helper(&db, &body.room_alias).await
|
||||
@@ -88,8 +86,7 @@ pub async fn get_alias_helper(
|
||||
match db.rooms.id_from_alias(&room_alias)? {
|
||||
Some(r) => room_id = Some(r),
|
||||
None => {
|
||||
let iter = db.appservice.iter_all()?;
|
||||
for (_id, registration) in iter.filter_map(|r| r.ok()) {
|
||||
for (_id, registration) in db.appservice.iter_all().filter_map(|r| r.ok()) {
|
||||
let aliases = registration
|
||||
.get("namespaces")
|
||||
.and_then(|ns| ns.get("aliases"))
|
||||
@@ -97,7 +94,13 @@ pub async fn get_alias_helper(
|
||||
.map_or_else(Vec::new, |aliases| {
|
||||
aliases
|
||||
.iter()
|
||||
.filter_map(|aliases| Regex::new(aliases.get("regex")?.as_str()?).ok())
|
||||
.map(|aliases| {
|
||||
aliases
|
||||
.get("regex")
|
||||
.and_then(|regex| regex.as_str())
|
||||
.and_then(|regex| Regex::new(regex).ok())
|
||||
})
|
||||
.filter_map(|o| o)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
|
||||
+15
-17
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::api::client::{
|
||||
@@ -21,7 +19,7 @@ use rocket::{delete, get, post, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn create_backup_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_backup::Request>,
|
||||
) -> ConduitResult<create_backup::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -40,7 +38,7 @@ pub async fn create_backup_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn update_backup_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<update_backup::Request<'_>>,
|
||||
) -> ConduitResult<update_backup::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -58,7 +56,7 @@ pub async fn update_backup_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_latest_backup_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_latest_backup::Request>,
|
||||
) -> ConduitResult<get_latest_backup::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -86,7 +84,7 @@ pub async fn get_latest_backup_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_backup_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_backup::Request<'_>>,
|
||||
) -> ConduitResult<get_backup::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -113,7 +111,7 @@ pub async fn get_backup_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_backup_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_backup::Request<'_>>,
|
||||
) -> ConduitResult<delete_backup::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -132,7 +130,7 @@ pub async fn delete_backup_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn add_backup_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<add_backup_keys::Request<'_>>,
|
||||
) -> ConduitResult<add_backup_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -166,7 +164,7 @@ pub async fn add_backup_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn add_backup_key_sessions_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<add_backup_key_sessions::Request<'_>>,
|
||||
) -> ConduitResult<add_backup_key_sessions::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -198,7 +196,7 @@ pub async fn add_backup_key_sessions_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn add_backup_key_session_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<add_backup_key_session::Request<'_>>,
|
||||
) -> ConduitResult<add_backup_key_session::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -227,7 +225,7 @@ pub async fn add_backup_key_session_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_backup_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_backup_keys::Request<'_>>,
|
||||
) -> ConduitResult<get_backup_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -243,14 +241,14 @@ pub async fn get_backup_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_backup_key_sessions_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_backup_key_sessions::Request<'_>>,
|
||||
) -> ConduitResult<get_backup_key_sessions::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let sessions = db
|
||||
.key_backups
|
||||
.get_room(&sender_user, &body.version, &body.room_id)?;
|
||||
.get_room(&sender_user, &body.version, &body.room_id);
|
||||
|
||||
Ok(get_backup_key_sessions::Response { sessions }.into())
|
||||
}
|
||||
@@ -261,7 +259,7 @@ pub async fn get_backup_key_sessions_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_backup_key_session_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_backup_key_session::Request<'_>>,
|
||||
) -> ConduitResult<get_backup_key_session::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -283,7 +281,7 @@ pub async fn get_backup_key_session_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_backup_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_backup_keys::Request<'_>>,
|
||||
) -> ConduitResult<delete_backup_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -306,7 +304,7 @@ pub async fn delete_backup_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_backup_key_sessions_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_backup_key_sessions::Request<'_>>,
|
||||
) -> ConduitResult<delete_backup_key_sessions::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -329,7 +327,7 @@ pub async fn delete_backup_key_sessions_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_backup_key_session_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_backup_key_session::Request<'_>>,
|
||||
) -> ConduitResult<delete_backup_key_session::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
+20
-29
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::{
|
||||
@@ -10,11 +8,11 @@ use ruma::{
|
||||
set_room_account_data,
|
||||
},
|
||||
},
|
||||
events::{AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent},
|
||||
events::{custom::CustomEventContent, AnyBasicEventContent, BasicEvent},
|
||||
serde::Raw,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, value::RawValue as RawJsonValue};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, put};
|
||||
@@ -25,12 +23,12 @@ use rocket::{get, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_global_account_data_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_global_account_data::Request<'_>>,
|
||||
) -> ConduitResult<set_global_account_data::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let data = serde_json::from_str::<serde_json::Value>(body.data.get())
|
||||
let data = serde_json::from_str(body.data.get())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
||||
|
||||
let event_type = body.event_type.to_string();
|
||||
@@ -39,10 +37,9 @@ pub async fn set_global_account_data_route(
|
||||
None,
|
||||
sender_user,
|
||||
event_type.clone().into(),
|
||||
&json!({
|
||||
"type": event_type,
|
||||
"content": data,
|
||||
}),
|
||||
&BasicEvent {
|
||||
content: CustomEventContent { event_type, data },
|
||||
},
|
||||
&db.globals,
|
||||
)?;
|
||||
|
||||
@@ -60,12 +57,12 @@ pub async fn set_global_account_data_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_room_account_data_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_room_account_data::Request<'_>>,
|
||||
) -> ConduitResult<set_room_account_data::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let data = serde_json::from_str::<serde_json::Value>(body.data.get())
|
||||
let data = serde_json::from_str(body.data.get())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
||||
|
||||
let event_type = body.event_type.to_string();
|
||||
@@ -74,10 +71,9 @@ pub async fn set_room_account_data_route(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
event_type.clone().into(),
|
||||
&json!({
|
||||
"type": event_type,
|
||||
"content": data,
|
||||
}),
|
||||
&BasicEvent {
|
||||
content: CustomEventContent { event_type, data },
|
||||
},
|
||||
&db.globals,
|
||||
)?;
|
||||
|
||||
@@ -92,7 +88,7 @@ pub async fn set_room_account_data_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_global_account_data_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_global_account_data::Request<'_>>,
|
||||
) -> ConduitResult<get_global_account_data::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -103,8 +99,8 @@ pub async fn get_global_account_data_route(
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||
db.flush().await?;
|
||||
|
||||
let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get())
|
||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
||||
let account_data = serde_json::from_str::<ExtractEventContent>(event.get())
|
||||
.expect("event to contain a content field")
|
||||
.content;
|
||||
|
||||
Ok(get_global_account_data::Response { account_data }.into())
|
||||
@@ -119,7 +115,7 @@ pub async fn get_global_account_data_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_room_account_data_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_room_account_data::Request<'_>>,
|
||||
) -> ConduitResult<get_room_account_data::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -134,19 +130,14 @@ pub async fn get_room_account_data_route(
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||
db.flush().await?;
|
||||
|
||||
let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get())
|
||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
||||
let account_data = serde_json::from_str::<ExtractEventContent>(event.get())
|
||||
.expect("event to contain a content field")
|
||||
.content;
|
||||
|
||||
Ok(get_room_account_data::Response { account_data }.into())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ExtractRoomEventContent {
|
||||
content: Raw<AnyRoomAccountDataEventContent>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ExtractGlobalEventContent {
|
||||
content: Raw<AnyGlobalAccountDataEventContent>,
|
||||
struct ExtractEventContent {
|
||||
content: Raw<AnyBasicEventContent>,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::api::client::{error::ErrorKind, r0::context::get_context};
|
||||
use std::{convert::TryFrom, sync::Arc};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::get;
|
||||
@@ -12,7 +12,7 @@ use rocket::get;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_context_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_context::Request<'_>>,
|
||||
) -> ConduitResult<get_context::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||
use ruma::api::client::{
|
||||
@@ -20,7 +18,7 @@ use rocket::{delete, get, post, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_devices_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_devices::Request>,
|
||||
) -> ConduitResult<get_devices::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -40,7 +38,7 @@ pub async fn get_devices_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_device_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_device::Request<'_>>,
|
||||
) -> ConduitResult<get_device::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -59,7 +57,7 @@ pub async fn get_device_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn update_device_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<update_device::Request<'_>>,
|
||||
) -> ConduitResult<update_device::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -85,7 +83,7 @@ pub async fn update_device_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_device_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_device::Request<'_>>,
|
||||
) -> ConduitResult<delete_device::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -117,12 +115,7 @@ pub async fn delete_device_route(
|
||||
// Success!
|
||||
} else {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
db.uiaa.create(
|
||||
&sender_user,
|
||||
&sender_device,
|
||||
&uiaainfo,
|
||||
&body.json_body.expect("body is json"),
|
||||
)?;
|
||||
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
@@ -139,7 +132,7 @@ pub async fn delete_device_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_devices_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_devices::Request<'_>>,
|
||||
) -> ConduitResult<delete_devices::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -171,12 +164,7 @@ pub async fn delete_devices_route(
|
||||
// Success!
|
||||
} else {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
db.uiaa.create(
|
||||
&sender_user,
|
||||
&sender_device,
|
||||
&uiaainfo,
|
||||
&body.json_body.expect("body is json"),
|
||||
)?;
|
||||
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Result, Ruma};
|
||||
use log::info;
|
||||
@@ -35,7 +33,7 @@ use rocket::{get, post, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_public_rooms_filtered_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_public_rooms_filtered::Request<'_>>,
|
||||
) -> ConduitResult<get_public_rooms_filtered::Response> {
|
||||
get_public_rooms_filtered_helper(
|
||||
@@ -55,7 +53,7 @@ pub async fn get_public_rooms_filtered_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_public_rooms_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_public_rooms::Request<'_>>,
|
||||
) -> ConduitResult<get_public_rooms::Response> {
|
||||
let response = get_public_rooms_filtered_helper(
|
||||
@@ -84,7 +82,7 @@ pub async fn get_public_rooms_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_room_visibility_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_room_visibility::Request<'_>>,
|
||||
) -> ConduitResult<set_room_visibility::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -114,7 +112,7 @@ pub async fn set_room_visibility_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_room_visibility_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_room_visibility::Request<'_>>,
|
||||
) -> ConduitResult<get_room_visibility::Response> {
|
||||
Ok(get_room_visibility::Response {
|
||||
@@ -135,7 +133,9 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
filter: &IncomingFilter,
|
||||
_network: &IncomingRoomNetwork,
|
||||
) -> ConduitResult<get_public_rooms_filtered::Response> {
|
||||
if let Some(other_server) = server.filter(|server| *server != db.globals.server_name().as_str())
|
||||
if let Some(other_server) = server
|
||||
.clone()
|
||||
.filter(|server| *server != db.globals.server_name().as_str())
|
||||
{
|
||||
let response = db
|
||||
.sending
|
||||
@@ -160,12 +160,15 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.map(|c| {
|
||||
// Convert ruma::api::federation::directory::get_public_rooms::v1::PublicRoomsChunk
|
||||
// to ruma::api::client::r0::directory::PublicRoomsChunk
|
||||
Ok::<_, Error>(
|
||||
serde_json::from_str(
|
||||
&serde_json::to_string(&c)
|
||||
.expect("PublicRoomsChunk::to_string always works"),
|
||||
)
|
||||
.expect("federation and client-server PublicRoomsChunk are the same type")
|
||||
.expect("federation and client-server PublicRoomsChunk are the same type"),
|
||||
)
|
||||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.collect(),
|
||||
prev_batch: response.prev_batch,
|
||||
next_batch: response.next_batch,
|
||||
@@ -214,7 +217,7 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.map_or(Ok::<_, Error>(None), |s| {
|
||||
Ok(serde_json::from_value::<
|
||||
Raw<canonical_alias::CanonicalAliasEventContent>,
|
||||
>(s.content.clone())
|
||||
>(s.content)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
.map_err(|_| {
|
||||
@@ -227,7 +230,7 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.room_state_get(&room_id, &EventType::RoomName, "")?
|
||||
.map_or(Ok::<_, Error>(None), |s| {
|
||||
Ok(serde_json::from_value::<Raw<name::NameEventContent>>(
|
||||
s.content.clone(),
|
||||
s.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -244,7 +247,7 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.map_or(Ok::<_, Error>(None), |s| {
|
||||
Ok(Some(
|
||||
serde_json::from_value::<Raw<topic::TopicEventContent>>(
|
||||
s.content.clone(),
|
||||
s.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -260,7 +263,7 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.map_or(Ok::<_, Error>(false), |s| {
|
||||
Ok(serde_json::from_value::<
|
||||
Raw<history_visibility::HistoryVisibilityEventContent>,
|
||||
>(s.content.clone())
|
||||
>(s.content)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
.map_err(|_| {
|
||||
@@ -277,7 +280,7 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.map_or(Ok::<_, Error>(false), |s| {
|
||||
Ok(
|
||||
serde_json::from_value::<Raw<guest_access::GuestAccessEventContent>>(
|
||||
s.content.clone(),
|
||||
s.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -294,7 +297,7 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
.map(|s| {
|
||||
Ok::<_, Error>(
|
||||
serde_json::from_value::<Raw<avatar::AvatarEventContent>>(
|
||||
s.content.clone(),
|
||||
s.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -312,36 +315,6 @@ pub async fn get_public_rooms_filtered_helper(
|
||||
Ok(chunk)
|
||||
})
|
||||
.filter_map(|r: Result<_>| r.ok()) // Filter out buggy rooms
|
||||
.filter(|chunk| {
|
||||
if let Some(query) = filter
|
||||
.generic_search_term
|
||||
.as_ref()
|
||||
.map(|q| q.to_lowercase())
|
||||
{
|
||||
if let Some(name) = &chunk.name {
|
||||
if name.to_lowercase().contains(&query) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(topic) = &chunk.topic {
|
||||
if topic.to_lowercase().contains(&query) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(canonical_alias) = &chunk.canonical_alias {
|
||||
if canonical_alias.as_str().to_lowercase().contains(&query) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
} else {
|
||||
// No search term
|
||||
true
|
||||
}
|
||||
})
|
||||
// We need to collect all, so we can sort by member count
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
+95
-130
@@ -1,5 +1,5 @@
|
||||
use super::{State, SESSION_ID_LENGTH};
|
||||
use crate::{utils, ConduitResult, Database, Error, Result, Ruma};
|
||||
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -12,12 +12,8 @@ use ruma::{
|
||||
},
|
||||
},
|
||||
encryption::UnsignedDeviceInfo,
|
||||
DeviceId, DeviceKeyAlgorithm, UserId,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, post};
|
||||
@@ -28,7 +24,7 @@ use rocket::{get, post};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn upload_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<upload_keys::Request>,
|
||||
) -> ConduitResult<upload_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -77,19 +73,79 @@ pub async fn upload_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_keys::Request<'_>>,
|
||||
) -> ConduitResult<get_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let response = get_keys_helper(
|
||||
Some(sender_user),
|
||||
&body.device_keys,
|
||||
|u| u == sender_user,
|
||||
&db,
|
||||
let mut master_keys = BTreeMap::new();
|
||||
let mut self_signing_keys = BTreeMap::new();
|
||||
let mut user_signing_keys = BTreeMap::new();
|
||||
let mut device_keys = BTreeMap::new();
|
||||
|
||||
for (user_id, device_ids) in &body.device_keys {
|
||||
if device_ids.is_empty() {
|
||||
let mut container = BTreeMap::new();
|
||||
for device_id in db.users.all_device_ids(user_id) {
|
||||
let device_id = device_id?;
|
||||
if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? {
|
||||
let metadata = db
|
||||
.users
|
||||
.get_device_metadata(user_id, &device_id)?
|
||||
.ok_or_else(|| {
|
||||
Error::bad_database("all_device_keys contained nonexistent device.")
|
||||
})?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id, keys);
|
||||
}
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
} else {
|
||||
for device_id in device_ids {
|
||||
let mut container = BTreeMap::new();
|
||||
if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? {
|
||||
let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or(
|
||||
Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Tried to get keys for nonexistent device.",
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(response.into())
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id.clone(), keys);
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(master_key) = db.users.get_master_key(user_id, sender_user)? {
|
||||
master_keys.insert(user_id.clone(), master_key);
|
||||
}
|
||||
if let Some(self_signing_key) = db.users.get_self_signing_key(user_id, sender_user)? {
|
||||
self_signing_keys.insert(user_id.clone(), self_signing_key);
|
||||
}
|
||||
if user_id == sender_user {
|
||||
if let Some(user_signing_key) = db.users.get_user_signing_key(sender_user)? {
|
||||
user_signing_keys.insert(user_id.clone(), user_signing_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(get_keys::Response {
|
||||
master_keys,
|
||||
self_signing_keys,
|
||||
user_signing_keys,
|
||||
device_keys,
|
||||
failures: BTreeMap::new(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -98,14 +154,32 @@ pub async fn get_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn claim_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<claim_keys::Request>,
|
||||
) -> ConduitResult<claim_keys::Response> {
|
||||
let response = claim_keys_helper(&body.one_time_keys, &db)?;
|
||||
let mut one_time_keys = BTreeMap::new();
|
||||
for (user_id, map) in &body.one_time_keys {
|
||||
let mut container = BTreeMap::new();
|
||||
for (device_id, key_algorithm) in map {
|
||||
if let Some(one_time_keys) =
|
||||
db.users
|
||||
.take_one_time_key(user_id, device_id, key_algorithm, &db.globals)?
|
||||
{
|
||||
let mut c = BTreeMap::new();
|
||||
c.insert(one_time_keys.0, one_time_keys.1);
|
||||
container.insert(device_id.clone(), c);
|
||||
}
|
||||
}
|
||||
one_time_keys.insert(user_id.clone(), container);
|
||||
}
|
||||
|
||||
db.flush().await?;
|
||||
|
||||
Ok(response.into())
|
||||
Ok(claim_keys::Response {
|
||||
failures: BTreeMap::new(),
|
||||
one_time_keys,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -114,7 +188,7 @@ pub async fn claim_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn upload_signing_keys_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<upload_signing_keys::Request<'_>>,
|
||||
) -> ConduitResult<upload_signing_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -146,12 +220,7 @@ pub async fn upload_signing_keys_route(
|
||||
// Success!
|
||||
} else {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
db.uiaa.create(
|
||||
&sender_user,
|
||||
&sender_device,
|
||||
&uiaainfo,
|
||||
&body.json_body.expect("body is json"),
|
||||
)?;
|
||||
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
@@ -177,7 +246,7 @@ pub async fn upload_signing_keys_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn upload_signatures_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<upload_signatures::Request>,
|
||||
) -> ConduitResult<upload_signatures::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -238,7 +307,7 @@ pub async fn upload_signatures_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_key_changes_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_key_changes::Request<'_>>,
|
||||
) -> ConduitResult<get_key_changes::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -282,107 +351,3 @@ pub async fn get_key_changes_route(
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
pub fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||
sender_user: Option<&UserId>,
|
||||
device_keys_input: &BTreeMap<UserId, Vec<Box<DeviceId>>>,
|
||||
allowed_signatures: F,
|
||||
db: &Database,
|
||||
) -> Result<get_keys::Response> {
|
||||
let mut master_keys = BTreeMap::new();
|
||||
let mut self_signing_keys = BTreeMap::new();
|
||||
let mut user_signing_keys = BTreeMap::new();
|
||||
let mut device_keys = BTreeMap::new();
|
||||
|
||||
for (user_id, device_ids) in device_keys_input {
|
||||
if device_ids.is_empty() {
|
||||
let mut container = BTreeMap::new();
|
||||
for device_id in db.users.all_device_ids(user_id) {
|
||||
let device_id = device_id?;
|
||||
if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? {
|
||||
let metadata = db
|
||||
.users
|
||||
.get_device_metadata(user_id, &device_id)?
|
||||
.ok_or_else(|| {
|
||||
Error::bad_database("all_device_keys contained nonexistent device.")
|
||||
})?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id, keys);
|
||||
}
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
} else {
|
||||
for device_id in device_ids {
|
||||
let mut container = BTreeMap::new();
|
||||
if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? {
|
||||
let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or(
|
||||
Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Tried to get keys for nonexistent device.",
|
||||
),
|
||||
)?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id.clone(), keys);
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(master_key) = db.users.get_master_key(user_id, &allowed_signatures)? {
|
||||
master_keys.insert(user_id.clone(), master_key);
|
||||
}
|
||||
if let Some(self_signing_key) = db
|
||||
.users
|
||||
.get_self_signing_key(user_id, &allowed_signatures)?
|
||||
{
|
||||
self_signing_keys.insert(user_id.clone(), self_signing_key);
|
||||
}
|
||||
if Some(user_id) == sender_user {
|
||||
if let Some(user_signing_key) = db.users.get_user_signing_key(user_id)? {
|
||||
user_signing_keys.insert(user_id.clone(), user_signing_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(get_keys::Response {
|
||||
master_keys,
|
||||
self_signing_keys,
|
||||
user_signing_keys,
|
||||
device_keys,
|
||||
failures: BTreeMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn claim_keys_helper(
|
||||
one_time_keys_input: &BTreeMap<UserId, BTreeMap<Box<DeviceId>, DeviceKeyAlgorithm>>,
|
||||
db: &Database,
|
||||
) -> Result<claim_keys::Response> {
|
||||
let mut one_time_keys = BTreeMap::new();
|
||||
for (user_id, map) in one_time_keys_input {
|
||||
let mut container = BTreeMap::new();
|
||||
for (device_id, key_algorithm) in map {
|
||||
if let Some(one_time_keys) =
|
||||
db.users
|
||||
.take_one_time_key(user_id, device_id, key_algorithm, &db.globals)?
|
||||
{
|
||||
let mut c = BTreeMap::new();
|
||||
c.insert(one_time_keys.0, one_time_keys.1);
|
||||
container.insert(device_id.clone(), c);
|
||||
}
|
||||
}
|
||||
one_time_keys.insert(user_id.clone(), container);
|
||||
}
|
||||
|
||||
Ok(claim_keys::Response {
|
||||
failures: BTreeMap::new(),
|
||||
one_time_keys,
|
||||
})
|
||||
}
|
||||
|
||||
+17
-36
@@ -7,14 +7,14 @@ use ruma::api::client::{
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, post};
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
use std::convert::TryInto;
|
||||
|
||||
const MXC_LENGTH: usize = 32;
|
||||
|
||||
#[cfg_attr(feature = "conduit_bin", get("/_matrix/media/r0/config"))]
|
||||
#[tracing::instrument(skip(db))]
|
||||
pub async fn get_media_config_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
) -> ConduitResult<get_media_config::Response> {
|
||||
Ok(get_media_config::Response {
|
||||
upload_size: db.globals.max_request_size().into(),
|
||||
@@ -28,7 +28,7 @@ pub async fn get_media_config_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn create_content_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_content::Request<'_>>,
|
||||
) -> ConduitResult<create_content::Response> {
|
||||
let mxc = format!(
|
||||
@@ -36,20 +36,12 @@ pub async fn create_content_route(
|
||||
db.globals.server_name(),
|
||||
utils::random_string(MXC_LENGTH)
|
||||
);
|
||||
|
||||
db.media
|
||||
.create(
|
||||
db.media.create(
|
||||
mxc.clone(),
|
||||
&db.globals,
|
||||
&body
|
||||
.filename
|
||||
.as_ref()
|
||||
.map(|filename| "inline; filename=".to_owned() + filename)
|
||||
.as_deref(),
|
||||
&body.filename.as_deref(),
|
||||
&body.content_type.as_deref(),
|
||||
&body.file,
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
db.flush().await?;
|
||||
|
||||
@@ -66,21 +58,21 @@ pub async fn create_content_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_content_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_content::Request<'_>>,
|
||||
) -> ConduitResult<get_content::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_disposition,
|
||||
filename,
|
||||
content_type,
|
||||
file,
|
||||
}) = db.media.get(&db.globals, &mxc).await?
|
||||
}) = db.media.get(&mxc)?
|
||||
{
|
||||
Ok(get_content::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition,
|
||||
content_disposition: filename,
|
||||
}
|
||||
.into())
|
||||
} else if &*body.server_name != db.globals.server_name() && body.allow_remote {
|
||||
@@ -97,15 +89,12 @@ pub async fn get_content_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
db.media
|
||||
.create(
|
||||
db.media.create(
|
||||
mxc,
|
||||
&db.globals,
|
||||
&get_content_response.content_disposition.as_deref(),
|
||||
&get_content_response.content_type.as_deref(),
|
||||
&get_content_response.file,
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
Ok(get_content_response.into())
|
||||
} else {
|
||||
@@ -119,27 +108,22 @@ pub async fn get_content_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_content_thumbnail_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_content_thumbnail::Request<'_>>,
|
||||
) -> ConduitResult<get_content_thumbnail::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_type, file, ..
|
||||
}) = db
|
||||
.media
|
||||
.get_thumbnail(
|
||||
}) = db.media.get_thumbnail(
|
||||
mxc.clone(),
|
||||
&db.globals,
|
||||
body.width
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
body.height
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
)? {
|
||||
Ok(get_content_thumbnail::Response { file, content_type }.into())
|
||||
} else if &*body.server_name != db.globals.server_name() && body.allow_remote {
|
||||
let get_thumbnail_response = db
|
||||
@@ -158,17 +142,14 @@ pub async fn get_content_thumbnail_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
db.media
|
||||
.upload_thumbnail(
|
||||
db.media.upload_thumbnail(
|
||||
mxc,
|
||||
&db.globals,
|
||||
&None,
|
||||
&get_thumbnail_response.content_type,
|
||||
body.width.try_into().expect("all UInts are valid u32s"),
|
||||
body.height.try_into().expect("all UInts are valid u32s"),
|
||||
&get_thumbnail_response.file,
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
Ok(get_thumbnail_response.into())
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
server_server, utils, ConduitResult, Database, Error, Result, Ruma,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use log::{error, warn};
|
||||
use member::{MemberEventContent, MembershipState};
|
||||
use rocket::futures;
|
||||
use ruma::{
|
||||
@@ -25,14 +25,13 @@ use ruma::{
|
||||
EventType,
|
||||
},
|
||||
serde::{to_canonical_value, CanonicalJsonObject, CanonicalJsonValue, Raw},
|
||||
state_res::{self, EventMap, RoomVersion},
|
||||
uint, EventId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
};
|
||||
use state_res::EventMap;
|
||||
use std::{
|
||||
collections::{btree_map::Entry, BTreeMap, HashSet},
|
||||
collections::{BTreeMap, HashSet},
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
@@ -44,7 +43,7 @@ use rocket::{get, post};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn join_room_by_id_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<join_room_by_id::Request<'_>>,
|
||||
) -> ConduitResult<join_room_by_id::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -81,7 +80,7 @@ pub async fn join_room_by_id_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn join_room_by_id_or_alias_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<join_room_by_id_or_alias::Request<'_>>,
|
||||
) -> ConduitResult<join_room_by_id_or_alias::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -135,7 +134,7 @@ pub async fn join_room_by_id_or_alias_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn leave_room_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<leave_room::Request<'_>>,
|
||||
) -> ConduitResult<leave_room::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -153,7 +152,7 @@ pub async fn leave_room_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn invite_user_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<invite_user::Request<'_>>,
|
||||
) -> ConduitResult<invite_user::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -173,7 +172,7 @@ pub async fn invite_user_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn kick_user_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<kick_user::Request<'_>>,
|
||||
) -> ConduitResult<kick_user::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -189,8 +188,7 @@ pub async fn kick_user_route(
|
||||
ErrorKind::BadState,
|
||||
"Cannot kick member that's not in the room.",
|
||||
))?
|
||||
.content
|
||||
.clone(),
|
||||
.content,
|
||||
)
|
||||
.expect("Raw::from_value always works")
|
||||
.deserialize()
|
||||
@@ -223,7 +221,7 @@ pub async fn kick_user_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn ban_user_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<ban_user::Request<'_>>,
|
||||
) -> ConduitResult<ban_user::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -246,9 +244,8 @@ pub async fn ban_user_route(
|
||||
third_party_invite: None,
|
||||
}),
|
||||
|event| {
|
||||
let mut event = serde_json::from_value::<Raw<member::MemberEventContent>>(
|
||||
event.content.clone(),
|
||||
)
|
||||
let mut event =
|
||||
serde_json::from_value::<Raw<member::MemberEventContent>>(event.content)
|
||||
.expect("Raw::from_value always works")
|
||||
.deserialize()
|
||||
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
|
||||
@@ -281,7 +278,7 @@ pub async fn ban_user_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn unban_user_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<unban_user::Request<'_>>,
|
||||
) -> ConduitResult<unban_user::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -297,8 +294,7 @@ pub async fn unban_user_route(
|
||||
ErrorKind::BadState,
|
||||
"Cannot unban a user who is not banned.",
|
||||
))?
|
||||
.content
|
||||
.clone(),
|
||||
.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -330,7 +326,7 @@ pub async fn unban_user_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn forget_room_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<forget_room::Request<'_>>,
|
||||
) -> ConduitResult<forget_room::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -348,7 +344,7 @@ pub async fn forget_room_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn joined_rooms_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<joined_rooms::Request>,
|
||||
) -> ConduitResult<joined_rooms::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -369,7 +365,7 @@ pub async fn joined_rooms_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_member_events_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_member_events::Request<'_>>,
|
||||
) -> ConduitResult<get_member_events::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -399,7 +395,7 @@ pub async fn get_member_events_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn joined_members_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<joined_members::Request<'_>>,
|
||||
) -> ConduitResult<joined_members::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -549,6 +545,12 @@ async fn join_room_by_id_helper(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let count = db.globals.next_count()?;
|
||||
|
||||
let mut pdu_id = room_id.as_bytes().to_vec();
|
||||
pdu_id.push(0xff);
|
||||
pdu_id.extend_from_slice(&count.to_be_bytes());
|
||||
|
||||
let pdu = PduEvent::from_id_val(&event_id, join_event.clone())
|
||||
.map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?;
|
||||
|
||||
@@ -576,6 +578,36 @@ async fn join_room_by_id_helper(
|
||||
|
||||
db.rooms.add_pdu_outlier(&event_id, &value)?;
|
||||
if let Some(state_key) = &pdu.state_key {
|
||||
if pdu.kind == EventType::RoomMember {
|
||||
let target_user_id = UserId::try_from(state_key.clone()).map_err(|e| {
|
||||
warn!(
|
||||
"Invalid user id in send_join response: {}: {}",
|
||||
state_key, e
|
||||
);
|
||||
Error::BadServerResponse("Invalid user id in send_join response.")
|
||||
})?;
|
||||
|
||||
let invite_state = Vec::new(); // TODO add a few important events
|
||||
|
||||
// Update our membership info, we do this here incase a user is invited
|
||||
// and immediately leaves we need the DB to record the invite event for auth
|
||||
db.rooms.update_membership(
|
||||
&pdu.room_id,
|
||||
&target_user_id,
|
||||
serde_json::from_value::<member::MembershipState>(
|
||||
pdu.content
|
||||
.get("membership")
|
||||
.ok_or(Error::BadServerResponse("Invalid member event content"))?
|
||||
.clone(),
|
||||
)
|
||||
.map_err(|_| {
|
||||
Error::BadServerResponse("Invalid membership state content.")
|
||||
})?,
|
||||
&pdu.sender,
|
||||
Some(invite_state),
|
||||
db,
|
||||
)?;
|
||||
}
|
||||
state.insert((pdu.kind.clone(), state_key.clone()), pdu.event_id.clone());
|
||||
}
|
||||
}
|
||||
@@ -615,16 +647,11 @@ async fn join_room_by_id_helper(
|
||||
// pdu without it's state. This is okay because append_pdu can't fail.
|
||||
let statehashid = db.rooms.append_to_state(&pdu, &db.globals)?;
|
||||
|
||||
let count = db.globals.next_count()?;
|
||||
let mut pdu_id = room_id.as_bytes().to_vec();
|
||||
pdu_id.push(0xff);
|
||||
pdu_id.extend_from_slice(&count.to_be_bytes());
|
||||
|
||||
db.rooms.append_pdu(
|
||||
&pdu,
|
||||
utils::to_canonical_object(&pdu).expect("Pdu is valid canonical object"),
|
||||
count,
|
||||
&pdu_id,
|
||||
db.globals.next_count()?,
|
||||
pdu_id.into(),
|
||||
&[pdu.event_id.clone()],
|
||||
db,
|
||||
)?;
|
||||
@@ -667,41 +694,9 @@ async fn validate_and_add_event_id(
|
||||
db: &Database,
|
||||
) -> Result<(EventId, CanonicalJsonObject)> {
|
||||
let mut value = serde_json::from_str::<CanonicalJsonObject>(pdu.json().get()).map_err(|e| {
|
||||
error!("Invalid PDU in server response: {:?}: {:?}", pdu, e);
|
||||
error!("{:?}: {:?}", pdu, e);
|
||||
Error::BadServerResponse("Invalid PDU in server response")
|
||||
})?;
|
||||
let event_id = EventId::try_from(&*format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(&value, &room_version)
|
||||
.expect("ruma can calculate reference hashes")
|
||||
))
|
||||
.expect("ruma's reference hashes are valid event ids");
|
||||
|
||||
let back_off = |id| match db.globals.bad_event_ratelimiter.write().unwrap().entry(id) {
|
||||
Entry::Vacant(e) => {
|
||||
e.insert((Instant::now(), 1));
|
||||
}
|
||||
Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
||||
};
|
||||
|
||||
if let Some((time, tries)) = db
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&event_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries);
|
||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||
}
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off from {}", event_id);
|
||||
return Err(Error::BadServerResponse("bad event, still backing off"));
|
||||
}
|
||||
}
|
||||
|
||||
server_server::fetch_required_signing_keys(&value, pub_key_map, db).await?;
|
||||
if let Err(e) = ruma::signatures::verify_event(
|
||||
@@ -711,11 +706,17 @@ async fn validate_and_add_event_id(
|
||||
&value,
|
||||
room_version,
|
||||
) {
|
||||
warn!("Event {} failed verification {:?} {}", event_id, pdu, e);
|
||||
back_off(event_id);
|
||||
warn!("Event failed verification: {}", e);
|
||||
return Err(Error::BadServerResponse("Event failed verification."));
|
||||
}
|
||||
|
||||
let event_id = EventId::try_from(&*format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(&value, &room_version)
|
||||
.expect("ruma can calculate reference hashes")
|
||||
))
|
||||
.expect("ruma's reference hashes are valid event ids");
|
||||
|
||||
value.insert(
|
||||
"event_id".to_owned(),
|
||||
CanonicalJsonValue::String(event_id.as_str().to_owned()),
|
||||
@@ -746,27 +747,27 @@ pub async fn invite_helper(
|
||||
let create_event_content = create_event
|
||||
.as_ref()
|
||||
.map(|create_event| {
|
||||
Ok::<_, Error>(
|
||||
serde_json::from_value::<Raw<CreateEventContent>>(create_event.content.clone())
|
||||
.expect("Raw::from_value always works.")
|
||||
.deserialize()
|
||||
.map_err(|_| Error::bad_database("Invalid PowerLevels event in db."))
|
||||
.map_err(|_| Error::bad_database("Invalid PowerLevels event in db."))?,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let create_prev_event = if prev_events.len() == 1
|
||||
&& Some(&prev_events[0]) == create_event.as_ref().map(|c| &c.event_id)
|
||||
{
|
||||
create_event
|
||||
create_event.map(Arc::new)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If there was no create event yet, assume we are creating a version 6 room right now
|
||||
let room_version_id = create_event_content
|
||||
.map_or(RoomVersionId::Version6, |create_event| {
|
||||
let room_version = create_event_content.map_or(RoomVersionId::Version6, |create_event| {
|
||||
create_event.room_version
|
||||
});
|
||||
let room_version = RoomVersion::new(&room_version_id).expect("room version is supported");
|
||||
|
||||
let content = serde_json::to_value(MemberEventContent {
|
||||
avatar_url: None,
|
||||
@@ -795,10 +796,10 @@ pub async fn invite_helper(
|
||||
let mut unsigned = BTreeMap::new();
|
||||
|
||||
if let Some(prev_pdu) = db.rooms.room_state_get(room_id, &kind, &state_key)? {
|
||||
unsigned.insert("prev_content".to_owned(), prev_pdu.content.clone());
|
||||
unsigned.insert("prev_content".to_owned(), prev_pdu.content);
|
||||
unsigned.insert(
|
||||
"prev_sender".to_owned(),
|
||||
serde_json::to_value(&prev_pdu.sender).expect("UserId::to_value always works"),
|
||||
serde_json::to_value(prev_pdu.sender).expect("UserId::to_value always works"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -840,7 +841,7 @@ pub async fn invite_helper(
|
||||
|
||||
if !auth_check {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
ErrorKind::InvalidParam,
|
||||
"Event is not authorized.",
|
||||
));
|
||||
}
|
||||
@@ -862,7 +863,7 @@ pub async fn invite_helper(
|
||||
db.globals.server_name().as_str(),
|
||||
db.globals.keypair(),
|
||||
&mut pdu_json,
|
||||
&room_version_id,
|
||||
&room_version,
|
||||
)
|
||||
.expect("event is valid, we just created it");
|
||||
|
||||
@@ -946,7 +947,7 @@ pub async fn invite_helper(
|
||||
membership: member::MembershipState::Invite,
|
||||
displayname: db.users.displayname(&user_id)?,
|
||||
avatar_url: db.users.avatar_url(&user_id)?,
|
||||
is_direct: Some(is_direct),
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
|
||||
@@ -11,7 +11,6 @@ use ruma::{
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
@@ -23,7 +22,7 @@ use rocket::{get, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn send_message_event_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<send_message_event::Request<'_>>,
|
||||
) -> ConduitResult<send_message_event::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -57,7 +56,12 @@ pub async fn send_message_event_route(
|
||||
let event_id = db.rooms.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: EventType::from(&body.event_type),
|
||||
content: serde_json::from_str(body.body.body.json().get())
|
||||
content: serde_json::from_str(
|
||||
body.json_body
|
||||
.as_ref()
|
||||
.ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?
|
||||
.get(),
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
|
||||
unsigned: Some(unsigned),
|
||||
state_key: None,
|
||||
@@ -86,7 +90,7 @@ pub async fn send_message_event_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_message_events_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_message_events::Request<'_>>,
|
||||
) -> ConduitResult<get_message_events::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -69,9 +69,9 @@ use {
|
||||
ruma::api::client::r0::to_device::send_event_to_device,
|
||||
};
|
||||
|
||||
pub const DEVICE_ID_LENGTH: usize = 10;
|
||||
pub const TOKEN_LENGTH: usize = 256;
|
||||
pub const SESSION_ID_LENGTH: usize = 256;
|
||||
const DEVICE_ID_LENGTH: usize = 10;
|
||||
const TOKEN_LENGTH: usize = 256;
|
||||
const SESSION_ID_LENGTH: usize = 256;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
#[options("/<_..>")]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::State;
|
||||
use crate::{utils, ConduitResult, Database, Ruma};
|
||||
use ruma::api::client::r0::presence::{get_presence, set_presence};
|
||||
use std::{convert::TryInto, sync::Arc, time::Duration};
|
||||
use ruma::api::client::r0::presence::set_presence;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, put};
|
||||
use rocket::put;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "conduit_bin",
|
||||
@@ -12,7 +12,7 @@ use rocket::{get, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_presence_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_presence::Request<'_>>,
|
||||
) -> ConduitResult<set_presence::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -46,48 +46,3 @@ pub async fn set_presence_route(
|
||||
|
||||
Ok(set_presence::Response.into())
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "conduit_bin",
|
||||
get("/_matrix/client/r0/presence/<_>/status", data = "<body>")
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_presence_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
body: Ruma<get_presence::Request<'_>>,
|
||||
) -> ConduitResult<get_presence::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut presence_event = None;
|
||||
|
||||
for room_id in db
|
||||
.rooms
|
||||
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
|
||||
{
|
||||
let room_id = room_id?;
|
||||
|
||||
if let Some(presence) = db
|
||||
.rooms
|
||||
.edus
|
||||
.get_last_presence_event(&sender_user, &room_id)?
|
||||
{
|
||||
presence_event = Some(presence);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(presence) = presence_event {
|
||||
Ok(get_presence::Response {
|
||||
// TODO: Should ruma just use the presenceeventcontent type here?
|
||||
status_msg: presence.content.status_msg,
|
||||
currently_active: presence.content.currently_active,
|
||||
last_active_ago: presence
|
||||
.content
|
||||
.last_active_ago
|
||||
.map(|millis| Duration::from_millis(millis.into())),
|
||||
presence: presence.content.presence,
|
||||
}
|
||||
.into())
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use ruma::{
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, put};
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "conduit_bin",
|
||||
@@ -21,7 +21,7 @@ use std::{convert::TryInto, sync::Arc};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_displayname_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_display_name::Request<'_>>,
|
||||
) -> ConduitResult<set_display_name::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -53,8 +53,7 @@ pub async fn set_displayname_route(
|
||||
room.",
|
||||
)
|
||||
})?
|
||||
.content
|
||||
.clone(),
|
||||
.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -108,7 +107,7 @@ pub async fn set_displayname_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_displayname_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_display_name::Request<'_>>,
|
||||
) -> ConduitResult<get_display_name::Response> {
|
||||
Ok(get_display_name::Response {
|
||||
@@ -123,7 +122,7 @@ pub async fn get_displayname_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_avatar_url_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_avatar_url::Request<'_>>,
|
||||
) -> ConduitResult<set_avatar_url::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -155,8 +154,7 @@ pub async fn set_avatar_url_route(
|
||||
room.",
|
||||
)
|
||||
})?
|
||||
.content
|
||||
.clone(),
|
||||
.content,
|
||||
)
|
||||
.expect("from_value::<Raw<..>> can never fail")
|
||||
.deserialize()
|
||||
@@ -210,7 +208,7 @@ pub async fn set_avatar_url_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_avatar_url_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_avatar_url::Request<'_>>,
|
||||
) -> ConduitResult<get_avatar_url::Response> {
|
||||
Ok(get_avatar_url::Response {
|
||||
@@ -225,7 +223,7 @@ pub async fn get_avatar_url_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_profile_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_profile::Request<'_>>,
|
||||
) -> ConduitResult<get_profile::Response> {
|
||||
if !db.users.exists(&body.user_id)? {
|
||||
|
||||
+10
-12
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::{
|
||||
@@ -24,7 +22,7 @@ use rocket::{delete, get, post, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_pushrules_all_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_pushrules_all::Request>,
|
||||
) -> ConduitResult<get_pushrules_all::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -49,7 +47,7 @@ pub async fn get_pushrules_all_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_pushrule_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_pushrule::Request<'_>>,
|
||||
) -> ConduitResult<get_pushrule::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -103,7 +101,7 @@ pub async fn get_pushrule_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, req))]
|
||||
pub async fn set_pushrule_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
req: Ruma<set_pushrule::Request<'_>>,
|
||||
) -> ConduitResult<set_pushrule::Response> {
|
||||
let sender_user = req.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -206,7 +204,7 @@ pub async fn set_pushrule_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_pushrule_actions_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_pushrule_actions::Request<'_>>,
|
||||
) -> ConduitResult<get_pushrule_actions::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -265,7 +263,7 @@ pub async fn get_pushrule_actions_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_pushrule_actions_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_pushrule_actions::Request<'_>>,
|
||||
) -> ConduitResult<set_pushrule_actions::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -339,7 +337,7 @@ pub async fn set_pushrule_actions_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_pushrule_enabled_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_pushrule_enabled::Request<'_>>,
|
||||
) -> ConduitResult<get_pushrule_enabled::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -400,7 +398,7 @@ pub async fn get_pushrule_enabled_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_pushrule_enabled_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_pushrule_enabled::Request<'_>>,
|
||||
) -> ConduitResult<set_pushrule_enabled::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -479,7 +477,7 @@ pub async fn set_pushrule_enabled_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_pushrule_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_pushrule::Request<'_>>,
|
||||
) -> ConduitResult<delete_pushrule::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -548,7 +546,7 @@ pub async fn delete_pushrule_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_pushers_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_pushers::Request>,
|
||||
) -> ConduitResult<get_pushers::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -565,7 +563,7 @@ pub async fn get_pushers_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_pushers_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_pusher::Request>,
|
||||
) -> ConduitResult<set_pusher::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -5,14 +5,12 @@ use ruma::{
|
||||
error::ErrorKind,
|
||||
r0::{read_marker::set_read_marker, receipt::create_receipt},
|
||||
},
|
||||
events::{AnyEphemeralRoomEvent, EventType},
|
||||
receipt::ReceiptType,
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
events::{AnyEphemeralRoomEvent, AnyEvent, EventType},
|
||||
};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::post;
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::{collections::BTreeMap, time::SystemTime};
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "conduit_bin",
|
||||
@@ -20,7 +18,7 @@ use std::{collections::BTreeMap, sync::Arc};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn set_read_marker_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<set_read_marker::Request<'_>>,
|
||||
) -> ConduitResult<set_read_marker::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -29,6 +27,7 @@ pub async fn set_read_marker_route(
|
||||
content: ruma::events::fully_read::FullyReadEventContent {
|
||||
event_id: body.fully_read.clone(),
|
||||
},
|
||||
room_id: body.room_id.clone(),
|
||||
};
|
||||
db.account_data.update(
|
||||
Some(&body.room_id),
|
||||
@@ -55,23 +54,26 @@ pub async fn set_read_marker_route(
|
||||
user_receipts.insert(
|
||||
sender_user.clone(),
|
||||
ruma::events::receipt::Receipt {
|
||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||
ts: Some(SystemTime::now()),
|
||||
},
|
||||
);
|
||||
|
||||
let mut receipts = BTreeMap::new();
|
||||
receipts.insert(ReceiptType::Read, user_receipts);
|
||||
|
||||
let mut receipt_content = BTreeMap::new();
|
||||
receipt_content.insert(event.to_owned(), receipts);
|
||||
receipt_content.insert(
|
||||
event.to_owned(),
|
||||
ruma::events::receipt::Receipts {
|
||||
read: Some(user_receipts),
|
||||
},
|
||||
);
|
||||
|
||||
db.rooms.edus.readreceipt_update(
|
||||
&sender_user,
|
||||
&body.room_id,
|
||||
AnyEphemeralRoomEvent::Receipt(ruma::events::receipt::ReceiptEvent {
|
||||
AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
|
||||
ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
}),
|
||||
},
|
||||
)),
|
||||
&db.globals,
|
||||
)?;
|
||||
}
|
||||
@@ -87,7 +89,7 @@ pub async fn set_read_marker_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn create_receipt_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_receipt::Request<'_>>,
|
||||
) -> ConduitResult<create_receipt::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -110,22 +112,26 @@ pub async fn create_receipt_route(
|
||||
user_receipts.insert(
|
||||
sender_user.clone(),
|
||||
ruma::events::receipt::Receipt {
|
||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||
ts: Some(SystemTime::now()),
|
||||
},
|
||||
);
|
||||
let mut receipts = BTreeMap::new();
|
||||
receipts.insert(ReceiptType::Read, user_receipts);
|
||||
|
||||
let mut receipt_content = BTreeMap::new();
|
||||
receipt_content.insert(body.event_id.to_owned(), receipts);
|
||||
receipt_content.insert(
|
||||
body.event_id.to_owned(),
|
||||
ruma::events::receipt::Receipts {
|
||||
read: Some(user_receipts),
|
||||
},
|
||||
);
|
||||
|
||||
db.rooms.edus.readreceipt_update(
|
||||
&sender_user,
|
||||
&body.room_id,
|
||||
AnyEphemeralRoomEvent::Receipt(ruma::events::receipt::ReceiptEvent {
|
||||
AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
|
||||
ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
}),
|
||||
},
|
||||
)),
|
||||
&db.globals,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use ruma::{
|
||||
api::client::r0::redact::redact_event,
|
||||
events::{room::redaction, EventType},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::put;
|
||||
@@ -15,7 +14,7 @@ use rocket::put;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn redact_event_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<redact_event::Request<'_>>,
|
||||
) -> ConduitResult<redact_event::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
+28
-28
@@ -1,5 +1,6 @@
|
||||
use super::State;
|
||||
use crate::{client_server::invite_helper, pdu::PduBuilder, ConduitResult, Database, Error, Ruma};
|
||||
use crate::client_server::invite_helper;
|
||||
use crate::{pdu::PduBuilder, ConduitResult, Database, Error, Ruma};
|
||||
use log::info;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
@@ -13,7 +14,7 @@ use ruma::{
|
||||
serde::Raw,
|
||||
RoomAliasId, RoomId, RoomVersionId,
|
||||
};
|
||||
use std::{cmp::max, collections::BTreeMap, convert::TryFrom, sync::Arc};
|
||||
use std::{cmp::max, collections::BTreeMap, convert::TryFrom};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, post};
|
||||
@@ -24,7 +25,7 @@ use rocket::{get, post};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn create_room_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_room::Request<'_>>,
|
||||
) -> ConduitResult<create_room::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -111,26 +112,27 @@ pub async fn create_room_route(
|
||||
}
|
||||
}
|
||||
|
||||
let mut power_levels_content =
|
||||
serde_json::to_value(ruma::events::room::power_levels::PowerLevelsEventContent {
|
||||
users,
|
||||
..Default::default()
|
||||
})
|
||||
.expect("event is valid, we just created it");
|
||||
|
||||
if let Some(power_level_content_override) = &body.power_level_content_override {
|
||||
let json = serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(
|
||||
power_level_content_override.json().get(),
|
||||
)
|
||||
.map_err(|_| {
|
||||
let power_levels_content = if let Some(power_levels) = &body.power_level_content_override {
|
||||
serde_json::from_str(power_levels.json().get()).map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
|
||||
})?;
|
||||
|
||||
for (key, value) in json {
|
||||
power_levels_content[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
})?
|
||||
} else {
|
||||
serde_json::to_value(ruma::events::room::power_levels::PowerLevelsEventContent {
|
||||
ban: 50.into(),
|
||||
events: BTreeMap::new(),
|
||||
events_default: 0.into(),
|
||||
invite: 50.into(),
|
||||
kick: 50.into(),
|
||||
redact: 50.into(),
|
||||
state_default: 50.into(),
|
||||
users,
|
||||
users_default: 0.into(),
|
||||
notifications: ruma::events::room::power_levels::NotificationPowerLevels {
|
||||
room: 50.into(),
|
||||
},
|
||||
})
|
||||
.expect("event is valid, we just created it")
|
||||
};
|
||||
db.rooms.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: EventType::RoomPowerLevels,
|
||||
@@ -294,7 +296,7 @@ pub async fn create_room_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_room_event_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_room_event::Request<'_>>,
|
||||
) -> ConduitResult<get_room_event::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -322,7 +324,7 @@ pub async fn get_room_event_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn upgrade_room_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<upgrade_room::Request<'_>>,
|
||||
_room_id: String,
|
||||
) -> ConduitResult<upgrade_room::Response> {
|
||||
@@ -362,8 +364,7 @@ pub async fn upgrade_room_route(
|
||||
db.rooms
|
||||
.room_state_get(&body.room_id, &EventType::RoomCreate, "")?
|
||||
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?
|
||||
.content
|
||||
.clone(),
|
||||
.content,
|
||||
)
|
||||
.expect("Raw::from_value always works")
|
||||
.deserialize()
|
||||
@@ -464,8 +465,7 @@ pub async fn upgrade_room_route(
|
||||
db.rooms
|
||||
.room_state_get(&body.room_id, &EventType::RoomPowerLevels, "")?
|
||||
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?
|
||||
.content
|
||||
.clone(),
|
||||
.content,
|
||||
)
|
||||
.expect("database contains invalid PDU")
|
||||
.deserialize()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::api::client::{error::ErrorKind, r0::search::search_events};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::post;
|
||||
@@ -14,26 +13,18 @@ use std::collections::BTreeMap;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn search_events_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<search_events::Request<'_>>,
|
||||
) -> ConduitResult<search_events::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
|
||||
let filter = search_criteria.filter.clone().unwrap_or_default();
|
||||
let filter = search_criteria.filter.as_ref().unwrap();
|
||||
|
||||
let room_ids = filter.rooms.clone().unwrap_or_else(|| {
|
||||
db.rooms
|
||||
.rooms_joined(&sender_user)
|
||||
.filter_map(|r| r.ok())
|
||||
.collect()
|
||||
});
|
||||
let room_id = filter.rooms.as_ref().unwrap().first().unwrap();
|
||||
|
||||
let limit = filter.limit.map_or(10, |l| u64::from(l) as usize);
|
||||
|
||||
let mut searches = Vec::new();
|
||||
|
||||
for room_id in room_ids {
|
||||
if !db.rooms.is_joined(sender_user, &room_id)? {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
@@ -41,13 +32,6 @@ pub async fn search_events_route(
|
||||
));
|
||||
}
|
||||
|
||||
let search = db
|
||||
.rooms
|
||||
.search_pdus(&room_id, &search_criteria.search_term)?;
|
||||
|
||||
searches.push(search.0.peekable());
|
||||
}
|
||||
|
||||
let skip = match body.next_batch.as_ref().map(|s| s.parse()) {
|
||||
Some(Ok(s)) => s,
|
||||
Some(Err(_)) => {
|
||||
@@ -59,20 +43,12 @@ pub async fn search_events_route(
|
||||
None => 0, // Default to the start
|
||||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
for _ in 0..skip + limit {
|
||||
if let Some(s) = searches
|
||||
.iter_mut()
|
||||
.map(|s| (s.peek().cloned(), s))
|
||||
.max_by_key(|(peek, _)| peek.clone())
|
||||
.and_then(|(_, i)| i.next())
|
||||
{
|
||||
results.push(s);
|
||||
}
|
||||
}
|
||||
let search = db
|
||||
.rooms
|
||||
.search_pdus(&room_id, &search_criteria.search_term)?;
|
||||
|
||||
let results = results
|
||||
.iter()
|
||||
let results = search
|
||||
.0
|
||||
.map(|result| {
|
||||
Ok::<_, Error>(SearchResult {
|
||||
context: EventContextResult {
|
||||
@@ -107,11 +83,7 @@ pub async fn search_events_route(
|
||||
next_batch,
|
||||
results,
|
||||
state: BTreeMap::new(), // TODO
|
||||
highlights: search_criteria
|
||||
.search_term
|
||||
.split_terminator(|c: char| !c.is_alphanumeric())
|
||||
.map(str::to_lowercase)
|
||||
.collect::<Vec<_>>(),
|
||||
highlights: search.1,
|
||||
},
|
||||
})
|
||||
.into())
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{State, DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||
use log::info;
|
||||
@@ -52,7 +50,7 @@ pub async fn get_login_types_route() -> ConduitResult<get_login_types::Response>
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn login_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<login::Request<'_>>,
|
||||
) -> ConduitResult<login::Response> {
|
||||
// Validate login method
|
||||
@@ -169,7 +167,7 @@ pub async fn login_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn logout_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<logout::Request>,
|
||||
) -> ConduitResult<logout::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -197,14 +195,16 @@ pub async fn logout_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn logout_all_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<logout_all::Request>,
|
||||
) -> ConduitResult<logout_all::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
for device_id in db.users.all_device_ids(sender_user).flatten() {
|
||||
for device_id in db.users.all_device_ids(sender_user) {
|
||||
if let Ok(device_id) = device_id {
|
||||
db.users.remove_device(&sender_user, &device_id)?;
|
||||
}
|
||||
}
|
||||
|
||||
db.flush().await?;
|
||||
|
||||
|
||||
+10
-12
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{pdu::PduBuilder, ConduitResult, Database, Error, Result, Ruma};
|
||||
use ruma::{
|
||||
@@ -27,7 +25,7 @@ use rocket::{get, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn send_state_event_for_key_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<send_state_event::Request<'_>>,
|
||||
) -> ConduitResult<send_state_event::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -53,7 +51,7 @@ pub async fn send_state_event_for_key_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn send_state_event_for_empty_key_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<send_state_event::Request<'_>>,
|
||||
) -> ConduitResult<send_state_event::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -79,7 +77,7 @@ pub async fn send_state_event_for_empty_key_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_state_events_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_state_events::Request<'_>>,
|
||||
) -> ConduitResult<get_state_events::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -92,7 +90,7 @@ pub async fn get_state_events_route(
|
||||
db.rooms
|
||||
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
||||
.map(|event| {
|
||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content.clone())
|
||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content)
|
||||
.map_err(|_| {
|
||||
Error::bad_database(
|
||||
"Invalid room history visibility event in database.",
|
||||
@@ -126,7 +124,7 @@ pub async fn get_state_events_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_state_events_for_key_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_state_events_for_key::Request<'_>>,
|
||||
) -> ConduitResult<get_state_events_for_key::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -139,7 +137,7 @@ pub async fn get_state_events_for_key_route(
|
||||
db.rooms
|
||||
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
||||
.map(|event| {
|
||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content.clone())
|
||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content)
|
||||
.map_err(|_| {
|
||||
Error::bad_database(
|
||||
"Invalid room history visibility event in database.",
|
||||
@@ -165,7 +163,7 @@ pub async fn get_state_events_for_key_route(
|
||||
))?;
|
||||
|
||||
Ok(get_state_events_for_key::Response {
|
||||
content: serde_json::from_value(event.content.clone())
|
||||
content: serde_json::from_value(event.content)
|
||||
.map_err(|_| Error::bad_database("Invalid event content in database"))?,
|
||||
}
|
||||
.into())
|
||||
@@ -177,7 +175,7 @@ pub async fn get_state_events_for_key_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_state_events_for_empty_key_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_state_events_for_key::Request<'_>>,
|
||||
) -> ConduitResult<get_state_events_for_key::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -190,7 +188,7 @@ pub async fn get_state_events_for_empty_key_route(
|
||||
db.rooms
|
||||
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
||||
.map(|event| {
|
||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content.clone())
|
||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content)
|
||||
.map_err(|_| {
|
||||
Error::bad_database(
|
||||
"Invalid room history visibility event in database.",
|
||||
@@ -216,7 +214,7 @@ pub async fn get_state_events_for_empty_key_route(
|
||||
))?;
|
||||
|
||||
Ok(get_state_events_for_key::Response {
|
||||
content: serde_json::from_value(event.content.clone())
|
||||
content: serde_json::from_value(event.content)
|
||||
.map_err(|_| Error::bad_database("Invalid event content in database"))?,
|
||||
}
|
||||
.into())
|
||||
|
||||
+52
-183
@@ -1,22 +1,19 @@
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Result, Ruma, RumaResponse};
|
||||
use log::error;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::{
|
||||
api::client::r0::{sync::sync_events, uiaa::UiaaResponse},
|
||||
api::client::r0::sync::sync_events,
|
||||
events::{room::member::MembershipState, AnySyncEphemeralRoomEvent, EventType},
|
||||
serde::Raw,
|
||||
DeviceId, RoomId, UserId,
|
||||
RoomId, UserId,
|
||||
};
|
||||
use std::{
|
||||
collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::sync::watch::Sender;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, tokio};
|
||||
use std::{
|
||||
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
||||
convert::{TryFrom, TryInto},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// # `GET /_matrix/client/r0/sync`
|
||||
///
|
||||
@@ -35,136 +32,23 @@ use rocket::{get, tokio};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn sync_events_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<sync_events::Request<'_>>,
|
||||
) -> std::result::Result<RumaResponse<sync_events::Response>, RumaResponse<UiaaResponse>> {
|
||||
) -> ConduitResult<sync_events::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut rx = match db
|
||||
.globals
|
||||
.sync_receivers
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry((sender_user.clone(), sender_device.clone()))
|
||||
{
|
||||
Entry::Vacant(v) => {
|
||||
let (tx, rx) = tokio::sync::watch::channel(None);
|
||||
|
||||
tokio::spawn(sync_helper_wrapper(
|
||||
Arc::clone(&db),
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
body.since.clone(),
|
||||
body.full_state,
|
||||
body.timeout,
|
||||
tx,
|
||||
));
|
||||
|
||||
v.insert((body.since.clone(), rx)).1.clone()
|
||||
}
|
||||
Entry::Occupied(mut o) => {
|
||||
if o.get().0 != body.since {
|
||||
let (tx, rx) = tokio::sync::watch::channel(None);
|
||||
|
||||
tokio::spawn(sync_helper_wrapper(
|
||||
Arc::clone(&db),
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
body.since.clone(),
|
||||
body.full_state,
|
||||
body.timeout,
|
||||
tx,
|
||||
));
|
||||
|
||||
o.insert((body.since.clone(), rx.clone()));
|
||||
|
||||
rx
|
||||
} else {
|
||||
o.get().1.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let we_have_to_wait = rx.borrow().is_none();
|
||||
if we_have_to_wait {
|
||||
let _ = rx.changed().await;
|
||||
}
|
||||
|
||||
let result = match rx
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.expect("When sync channel changes it's always set to some")
|
||||
{
|
||||
Ok(response) => Ok(response.clone()),
|
||||
Err(error) => Err(error.to_response()),
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn sync_helper_wrapper(
|
||||
db: Arc<Database>,
|
||||
sender_user: UserId,
|
||||
sender_device: Box<DeviceId>,
|
||||
since: Option<String>,
|
||||
full_state: bool,
|
||||
timeout: Option<Duration>,
|
||||
tx: Sender<Option<ConduitResult<sync_events::Response>>>,
|
||||
) {
|
||||
let r = sync_helper(
|
||||
Arc::clone(&db),
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
since.clone(),
|
||||
full_state,
|
||||
timeout,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Ok((_, caching_allowed)) = r {
|
||||
if !caching_allowed {
|
||||
match db
|
||||
.globals
|
||||
.sync_receivers
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry((sender_user, sender_device))
|
||||
{
|
||||
Entry::Occupied(o) => {
|
||||
// Only remove if the device didn't start a different /sync already
|
||||
if o.get().0 == since {
|
||||
o.remove();
|
||||
}
|
||||
}
|
||||
Entry::Vacant(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = tx.send(Some(r.map(|(r, _)| r.into())));
|
||||
}
|
||||
|
||||
async fn sync_helper(
|
||||
db: Arc<Database>,
|
||||
sender_user: UserId,
|
||||
sender_device: Box<DeviceId>,
|
||||
since: Option<String>,
|
||||
full_state: bool,
|
||||
timeout: Option<Duration>,
|
||||
// bool = caching allowed
|
||||
) -> std::result::Result<(sync_events::Response, bool), Error> {
|
||||
// TODO: match body.set_presence {
|
||||
db.rooms.edus.ping_presence(&sender_user)?;
|
||||
|
||||
// Setup watchers, so if there's no response, we can wait for them
|
||||
let watcher = db.watch(&sender_user, &sender_device);
|
||||
let watcher = db.watch(sender_user, sender_device);
|
||||
|
||||
let next_batch = db.globals.current_count()?;
|
||||
let next_batch_string = next_batch.to_string();
|
||||
let next_batch = db.globals.current_count()?.to_string();
|
||||
|
||||
let mut joined_rooms = BTreeMap::new();
|
||||
let since = since
|
||||
let since = body
|
||||
.since
|
||||
.clone()
|
||||
.and_then(|string| string.parse().ok())
|
||||
.unwrap_or(0);
|
||||
@@ -186,23 +70,13 @@ async fn sync_helper(
|
||||
|
||||
let mut non_timeline_pdus = db
|
||||
.rooms
|
||||
.pdus_until(&sender_user, &room_id, u64::MAX)
|
||||
.filter_map(|r| {
|
||||
// Filter out buggy events
|
||||
if r.is_err() {
|
||||
error!("Bad pdu in pdus_since: {:?}", r);
|
||||
}
|
||||
r.ok()
|
||||
})
|
||||
.take_while(|(pduid, _)| {
|
||||
db.rooms
|
||||
.pdu_count(pduid)
|
||||
.map_or(false, |count| count > since)
|
||||
});
|
||||
.pdus_since(&sender_user, &room_id, since)?
|
||||
.filter_map(|r| r.ok()); // Filter out buggy events
|
||||
|
||||
// Take the last 10 events for the timeline
|
||||
let timeline_pdus = non_timeline_pdus
|
||||
.by_ref()
|
||||
.rev()
|
||||
.take(10)
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
@@ -228,11 +102,10 @@ async fn sync_helper(
|
||||
// since and the current room state, meaning there should be no updates.
|
||||
// The inner Option is None when there is an event, but there is no state hash associated
|
||||
// with it. This can happen for the RoomCreate event, so all updates should arrive.
|
||||
let first_pdu_before_since = db.rooms.pdus_until(&sender_user, &room_id, since).next();
|
||||
|
||||
let first_pdu_before_since = db.rooms.pdus_until(sender_user, &room_id, since).next();
|
||||
let pdus_after_since = db
|
||||
.rooms
|
||||
.pdus_after(&sender_user, &room_id, since)
|
||||
.pdus_after(sender_user, &room_id, since)
|
||||
.next()
|
||||
.is_some();
|
||||
|
||||
@@ -261,9 +134,11 @@ async fn sync_helper(
|
||||
let since_state = since_shortstatehash
|
||||
.as_ref()
|
||||
.map(|since_shortstatehash| {
|
||||
Ok::<_, Error>(
|
||||
since_shortstatehash
|
||||
.map(|since_shortstatehash| db.rooms.state_full(since_shortstatehash))
|
||||
.transpose()
|
||||
.transpose()?,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
@@ -345,11 +220,10 @@ async fn sync_helper(
|
||||
match (since_membership, current_membership) {
|
||||
(MembershipState::Leave, MembershipState::Join) => {
|
||||
// A new user joined an encrypted room
|
||||
if !share_encrypted_room(&db, &sender_user, &user_id, &room_id)? {
|
||||
if !share_encrypted_room(&db, &sender_user, &user_id, &room_id) {
|
||||
device_list_updates.insert(user_id);
|
||||
}
|
||||
}
|
||||
// TODO: Remove, this should never happen here, right?
|
||||
(MembershipState::Join, MembershipState::Leave) => {
|
||||
// Write down users that have left encrypted rooms we are in
|
||||
left_encrypted_users.insert(user_id);
|
||||
@@ -368,15 +242,14 @@ async fn sync_helper(
|
||||
device_list_updates.extend(
|
||||
db.rooms
|
||||
.room_members(&room_id)
|
||||
.flatten()
|
||||
.filter_map(|user_id| Some(user_id.ok()?))
|
||||
.filter(|user_id| {
|
||||
// Don't send key updates from the sender to the sender
|
||||
&sender_user != user_id
|
||||
sender_user != user_id
|
||||
})
|
||||
.filter(|user_id| {
|
||||
// Only send keys if the sender doesn't share an encrypted room with the target already
|
||||
!share_encrypted_room(&db, &sender_user, user_id, &room_id)
|
||||
.unwrap_or(false)
|
||||
!share_encrypted_room(&db, sender_user, user_id, &room_id)
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -394,13 +267,15 @@ async fn sync_helper(
|
||||
|
||||
for hero in db
|
||||
.rooms
|
||||
.all_pdus(&sender_user, &room_id)
|
||||
.all_pdus(&sender_user, &room_id)?
|
||||
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
||||
.filter(|(_, pdu)| pdu.kind == EventType::RoomMember)
|
||||
.map(|(_, pdu)| {
|
||||
let content = serde_json::from_value::<
|
||||
ruma::events::room::member::MemberEventContent,
|
||||
Raw<ruma::events::room::member::MemberEventContent>,
|
||||
>(pdu.content.clone())
|
||||
.expect("Raw::from_value always works")
|
||||
.deserialize()
|
||||
.map_err(|_| {
|
||||
Error::bad_database("Invalid member event in database.")
|
||||
})?;
|
||||
@@ -426,10 +301,9 @@ async fn sync_helper(
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
// Filter out buggy users
|
||||
.filter_map(|u| u.ok())
|
||||
.filter_map(|u| u.ok()) // Filter out buggy users
|
||||
// Filter for possible heroes
|
||||
.flatten()
|
||||
.filter_map(|u| u)
|
||||
{
|
||||
if heroes.contains(&hero) || hero == sender_user.as_str() {
|
||||
continue;
|
||||
@@ -450,7 +324,7 @@ async fn sync_helper(
|
||||
|
||||
let state_events = if joined_since_last_sync {
|
||||
current_state
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|(_, pdu)| pdu.to_sync_state_event())
|
||||
.collect()
|
||||
} else {
|
||||
@@ -530,9 +404,8 @@ async fn sync_helper(
|
||||
let mut edus = db
|
||||
.rooms
|
||||
.edus
|
||||
.readreceipts_since(&room_id, since)
|
||||
.readreceipts_since(&room_id, since)?
|
||||
.filter_map(|r| r.ok()) // Filter out buggy events
|
||||
.map(|(_, _, v)| v)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if db.rooms.edus.last_typing_update(&room_id, &db.globals)? > since {
|
||||
@@ -548,7 +421,7 @@ async fn sync_helper(
|
||||
}
|
||||
|
||||
let joined_room = sync_events::JoinedRoom {
|
||||
account_data: sync_events::RoomAccountData {
|
||||
account_data: sync_events::AccountData {
|
||||
events: db
|
||||
.account_data
|
||||
.changes_since(Some(&room_id), &sender_user, since)?
|
||||
@@ -632,10 +505,10 @@ async fn sync_helper(
|
||||
left_rooms.insert(
|
||||
room_id.clone(),
|
||||
sync_events::LeftRoom {
|
||||
account_data: sync_events::RoomAccountData { events: Vec::new() },
|
||||
account_data: sync_events::AccountData { events: Vec::new() },
|
||||
timeline: sync_events::Timeline {
|
||||
limited: false,
|
||||
prev_batch: Some(next_batch_string.clone()),
|
||||
prev_batch: Some(next_batch.clone()),
|
||||
events: Vec::new(),
|
||||
},
|
||||
state: sync_events::State {
|
||||
@@ -668,7 +541,7 @@ async fn sync_helper(
|
||||
for user_id in left_encrypted_users {
|
||||
let still_share_encrypted_room = db
|
||||
.rooms
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])
|
||||
.filter_map(|r| r.ok())
|
||||
.filter_map(|other_room_id| {
|
||||
Some(
|
||||
@@ -688,10 +561,10 @@ async fn sync_helper(
|
||||
|
||||
// Remove all to-device events the device received *last time*
|
||||
db.users
|
||||
.remove_to_device_events(&sender_user, &sender_device, since)?;
|
||||
.remove_to_device_events(sender_user, sender_device, since)?;
|
||||
|
||||
let response = sync_events::Response {
|
||||
next_batch: next_batch_string,
|
||||
next_batch,
|
||||
rooms: sync_events::Rooms {
|
||||
leave: left_rooms,
|
||||
join: joined_rooms,
|
||||
@@ -703,7 +576,7 @@ async fn sync_helper(
|
||||
.map(|(_, v)| Raw::from(v))
|
||||
.collect(),
|
||||
},
|
||||
account_data: sync_events::GlobalAccountData {
|
||||
account_data: sync_events::AccountData {
|
||||
events: db
|
||||
.account_data
|
||||
.changes_since(None, &sender_user, since)?
|
||||
@@ -719,22 +592,20 @@ async fn sync_helper(
|
||||
changed: device_list_updates.into_iter().collect(),
|
||||
left: device_list_left.into_iter().collect(),
|
||||
},
|
||||
device_one_time_keys_count: if db.users.last_one_time_keys_update(&sender_user)? > since
|
||||
device_one_time_keys_count: if db.users.last_one_time_keys_update(sender_user)? > since
|
||||
|| since == 0
|
||||
{
|
||||
db.users.count_one_time_keys(&sender_user, &sender_device)?
|
||||
db.users.count_one_time_keys(sender_user, sender_device)?
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
},
|
||||
to_device: sync_events::ToDevice {
|
||||
events: db
|
||||
.users
|
||||
.get_to_device_events(&sender_user, &sender_device)?,
|
||||
events: db.users.get_to_device_events(sender_user, sender_device)?,
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: Retry the endpoint instead of returning (waiting for #118)
|
||||
if !full_state
|
||||
if !body.full_state
|
||||
&& response.rooms.is_empty()
|
||||
&& response.presence.is_empty()
|
||||
&& response.account_data.is_empty()
|
||||
@@ -744,15 +615,14 @@ async fn sync_helper(
|
||||
{
|
||||
// Hang a few seconds so requests are not spammed
|
||||
// Stop hanging if new info arrives
|
||||
let mut duration = timeout.unwrap_or_default();
|
||||
let mut duration = body.timeout.unwrap_or_default();
|
||||
if duration.as_secs() > 30 {
|
||||
duration = Duration::from_secs(30);
|
||||
}
|
||||
let _ = tokio::time::timeout(duration, watcher).await;
|
||||
Ok((response, false))
|
||||
} else {
|
||||
Ok((response, since != next_batch)) // Only cache if we made progress
|
||||
}
|
||||
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(db))]
|
||||
@@ -761,10 +631,9 @@ fn share_encrypted_room(
|
||||
sender_user: &UserId,
|
||||
user_id: &UserId,
|
||||
ignore_room: &RoomId,
|
||||
) -> Result<bool> {
|
||||
Ok(db
|
||||
.rooms
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
||||
) -> bool {
|
||||
db.rooms
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])
|
||||
.filter_map(|r| r.ok())
|
||||
.filter(|room_id| room_id != ignore_room)
|
||||
.filter_map(|other_room_id| {
|
||||
@@ -775,5 +644,5 @@ fn share_encrypted_room(
|
||||
.is_some(),
|
||||
)
|
||||
})
|
||||
.any(|encrypted| encrypted))
|
||||
.any(|encrypted| encrypted)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruma::{
|
||||
api::client::r0::tag::{create_tag, delete_tag, get_tags},
|
||||
events::EventType,
|
||||
};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{delete, get, put};
|
||||
@@ -15,7 +15,7 @@ use rocket::{delete, get, put};
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn update_tag_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_tag::Request<'_>>,
|
||||
) -> ConduitResult<create_tag::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -31,7 +31,7 @@ pub async fn update_tag_route(
|
||||
tags_event
|
||||
.content
|
||||
.tags
|
||||
.insert(body.tag.clone().into(), body.tag_info.clone());
|
||||
.insert(body.tag.to_string(), body.tag_info.clone());
|
||||
|
||||
db.account_data.update(
|
||||
Some(&body.room_id),
|
||||
@@ -52,7 +52,7 @@ pub async fn update_tag_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn delete_tag_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<delete_tag::Request<'_>>,
|
||||
) -> ConduitResult<delete_tag::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -65,7 +65,7 @@ pub async fn delete_tag_route(
|
||||
tags: BTreeMap::new(),
|
||||
},
|
||||
});
|
||||
tags_event.content.tags.remove(&body.tag.clone().into());
|
||||
tags_event.content.tags.remove(&body.tag);
|
||||
|
||||
db.account_data.update(
|
||||
Some(&body.room_id),
|
||||
@@ -86,7 +86,7 @@ pub async fn delete_tag_route(
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn get_tags_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<get_tags::Request<'_>>,
|
||||
) -> ConduitResult<get_tags::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Error, Ruma};
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, r0::to_device::send_event_to_device},
|
||||
to_device::DeviceIdOrAllDevices,
|
||||
use ruma::api::client::{
|
||||
error::ErrorKind,
|
||||
r0::to_device::{self, send_event_to_device},
|
||||
};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
@@ -16,7 +14,7 @@ use rocket::put;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn send_event_to_device_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<send_event_to_device::Request<'_>>,
|
||||
) -> ConduitResult<send_event_to_device::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -34,25 +32,27 @@ pub async fn send_event_to_device_route(
|
||||
for (target_user_id, map) in &body.messages {
|
||||
for (target_device_id_maybe, event) in map {
|
||||
match target_device_id_maybe {
|
||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => db.users.add_to_device_event(
|
||||
to_device::DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||
db.users.add_to_device_event(
|
||||
sender_user,
|
||||
&target_user_id,
|
||||
&target_device_id,
|
||||
&body.event_type,
|
||||
event.deserialize_as().map_err(|_| {
|
||||
serde_json::from_str(event.get()).map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
&db.globals,
|
||||
)?,
|
||||
)?
|
||||
}
|
||||
|
||||
DeviceIdOrAllDevices::AllDevices => {
|
||||
to_device::DeviceIdOrAllDevices::AllDevices => {
|
||||
for target_device_id in db.users.all_device_ids(&target_user_id) {
|
||||
db.users.add_to_device_event(
|
||||
sender_user,
|
||||
&target_user_id,
|
||||
&target_device_id?,
|
||||
&body.event_type,
|
||||
event.deserialize_as().map_err(|_| {
|
||||
serde_json::from_str(event.get()).map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
&db.globals,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{utils, ConduitResult, Database, Ruma};
|
||||
use create_typing_event::Typing;
|
||||
@@ -14,7 +12,7 @@ use rocket::put;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub fn create_typing_event_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<create_typing_event::Request<'_>>,
|
||||
) -> ConduitResult<create_typing_event::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::State;
|
||||
use crate::{ConduitResult, Database, Ruma};
|
||||
use ruma::api::client::r0::user_directory::search_users;
|
||||
@@ -13,7 +11,7 @@ use rocket::post;
|
||||
)]
|
||||
#[tracing::instrument(skip(db, body))]
|
||||
pub async fn search_users_route(
|
||||
db: State<'_, Arc<Database>>,
|
||||
db: State<'_, Database>,
|
||||
body: Ruma<search_users::Request<'_>>,
|
||||
) -> ConduitResult<search_users::Response> {
|
||||
let limit = u64::from(body.limit) as usize;
|
||||
@@ -21,6 +19,9 @@ pub async fn search_users_route(
|
||||
let mut users = db.users.iter().filter_map(|user_id| {
|
||||
// Filter out buggy users (they should not exist, but you never know...)
|
||||
let user_id = user_id.ok()?;
|
||||
if db.users.is_deactivated(&user_id).ok()? {
|
||||
return None;
|
||||
}
|
||||
|
||||
let user = search_users::User {
|
||||
user_id: user_id.clone(),
|
||||
@@ -28,18 +29,11 @@ pub async fn search_users_route(
|
||||
avatar_url: db.users.avatar_url(&user_id).ok()?,
|
||||
};
|
||||
|
||||
if !user
|
||||
.user_id
|
||||
.to_string()
|
||||
.to_lowercase()
|
||||
.contains(&body.search_term.to_lowercase())
|
||||
if !user.user_id.to_string().contains(&body.search_term)
|
||||
&& user
|
||||
.display_name
|
||||
.as_ref()
|
||||
.filter(|name| {
|
||||
name.to_lowercase()
|
||||
.contains(&body.search_term.to_lowercase())
|
||||
})
|
||||
.filter(|name| name.contains(&body.search_term))
|
||||
.is_none()
|
||||
{
|
||||
return None;
|
||||
|
||||
+80
-183
@@ -1,5 +1,3 @@
|
||||
pub mod abstraction;
|
||||
|
||||
pub mod account_data;
|
||||
pub mod admin;
|
||||
pub mod appservice;
|
||||
@@ -13,18 +11,16 @@ pub mod transaction_ids;
|
||||
pub mod uiaa;
|
||||
pub mod users;
|
||||
|
||||
use crate::{utils, Error, Result};
|
||||
use abstraction::DatabaseEngine;
|
||||
use crate::{Error, Result};
|
||||
use directories::ProjectDirs;
|
||||
use log::error;
|
||||
use lru_cache::LruCache;
|
||||
use rocket::futures::{channel::mpsc, stream::FuturesUnordered, StreamExt};
|
||||
use futures::StreamExt;
|
||||
use log::info;
|
||||
use rocket::futures::{self, channel::mpsc};
|
||||
use ruma::{DeviceId, ServerName, UserId};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, remove_dir_all},
|
||||
io::Write,
|
||||
fs::remove_dir_all,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
@@ -71,19 +67,14 @@ fn default_max_request_size() -> u32 {
|
||||
}
|
||||
|
||||
fn default_max_concurrent_requests() -> u16 {
|
||||
100
|
||||
4
|
||||
}
|
||||
|
||||
fn default_log() -> String {
|
||||
"info,state_res=warn,rocket=off,_=off,sled=off".to_owned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "sled")]
|
||||
pub type Engine = abstraction::SledEngine;
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
pub type Engine = abstraction::RocksDbEngine;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pub globals: globals::Globals,
|
||||
pub users: users::Users,
|
||||
@@ -97,6 +88,7 @@ pub struct Database {
|
||||
pub admin: admin::Admin,
|
||||
pub appservice: appservice::Appservice,
|
||||
pub pusher: pusher::PushData,
|
||||
pub _db: sled::Db,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
@@ -113,213 +105,118 @@ impl Database {
|
||||
}
|
||||
|
||||
/// Load an existing database or create a new one.
|
||||
pub async fn load_or_create(config: Config) -> Result<Arc<Self>> {
|
||||
let builder = Engine::open(&config)?;
|
||||
pub async fn load_or_create(config: Config) -> Result<Self> {
|
||||
let db = sled::Config::default()
|
||||
.path(&config.database_path)
|
||||
.cache_capacity(config.cache_capacity as u64)
|
||||
.use_compression(true)
|
||||
.open()?;
|
||||
|
||||
if config.max_request_size < 1024 {
|
||||
eprintln!("ERROR: Max request size is less than 1KB. Please increase it.");
|
||||
}
|
||||
info!("Opened sled database at {}", config.database_path);
|
||||
|
||||
let (admin_sender, admin_receiver) = mpsc::unbounded();
|
||||
let (sending_sender, sending_receiver) = mpsc::unbounded();
|
||||
|
||||
let db = Arc::new(Self {
|
||||
let db = Self {
|
||||
users: users::Users {
|
||||
userid_password: builder.open_tree("userid_password")?,
|
||||
userid_displayname: builder.open_tree("userid_displayname")?,
|
||||
userid_avatarurl: builder.open_tree("userid_avatarurl")?,
|
||||
userdeviceid_token: builder.open_tree("userdeviceid_token")?,
|
||||
userdeviceid_metadata: builder.open_tree("userdeviceid_metadata")?,
|
||||
userid_devicelistversion: builder.open_tree("userid_devicelistversion")?,
|
||||
token_userdeviceid: builder.open_tree("token_userdeviceid")?,
|
||||
onetimekeyid_onetimekeys: builder.open_tree("onetimekeyid_onetimekeys")?,
|
||||
userid_lastonetimekeyupdate: builder.open_tree("userid_lastonetimekeyupdate")?,
|
||||
keychangeid_userid: builder.open_tree("keychangeid_userid")?,
|
||||
keyid_key: builder.open_tree("keyid_key")?,
|
||||
userid_masterkeyid: builder.open_tree("userid_masterkeyid")?,
|
||||
userid_selfsigningkeyid: builder.open_tree("userid_selfsigningkeyid")?,
|
||||
userid_usersigningkeyid: builder.open_tree("userid_usersigningkeyid")?,
|
||||
todeviceid_events: builder.open_tree("todeviceid_events")?,
|
||||
userid_password: db.open_tree("userid_password")?,
|
||||
userid_displayname: db.open_tree("userid_displayname")?,
|
||||
userid_avatarurl: db.open_tree("userid_avatarurl")?,
|
||||
userdeviceid_token: db.open_tree("userdeviceid_token")?,
|
||||
userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?,
|
||||
userid_devicelistversion: db.open_tree("userid_devicelistversion")?,
|
||||
token_userdeviceid: db.open_tree("token_userdeviceid")?,
|
||||
onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?,
|
||||
userid_lastonetimekeyupdate: db.open_tree("userid_lastonetimekeyupdate")?,
|
||||
keychangeid_userid: db.open_tree("keychangeid_userid")?,
|
||||
keyid_key: db.open_tree("keyid_key")?,
|
||||
userid_masterkeyid: db.open_tree("userid_masterkeyid")?,
|
||||
userid_selfsigningkeyid: db.open_tree("userid_selfsigningkeyid")?,
|
||||
userid_usersigningkeyid: db.open_tree("userid_usersigningkeyid")?,
|
||||
todeviceid_events: db.open_tree("todeviceid_events")?,
|
||||
},
|
||||
uiaa: uiaa::Uiaa {
|
||||
userdevicesessionid_uiaainfo: builder.open_tree("userdevicesessionid_uiaainfo")?,
|
||||
userdevicesessionid_uiaarequest: builder
|
||||
.open_tree("userdevicesessionid_uiaarequest")?,
|
||||
userdeviceid_uiaainfo: db.open_tree("userdeviceid_uiaainfo")?,
|
||||
},
|
||||
rooms: rooms::Rooms {
|
||||
edus: rooms::RoomEdus {
|
||||
readreceiptid_readreceipt: builder.open_tree("readreceiptid_readreceipt")?,
|
||||
roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, // "Private" read receipt
|
||||
roomuserid_lastprivatereadupdate: builder
|
||||
readreceiptid_readreceipt: db.open_tree("readreceiptid_readreceipt")?,
|
||||
roomuserid_privateread: db.open_tree("roomuserid_privateread")?, // "Private" read receipt
|
||||
roomuserid_lastprivatereadupdate: db
|
||||
.open_tree("roomuserid_lastprivatereadupdate")?,
|
||||
typingid_userid: builder.open_tree("typingid_userid")?,
|
||||
roomid_lasttypingupdate: builder.open_tree("roomid_lasttypingupdate")?,
|
||||
presenceid_presence: builder.open_tree("presenceid_presence")?,
|
||||
userid_lastpresenceupdate: builder.open_tree("userid_lastpresenceupdate")?,
|
||||
typingid_userid: db.open_tree("typingid_userid")?,
|
||||
roomid_lasttypingupdate: db.open_tree("roomid_lasttypingupdate")?,
|
||||
presenceid_presence: db.open_tree("presenceid_presence")?,
|
||||
userid_lastpresenceupdate: db.open_tree("userid_lastpresenceupdate")?,
|
||||
},
|
||||
pduid_pdu: builder.open_tree("pduid_pdu")?,
|
||||
eventid_pduid: builder.open_tree("eventid_pduid")?,
|
||||
roomid_pduleaves: builder.open_tree("roomid_pduleaves")?,
|
||||
pduid_pdu: db.open_tree("pduid_pdu")?,
|
||||
eventid_pduid: db.open_tree("eventid_pduid")?,
|
||||
roomid_pduleaves: db.open_tree("roomid_pduleaves")?,
|
||||
|
||||
alias_roomid: builder.open_tree("alias_roomid")?,
|
||||
aliasid_alias: builder.open_tree("aliasid_alias")?,
|
||||
publicroomids: builder.open_tree("publicroomids")?,
|
||||
alias_roomid: db.open_tree("alias_roomid")?,
|
||||
aliasid_alias: db.open_tree("aliasid_alias")?,
|
||||
publicroomids: db.open_tree("publicroomids")?,
|
||||
|
||||
tokenids: builder.open_tree("tokenids")?,
|
||||
tokenids: db.open_tree("tokenids")?,
|
||||
|
||||
roomserverids: builder.open_tree("roomserverids")?,
|
||||
serverroomids: builder.open_tree("serverroomids")?,
|
||||
userroomid_joined: builder.open_tree("userroomid_joined")?,
|
||||
roomuserid_joined: builder.open_tree("roomuserid_joined")?,
|
||||
roomuseroncejoinedids: builder.open_tree("roomuseroncejoinedids")?,
|
||||
userroomid_invitestate: builder.open_tree("userroomid_invitestate")?,
|
||||
roomuserid_invitecount: builder.open_tree("roomuserid_invitecount")?,
|
||||
userroomid_leftstate: builder.open_tree("userroomid_leftstate")?,
|
||||
roomuserid_leftcount: builder.open_tree("roomuserid_leftcount")?,
|
||||
roomserverids: db.open_tree("roomserverids")?,
|
||||
userroomid_joined: db.open_tree("userroomid_joined")?,
|
||||
roomuserid_joined: db.open_tree("roomuserid_joined")?,
|
||||
roomuseroncejoinedids: db.open_tree("roomuseroncejoinedids")?,
|
||||
userroomid_invitestate: db.open_tree("userroomid_invitestate")?,
|
||||
roomuserid_invitecount: db.open_tree("roomuserid_invitecount")?,
|
||||
userroomid_leftstate: db.open_tree("userroomid_leftstate")?,
|
||||
roomuserid_leftcount: db.open_tree("roomuserid_leftcount")?,
|
||||
|
||||
userroomid_notificationcount: builder.open_tree("userroomid_notificationcount")?,
|
||||
userroomid_highlightcount: builder.open_tree("userroomid_highlightcount")?,
|
||||
userroomid_notificationcount: db.open_tree("userroomid_notificationcount")?,
|
||||
userroomid_highlightcount: db.open_tree("userroomid_highlightcount")?,
|
||||
|
||||
statekey_shortstatekey: builder.open_tree("statekey_shortstatekey")?,
|
||||
stateid_shorteventid: builder.open_tree("stateid_shorteventid")?,
|
||||
eventid_shorteventid: builder.open_tree("eventid_shorteventid")?,
|
||||
shorteventid_eventid: builder.open_tree("shorteventid_eventid")?,
|
||||
shorteventid_shortstatehash: builder.open_tree("shorteventid_shortstatehash")?,
|
||||
roomid_shortstatehash: builder.open_tree("roomid_shortstatehash")?,
|
||||
statehash_shortstatehash: builder.open_tree("statehash_shortstatehash")?,
|
||||
statekey_shortstatekey: db.open_tree("statekey_shortstatekey")?,
|
||||
stateid_shorteventid: db.open_tree("stateid_shorteventid")?,
|
||||
eventid_shorteventid: db.open_tree("eventid_shorteventid")?,
|
||||
shorteventid_eventid: db.open_tree("shorteventid_eventid")?,
|
||||
shorteventid_shortstatehash: db.open_tree("shorteventid_shortstatehash")?,
|
||||
roomid_shortstatehash: db.open_tree("roomid_shortstatehash")?,
|
||||
statehash_shortstatehash: db.open_tree("statehash_shortstatehash")?,
|
||||
|
||||
eventid_outlierpdu: builder.open_tree("eventid_outlierpdu")?,
|
||||
prevevent_parent: builder.open_tree("prevevent_parent")?,
|
||||
pdu_cache: RwLock::new(LruCache::new(1_000_000)),
|
||||
eventid_outlierpdu: db.open_tree("eventid_outlierpdu")?,
|
||||
prevevent_parent: db.open_tree("prevevent_parent")?,
|
||||
},
|
||||
account_data: account_data::AccountData {
|
||||
roomuserdataid_accountdata: builder.open_tree("roomuserdataid_accountdata")?,
|
||||
roomuserdataid_accountdata: db.open_tree("roomuserdataid_accountdata")?,
|
||||
},
|
||||
media: media::Media {
|
||||
mediaid_file: builder.open_tree("mediaid_file")?,
|
||||
mediaid_file: db.open_tree("mediaid_file")?,
|
||||
},
|
||||
key_backups: key_backups::KeyBackups {
|
||||
backupid_algorithm: builder.open_tree("backupid_algorithm")?,
|
||||
backupid_etag: builder.open_tree("backupid_etag")?,
|
||||
backupkeyid_backup: builder.open_tree("backupkeyid_backup")?,
|
||||
backupid_algorithm: db.open_tree("backupid_algorithm")?,
|
||||
backupid_etag: db.open_tree("backupid_etag")?,
|
||||
backupkeyid_backup: db.open_tree("backupkeyid_backup")?,
|
||||
},
|
||||
transaction_ids: transaction_ids::TransactionIds {
|
||||
userdevicetxnid_response: builder.open_tree("userdevicetxnid_response")?,
|
||||
userdevicetxnid_response: db.open_tree("userdevicetxnid_response")?,
|
||||
},
|
||||
sending: sending::Sending {
|
||||
servername_educount: builder.open_tree("servername_educount")?,
|
||||
servernamepduids: builder.open_tree("servernamepduids")?,
|
||||
servercurrentevents: builder.open_tree("servercurrentevents")?,
|
||||
servernamepduids: db.open_tree("servernamepduids")?,
|
||||
servercurrentpdus: db.open_tree("servercurrentpdus")?,
|
||||
maximum_requests: Arc::new(Semaphore::new(config.max_concurrent_requests as usize)),
|
||||
sender: sending_sender,
|
||||
},
|
||||
admin: admin::Admin {
|
||||
sender: admin_sender,
|
||||
},
|
||||
appservice: appservice::Appservice {
|
||||
cached_registrations: Arc::new(RwLock::new(HashMap::new())),
|
||||
id_appserviceregistrations: builder.open_tree("id_appserviceregistrations")?,
|
||||
},
|
||||
pusher: pusher::PushData {
|
||||
senderkey_pusher: builder.open_tree("senderkey_pusher")?,
|
||||
id_appserviceregistrations: db.open_tree("id_appserviceregistrations")?,
|
||||
},
|
||||
pusher: pusher::PushData::new(&db)?,
|
||||
globals: globals::Globals::load(
|
||||
builder.open_tree("global")?,
|
||||
builder.open_tree("server_signingkeys")?,
|
||||
db.open_tree("global")?,
|
||||
db.open_tree("servertimeout_signingkey")?,
|
||||
config,
|
||||
)?,
|
||||
});
|
||||
|
||||
// MIGRATIONS
|
||||
// TODO: database versions of new dbs should probably not be 0
|
||||
if db.globals.database_version()? < 1 {
|
||||
for (roomserverid, _) in db.rooms.roomserverids.iter() {
|
||||
let mut parts = roomserverid.split(|&b| b == 0xff);
|
||||
let room_id = parts.next().expect("split always returns one element");
|
||||
let servername = match parts.next() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
error!("Migration: Invalid roomserverid in db.");
|
||||
continue;
|
||||
}
|
||||
_db: db,
|
||||
};
|
||||
let mut serverroomid = servername.to_vec();
|
||||
serverroomid.push(0xff);
|
||||
serverroomid.extend_from_slice(room_id);
|
||||
|
||||
db.rooms.serverroomids.insert(&serverroomid, &[])?;
|
||||
}
|
||||
|
||||
db.globals.bump_database_version(1)?;
|
||||
|
||||
println!("Migration: 0 -> 1 finished");
|
||||
}
|
||||
|
||||
if db.globals.database_version()? < 2 {
|
||||
// We accidentally inserted hashed versions of "" into the db instead of just ""
|
||||
for (userid, password) in db.users.userid_password.iter() {
|
||||
let password = utils::string_from_bytes(&password);
|
||||
|
||||
let empty_hashed_password = password.map_or(false, |password| {
|
||||
argon2::verify_encoded(&password, b"").unwrap_or(false)
|
||||
});
|
||||
|
||||
if empty_hashed_password {
|
||||
db.users.userid_password.insert(&userid, b"")?;
|
||||
}
|
||||
}
|
||||
|
||||
db.globals.bump_database_version(2)?;
|
||||
|
||||
println!("Migration: 1 -> 2 finished");
|
||||
}
|
||||
|
||||
if db.globals.database_version()? < 3 {
|
||||
// Move media to filesystem
|
||||
for (key, content) in db.media.mediaid_file.iter() {
|
||||
if content.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = db.globals.get_media_file(&key);
|
||||
let mut file = fs::File::create(path)?;
|
||||
file.write_all(&content)?;
|
||||
db.media.mediaid_file.insert(&key, &[])?;
|
||||
}
|
||||
|
||||
db.globals.bump_database_version(3)?;
|
||||
|
||||
println!("Migration: 2 -> 3 finished");
|
||||
}
|
||||
|
||||
if db.globals.database_version()? < 4 {
|
||||
// Add federated users to db as deactivated
|
||||
for our_user in db.users.iter() {
|
||||
let our_user = our_user?;
|
||||
if db.users.is_deactivated(&our_user)? {
|
||||
continue;
|
||||
}
|
||||
for room in db.rooms.rooms_joined(&our_user) {
|
||||
for user in db.rooms.room_members(&room?) {
|
||||
let user = user?;
|
||||
if user.server_name() != db.globals.server_name() {
|
||||
println!("Migration: Creating user {}", user);
|
||||
db.users.create(&user, None)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.globals.bump_database_version(4)?;
|
||||
|
||||
println!("Migration: 3 -> 4 finished");
|
||||
}
|
||||
|
||||
// This data is probably outdated
|
||||
db.rooms.edus.presenceid_presence.clear()?;
|
||||
|
||||
db.admin.start_handler(Arc::clone(&db), admin_receiver);
|
||||
db.sending.start_handler(Arc::clone(&db), sending_receiver);
|
||||
db.admin.start_handler(db.clone(), admin_receiver);
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
@@ -333,7 +230,7 @@ impl Database {
|
||||
userdeviceid_prefix.extend_from_slice(device_id.as_bytes());
|
||||
userdeviceid_prefix.push(0xff);
|
||||
|
||||
let mut futures = FuturesUnordered::new();
|
||||
let mut futures = futures::stream::FuturesUnordered::new();
|
||||
|
||||
// Return when *any* user changed his key
|
||||
// TODO: only send for user they share a room with
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
use super::Config;
|
||||
use crate::{utils, Result};
|
||||
use log::warn;
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
use std::{collections::BTreeMap, sync::RwLock};
|
||||
|
||||
#[cfg(feature = "sled")]
|
||||
pub struct SledEngine(sled::Db);
|
||||
#[cfg(feature = "sled")]
|
||||
pub struct SledEngineTree(sled::Tree);
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
pub struct RocksDbEngine(rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>);
|
||||
#[cfg(feature = "rocksdb")]
|
||||
pub struct RocksDbEngineTree<'a> {
|
||||
db: Arc<RocksDbEngine>,
|
||||
name: &'a str,
|
||||
watchers: RwLock<BTreeMap<Vec<u8>, Vec<tokio::sync::oneshot::Sender<()>>>>,
|
||||
}
|
||||
|
||||
pub trait DatabaseEngine: Sized {
|
||||
fn open(config: &Config) -> Result<Arc<Self>>;
|
||||
fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>>;
|
||||
}
|
||||
|
||||
pub trait Tree: Send + Sync {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
|
||||
|
||||
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()>;
|
||||
|
||||
fn remove(&self, key: &[u8]) -> Result<()>;
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + Send + Sync + 'a>;
|
||||
|
||||
fn iter_from<'a>(
|
||||
&'a self,
|
||||
from: &[u8],
|
||||
backwards: bool,
|
||||
) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + 'a>;
|
||||
|
||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>>;
|
||||
|
||||
fn scan_prefix<'a>(
|
||||
&'a self,
|
||||
prefix: Vec<u8>,
|
||||
) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + Send + 'a>;
|
||||
|
||||
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
|
||||
|
||||
fn clear(&self) -> Result<()> {
|
||||
for (key, _) in self.iter() {
|
||||
self.remove(&key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sled")]
|
||||
impl DatabaseEngine for SledEngine {
|
||||
fn open(config: &Config) -> Result<Arc<Self>> {
|
||||
Ok(Arc::new(SledEngine(
|
||||
sled::Config::default()
|
||||
.path(&config.database_path)
|
||||
.cache_capacity(config.cache_capacity as u64)
|
||||
.use_compression(false)
|
||||
.open()?,
|
||||
)))
|
||||
}
|
||||
|
||||
fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>> {
|
||||
Ok(Arc::new(SledEngineTree(self.0.open_tree(name)?)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sled")]
|
||||
impl Tree for SledEngineTree {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||
Ok(self.0.get(key)?.map(|v| v.to_vec()))
|
||||
}
|
||||
|
||||
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
self.0.insert(key, value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, key: &[u8]) -> Result<()> {
|
||||
self.0.remove(key)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + Send + Sync + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.iter()
|
||||
.filter_map(|r| {
|
||||
if let Err(e) = &r {
|
||||
warn!("Error: {}", e);
|
||||
}
|
||||
r.ok()
|
||||
})
|
||||
.map(|(k, v)| (k.to_vec().into(), v.to_vec().into())),
|
||||
)
|
||||
}
|
||||
|
||||
fn iter_from(
|
||||
&self,
|
||||
from: &[u8],
|
||||
backwards: bool,
|
||||
) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)>> {
|
||||
let iter = if backwards {
|
||||
self.0.range(..from)
|
||||
} else {
|
||||
self.0.range(from..)
|
||||
};
|
||||
|
||||
let iter = iter
|
||||
.filter_map(|r| {
|
||||
if let Err(e) = &r {
|
||||
warn!("Error: {}", e);
|
||||
}
|
||||
r.ok()
|
||||
})
|
||||
.map(|(k, v)| (k.to_vec().into(), v.to_vec().into()));
|
||||
|
||||
if backwards {
|
||||
Box::new(iter.rev())
|
||||
} else {
|
||||
Box::new(iter)
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
|
||||
Ok(self
|
||||
.0
|
||||
.update_and_fetch(key, utils::increment)
|
||||
.map(|o| o.expect("increment always sets a value").to_vec())?)
|
||||
}
|
||||
|
||||
fn scan_prefix<'a>(
|
||||
&'a self,
|
||||
prefix: Vec<u8>,
|
||||
) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + Send + 'a> {
|
||||
let iter = self
|
||||
.0
|
||||
.scan_prefix(prefix)
|
||||
.filter_map(|r| {
|
||||
if let Err(e) = &r {
|
||||
warn!("Error: {}", e);
|
||||
}
|
||||
r.ok()
|
||||
})
|
||||
.map(|(k, v)| (k.to_vec().into(), v.to_vec().into()));
|
||||
|
||||
Box::new(iter)
|
||||
}
|
||||
|
||||
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
let prefix = prefix.to_vec();
|
||||
Box::pin(async move {
|
||||
self.0.watch_prefix(prefix).await;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
impl DatabaseEngine for RocksDbEngine {
|
||||
fn open(config: &Config) -> Result<Arc<Self>> {
|
||||
let mut db_opts = rocksdb::Options::default();
|
||||
db_opts.create_if_missing(true);
|
||||
db_opts.set_max_open_files(16);
|
||||
db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
|
||||
db_opts.set_compression_type(rocksdb::DBCompressionType::Snappy);
|
||||
db_opts.set_target_file_size_base(256 << 20);
|
||||
db_opts.set_write_buffer_size(256 << 20);
|
||||
|
||||
let mut block_based_options = rocksdb::BlockBasedOptions::default();
|
||||
block_based_options.set_block_size(512 << 10);
|
||||
db_opts.set_block_based_table_factory(&block_based_options);
|
||||
|
||||
let cfs = rocksdb::DBWithThreadMode::<rocksdb::MultiThreaded>::list_cf(
|
||||
&db_opts,
|
||||
&config.database_path,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut options = rocksdb::Options::default();
|
||||
options.set_merge_operator_associative("increment", utils::increment_rocksdb);
|
||||
|
||||
let db = rocksdb::DBWithThreadMode::<rocksdb::MultiThreaded>::open_cf_descriptors(
|
||||
&db_opts,
|
||||
&config.database_path,
|
||||
cfs.iter()
|
||||
.map(|name| rocksdb::ColumnFamilyDescriptor::new(name, options.clone())),
|
||||
)?;
|
||||
|
||||
Ok(Arc::new(RocksDbEngine(db)))
|
||||
}
|
||||
|
||||
fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>> {
|
||||
let mut options = rocksdb::Options::default();
|
||||
options.set_merge_operator_associative("increment", utils::increment_rocksdb);
|
||||
|
||||
// Create if it doesn't exist
|
||||
let _ = self.0.create_cf(name, &options);
|
||||
|
||||
Ok(Arc::new(RocksDbEngineTree {
|
||||
name,
|
||||
db: Arc::clone(self),
|
||||
watchers: RwLock::new(BTreeMap::new()),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
impl RocksDbEngineTree<'_> {
|
||||
fn cf(&self) -> rocksdb::BoundColumnFamily<'_> {
|
||||
self.db.0.cf_handle(self.name).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
impl Tree for RocksDbEngineTree<'_> {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||
Ok(self.db.0.get_cf(self.cf(), key)?)
|
||||
}
|
||||
|
||||
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
let watchers = self.watchers.read().unwrap();
|
||||
let mut triggered = Vec::new();
|
||||
|
||||
for length in 0..=key.len() {
|
||||
if watchers.contains_key(&key[..length]) {
|
||||
triggered.push(&key[..length]);
|
||||
}
|
||||
}
|
||||
|
||||
drop(watchers);
|
||||
|
||||
if !triggered.is_empty() {
|
||||
let mut watchers = self.watchers.write().unwrap();
|
||||
for prefix in triggered {
|
||||
if let Some(txs) = watchers.remove(prefix) {
|
||||
for tx in txs {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.db.0.put_cf(self.cf(), key, value)?)
|
||||
}
|
||||
|
||||
fn remove(&self, key: &[u8]) -> Result<()> {
|
||||
Ok(self.db.0.delete_cf(self.cf(), key)?)
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + Send + Sync + 'a> {
|
||||
Box::new(
|
||||
self.db
|
||||
.0
|
||||
.iterator_cf(self.cf(), rocksdb::IteratorMode::Start),
|
||||
)
|
||||
}
|
||||
|
||||
fn iter_from<'a>(
|
||||
&'a self,
|
||||
from: &[u8],
|
||||
backwards: bool,
|
||||
) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + 'a> {
|
||||
Box::new(self.db.0.iterator_cf(
|
||||
self.cf(),
|
||||
rocksdb::IteratorMode::From(
|
||||
from,
|
||||
if backwards {
|
||||
rocksdb::Direction::Reverse
|
||||
} else {
|
||||
rocksdb::Direction::Forward
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
|
||||
let stats = rocksdb::perf::get_memory_usage_stats(Some(&[&self.db.0]), None).unwrap();
|
||||
dbg!(stats.mem_table_total);
|
||||
dbg!(stats.mem_table_unflushed);
|
||||
dbg!(stats.mem_table_readers_total);
|
||||
dbg!(stats.cache_total);
|
||||
// TODO: atomic?
|
||||
let old = self.get(key)?;
|
||||
let new = utils::increment(old.as_deref()).unwrap();
|
||||
self.insert(key, &new)?;
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
fn scan_prefix<'a>(
|
||||
&'a self,
|
||||
prefix: Vec<u8>,
|
||||
) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + Send + 'a> {
|
||||
Box::new(
|
||||
self.db
|
||||
.0
|
||||
.iterator_cf(
|
||||
self.cf(),
|
||||
rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward),
|
||||
)
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix)),
|
||||
)
|
||||
}
|
||||
|
||||
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
self.watchers
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(prefix.to_vec())
|
||||
.or_default()
|
||||
.push(tx);
|
||||
|
||||
Box::pin(async move {
|
||||
// Tx is never destroyed
|
||||
rx.await.unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::{utils, Error, Result};
|
||||
use ruma::{
|
||||
api::client::error::ErrorKind,
|
||||
events::{AnyEphemeralRoomEvent, EventType},
|
||||
events::{AnyEvent as EduEvent, EventType},
|
||||
serde::Raw,
|
||||
RoomId, UserId,
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::{collections::HashMap, convert::TryFrom, sync::Arc};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
use sled::IVec;
|
||||
use std::{collections::HashMap, convert::TryFrom};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AccountData {
|
||||
pub(super) roomuserdataid_accountdata: Arc<dyn Tree>, // RoomUserDataId = Room + User + Count + Type
|
||||
pub(super) roomuserdataid_accountdata: sled::Tree, // RoomUserDataId = Room + User + Count + Type
|
||||
}
|
||||
|
||||
impl AccountData {
|
||||
@@ -34,8 +34,9 @@ impl AccountData {
|
||||
prefix.push(0xff);
|
||||
|
||||
// Remove old entry
|
||||
if let Some((old_key, _)) = self.find_event(room_id, user_id, &event_type)? {
|
||||
self.roomuserdataid_accountdata.remove(&old_key)?;
|
||||
if let Some(previous) = self.find_event(room_id, user_id, &event_type) {
|
||||
let (old_key, _) = previous?;
|
||||
self.roomuserdataid_accountdata.remove(old_key)?;
|
||||
}
|
||||
|
||||
let mut key = prefix;
|
||||
@@ -51,10 +52,8 @@ impl AccountData {
|
||||
));
|
||||
}
|
||||
|
||||
self.roomuserdataid_accountdata.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(&json).expect("to_vec always works on json values"),
|
||||
)?;
|
||||
self.roomuserdataid_accountdata
|
||||
.insert(key, &*json.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -66,8 +65,9 @@ impl AccountData {
|
||||
user_id: &UserId,
|
||||
kind: EventType,
|
||||
) -> Result<Option<T>> {
|
||||
self.find_event(room_id, user_id, &kind)?
|
||||
.map(|(_, v)| {
|
||||
self.find_event(room_id, user_id, &kind)
|
||||
.map(|r| {
|
||||
let (_, v) = r?;
|
||||
serde_json::from_slice(&v).map_err(|_| Error::bad_database("could not deserialize"))
|
||||
})
|
||||
.transpose()
|
||||
@@ -80,7 +80,7 @@ impl AccountData {
|
||||
room_id: Option<&RoomId>,
|
||||
user_id: &UserId,
|
||||
since: u64,
|
||||
) -> Result<HashMap<EventType, Raw<AnyEphemeralRoomEvent>>> {
|
||||
) -> Result<HashMap<EventType, Raw<EduEvent>>> {
|
||||
let mut userdata = HashMap::new();
|
||||
|
||||
let mut prefix = room_id
|
||||
@@ -98,7 +98,8 @@ impl AccountData {
|
||||
|
||||
for r in self
|
||||
.roomuserdataid_accountdata
|
||||
.iter_from(&first_possible, false)
|
||||
.range(&*first_possible..)
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.map(|(k, v)| {
|
||||
Ok::<_, Error>((
|
||||
@@ -109,7 +110,7 @@ impl AccountData {
|
||||
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
||||
serde_json::from_slice::<Raw<AnyEphemeralRoomEvent>>(&v).map_err(|_| {
|
||||
serde_json::from_slice::<Raw<EduEvent>>(&v).map_err(|_| {
|
||||
Error::bad_database("Database contains invalid account data.")
|
||||
})?,
|
||||
))
|
||||
@@ -127,7 +128,7 @@ impl AccountData {
|
||||
room_id: Option<&RoomId>,
|
||||
user_id: &UserId,
|
||||
kind: &EventType,
|
||||
) -> Result<Option<(Box<[u8]>, Box<[u8]>)>> {
|
||||
) -> Option<Result<(IVec, IVec)>> {
|
||||
let mut prefix = room_id
|
||||
.map(|r| r.to_string())
|
||||
.unwrap_or_default()
|
||||
@@ -136,21 +137,23 @@ impl AccountData {
|
||||
prefix.push(0xff);
|
||||
prefix.extend_from_slice(&user_id.as_bytes());
|
||||
prefix.push(0xff);
|
||||
|
||||
let mut last_possible_key = prefix.clone();
|
||||
last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes());
|
||||
|
||||
let kind = kind.clone();
|
||||
|
||||
Ok(self
|
||||
.roomuserdataid_accountdata
|
||||
.iter_from(&last_possible_key, true)
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.find(move |(k, _)| {
|
||||
self.roomuserdataid_accountdata
|
||||
.scan_prefix(prefix)
|
||||
.rev()
|
||||
.find(move |r| {
|
||||
r.as_ref()
|
||||
.map(|(k, _)| {
|
||||
k.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.map(|current_event_type| current_event_type == kind.as_ref().as_bytes())
|
||||
.map(|current_event_type| {
|
||||
current_event_type == kind.as_ref().as_bytes()
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|r| Ok(r?))
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -1,9 +1,6 @@
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use crate::{pdu::PduBuilder, Database};
|
||||
use crate::pdu::PduBuilder;
|
||||
use log::warn;
|
||||
use rocket::futures::{channel::mpsc, stream::StreamExt};
|
||||
use ruma::{
|
||||
@@ -25,7 +22,7 @@ pub struct Admin {
|
||||
impl Admin {
|
||||
pub fn start_handler(
|
||||
&self,
|
||||
db: Arc<Database>,
|
||||
db: super::Database,
|
||||
mut receiver: mpsc::UnboundedReceiver<AdminCommand>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
@@ -76,7 +73,7 @@ impl Admin {
|
||||
db.appservice.register_appservice(yaml).unwrap(); // TODO handle error
|
||||
}
|
||||
AdminCommand::ListAppservices => {
|
||||
if let Ok(appservices) = db.appservice.iter_ids().map(|ids| ids.collect::<Vec<_>>()) {
|
||||
let appservices = db.appservice.iter_ids().collect::<Vec<_>>();
|
||||
let count = appservices.len();
|
||||
let output = format!(
|
||||
"Appservices ({}): {}",
|
||||
@@ -84,9 +81,6 @@ impl Admin {
|
||||
appservices.into_iter().filter_map(|r| r.ok()).collect::<Vec<_>>().join(", ")
|
||||
);
|
||||
send_message(message::MessageEventContent::text_plain(output));
|
||||
} else {
|
||||
send_message(message::MessageEventContent::text_plain("Failed to get appservices."));
|
||||
}
|
||||
}
|
||||
AdminCommand::SendMessage(message) => {
|
||||
send_message(message);
|
||||
@@ -99,6 +93,6 @@ impl Admin {
|
||||
}
|
||||
|
||||
pub fn send(&self, command: AdminCommand) {
|
||||
self.sender.unbounded_send(command).unwrap();
|
||||
self.sender.unbounded_send(command).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
+19
-22
@@ -4,21 +4,18 @@ use std::{
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Appservice {
|
||||
pub(super) cached_registrations: Arc<RwLock<HashMap<String, serde_yaml::Value>>>,
|
||||
pub(super) id_appserviceregistrations: Arc<dyn Tree>,
|
||||
pub(super) id_appserviceregistrations: sled::Tree,
|
||||
}
|
||||
|
||||
impl Appservice {
|
||||
pub fn register_appservice(&self, yaml: serde_yaml::Value) -> Result<()> {
|
||||
// TODO: Rumaify
|
||||
let id = yaml.get("id").unwrap().as_str().unwrap();
|
||||
self.id_appserviceregistrations.insert(
|
||||
id.as_bytes(),
|
||||
serde_yaml::to_string(&yaml).unwrap().as_bytes(),
|
||||
)?;
|
||||
self.id_appserviceregistrations
|
||||
.insert(id, serde_yaml::to_string(&yaml).unwrap().as_bytes())?;
|
||||
self.cached_registrations
|
||||
.write()
|
||||
.unwrap()
|
||||
@@ -34,37 +31,37 @@ impl Appservice {
|
||||
.get(id)
|
||||
.map_or_else(
|
||||
|| {
|
||||
self.id_appserviceregistrations
|
||||
.get(id.as_bytes())?
|
||||
Ok(self
|
||||
.id_appserviceregistrations
|
||||
.get(id)?
|
||||
.map(|bytes| {
|
||||
serde_yaml::from_slice(&bytes).map_err(|_| {
|
||||
Ok::<_, Error>(serde_yaml::from_slice(&bytes).map_err(|_| {
|
||||
Error::bad_database(
|
||||
"Invalid registration bytes in id_appserviceregistrations.",
|
||||
)
|
||||
})?)
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
.transpose()?)
|
||||
},
|
||||
|r| Ok(Some(r.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn iter_ids(&self) -> Result<impl Iterator<Item = Result<String>> + Send + Sync + '_> {
|
||||
Ok(self.id_appserviceregistrations.iter().map(|(id, _)| {
|
||||
utils::string_from_bytes(&id)
|
||||
.map_err(|_| Error::bad_database("Invalid id bytes in id_appserviceregistrations."))
|
||||
}))
|
||||
pub fn iter_ids(&self) -> impl Iterator<Item = Result<String>> {
|
||||
self.id_appserviceregistrations.iter().keys().map(|id| {
|
||||
Ok(utils::string_from_bytes(&id?).map_err(|_| {
|
||||
Error::bad_database("Invalid id bytes in id_appserviceregistrations.")
|
||||
})?)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter_all(
|
||||
&self,
|
||||
) -> Result<impl Iterator<Item = Result<(String, serde_yaml::Value)>> + '_ + Send + Sync> {
|
||||
Ok(self.iter_ids()?.filter_map(|id| id.ok()).map(move |id| {
|
||||
pub fn iter_all(&self) -> impl Iterator<Item = Result<(String, serde_yaml::Value)>> + '_ {
|
||||
self.iter_ids().filter_map(|id| id.ok()).map(move |id| {
|
||||
Ok((
|
||||
id.clone(),
|
||||
self.get_registration(&id)?
|
||||
.expect("iter_ids only returns appservices that exist"),
|
||||
))
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+64
-115
@@ -1,52 +1,32 @@
|
||||
use crate::{database::Config, utils, ConduitResult, Error, Result};
|
||||
use crate::{database::Config, utils, Error, Result};
|
||||
use log::{error, info};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::r0::sync::sync_events,
|
||||
federation::discovery::{ServerSigningKeys, VerifyKey},
|
||||
},
|
||||
DeviceId, EventId, MilliSecondsSinceUnixEpoch, ServerName, ServerSigningKeyId, UserId,
|
||||
api::federation::discovery::{ServerSigningKeys, VerifyKey},
|
||||
ServerName, ServerSigningKeyId,
|
||||
};
|
||||
use rustls::{ServerCertVerifier, WebPKIVerifier};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fs,
|
||||
path::PathBuf,
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, Instant},
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use trust_dns_resolver::TokioAsyncResolver;
|
||||
|
||||
use super::abstraction::Tree;
|
||||
|
||||
pub const COUNTER: &[u8] = b"c";
|
||||
pub const COUNTER: &str = "c";
|
||||
|
||||
type WellKnownMap = HashMap<Box<ServerName>, (String, String)>;
|
||||
type TlsNameMap = HashMap<String, webpki::DNSName>;
|
||||
type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries
|
||||
#[derive(Clone)]
|
||||
pub struct Globals {
|
||||
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
||||
pub tls_name_override: Arc<RwLock<TlsNameMap>>,
|
||||
pub(super) globals: Arc<dyn Tree>,
|
||||
pub(super) globals: sled::Tree,
|
||||
config: Config,
|
||||
keypair: Arc<ruma::signatures::Ed25519KeyPair>,
|
||||
reqwest_client: reqwest::Client,
|
||||
dns_resolver: TokioAsyncResolver,
|
||||
jwt_decoding_key: Option<jsonwebtoken::DecodingKey<'static>>,
|
||||
pub(super) server_signingkeys: Arc<dyn Tree>,
|
||||
pub bad_event_ratelimiter: Arc<RwLock<BTreeMap<EventId, RateLimitState>>>,
|
||||
pub bad_signature_ratelimiter: Arc<RwLock<BTreeMap<Vec<String>, RateLimitState>>>,
|
||||
pub servername_ratelimiter: Arc<RwLock<BTreeMap<Box<ServerName>, Arc<Semaphore>>>>,
|
||||
pub sync_receivers: RwLock<
|
||||
BTreeMap<
|
||||
(UserId, Box<DeviceId>),
|
||||
(
|
||||
Option<String>,
|
||||
tokio::sync::watch::Receiver<Option<ConduitResult<sync_events::Response>>>,
|
||||
), // since, rx
|
||||
>,
|
||||
>,
|
||||
pub(super) servertimeout_signingkey: sled::Tree, // ServerName + Timeout Timestamp -> algorithm:key + pubkey
|
||||
}
|
||||
|
||||
struct MatrixServerVerifier {
|
||||
@@ -84,20 +64,15 @@ impl ServerCertVerifier for MatrixServerVerifier {
|
||||
|
||||
impl Globals {
|
||||
pub fn load(
|
||||
globals: Arc<dyn Tree>,
|
||||
server_signingkeys: Arc<dyn Tree>,
|
||||
globals: sled::Tree,
|
||||
servertimeout_signingkey: sled::Tree,
|
||||
config: Config,
|
||||
) -> Result<Self> {
|
||||
let keypair_bytes = globals.get(b"keypair")?.map_or_else(
|
||||
|| {
|
||||
let keypair = utils::generate_keypair();
|
||||
globals.insert(b"keypair", &keypair)?;
|
||||
Ok::<_, Error>(keypair)
|
||||
},
|
||||
|s| Ok(s.to_vec()),
|
||||
)?;
|
||||
let bytes = &*globals
|
||||
.update_and_fetch("keypair", utils::generate_keypair)?
|
||||
.expect("utils::generate_keypair always returns Some");
|
||||
|
||||
let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff);
|
||||
let mut parts = bytes.splitn(2, |&b| b == 0xff);
|
||||
|
||||
let keypair = utils::string_from_bytes(
|
||||
// 1. version
|
||||
@@ -114,7 +89,7 @@ impl Globals {
|
||||
.map(|key| (version, key))
|
||||
})
|
||||
.and_then(|(version, key)| {
|
||||
ruma::signatures::Ed25519KeyPair::from_der(&key, version)
|
||||
ruma::signatures::Ed25519KeyPair::new(&key, version)
|
||||
.map_err(|_| Error::bad_database("Private or public keys are invalid."))
|
||||
});
|
||||
|
||||
@@ -122,7 +97,7 @@ impl Globals {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
error!("Keypair invalid. Deleting...");
|
||||
globals.remove(b"keypair")?;
|
||||
globals.remove("keypair")?;
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
@@ -150,7 +125,7 @@ impl Globals {
|
||||
.as_ref()
|
||||
.map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static());
|
||||
|
||||
let s = Self {
|
||||
Ok(Self {
|
||||
globals,
|
||||
config,
|
||||
keypair: Arc::new(keypair),
|
||||
@@ -160,17 +135,9 @@ impl Globals {
|
||||
})?,
|
||||
actual_destination_cache: Arc::new(RwLock::new(WellKnownMap::new())),
|
||||
tls_name_override,
|
||||
server_signingkeys,
|
||||
servertimeout_signingkey,
|
||||
jwt_decoding_key,
|
||||
bad_event_ratelimiter: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
bad_signature_ratelimiter: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
servername_ratelimiter: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
sync_receivers: RwLock::new(BTreeMap::new()),
|
||||
};
|
||||
|
||||
fs::create_dir_all(s.get_media_folder())?;
|
||||
|
||||
Ok(s)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns this server's keypair.
|
||||
@@ -184,14 +151,19 @@ impl Globals {
|
||||
}
|
||||
|
||||
pub fn next_count(&self) -> Result<u64> {
|
||||
utils::u64_from_bytes(&self.globals.increment(COUNTER)?)
|
||||
.map_err(|_| Error::bad_database("Count has invalid bytes."))
|
||||
Ok(utils::u64_from_bytes(
|
||||
&self
|
||||
.globals
|
||||
.update_and_fetch(COUNTER, utils::increment)?
|
||||
.expect("utils::increment will always put in a value"),
|
||||
)
|
||||
.map_err(|_| Error::bad_database("Count has invalid bytes."))?)
|
||||
}
|
||||
|
||||
pub fn current_count(&self) -> Result<u64> {
|
||||
self.globals.get(COUNTER)?.map_or(Ok(0_u64), |bytes| {
|
||||
utils::u64_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Count has invalid bytes."))
|
||||
Ok(utils::u64_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Count has invalid bytes."))?)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -231,29 +203,30 @@ impl Globals {
|
||||
/// Remove the outdated keys and insert the new ones.
|
||||
///
|
||||
/// This doesn't actually check that the keys provided are newer than the old set.
|
||||
pub fn add_signing_key(&self, origin: &ServerName, new_keys: ServerSigningKeys) -> Result<()> {
|
||||
// Not atomic, but this is not critical
|
||||
let signingkeys = self.server_signingkeys.get(origin.as_bytes())?;
|
||||
pub fn add_signing_key(&self, origin: &ServerName, keys: &ServerSigningKeys) -> Result<()> {
|
||||
let mut key1 = origin.as_bytes().to_vec();
|
||||
key1.push(0xff);
|
||||
|
||||
let mut keys = signingkeys
|
||||
.and_then(|keys| serde_json::from_slice(&keys).ok())
|
||||
.unwrap_or_else(|| {
|
||||
// Just insert "now", it doesn't matter
|
||||
ServerSigningKeys::new(origin.to_owned(), MilliSecondsSinceUnixEpoch::now())
|
||||
});
|
||||
let mut key2 = key1.clone();
|
||||
|
||||
let ServerSigningKeys {
|
||||
verify_keys,
|
||||
old_verify_keys,
|
||||
..
|
||||
} = new_keys;
|
||||
let ts = keys
|
||||
.valid_until_ts
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.expect("time is valid")
|
||||
.as_millis() as u64;
|
||||
|
||||
keys.verify_keys.extend(verify_keys.into_iter());
|
||||
keys.old_verify_keys.extend(old_verify_keys.into_iter());
|
||||
key1.extend_from_slice(&ts.to_be_bytes());
|
||||
key2.extend_from_slice(&(ts + 1).to_be_bytes());
|
||||
|
||||
self.server_signingkeys.insert(
|
||||
origin.as_bytes(),
|
||||
&serde_json::to_vec(&keys).expect("serversigningkeys can be serialized"),
|
||||
self.servertimeout_signingkey.insert(
|
||||
key1,
|
||||
serde_json::to_vec(&keys.verify_keys).expect("ServerSigningKeys are a valid string"),
|
||||
)?;
|
||||
|
||||
self.servertimeout_signingkey.insert(
|
||||
key2,
|
||||
serde_json::to_vec(&keys.old_verify_keys)
|
||||
.expect("ServerSigningKeys are a valid string"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -264,49 +237,25 @@ impl Globals {
|
||||
&self,
|
||||
origin: &ServerName,
|
||||
) -> Result<BTreeMap<ServerSigningKeyId, VerifyKey>> {
|
||||
let signingkeys = self
|
||||
.server_signingkeys
|
||||
.get(origin.as_bytes())?
|
||||
.and_then(|bytes| serde_json::from_slice::<ServerSigningKeys>(&bytes).ok())
|
||||
.map(|keys| {
|
||||
let mut tree = keys.verify_keys;
|
||||
tree.extend(
|
||||
keys.old_verify_keys
|
||||
.into_iter()
|
||||
.map(|old| (old.0, VerifyKey::new(old.1.key))),
|
||||
);
|
||||
tree
|
||||
})
|
||||
.unwrap_or_else(BTreeMap::new);
|
||||
let mut response = BTreeMap::new();
|
||||
|
||||
Ok(signingkeys)
|
||||
}
|
||||
let now = crate::utils::millis_since_unix_epoch();
|
||||
|
||||
pub fn database_version(&self) -> Result<u64> {
|
||||
self.globals.get(b"version")?.map_or(Ok(0), |version| {
|
||||
utils::u64_from_bytes(&version)
|
||||
.map_err(|_| Error::bad_database("Database version id is invalid."))
|
||||
})
|
||||
for item in self.servertimeout_signingkey.scan_prefix(origin.as_bytes()) {
|
||||
let (k, bytes) = item?;
|
||||
let valid_until = k
|
||||
.splitn(2, |&b| b == 0xff)
|
||||
.nth(1)
|
||||
.map(crate::utils::u64_from_bytes)
|
||||
.ok_or_else(|| Error::bad_database("Invalid signing keys."))?
|
||||
.map_err(|_| Error::bad_database("Invalid signing key valid until bytes"))?;
|
||||
// If these keys are still valid use em!
|
||||
if valid_until > now {
|
||||
let btree: BTreeMap<_, _> = serde_json::from_slice(&bytes)
|
||||
.map_err(|_| Error::bad_database("Invalid BTreeMap<> of signing keys"))?;
|
||||
response.extend(btree);
|
||||
}
|
||||
|
||||
pub fn bump_database_version(&self, new_version: u64) -> Result<()> {
|
||||
self.globals
|
||||
.insert(b"version", &new_version.to_be_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_media_folder(&self) -> PathBuf {
|
||||
let mut r = PathBuf::new();
|
||||
r.push(self.config.database_path.clone());
|
||||
r.push("media");
|
||||
r
|
||||
}
|
||||
|
||||
pub fn get_media_file(&self, key: &[u8]) -> PathBuf {
|
||||
let mut r = PathBuf::new();
|
||||
r.push(self.config.database_path.clone());
|
||||
r.push("media");
|
||||
r.push(base64::encode_config(key, base64::URL_SAFE_NO_PAD));
|
||||
r
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
+67
-57
@@ -6,14 +6,13 @@ use ruma::{
|
||||
},
|
||||
RoomId, UserId,
|
||||
};
|
||||
use std::{collections::BTreeMap, convert::TryFrom, sync::Arc};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
use std::{collections::BTreeMap, convert::TryFrom};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KeyBackups {
|
||||
pub(super) backupid_algorithm: Arc<dyn Tree>, // BackupId = UserId + Version(Count)
|
||||
pub(super) backupid_etag: Arc<dyn Tree>, // BackupId = UserId + Version(Count)
|
||||
pub(super) backupkeyid_backup: Arc<dyn Tree>, // BackupKeyId = UserId + Version + RoomId + SessionId
|
||||
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 {
|
||||
@@ -31,7 +30,8 @@ impl KeyBackups {
|
||||
|
||||
self.backupid_algorithm.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(backup_metadata).expect("BackupAlgorithm::to_vec always works"),
|
||||
&*serde_json::to_string(backup_metadata)
|
||||
.expect("BackupAlgorithm::to_string always works"),
|
||||
)?;
|
||||
self.backupid_etag
|
||||
.insert(&key, &globals.next_count()?.to_be_bytes())?;
|
||||
@@ -48,8 +48,13 @@ impl KeyBackups {
|
||||
|
||||
key.push(0xff);
|
||||
|
||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
||||
for outdated_key in self
|
||||
.backupkeyid_backup
|
||||
.scan_prefix(&key)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
{
|
||||
self.backupkeyid_backup.remove(outdated_key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -75,9 +80,8 @@ impl KeyBackups {
|
||||
|
||||
self.backupid_algorithm.insert(
|
||||
&key,
|
||||
&serde_json::to_string(backup_metadata)
|
||||
.expect("BackupAlgorithm::to_string always works")
|
||||
.as_bytes(),
|
||||
&*serde_json::to_string(backup_metadata)
|
||||
.expect("BackupAlgorithm::to_string always works"),
|
||||
)?;
|
||||
self.backupid_etag
|
||||
.insert(&key, &globals.next_count()?.to_be_bytes())?;
|
||||
@@ -87,14 +91,11 @@ impl KeyBackups {
|
||||
pub fn get_latest_backup(&self, user_id: &UserId) -> Result<Option<(String, BackupAlgorithm)>> {
|
||||
let mut prefix = user_id.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
let mut last_possible_key = prefix.clone();
|
||||
last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes());
|
||||
|
||||
self.backupid_algorithm
|
||||
.iter_from(&last_possible_key, true)
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.next()
|
||||
.map_or(Ok(None), |(key, value)| {
|
||||
.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()
|
||||
@@ -116,11 +117,9 @@ impl KeyBackups {
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(version.as_bytes());
|
||||
|
||||
self.backupid_algorithm
|
||||
.get(&key)?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
serde_json::from_slice(&bytes)
|
||||
.map_err(|_| Error::bad_database("Algorithm in backupid_algorithm is invalid."))
|
||||
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."))?)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -154,7 +153,7 @@ impl KeyBackups {
|
||||
|
||||
self.backupkeyid_backup.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(&key_data).expect("KeyBackupData::to_vec always works"),
|
||||
&*serde_json::to_string(&key_data).expect("KeyBackupData::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -165,7 +164,7 @@ impl KeyBackups {
|
||||
prefix.push(0xff);
|
||||
prefix.extend_from_slice(version.as_bytes());
|
||||
|
||||
Ok(self.backupkeyid_backup.scan_prefix(prefix).count())
|
||||
Ok(self.backupkeyid_backup.scan_prefix(&prefix).count())
|
||||
}
|
||||
|
||||
pub fn get_etag(&self, user_id: &UserId, version: &str) -> Result<String> {
|
||||
@@ -195,37 +194,33 @@ impl KeyBackups {
|
||||
|
||||
let mut rooms = BTreeMap::<RoomId, RoomKeyBackup>::new();
|
||||
|
||||
for result in self
|
||||
.backupkeyid_backup
|
||||
.scan_prefix(prefix)
|
||||
.map(|(key, value)| {
|
||||
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 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.")
|
||||
})?)
|
||||
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.")
|
||||
})?;
|
||||
.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("KeyBackupData 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)
|
||||
@@ -244,7 +239,7 @@ impl KeyBackups {
|
||||
user_id: &UserId,
|
||||
version: &str,
|
||||
room_id: &RoomId,
|
||||
) -> Result<BTreeMap<String, KeyBackupData>> {
|
||||
) -> BTreeMap<String, KeyBackupData> {
|
||||
let mut prefix = user_id.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
prefix.extend_from_slice(version.as_bytes());
|
||||
@@ -252,10 +247,10 @@ impl KeyBackups {
|
||||
prefix.extend_from_slice(room_id.as_bytes());
|
||||
prefix.push(0xff);
|
||||
|
||||
Ok(self
|
||||
.backupkeyid_backup
|
||||
.scan_prefix(prefix)
|
||||
.map(|(key, value)| {
|
||||
self.backupkeyid_backup
|
||||
.scan_prefix(&prefix)
|
||||
.map(|r| {
|
||||
let (key, value) = r?;
|
||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||
|
||||
let session_id =
|
||||
@@ -273,7 +268,7 @@ impl KeyBackups {
|
||||
Ok::<_, Error>((session_id, key_data))
|
||||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_session(
|
||||
@@ -307,8 +302,13 @@ impl KeyBackups {
|
||||
key.extend_from_slice(&version.as_bytes());
|
||||
key.push(0xff);
|
||||
|
||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
||||
for outdated_key in self
|
||||
.backupkeyid_backup
|
||||
.scan_prefix(&key)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
{
|
||||
self.backupkeyid_backup.remove(outdated_key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -327,8 +327,13 @@ impl KeyBackups {
|
||||
key.extend_from_slice(&room_id.as_bytes());
|
||||
key.push(0xff);
|
||||
|
||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
||||
for outdated_key in self
|
||||
.backupkeyid_backup
|
||||
.scan_prefix(&key)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
{
|
||||
self.backupkeyid_backup.remove(outdated_key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -349,8 +354,13 @@ impl KeyBackups {
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(&session_id.as_bytes());
|
||||
|
||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
||||
for outdated_key in self
|
||||
.backupkeyid_backup
|
||||
.scan_prefix(&key)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
{
|
||||
self.backupkeyid_backup.remove(outdated_key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+48
-95
@@ -1,28 +1,25 @@
|
||||
use crate::database::globals::Globals;
|
||||
use image::{imageops::FilterType, GenericImageView};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
use crate::{utils, Error, Result};
|
||||
use std::{mem, sync::Arc};
|
||||
use tokio::{fs::File, io::AsyncReadExt, io::AsyncWriteExt};
|
||||
use std::mem;
|
||||
|
||||
pub struct FileMeta {
|
||||
pub content_disposition: Option<String>,
|
||||
pub filename: Option<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub file: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Media {
|
||||
pub(super) mediaid_file: Arc<dyn Tree>, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType
|
||||
pub(super) mediaid_file: sled::Tree, // MediaId = MXC + WidthHeight + Filename + ContentType
|
||||
}
|
||||
|
||||
impl Media {
|
||||
/// Uploads a file.
|
||||
pub async fn create(
|
||||
/// Uploads or replaces a file.
|
||||
pub fn create(
|
||||
&self,
|
||||
mxc: String,
|
||||
globals: &Globals,
|
||||
content_disposition: &Option<&str>,
|
||||
filename: &Option<&str>,
|
||||
content_type: &Option<&str>,
|
||||
file: &[u8],
|
||||
) -> Result<()> {
|
||||
@@ -31,12 +28,7 @@ impl Media {
|
||||
key.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail
|
||||
key.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(
|
||||
content_disposition
|
||||
.as_ref()
|
||||
.map(|f| f.as_bytes())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default());
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(
|
||||
content_type
|
||||
@@ -45,20 +37,16 @@ impl Media {
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
let path = globals.get_media_file(&key);
|
||||
let mut f = File::create(path).await?;
|
||||
f.write_all(file).await?;
|
||||
self.mediaid_file.insert(key, file)?;
|
||||
|
||||
self.mediaid_file.insert(&key, &[])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Uploads or replaces a file thumbnail.
|
||||
pub async fn upload_thumbnail(
|
||||
pub fn upload_thumbnail(
|
||||
&self,
|
||||
mxc: String,
|
||||
globals: &Globals,
|
||||
content_disposition: &Option<String>,
|
||||
filename: &Option<String>,
|
||||
content_type: &Option<String>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
@@ -69,12 +57,7 @@ impl Media {
|
||||
key.extend_from_slice(&width.to_be_bytes());
|
||||
key.extend_from_slice(&height.to_be_bytes());
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(
|
||||
content_disposition
|
||||
.as_ref()
|
||||
.map(|f| f.as_bytes())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default());
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(
|
||||
content_type
|
||||
@@ -83,59 +66,48 @@ impl Media {
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
let path = globals.get_media_file(&key);
|
||||
let mut f = File::create(path).await?;
|
||||
f.write_all(file).await?;
|
||||
|
||||
self.mediaid_file.insert(&key, &[])?;
|
||||
self.mediaid_file.insert(key, file)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Downloads a file.
|
||||
pub async fn get(&self, globals: &Globals, mxc: &str) -> Result<Option<FileMeta>> {
|
||||
pub fn get(&self, mxc: &str) -> Result<Option<FileMeta>> {
|
||||
let mut prefix = mxc.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
prefix.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail
|
||||
prefix.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
|
||||
prefix.push(0xff);
|
||||
|
||||
let mut iter = self.mediaid_file.scan_prefix(prefix);
|
||||
if let Some((key, _)) = iter.next() {
|
||||
let path = globals.get_media_file(&key);
|
||||
let mut file = Vec::new();
|
||||
File::open(path).await?.read_to_end(&mut file).await?;
|
||||
if let Some(r) = self.mediaid_file.scan_prefix(&prefix).next() {
|
||||
let (key, file) = r?;
|
||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||
|
||||
let content_type = parts
|
||||
.next()
|
||||
.map(|bytes| {
|
||||
utils::string_from_bytes(bytes).map_err(|_| {
|
||||
Ok::<_, Error>(utils::string_from_bytes(bytes).map_err(|_| {
|
||||
Error::bad_database("Content type in mediaid_file is invalid unicode.")
|
||||
})
|
||||
})?)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let content_disposition_bytes = parts
|
||||
let filename_bytes = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
|
||||
|
||||
let content_disposition = if content_disposition_bytes.is_empty() {
|
||||
let filename = if filename_bytes.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
|
||||
Error::bad_database(
|
||||
"Content Disposition in mediaid_file is invalid unicode.",
|
||||
)
|
||||
})?,
|
||||
)
|
||||
Some(utils::string_from_bytes(filename_bytes).map_err(|_| {
|
||||
Error::bad_database("Filename in mediaid_file is invalid unicode.")
|
||||
})?)
|
||||
};
|
||||
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
filename,
|
||||
content_type,
|
||||
file,
|
||||
file: file.to_vec(),
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -165,13 +137,7 @@ impl Media {
|
||||
/// - Server creates the thumbnail and sends it to the user
|
||||
///
|
||||
/// For width,height <= 96 the server uses another thumbnailing algorithm which crops the image afterwards.
|
||||
pub async fn get_thumbnail(
|
||||
&self,
|
||||
mxc: String,
|
||||
globals: &Globals,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<Option<FileMeta>> {
|
||||
pub fn get_thumbnail(&self, mxc: String, width: u32, height: u32) -> Result<Option<FileMeta>> {
|
||||
let (width, height, crop) = self
|
||||
.thumbnail_properties(width, height)
|
||||
.unwrap_or((0, 0, false)); // 0, 0 because that's the original file
|
||||
@@ -189,72 +155,63 @@ impl Media {
|
||||
original_prefix.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
|
||||
original_prefix.push(0xff);
|
||||
|
||||
if let Some((key, _)) = self.mediaid_file.scan_prefix(thumbnail_prefix).next() {
|
||||
if let Some(r) = self.mediaid_file.scan_prefix(&thumbnail_prefix).next() {
|
||||
// Using saved thumbnail
|
||||
let path = globals.get_media_file(&key);
|
||||
let mut file = Vec::new();
|
||||
File::open(path).await?.read_to_end(&mut file).await?;
|
||||
let (key, file) = r?;
|
||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||
|
||||
let content_type = parts
|
||||
.next()
|
||||
.map(|bytes| {
|
||||
utils::string_from_bytes(bytes).map_err(|_| {
|
||||
Ok::<_, Error>(utils::string_from_bytes(bytes).map_err(|_| {
|
||||
Error::bad_database("Content type in mediaid_file is invalid unicode.")
|
||||
})
|
||||
})?)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let content_disposition_bytes = parts
|
||||
let filename_bytes = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
|
||||
|
||||
let content_disposition = if content_disposition_bytes.is_empty() {
|
||||
let filename = if filename_bytes.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
|
||||
Error::bad_database("Content Disposition in db is invalid.")
|
||||
})?,
|
||||
utils::string_from_bytes(filename_bytes)
|
||||
.map_err(|_| Error::bad_database("Filename in db is invalid."))?,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
filename,
|
||||
content_type,
|
||||
file: file.to_vec(),
|
||||
}))
|
||||
} else if let Some((key, _)) = self.mediaid_file.scan_prefix(original_prefix).next() {
|
||||
} else if let Some(r) = self.mediaid_file.scan_prefix(&original_prefix).next() {
|
||||
// Generate a thumbnail
|
||||
let path = globals.get_media_file(&key);
|
||||
let mut file = Vec::new();
|
||||
File::open(path).await?.read_to_end(&mut file).await?;
|
||||
|
||||
let (key, file) = r?;
|
||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||
|
||||
let content_type = parts
|
||||
.next()
|
||||
.map(|bytes| {
|
||||
utils::string_from_bytes(bytes).map_err(|_| {
|
||||
Ok::<_, Error>(utils::string_from_bytes(bytes).map_err(|_| {
|
||||
Error::bad_database("Content type in mediaid_file is invalid unicode.")
|
||||
})
|
||||
})?)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let content_disposition_bytes = parts
|
||||
let filename_bytes = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
|
||||
|
||||
let content_disposition = if content_disposition_bytes.is_empty() {
|
||||
let filename = if filename_bytes.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
|
||||
Error::bad_database(
|
||||
"Content Disposition in mediaid_file is invalid unicode.",
|
||||
)
|
||||
})?,
|
||||
)
|
||||
Some(utils::string_from_bytes(filename_bytes).map_err(|_| {
|
||||
Error::bad_database("Filename in mediaid_file is invalid unicode.")
|
||||
})?)
|
||||
};
|
||||
|
||||
if let Ok(image) = image::load_from_memory(&file) {
|
||||
@@ -262,7 +219,7 @@ impl Media {
|
||||
let original_height = image.height();
|
||||
if width > original_width || height > original_height {
|
||||
return Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
filename,
|
||||
content_type,
|
||||
file: file.to_vec(),
|
||||
}));
|
||||
@@ -326,21 +283,17 @@ impl Media {
|
||||
widthheight,
|
||||
);
|
||||
|
||||
let path = globals.get_media_file(&thumbnail_key);
|
||||
let mut f = File::create(path).await?;
|
||||
f.write_all(&thumbnail_bytes).await?;
|
||||
|
||||
self.mediaid_file.insert(&thumbnail_key, &[])?;
|
||||
self.mediaid_file.insert(thumbnail_key, &*thumbnail_bytes)?;
|
||||
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
filename,
|
||||
content_type,
|
||||
file: thumbnail_bytes.to_vec(),
|
||||
}))
|
||||
} else {
|
||||
// Couldn't parse file to generate thumbnail, send original
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
filename,
|
||||
content_type,
|
||||
file: file.to_vec(),
|
||||
}))
|
||||
|
||||
+44
-36
@@ -14,17 +14,23 @@ use ruma::{
|
||||
push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak},
|
||||
uint, UInt, UserId,
|
||||
};
|
||||
use sled::IVec;
|
||||
|
||||
use std::{convert::TryFrom, fmt::Debug, mem, sync::Arc};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
use std::{convert::TryFrom, fmt::Debug};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PushData {
|
||||
/// UserId + pushkey -> Pusher
|
||||
pub(super) senderkey_pusher: Arc<dyn Tree>,
|
||||
pub(super) senderkey_pusher: sled::Tree,
|
||||
}
|
||||
|
||||
impl PushData {
|
||||
pub fn new(db: &sled::Db) -> Result<Self> {
|
||||
Ok(Self {
|
||||
senderkey_pusher: db.open_tree("senderkey_pusher")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_pusher(&self, sender: &UserId, pusher: set_pusher::Pusher) -> Result<()> {
|
||||
let mut key = sender.as_bytes().to_vec();
|
||||
key.push(0xff);
|
||||
@@ -34,14 +40,14 @@ impl PushData {
|
||||
if pusher.kind.is_none() {
|
||||
return self
|
||||
.senderkey_pusher
|
||||
.remove(&key)
|
||||
.remove(key)
|
||||
.map(|_| ())
|
||||
.map_err(Into::into);
|
||||
}
|
||||
|
||||
self.senderkey_pusher.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(&pusher).expect("Pusher is valid JSON value"),
|
||||
key,
|
||||
&*serde_json::to_string(&pusher).expect("Pusher is valid JSON string"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -51,8 +57,8 @@ impl PushData {
|
||||
self.senderkey_pusher
|
||||
.get(senderkey)?
|
||||
.map(|push| {
|
||||
serde_json::from_slice(&*push)
|
||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
||||
Ok(serde_json::from_slice(&*push)
|
||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))?)
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
@@ -63,21 +69,23 @@ impl PushData {
|
||||
|
||||
self.senderkey_pusher
|
||||
.scan_prefix(prefix)
|
||||
.map(|(_, push)| {
|
||||
serde_json::from_slice(&*push)
|
||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
||||
.values()
|
||||
.map(|push| {
|
||||
let push = push.map_err(|_| Error::bad_database("Invalid push bytes in db."))?;
|
||||
Ok(serde_json::from_slice(&*push)
|
||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))?)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_pusher_senderkeys<'a>(
|
||||
&'a self,
|
||||
sender: &UserId,
|
||||
) -> impl Iterator<Item = Box<[u8]>> + 'a {
|
||||
pub fn get_pusher_senderkeys(&self, sender: &UserId) -> impl Iterator<Item = Result<IVec>> {
|
||||
let mut prefix = sender.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
self.senderkey_pusher.scan_prefix(prefix).map(|(k, _)| k)
|
||||
self.senderkey_pusher
|
||||
.scan_prefix(prefix)
|
||||
.keys()
|
||||
.map(|r| Ok(r?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,23 +114,24 @@ where
|
||||
//*reqwest_request.timeout_mut() = Some(Duration::from_secs(5));
|
||||
|
||||
let url = reqwest_request.url().clone();
|
||||
let response = globals.reqwest_client().execute(reqwest_request).await;
|
||||
let reqwest_response = globals.reqwest_client().execute(reqwest_request).await;
|
||||
|
||||
match response {
|
||||
Ok(mut response) => {
|
||||
// reqwest::Response -> http::Response conversion
|
||||
let status = response.status();
|
||||
let mut http_response_builder = http::Response::builder()
|
||||
.status(status)
|
||||
.version(response.version());
|
||||
mem::swap(
|
||||
response.headers_mut(),
|
||||
http_response_builder
|
||||
.headers_mut()
|
||||
.expect("http::response::Builder is usable"),
|
||||
);
|
||||
// Because reqwest::Response -> http::Response is complicated:
|
||||
match reqwest_response {
|
||||
Ok(mut reqwest_response) => {
|
||||
let status = reqwest_response.status();
|
||||
let mut http_response = http::Response::builder().status(status);
|
||||
let headers = http_response.headers_mut().unwrap();
|
||||
|
||||
let body = response.bytes().await.unwrap_or_else(|e| {
|
||||
for (k, v) in reqwest_response.headers_mut().drain() {
|
||||
if let Some(key) = k {
|
||||
headers.insert(key, v);
|
||||
}
|
||||
}
|
||||
|
||||
let status = reqwest_response.status();
|
||||
|
||||
let body = reqwest_response.bytes().await.unwrap_or_else(|e| {
|
||||
warn!("server error {}", e);
|
||||
Vec::new().into()
|
||||
}); // TODO: handle timeout
|
||||
@@ -138,7 +147,7 @@ where
|
||||
}
|
||||
|
||||
let response = T::IncomingResponse::try_from_http_response(
|
||||
http_response_builder
|
||||
http_response
|
||||
.body(body)
|
||||
.expect("reqwest body is valid http body"),
|
||||
);
|
||||
@@ -203,7 +212,7 @@ pub fn get_actions<'a>(
|
||||
.rooms
|
||||
.room_state_get(&pdu.room_id, &EventType::RoomPowerLevels, "")?
|
||||
.map(|ev| {
|
||||
serde_json::from_value(ev.content.clone())
|
||||
serde_json::from_value(ev.content)
|
||||
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
|
||||
})
|
||||
.transpose()?
|
||||
@@ -286,8 +295,7 @@ async fn send_notice(
|
||||
} else {
|
||||
notifi.sender = Some(&event.sender);
|
||||
notifi.event_type = Some(&event.kind);
|
||||
let content = serde_json::value::to_raw_value(&event.content).ok();
|
||||
notifi.content = content.as_deref();
|
||||
notifi.content = serde_json::value::to_raw_value(&event.content).ok();
|
||||
|
||||
if event.kind == EventType::RoomMember {
|
||||
notifi.user_is_target = event.state_key.as_deref() == Some(event.sender.as_str());
|
||||
|
||||
+283
-307
File diff suppressed because it is too large
Load Diff
+58
-114
@@ -1,29 +1,28 @@
|
||||
use crate::{database::abstraction::Tree, utils, Error, Result};
|
||||
use crate::{utils, Error, Result};
|
||||
use ruma::{
|
||||
events::{
|
||||
presence::{PresenceEvent, PresenceEventContent},
|
||||
AnyEphemeralRoomEvent, SyncEphemeralRoomEvent,
|
||||
AnyEvent as EduEvent, SyncEphemeralRoomEvent,
|
||||
},
|
||||
presence::PresenceState,
|
||||
serde::Raw,
|
||||
signatures::CanonicalJsonObject,
|
||||
RoomId, UInt, UserId,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
convert::{TryFrom, TryInto},
|
||||
mem,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RoomEdus {
|
||||
pub(in super::super) readreceiptid_readreceipt: Arc<dyn Tree>, // ReadReceiptId = RoomId + Count + UserId
|
||||
pub(in super::super) roomuserid_privateread: Arc<dyn Tree>, // RoomUserId = Room + User, PrivateRead = Count
|
||||
pub(in super::super) roomuserid_lastprivatereadupdate: Arc<dyn Tree>, // LastPrivateReadUpdate = Count
|
||||
pub(in super::super) typingid_userid: Arc<dyn Tree>, // TypingId = RoomId + TimeoutTime + Count
|
||||
pub(in super::super) roomid_lasttypingupdate: Arc<dyn Tree>, // LastRoomTypingUpdate = Count
|
||||
pub(in super::super) presenceid_presence: Arc<dyn Tree>, // PresenceId = RoomId + Count + UserId
|
||||
pub(in super::super) userid_lastpresenceupdate: Arc<dyn Tree>, // LastPresenceUpdate = Count
|
||||
pub(in super::super) readreceiptid_readreceipt: sled::Tree, // ReadReceiptId = RoomId + Count + UserId
|
||||
pub(in super::super) roomuserid_privateread: sled::Tree, // RoomUserId = Room + User, PrivateRead = Count
|
||||
pub(in super::super) roomuserid_lastprivatereadupdate: sled::Tree, // LastPrivateReadUpdate = Count
|
||||
pub(in super::super) typingid_userid: sled::Tree, // TypingId = RoomId + TimeoutTime + Count
|
||||
pub(in super::super) roomid_lasttypingupdate: sled::Tree, // LastRoomTypingUpdate = Count
|
||||
pub(in super::super) presenceid_presence: sled::Tree, // PresenceId = RoomId + Count + UserId
|
||||
pub(in super::super) userid_lastpresenceupdate: sled::Tree, // LastPresenceUpdate = Count
|
||||
}
|
||||
|
||||
impl RoomEdus {
|
||||
@@ -32,21 +31,21 @@ impl RoomEdus {
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
event: AnyEphemeralRoomEvent,
|
||||
event: EduEvent,
|
||||
globals: &super::super::globals::Globals,
|
||||
) -> Result<()> {
|
||||
let mut prefix = room_id.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
let mut last_possible_key = prefix.clone();
|
||||
last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes());
|
||||
|
||||
// Remove old entry
|
||||
if let Some((old, _)) = self
|
||||
if let Some(old) = self
|
||||
.readreceiptid_readreceipt
|
||||
.iter_from(&last_possible_key, true)
|
||||
.take_while(|(key, _)| key.starts_with(&prefix))
|
||||
.find(|(key, _)| {
|
||||
.scan_prefix(&prefix)
|
||||
.keys()
|
||||
.rev()
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(|key| key.starts_with(&prefix))
|
||||
.find(|key| {
|
||||
key.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.expect("rsplit always returns an element")
|
||||
@@ -54,7 +53,7 @@ impl RoomEdus {
|
||||
})
|
||||
{
|
||||
// This is the old room_latest
|
||||
self.readreceiptid_readreceipt.remove(&old)?;
|
||||
self.readreceiptid_readreceipt.remove(old)?;
|
||||
}
|
||||
|
||||
let mut room_latest_id = prefix;
|
||||
@@ -63,8 +62,8 @@ impl RoomEdus {
|
||||
room_latest_id.extend_from_slice(&user_id.as_bytes());
|
||||
|
||||
self.readreceiptid_readreceipt.insert(
|
||||
&room_latest_id,
|
||||
&serde_json::to_vec(&event).expect("EduEvent::to_string always works"),
|
||||
room_latest_id,
|
||||
&*serde_json::to_string(&event).expect("EduEvent::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -72,47 +71,27 @@ impl RoomEdus {
|
||||
|
||||
/// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn readreceipts_since<'a>(
|
||||
&'a self,
|
||||
pub fn readreceipts_since(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
since: u64,
|
||||
) -> impl Iterator<Item = Result<(UserId, u64, Raw<ruma::events::AnySyncEphemeralRoomEvent>)>> + 'a
|
||||
{
|
||||
) -> Result<impl Iterator<Item = Result<Raw<ruma::events::AnySyncEphemeralRoomEvent>>>> {
|
||||
let mut prefix = room_id.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
let prefix2 = prefix.clone();
|
||||
|
||||
let mut first_possible_edu = prefix.clone();
|
||||
first_possible_edu.extend_from_slice(&(since + 1).to_be_bytes()); // +1 so we don't send the event at since
|
||||
|
||||
self.readreceiptid_readreceipt
|
||||
.iter_from(&first_possible_edu, false)
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix2))
|
||||
.map(move |(k, v)| {
|
||||
let count =
|
||||
utils::u64_from_bytes(&k[prefix.len()..prefix.len() + mem::size_of::<u64>()])
|
||||
.map_err(|_| Error::bad_database("Invalid readreceiptid count in db."))?;
|
||||
let user_id = UserId::try_from(
|
||||
utils::string_from_bytes(&k[prefix.len() + mem::size_of::<u64>() + 1..])
|
||||
.map_err(|_| {
|
||||
Error::bad_database("Invalid readreceiptid userid bytes in db.")
|
||||
})?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("Invalid readreceiptid userid in db."))?;
|
||||
|
||||
let mut json = serde_json::from_slice::<CanonicalJsonObject>(&v).map_err(|_| {
|
||||
Error::bad_database("Read receipt in roomlatestid_roomlatest is invalid json.")
|
||||
})?;
|
||||
json.remove("room_id");
|
||||
|
||||
Ok((
|
||||
user_id,
|
||||
count,
|
||||
Raw::from_json(
|
||||
serde_json::value::to_raw_value(&json).expect("json is valid raw value"),
|
||||
),
|
||||
))
|
||||
})
|
||||
Ok(self
|
||||
.readreceiptid_readreceipt
|
||||
.range(&*first_possible_edu..)
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.map(|(_, v)| {
|
||||
Ok(serde_json::from_slice(&v).map_err(|_| {
|
||||
Error::bad_database("Read receipt in roomlatestid_roomlatest is invalid.")
|
||||
})?)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Sets a private read marker at `count`.
|
||||
@@ -143,9 +122,7 @@ impl RoomEdus {
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(&user_id.as_bytes());
|
||||
|
||||
self.roomuserid_privateread
|
||||
.get(&key)?
|
||||
.map_or(Ok(None), |v| {
|
||||
self.roomuserid_privateread.get(key)?.map_or(Ok(None), |v| {
|
||||
Ok(Some(utils::u64_from_bytes(&v).map_err(|_| {
|
||||
Error::bad_database("Invalid private read marker bytes")
|
||||
})?))
|
||||
@@ -214,10 +191,11 @@ impl RoomEdus {
|
||||
// Maybe there are multiple ones from calling roomtyping_add multiple times
|
||||
for outdated_edu in self
|
||||
.typingid_userid
|
||||
.scan_prefix(prefix)
|
||||
.filter(|(_, v)| &**v == user_id.as_bytes())
|
||||
.scan_prefix(&prefix)
|
||||
.filter_map(|r| r.ok())
|
||||
.filter(|(_, v)| v == user_id.as_bytes())
|
||||
{
|
||||
self.typingid_userid.remove(&outdated_edu.0)?;
|
||||
self.typingid_userid.remove(outdated_edu.0)?;
|
||||
found_outdated = true;
|
||||
}
|
||||
|
||||
@@ -245,8 +223,10 @@ impl RoomEdus {
|
||||
// Find all outdated edus before inserting a new one
|
||||
for outdated_edu in self
|
||||
.typingid_userid
|
||||
.scan_prefix(prefix)
|
||||
.map(|(key, _)| {
|
||||
.scan_prefix(&prefix)
|
||||
.keys()
|
||||
.map(|key| {
|
||||
let key = key?;
|
||||
Ok::<_, Error>((
|
||||
key.clone(),
|
||||
utils::u64_from_bytes(
|
||||
@@ -261,7 +241,7 @@ impl RoomEdus {
|
||||
.take_while(|&(_, timestamp)| timestamp < current_timestamp)
|
||||
{
|
||||
// This is an outdated edu (time > timestamp)
|
||||
self.typingid_userid.remove(&outdated_edu.0)?;
|
||||
self.typingid_userid.remove(outdated_edu.0)?;
|
||||
found_outdated = true;
|
||||
}
|
||||
|
||||
@@ -305,11 +285,14 @@ impl RoomEdus {
|
||||
for user_id in self
|
||||
.typingid_userid
|
||||
.scan_prefix(prefix)
|
||||
.map(|(_, user_id)| {
|
||||
UserId::try_from(utils::string_from_bytes(&user_id).map_err(|_| {
|
||||
.values()
|
||||
.map(|user_id| {
|
||||
Ok::<_, Error>(
|
||||
UserId::try_from(utils::string_from_bytes(&user_id?).map_err(|_| {
|
||||
Error::bad_database("User ID in typingid_userid is invalid unicode.")
|
||||
})?)
|
||||
.map_err(|_| Error::bad_database("User ID in typingid_userid is invalid."))
|
||||
.map_err(|_| Error::bad_database("User ID in typingid_userid is invalid."))?,
|
||||
)
|
||||
})
|
||||
{
|
||||
user_ids.insert(user_id?);
|
||||
@@ -344,12 +327,12 @@ impl RoomEdus {
|
||||
presence_id.extend_from_slice(&presence.sender.as_bytes());
|
||||
|
||||
self.presenceid_presence.insert(
|
||||
&presence_id,
|
||||
&serde_json::to_vec(&presence).expect("PresenceEvent can be serialized"),
|
||||
presence_id,
|
||||
&*serde_json::to_string(&presence).expect("PresenceEvent can be serialized"),
|
||||
)?;
|
||||
|
||||
self.userid_lastpresenceupdate.insert(
|
||||
user_id.as_bytes(),
|
||||
&user_id.as_bytes(),
|
||||
&utils::millis_since_unix_epoch().to_be_bytes(),
|
||||
)?;
|
||||
|
||||
@@ -379,47 +362,6 @@ impl RoomEdus {
|
||||
.transpose()
|
||||
}
|
||||
|
||||
pub fn get_last_presence_event(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
) -> Result<Option<PresenceEvent>> {
|
||||
let last_update = match self.last_presence_update(user_id)? {
|
||||
Some(last) => last,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let mut presence_id = room_id.as_bytes().to_vec();
|
||||
presence_id.push(0xff);
|
||||
presence_id.extend_from_slice(&last_update.to_be_bytes());
|
||||
presence_id.push(0xff);
|
||||
presence_id.extend_from_slice(&user_id.as_bytes());
|
||||
|
||||
self.presenceid_presence
|
||||
.get(&presence_id)?
|
||||
.map(|value| {
|
||||
let mut presence = serde_json::from_slice::<PresenceEvent>(&value)
|
||||
.map_err(|_| Error::bad_database("Invalid presence event in db."))?;
|
||||
let current_timestamp: UInt = utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("time is valid");
|
||||
|
||||
if presence.content.presence == PresenceState::Online {
|
||||
// Don't set last_active_ago when the user is online
|
||||
presence.content.last_active_ago = None;
|
||||
} else {
|
||||
// Convert from timestamp to duration
|
||||
presence.content.last_active_ago = presence
|
||||
.content
|
||||
.last_active_ago
|
||||
.map(|timestamp| current_timestamp - timestamp);
|
||||
}
|
||||
|
||||
Ok(presence)
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Sets all users to offline who have been quiet for too long.
|
||||
pub fn presence_maintain(
|
||||
&self,
|
||||
@@ -431,6 +373,7 @@ impl RoomEdus {
|
||||
for (user_id_bytes, last_timestamp) in self
|
||||
.userid_lastpresenceupdate
|
||||
.iter()
|
||||
.filter_map(|r| r.ok())
|
||||
.filter_map(|(k, bytes)| {
|
||||
Some((
|
||||
k,
|
||||
@@ -460,8 +403,8 @@ impl RoomEdus {
|
||||
presence_id.extend_from_slice(&user_id_bytes);
|
||||
|
||||
self.presenceid_presence.insert(
|
||||
&presence_id,
|
||||
&serde_json::to_vec(&PresenceEvent {
|
||||
presence_id,
|
||||
&*serde_json::to_string(&PresenceEvent {
|
||||
content: PresenceEventContent {
|
||||
avatar_url: None,
|
||||
currently_active: None,
|
||||
@@ -507,7 +450,8 @@ impl RoomEdus {
|
||||
|
||||
for (key, value) in self
|
||||
.presenceid_presence
|
||||
.iter_from(&*first_possible_edu, false)
|
||||
.range(&*first_possible_edu..)
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(|(key, _)| key.starts_with(&prefix))
|
||||
{
|
||||
let user_id = UserId::try_from(
|
||||
|
||||
+249
-344
@@ -1,9 +1,9 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
collections::HashMap,
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::Debug,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
time::{Duration, Instant, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -12,28 +12,15 @@ use crate::{
|
||||
use federation::transactions::send_transaction_message;
|
||||
use log::{error, warn};
|
||||
use ring::digest;
|
||||
use rocket::futures::{
|
||||
channel::mpsc,
|
||||
stream::{FuturesUnordered, StreamExt},
|
||||
};
|
||||
use rocket::futures::stream::{FuturesUnordered, StreamExt};
|
||||
use ruma::{
|
||||
api::{
|
||||
appservice,
|
||||
federation::{
|
||||
self,
|
||||
transactions::edu::{Edu, ReceiptContent, ReceiptData, ReceiptMap},
|
||||
},
|
||||
OutgoingRequest,
|
||||
},
|
||||
events::{push_rules, AnySyncEphemeralRoomEvent, EventType},
|
||||
push,
|
||||
receipt::ReceiptType,
|
||||
MilliSecondsSinceUnixEpoch, ServerName, UInt, UserId,
|
||||
api::{appservice, federation, OutgoingRequest},
|
||||
events::{push_rules, EventType},
|
||||
push, ServerName, UInt, UserId,
|
||||
};
|
||||
use sled::IVec;
|
||||
use tokio::{select, sync::Semaphore};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum OutgoingKind {
|
||||
Appservice(Box<ServerName>),
|
||||
@@ -41,9 +28,62 @@ pub enum OutgoingKind {
|
||||
Normal(Box<ServerName>),
|
||||
}
|
||||
|
||||
impl OutgoingKind {
|
||||
pub fn get_prefix(&self) -> Vec<u8> {
|
||||
let mut prefix = match self {
|
||||
#[derive(Clone)]
|
||||
pub struct Sending {
|
||||
/// The state for a given state hash.
|
||||
pub(super) servernamepduids: sled::Tree, // ServernamePduId = (+ / $)SenderKey / ServerName / UserId + PduId
|
||||
pub(super) servercurrentpdus: sled::Tree, // ServerCurrentPdus = (+ / $)ServerName / UserId + PduId
|
||||
pub(super) maximum_requests: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
enum TransactionStatus {
|
||||
Running,
|
||||
Failed(u32, Instant), // number of times failed, time of last failure
|
||||
Retrying(u32), // number of times failed
|
||||
}
|
||||
|
||||
impl Sending {
|
||||
pub fn start_handler(&self, db: &Database) {
|
||||
let servernamepduids = self.servernamepduids.clone();
|
||||
let servercurrentpdus = self.servercurrentpdus.clone();
|
||||
|
||||
let db = db.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut futures = FuturesUnordered::new();
|
||||
|
||||
// Retry requests we could not finish yet
|
||||
let mut subscriber = servernamepduids.watch_prefix(b"");
|
||||
let mut current_transaction_status = HashMap::<Vec<u8>, TransactionStatus>::new();
|
||||
|
||||
let mut initial_transactions = HashMap::<OutgoingKind, Vec<Vec<u8>>>::new();
|
||||
for (key, outgoing_kind, pdu) in servercurrentpdus
|
||||
.iter()
|
||||
.filter_map(|r| r.ok())
|
||||
.filter_map(|(key, _)| {
|
||||
Self::parse_servercurrentpdus(&key)
|
||||
.ok()
|
||||
.map(|(k, p)| (key, k, p.to_vec()))
|
||||
})
|
||||
{
|
||||
let entry = initial_transactions
|
||||
.entry(outgoing_kind.clone())
|
||||
.or_insert_with(Vec::new);
|
||||
|
||||
if entry.len() > 30 {
|
||||
warn!(
|
||||
"Dropping some current pdu: {:?} {:?} {:?}",
|
||||
key, outgoing_kind, pdu
|
||||
);
|
||||
servercurrentpdus.remove(key).unwrap();
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.push(pdu);
|
||||
}
|
||||
|
||||
for (outgoing_kind, pdus) in initial_transactions {
|
||||
let mut prefix = match &outgoing_kind {
|
||||
OutgoingKind::Appservice(server) => {
|
||||
let mut p = b"+".to_vec();
|
||||
p.extend_from_slice(server.as_bytes());
|
||||
@@ -63,71 +103,8 @@ impl OutgoingKind {
|
||||
}
|
||||
};
|
||||
prefix.push(0xff);
|
||||
|
||||
prefix
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum SendingEventType {
|
||||
Pdu(Vec<u8>),
|
||||
Edu(Vec<u8>),
|
||||
}
|
||||
|
||||
pub struct Sending {
|
||||
/// The state for a given state hash.
|
||||
pub(super) servername_educount: Arc<dyn Tree>, // EduCount: Count of last EDU sync
|
||||
pub(super) servernamepduids: Arc<dyn Tree>, // ServernamePduId = (+ / $)SenderKey / ServerName / UserId + PduId
|
||||
pub(super) servercurrentevents: Arc<dyn Tree>, // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / (*)EduEvent
|
||||
pub(super) maximum_requests: Arc<Semaphore>,
|
||||
pub sender: mpsc::UnboundedSender<Vec<u8>>,
|
||||
}
|
||||
|
||||
enum TransactionStatus {
|
||||
Running,
|
||||
Failed(u32, Instant), // number of times failed, time of last failure
|
||||
Retrying(u32), // number of times failed
|
||||
}
|
||||
|
||||
impl Sending {
|
||||
pub fn start_handler(&self, db: Arc<Database>, mut receiver: mpsc::UnboundedReceiver<Vec<u8>>) {
|
||||
tokio::spawn(async move {
|
||||
let mut futures = FuturesUnordered::new();
|
||||
|
||||
let mut current_transaction_status = HashMap::<Vec<u8>, TransactionStatus>::new();
|
||||
|
||||
// Retry requests we could not finish yet
|
||||
let mut initial_transactions = HashMap::<OutgoingKind, Vec<SendingEventType>>::new();
|
||||
for (key, outgoing_kind, event) in
|
||||
db.sending
|
||||
.servercurrentevents
|
||||
.iter()
|
||||
.filter_map(|(key, _)| {
|
||||
Self::parse_servercurrentevent(&key)
|
||||
.ok()
|
||||
.map(|(k, e)| (key, k, e))
|
||||
})
|
||||
{
|
||||
let entry = initial_transactions
|
||||
.entry(outgoing_kind.clone())
|
||||
.or_insert_with(Vec::new);
|
||||
|
||||
if entry.len() > 30 {
|
||||
warn!(
|
||||
"Dropping some current events: {:?} {:?} {:?}",
|
||||
key, outgoing_kind, event
|
||||
);
|
||||
db.sending.servercurrentevents.remove(&key).unwrap();
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.push(event);
|
||||
}
|
||||
|
||||
for (outgoing_kind, events) in initial_transactions {
|
||||
current_transaction_status
|
||||
.insert(outgoing_kind.get_prefix(), TransactionStatus::Running);
|
||||
futures.push(Self::handle_events(outgoing_kind.clone(), events, &db));
|
||||
current_transaction_status.insert(prefix, TransactionStatus::Running);
|
||||
futures.push(Self::handle_event(outgoing_kind.clone(), pdus, &db));
|
||||
}
|
||||
|
||||
loop {
|
||||
@@ -135,42 +112,58 @@ impl Sending {
|
||||
Some(response) = futures.next() => {
|
||||
match response {
|
||||
Ok(outgoing_kind) => {
|
||||
let prefix = outgoing_kind.get_prefix();
|
||||
for (key, _) in db.sending.servercurrentevents
|
||||
.scan_prefix(prefix.clone())
|
||||
let mut prefix = match &outgoing_kind {
|
||||
OutgoingKind::Appservice(server) => {
|
||||
let mut p = b"+".to_vec();
|
||||
p.extend_from_slice(server.as_bytes());
|
||||
p
|
||||
}
|
||||
OutgoingKind::Push(user, pushkey) => {
|
||||
let mut p = b"$".to_vec();
|
||||
p.extend_from_slice(&user);
|
||||
p.push(0xff);
|
||||
p.extend_from_slice(&pushkey);
|
||||
p
|
||||
},
|
||||
OutgoingKind::Normal(server) => {
|
||||
let mut p = vec![];
|
||||
p.extend_from_slice(server.as_bytes());
|
||||
p
|
||||
},
|
||||
};
|
||||
prefix.push(0xff);
|
||||
|
||||
for key in servercurrentpdus
|
||||
.scan_prefix(&prefix)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
{
|
||||
db.sending.servercurrentevents.remove(&key).unwrap();
|
||||
servercurrentpdus.remove(key).unwrap();
|
||||
}
|
||||
|
||||
// Find events that have been added since starting the last request
|
||||
let new_events = db.sending.servernamepduids
|
||||
.scan_prefix(prefix.clone())
|
||||
.map(|(k, _)| {
|
||||
SendingEventType::Pdu(k[prefix.len()..].to_vec())
|
||||
let new_pdus = servernamepduids
|
||||
.scan_prefix(&prefix)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
.map(|k| {
|
||||
k[prefix.len()..].to_vec()
|
||||
})
|
||||
.take(30)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// TODO: find edus
|
||||
|
||||
if !new_events.is_empty() {
|
||||
// Insert pdus we found
|
||||
for event in &new_events {
|
||||
if !new_pdus.is_empty() {
|
||||
for pdu_id in &new_pdus {
|
||||
let mut current_key = prefix.clone();
|
||||
match event {
|
||||
SendingEventType::Pdu(b) |
|
||||
SendingEventType::Edu(b) => {
|
||||
current_key.extend_from_slice(&b);
|
||||
db.sending.servercurrentevents.insert(¤t_key, &[]).unwrap();
|
||||
db.sending.servernamepduids.remove(¤t_key).unwrap();
|
||||
}
|
||||
}
|
||||
current_key.extend_from_slice(pdu_id);
|
||||
servercurrentpdus.insert(¤t_key, &[]).unwrap();
|
||||
servernamepduids.remove(¤t_key).unwrap();
|
||||
}
|
||||
|
||||
futures.push(
|
||||
Self::handle_events(
|
||||
Self::handle_event(
|
||||
outgoing_kind.clone(),
|
||||
new_events,
|
||||
new_pdus,
|
||||
&db,
|
||||
)
|
||||
);
|
||||
@@ -179,7 +172,29 @@ impl Sending {
|
||||
}
|
||||
}
|
||||
Err((outgoing_kind, _)) => {
|
||||
current_transaction_status.entry(outgoing_kind.get_prefix()).and_modify(|e| *e = match e {
|
||||
let mut prefix = match &outgoing_kind {
|
||||
OutgoingKind::Appservice(serv) => {
|
||||
let mut p = b"+".to_vec();
|
||||
p.extend_from_slice(serv.as_bytes());
|
||||
p
|
||||
},
|
||||
OutgoingKind::Push(user, pushkey) => {
|
||||
let mut p = b"$".to_vec();
|
||||
p.extend_from_slice(&user);
|
||||
p.push(0xff);
|
||||
p.extend_from_slice(&pushkey);
|
||||
p
|
||||
},
|
||||
OutgoingKind::Normal(serv) => {
|
||||
let mut p = vec![];
|
||||
p.extend_from_slice(serv.as_bytes());
|
||||
p
|
||||
},
|
||||
};
|
||||
|
||||
prefix.push(0xff);
|
||||
|
||||
current_transaction_status.entry(prefix).and_modify(|e| *e = match e {
|
||||
TransactionStatus::Running => TransactionStatus::Failed(1, Instant::now()),
|
||||
TransactionStatus::Retrying(n) => TransactionStatus::Failed(*n+1, Instant::now()),
|
||||
TransactionStatus::Failed(_, _) => {
|
||||
@@ -190,40 +205,52 @@ impl Sending {
|
||||
}
|
||||
};
|
||||
},
|
||||
Some(key) = receiver.next() => {
|
||||
if let Ok((outgoing_kind, event)) = Self::parse_servercurrentevent(&key) {
|
||||
if let Ok(Some(events)) = Self::select_events(
|
||||
&outgoing_kind,
|
||||
vec![(event, key)],
|
||||
&mut current_transaction_status,
|
||||
&db
|
||||
) {
|
||||
futures.push(Self::handle_events(outgoing_kind, events, &db));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Some(event) = &mut subscriber => {
|
||||
if let sled::Event::Insert { key, .. } = event {
|
||||
// New sled version:
|
||||
//for (_tree, key, value_opt) in &event {
|
||||
// if value_opt.is_none() {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
let servernamepduid = key.clone();
|
||||
|
||||
fn select_events(
|
||||
outgoing_kind: &OutgoingKind,
|
||||
new_events: Vec<(SendingEventType, Vec<u8>)>, // Events we want to send: event and full key
|
||||
current_transaction_status: &mut HashMap<Vec<u8>, TransactionStatus>,
|
||||
db: &Database,
|
||||
) -> Result<Option<Vec<SendingEventType>>> {
|
||||
let mut retry = false;
|
||||
|
||||
if let Some((outgoing_kind, prefix, pdu_id)) = Self::parse_servercurrentpdus(&servernamepduid)
|
||||
.ok()
|
||||
.map(|(outgoing_kind, pdu_id)| {
|
||||
let mut prefix = match &outgoing_kind {
|
||||
OutgoingKind::Appservice(serv) => {
|
||||
let mut p = b"+".to_vec();
|
||||
p.extend_from_slice(serv.as_bytes());
|
||||
p
|
||||
},
|
||||
OutgoingKind::Push(user, pushkey) => {
|
||||
let mut p = b"$".to_vec();
|
||||
p.extend_from_slice(&user);
|
||||
p.push(0xff);
|
||||
p.extend_from_slice(&pushkey);
|
||||
p
|
||||
},
|
||||
OutgoingKind::Normal(serv) => {
|
||||
let mut p = vec![];
|
||||
p.extend_from_slice(serv.as_bytes());
|
||||
p
|
||||
},
|
||||
};
|
||||
prefix.push(0xff);
|
||||
|
||||
(outgoing_kind, prefix, pdu_id)
|
||||
})
|
||||
.filter(|(_, prefix, _)| {
|
||||
let entry = current_transaction_status.entry(prefix.clone());
|
||||
let mut allow = true;
|
||||
|
||||
let prefix = outgoing_kind.get_prefix();
|
||||
let entry = current_transaction_status.entry(prefix.clone());
|
||||
|
||||
entry
|
||||
.and_modify(|e| match e {
|
||||
entry.and_modify(|e| match e {
|
||||
TransactionStatus::Running | TransactionStatus::Retrying(_) => {
|
||||
allow = false; // already running
|
||||
}
|
||||
},
|
||||
TransactionStatus::Failed(tries, time) => {
|
||||
// Fail if a request has failed recently (exponential backoff)
|
||||
let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries);
|
||||
@@ -238,132 +265,53 @@ impl Sending {
|
||||
*e = TransactionStatus::Retrying(*tries);
|
||||
}
|
||||
}
|
||||
}).or_insert(TransactionStatus::Running);
|
||||
|
||||
allow
|
||||
})
|
||||
.or_insert(TransactionStatus::Running);
|
||||
|
||||
if !allow {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut events = Vec::new();
|
||||
{
|
||||
let mut pdus = Vec::new();
|
||||
|
||||
if retry {
|
||||
// We retry the previous transaction
|
||||
for (key, _) in db.sending.servercurrentevents.scan_prefix(prefix) {
|
||||
if let Ok((_, e)) = Self::parse_servercurrentevent(&key) {
|
||||
events.push(e);
|
||||
}
|
||||
for pdu in servercurrentpdus
|
||||
.scan_prefix(&prefix)
|
||||
.filter_map(|r| r.ok())
|
||||
.filter_map(|(key, _)| {
|
||||
Self::parse_servercurrentpdus(&key)
|
||||
.ok()
|
||||
.map(|(_, p)| p.to_vec())
|
||||
})
|
||||
{
|
||||
pdus.push(pdu);
|
||||
}
|
||||
} else {
|
||||
for (e, full_key) in new_events {
|
||||
db.sending.servercurrentevents.insert(&full_key, &[])?;
|
||||
|
||||
// If it was a PDU we have to unqueue it
|
||||
// TODO: don't try to unqueue EDUs
|
||||
db.sending.servernamepduids.remove(&full_key)?;
|
||||
|
||||
events.push(e);
|
||||
servercurrentpdus.insert(&key, &[]).unwrap();
|
||||
servernamepduids.remove(&key).unwrap();
|
||||
pdus.push(pdu_id.to_vec());
|
||||
}
|
||||
|
||||
if let OutgoingKind::Normal(server_name) = outgoing_kind {
|
||||
if let Ok((select_edus, last_count)) = Self::select_edus(db, server_name) {
|
||||
events.extend_from_slice(&select_edus);
|
||||
db.sending
|
||||
.servername_educount
|
||||
.insert(server_name.as_bytes(), &last_count.to_be_bytes())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(events))
|
||||
}
|
||||
|
||||
pub fn select_edus(db: &Database, server: &ServerName) -> Result<(Vec<SendingEventType>, u64)> {
|
||||
// u64: count of last edu
|
||||
let since = db
|
||||
.sending
|
||||
.servername_educount
|
||||
.get(server.as_bytes())?
|
||||
.map_or(Ok(0), |bytes| {
|
||||
utils::u64_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Invalid u64 in servername_educount."))
|
||||
})?;
|
||||
let mut events = Vec::new();
|
||||
let mut max_edu_count = since;
|
||||
'outer: for room_id in db.rooms.server_rooms(server) {
|
||||
let room_id = room_id?;
|
||||
for r in db.rooms.edus.readreceipts_since(&room_id, since) {
|
||||
let (user_id, count, read_receipt) = r?;
|
||||
|
||||
if count > max_edu_count {
|
||||
max_edu_count = count;
|
||||
}
|
||||
|
||||
if user_id.server_name() != db.globals.server_name() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let event =
|
||||
serde_json::from_str::<AnySyncEphemeralRoomEvent>(&read_receipt.json().get())
|
||||
.map_err(|_| Error::bad_database("Invalid edu event in read_receipts."))?;
|
||||
let federation_event = match event {
|
||||
AnySyncEphemeralRoomEvent::Receipt(r) => {
|
||||
let mut read = BTreeMap::new();
|
||||
|
||||
let (event_id, mut receipt) = r
|
||||
.content
|
||||
.0
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("we only use one event per read receipt");
|
||||
let receipt = receipt
|
||||
.remove(&ReceiptType::Read)
|
||||
.expect("our read receipts always set this")
|
||||
.remove(&user_id)
|
||||
.expect("our read receipts always have the user here");
|
||||
|
||||
read.insert(
|
||||
user_id,
|
||||
ReceiptData {
|
||||
data: receipt.clone(),
|
||||
event_ids: vec![event_id.clone()],
|
||||
},
|
||||
futures.push(
|
||||
Self::handle_event(
|
||||
outgoing_kind,
|
||||
pdus,
|
||||
&db,
|
||||
)
|
||||
);
|
||||
|
||||
let receipt_map = ReceiptMap { read };
|
||||
|
||||
let mut receipts = BTreeMap::new();
|
||||
receipts.insert(room_id.clone(), receipt_map);
|
||||
|
||||
Edu::Receipt(ReceiptContent { receipts })
|
||||
}
|
||||
_ => {
|
||||
Error::bad_database("Invalid event type in read_receipts");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
events.push(SendingEventType::Edu(
|
||||
serde_json::to_vec(&federation_event).expect("json can be serialized"),
|
||||
));
|
||||
|
||||
if events.len() >= 20 {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((events, max_edu_count))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn send_push_pdu(&self, pdu_id: &[u8], senderkey: Box<[u8]>) -> Result<()> {
|
||||
pub fn send_push_pdu(&self, pdu_id: &[u8], senderkey: IVec) -> Result<()> {
|
||||
let mut key = b"$".to_vec();
|
||||
key.extend_from_slice(&senderkey);
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(pdu_id);
|
||||
self.servernamepduids.insert(&key, b"")?;
|
||||
self.sender.unbounded_send(key).unwrap();
|
||||
self.servernamepduids.insert(key, b"")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -373,8 +321,7 @@ impl Sending {
|
||||
let mut key = server.as_bytes().to_vec();
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(pdu_id);
|
||||
self.servernamepduids.insert(&key, b"")?;
|
||||
self.sender.unbounded_send(key).unwrap();
|
||||
self.servernamepduids.insert(key, b"")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -385,14 +332,13 @@ impl Sending {
|
||||
key.extend_from_slice(appservice_id.as_bytes());
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(pdu_id);
|
||||
self.servernamepduids.insert(&key, b"")?;
|
||||
self.sender.unbounded_send(key).unwrap();
|
||||
self.servernamepduids.insert(key, b"")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
fn calculate_hash(keys: &[&[u8]]) -> Vec<u8> {
|
||||
fn calculate_hash(keys: &[Vec<u8>]) -> Vec<u8> {
|
||||
// We only hash the pdu's event ids, not the whole pdu
|
||||
let bytes = keys.join(&0xff);
|
||||
let hash = digest::digest(&digest::SHA256, &bytes);
|
||||
@@ -400,37 +346,33 @@ impl Sending {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(db))]
|
||||
async fn handle_events(
|
||||
async fn handle_event(
|
||||
kind: OutgoingKind,
|
||||
events: Vec<SendingEventType>,
|
||||
pdu_ids: Vec<Vec<u8>>,
|
||||
db: &Database,
|
||||
) -> std::result::Result<OutgoingKind, (OutgoingKind, Error)> {
|
||||
match &kind {
|
||||
OutgoingKind::Appservice(server) => {
|
||||
let mut pdu_jsons = Vec::new();
|
||||
|
||||
for event in &events {
|
||||
match event {
|
||||
SendingEventType::Pdu(pdu_id) => {
|
||||
pdu_jsons.push(db.rooms
|
||||
.get_pdu_from_id(&pdu_id)
|
||||
.map_err(|e| (kind.clone(), e))?
|
||||
let pdu_jsons = pdu_ids
|
||||
.iter()
|
||||
.map(|pdu_id| {
|
||||
Ok::<_, (Box<ServerName>, Error)>(
|
||||
db.rooms
|
||||
.get_pdu_from_id(pdu_id)
|
||||
.map_err(|e| (server.clone(), e))?
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
kind.clone(),
|
||||
server.clone(),
|
||||
Error::bad_database(
|
||||
"[Appservice] Event in servernamepduids not found in db.",
|
||||
),
|
||||
)
|
||||
})?
|
||||
.to_room_event())
|
||||
}
|
||||
SendingEventType::Edu(_) => {
|
||||
// Appservices don't need EDUs (?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.to_any_event(),
|
||||
)
|
||||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.collect::<Vec<_>>();
|
||||
let permit = db.sending.maximum_requests.acquire().await;
|
||||
|
||||
let response = appservice_server::send_request(
|
||||
@@ -442,14 +384,7 @@ impl Sending {
|
||||
appservice::event::push_events::v1::Request {
|
||||
events: &pdu_jsons,
|
||||
txn_id: &base64::encode_config(
|
||||
Self::calculate_hash(
|
||||
&events
|
||||
.iter()
|
||||
.map(|e| match e {
|
||||
SendingEventType::Edu(b) | SendingEventType::Pdu(b) => &**b,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
Self::calculate_hash(&pdu_ids),
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
),
|
||||
},
|
||||
@@ -463,30 +398,25 @@ impl Sending {
|
||||
response
|
||||
}
|
||||
OutgoingKind::Push(user, pushkey) => {
|
||||
let mut pdus = Vec::new();
|
||||
|
||||
for event in &events {
|
||||
match event {
|
||||
SendingEventType::Pdu(pdu_id) => {
|
||||
pdus.push(
|
||||
let pdus = pdu_ids
|
||||
.iter()
|
||||
.map(|pdu_id| {
|
||||
Ok::<_, (Vec<u8>, Error)>(
|
||||
db.rooms
|
||||
.get_pdu_from_id(&pdu_id)
|
||||
.map_err(|e| (kind.clone(), e))?
|
||||
.get_pdu_from_id(pdu_id)
|
||||
.map_err(|e| (pushkey.clone(), e))?
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
kind.clone(),
|
||||
pushkey.clone(),
|
||||
Error::bad_database(
|
||||
"[Push] Event in servernamepduids not found in db.",
|
||||
),
|
||||
)
|
||||
})?,
|
||||
);
|
||||
}
|
||||
SendingEventType::Edu(_) => {
|
||||
// Push gateways don't need EDUs (?)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for pdu in pdus {
|
||||
// Redacted events are not notification targets (we don't send push for them)
|
||||
@@ -497,13 +427,13 @@ impl Sending {
|
||||
let userid =
|
||||
UserId::try_from(utils::string_from_bytes(user).map_err(|_| {
|
||||
(
|
||||
kind.clone(),
|
||||
OutgoingKind::Push(user.clone(), pushkey.clone()),
|
||||
Error::bad_database("Invalid push user string in db."),
|
||||
)
|
||||
})?)
|
||||
.map_err(|_| {
|
||||
(
|
||||
kind.clone(),
|
||||
OutgoingKind::Push(user.clone(), pushkey.clone()),
|
||||
Error::bad_database("Invalid push user id in db."),
|
||||
)
|
||||
})?;
|
||||
@@ -554,17 +484,15 @@ impl Sending {
|
||||
Ok(OutgoingKind::Push(user.clone(), pushkey.clone()))
|
||||
}
|
||||
OutgoingKind::Normal(server) => {
|
||||
let mut edu_jsons = Vec::new();
|
||||
let mut pdu_jsons = Vec::new();
|
||||
|
||||
for event in &events {
|
||||
match event {
|
||||
SendingEventType::Pdu(pdu_id) => {
|
||||
let pdu_jsons = pdu_ids
|
||||
.iter()
|
||||
.map(|pdu_id| {
|
||||
Ok::<_, (OutgoingKind, Error)>(
|
||||
// TODO: check room version and remove event_id if needed
|
||||
pdu_jsons.push(serde_json::from_str(
|
||||
serde_json::from_str(
|
||||
PduEvent::convert_to_outgoing_federation_event(
|
||||
db.rooms
|
||||
.get_pdu_json_from_id(&pdu_id)
|
||||
.get_pdu_json_from_id(pdu_id)
|
||||
.map_err(|e| (OutgoingKind::Normal(server.clone()), e))?
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
@@ -578,15 +506,11 @@ impl Sending {
|
||||
.json()
|
||||
.get(),
|
||||
)
|
||||
.expect("Raw<..> is always valid"));
|
||||
}
|
||||
SendingEventType::Edu(edu) => {
|
||||
edu_jsons.push(
|
||||
serde_json::from_slice(edu).expect("Raw<..> is always valid"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
.expect("Raw<..> is always valid"),
|
||||
)
|
||||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let permit = db.sending.maximum_requests.acquire().await;
|
||||
|
||||
@@ -596,17 +520,10 @@ impl Sending {
|
||||
send_transaction_message::v1::Request {
|
||||
origin: db.globals.server_name(),
|
||||
pdus: &pdu_jsons,
|
||||
edus: &edu_jsons,
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
||||
edus: &[],
|
||||
origin_server_ts: SystemTime::now(),
|
||||
transaction_id: &base64::encode_config(
|
||||
Self::calculate_hash(
|
||||
&events
|
||||
.iter()
|
||||
.map(|e| match e {
|
||||
SendingEventType::Edu(b) | SendingEventType::Pdu(b) => &**b,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
Self::calculate_hash(&pdu_ids),
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
),
|
||||
},
|
||||
@@ -629,13 +546,13 @@ impl Sending {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_servercurrentevent(key: &[u8]) -> Result<(OutgoingKind, SendingEventType)> {
|
||||
fn parse_servercurrentpdus(key: &IVec) -> Result<(OutgoingKind, IVec)> {
|
||||
// Appservices start with a plus
|
||||
Ok::<_, Error>(if key.starts_with(b"+") {
|
||||
let mut parts = key[1..].splitn(2, |&b| b == 0xff);
|
||||
|
||||
let server = parts.next().expect("splitn always returns one element");
|
||||
let event = parts
|
||||
let pdu = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||
let server = utils::string_from_bytes(&server).map_err(|_| {
|
||||
@@ -646,11 +563,7 @@ impl Sending {
|
||||
OutgoingKind::Appservice(Box::<ServerName>::try_from(server).map_err(|_| {
|
||||
Error::bad_database("Invalid server string in server_currenttransaction")
|
||||
})?),
|
||||
if event.starts_with(b"*") {
|
||||
SendingEventType::Edu(event[1..].to_vec())
|
||||
} else {
|
||||
SendingEventType::Pdu(event.to_vec())
|
||||
},
|
||||
IVec::from(pdu),
|
||||
)
|
||||
} else if key.starts_with(b"$") {
|
||||
let mut parts = key[1..].splitn(3, |&b| b == 0xff);
|
||||
@@ -659,22 +572,18 @@ impl Sending {
|
||||
let pushkey = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||
let event = parts
|
||||
let pdu = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||
(
|
||||
OutgoingKind::Push(user.to_vec(), pushkey.to_vec()),
|
||||
if event.starts_with(b"*") {
|
||||
SendingEventType::Edu(event[1..].to_vec())
|
||||
} else {
|
||||
SendingEventType::Pdu(event.to_vec())
|
||||
},
|
||||
IVec::from(pdu),
|
||||
)
|
||||
} else {
|
||||
let mut parts = key.splitn(2, |&b| b == 0xff);
|
||||
|
||||
let server = parts.next().expect("splitn always returns one element");
|
||||
let event = parts
|
||||
let pdu = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||
let server = utils::string_from_bytes(&server).map_err(|_| {
|
||||
@@ -685,11 +594,7 @@ impl Sending {
|
||||
OutgoingKind::Normal(Box::<ServerName>::try_from(server).map_err(|_| {
|
||||
Error::bad_database("Invalid server string in server_currenttransaction")
|
||||
})?),
|
||||
if event.starts_with(b"*") {
|
||||
SendingEventType::Edu(event[1..].to_vec())
|
||||
} else {
|
||||
SendingEventType::Pdu(event.to_vec())
|
||||
},
|
||||
IVec::from(pdu),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Result;
|
||||
use ruma::{DeviceId, UserId};
|
||||
use sled::IVec;
|
||||
|
||||
use super::abstraction::Tree;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TransactionIds {
|
||||
pub(super) userdevicetxnid_response: Arc<dyn Tree>, // Response can be empty (/sendToDevice) or the event id (/send)
|
||||
pub(super) userdevicetxnid_response: sled::Tree, // Response can be empty (/sendToDevice) or the event id (/send)
|
||||
}
|
||||
|
||||
impl TransactionIds {
|
||||
@@ -23,7 +21,7 @@ impl TransactionIds {
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(txn_id.as_bytes());
|
||||
|
||||
self.userdevicetxnid_response.insert(&key, data)?;
|
||||
self.userdevicetxnid_response.insert(key, data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -33,7 +31,7 @@ impl TransactionIds {
|
||||
user_id: &UserId,
|
||||
device_id: Option<&DeviceId>,
|
||||
txn_id: &str,
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
) -> Result<Option<IVec>> {
|
||||
let mut key = user_id.as_bytes().to_vec();
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default());
|
||||
@@ -41,6 +39,6 @@ impl TransactionIds {
|
||||
key.extend_from_slice(txn_id.as_bytes());
|
||||
|
||||
// If there's no entry, this is a new transaction
|
||||
self.userdevicetxnid_response.get(&key)
|
||||
Ok(self.userdevicetxnid_response.get(key)?)
|
||||
}
|
||||
}
|
||||
|
||||
+33
-103
@@ -1,20 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{client_server::SESSION_ID_LENGTH, utils, Error, Result};
|
||||
use crate::{Error, Result};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
r0::uiaa::{IncomingAuthData, UiaaInfo},
|
||||
},
|
||||
signatures::CanonicalJsonValue,
|
||||
DeviceId, UserId,
|
||||
};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Uiaa {
|
||||
pub(super) userdevicesessionid_uiaainfo: Arc<dyn Tree>, // User-interactive authentication
|
||||
pub(super) userdevicesessionid_uiaarequest: Arc<dyn Tree>, // UiaaRequest = canonical json value
|
||||
pub(super) userdeviceid_uiaainfo: sled::Tree, // User-interactive authentication
|
||||
}
|
||||
|
||||
impl Uiaa {
|
||||
@@ -24,20 +19,8 @@ impl Uiaa {
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
uiaainfo: &UiaaInfo,
|
||||
json_body: &CanonicalJsonValue,
|
||||
) -> Result<()> {
|
||||
self.set_uiaa_request(
|
||||
user_id,
|
||||
device_id,
|
||||
uiaainfo.session.as_ref().expect("session should be set"), // TODO: better session error handling (why is it optional in ruma?)
|
||||
json_body,
|
||||
)?;
|
||||
self.update_uiaa_session(
|
||||
user_id,
|
||||
device_id,
|
||||
uiaainfo.session.as_ref().expect("session should be set"),
|
||||
Some(uiaainfo),
|
||||
)
|
||||
self.update_uiaa_session(user_id, device_id, Some(uiaainfo))
|
||||
}
|
||||
|
||||
pub fn try_auth(
|
||||
@@ -57,13 +40,11 @@ impl Uiaa {
|
||||
{
|
||||
let mut uiaainfo = session
|
||||
.as_ref()
|
||||
.map(|session| self.get_uiaa_session(&user_id, &device_id, session))
|
||||
.map(|session| {
|
||||
Ok::<_, Error>(self.get_uiaa_session(&user_id, &device_id, session)?)
|
||||
})
|
||||
.unwrap_or_else(|| Ok(uiaainfo.clone()))?;
|
||||
|
||||
if uiaainfo.session.is_none() {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
}
|
||||
|
||||
// Find out what the user completed
|
||||
match &**kind {
|
||||
"m.login.password" => {
|
||||
@@ -149,96 +130,35 @@ impl Uiaa {
|
||||
}
|
||||
|
||||
if !completed {
|
||||
self.update_uiaa_session(
|
||||
user_id,
|
||||
device_id,
|
||||
uiaainfo.session.as_ref().expect("session is always set"),
|
||||
Some(&uiaainfo),
|
||||
)?;
|
||||
self.update_uiaa_session(user_id, device_id, Some(&uiaainfo))?;
|
||||
return Ok((false, uiaainfo));
|
||||
}
|
||||
|
||||
// UIAA was successful! Remove this session and return true
|
||||
self.update_uiaa_session(
|
||||
user_id,
|
||||
device_id,
|
||||
uiaainfo.session.as_ref().expect("session is always set"),
|
||||
None,
|
||||
)?;
|
||||
self.update_uiaa_session(user_id, device_id, None)?;
|
||||
Ok((true, uiaainfo))
|
||||
} else {
|
||||
panic!("FallbackAcknowledgement is not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
fn set_uiaa_request(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
session: &str,
|
||||
request: &CanonicalJsonValue,
|
||||
) -> Result<()> {
|
||||
let mut userdevicesessionid = user_id.as_bytes().to_vec();
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(device_id.as_bytes());
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(session.as_bytes());
|
||||
|
||||
self.userdevicesessionid_uiaarequest.insert(
|
||||
&userdevicesessionid,
|
||||
&serde_json::to_vec(request).expect("json value to vec always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_uiaa_request(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
session: &str,
|
||||
) -> Result<Option<CanonicalJsonValue>> {
|
||||
let mut userdevicesessionid = user_id.as_bytes().to_vec();
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(device_id.as_bytes());
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(session.as_bytes());
|
||||
|
||||
self.userdevicesessionid_uiaarequest
|
||||
.get(&userdevicesessionid)?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
Ok::<_, Error>(Some(
|
||||
serde_json::from_str::<CanonicalJsonValue>(
|
||||
&utils::string_from_bytes(&bytes).map_err(|_| {
|
||||
Error::bad_database("Invalid uiaa request bytes in db.")
|
||||
})?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("Invalid uiaa request in db."))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn update_uiaa_session(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
session: &str,
|
||||
uiaainfo: Option<&UiaaInfo>,
|
||||
) -> Result<()> {
|
||||
let mut userdevicesessionid = user_id.as_bytes().to_vec();
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(device_id.as_bytes());
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(session.as_bytes());
|
||||
let mut userdeviceid = user_id.as_bytes().to_vec();
|
||||
userdeviceid.push(0xff);
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
if let Some(uiaainfo) = uiaainfo {
|
||||
self.userdevicesessionid_uiaainfo.insert(
|
||||
&userdevicesessionid,
|
||||
&serde_json::to_vec(&uiaainfo).expect("UiaaInfo::to_vec always works"),
|
||||
self.userdeviceid_uiaainfo.insert(
|
||||
&userdeviceid,
|
||||
&*serde_json::to_string(&uiaainfo).expect("UiaaInfo::to_string always works"),
|
||||
)?;
|
||||
} else {
|
||||
self.userdevicesessionid_uiaainfo
|
||||
.remove(&userdevicesessionid)?;
|
||||
self.userdeviceid_uiaainfo.remove(&userdeviceid)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -250,16 +170,14 @@ impl Uiaa {
|
||||
device_id: &DeviceId,
|
||||
session: &str,
|
||||
) -> Result<UiaaInfo> {
|
||||
let mut userdevicesessionid = user_id.as_bytes().to_vec();
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(device_id.as_bytes());
|
||||
userdevicesessionid.push(0xff);
|
||||
userdevicesessionid.extend_from_slice(session.as_bytes());
|
||||
let mut userdeviceid = user_id.as_bytes().to_vec();
|
||||
userdeviceid.push(0xff);
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
let uiaainfo = serde_json::from_slice::<UiaaInfo>(
|
||||
&self
|
||||
.userdevicesessionid_uiaainfo
|
||||
.get(&userdevicesessionid)?
|
||||
.userdeviceid_uiaainfo
|
||||
.get(&userdeviceid)?
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
"UIAA session does not exist.",
|
||||
@@ -267,6 +185,18 @@ impl Uiaa {
|
||||
)
|
||||
.map_err(|_| Error::bad_database("UiaaInfo in userdeviceid_uiaainfo is invalid."))?;
|
||||
|
||||
if uiaainfo
|
||||
.session
|
||||
.as_ref()
|
||||
.filter(|&s| s == session)
|
||||
.is_none()
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
"UIAA session token invalid.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(uiaainfo)
|
||||
}
|
||||
}
|
||||
|
||||
+143
-126
@@ -1,47 +1,52 @@
|
||||
use crate::{utils, Error, Result};
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, r0::device::Device},
|
||||
encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
r0::{
|
||||
device::Device,
|
||||
keys::{CrossSigningKey, OneTimeKey},
|
||||
},
|
||||
},
|
||||
encryption::DeviceKeys,
|
||||
events::{AnyToDeviceEvent, EventType},
|
||||
identifiers::MxcUri,
|
||||
serde::Raw,
|
||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, UInt, UserId,
|
||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UInt, UserId,
|
||||
};
|
||||
use std::{collections::BTreeMap, convert::TryFrom, mem, sync::Arc};
|
||||
|
||||
use super::abstraction::Tree;
|
||||
use std::{collections::BTreeMap, convert::TryFrom, mem, time::SystemTime};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Users {
|
||||
pub(super) userid_password: Arc<dyn Tree>,
|
||||
pub(super) userid_displayname: Arc<dyn Tree>,
|
||||
pub(super) userid_avatarurl: Arc<dyn Tree>,
|
||||
pub(super) userdeviceid_token: Arc<dyn Tree>,
|
||||
pub(super) userdeviceid_metadata: Arc<dyn Tree>, // This is also used to check if a device exists
|
||||
pub(super) userid_devicelistversion: Arc<dyn Tree>, // DevicelistVersion = u64
|
||||
pub(super) token_userdeviceid: Arc<dyn Tree>,
|
||||
pub(super) userid_password: sled::Tree,
|
||||
pub(super) userid_displayname: sled::Tree,
|
||||
pub(super) userid_avatarurl: sled::Tree,
|
||||
pub(super) userdeviceid_token: sled::Tree,
|
||||
pub(super) userdeviceid_metadata: sled::Tree, // This is also used to check if a device exists
|
||||
pub(super) userid_devicelistversion: sled::Tree, // DevicelistVersion = u64
|
||||
pub(super) token_userdeviceid: sled::Tree,
|
||||
|
||||
pub(super) onetimekeyid_onetimekeys: Arc<dyn Tree>, // OneTimeKeyId = UserId + DeviceKeyId
|
||||
pub(super) userid_lastonetimekeyupdate: Arc<dyn Tree>, // LastOneTimeKeyUpdate = Count
|
||||
pub(super) keychangeid_userid: Arc<dyn Tree>, // KeyChangeId = UserId/RoomId + Count
|
||||
pub(super) keyid_key: Arc<dyn Tree>, // KeyId = UserId + KeyId (depends on key type)
|
||||
pub(super) userid_masterkeyid: Arc<dyn Tree>,
|
||||
pub(super) userid_selfsigningkeyid: Arc<dyn Tree>,
|
||||
pub(super) userid_usersigningkeyid: Arc<dyn Tree>,
|
||||
pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + DeviceKeyId
|
||||
pub(super) userid_lastonetimekeyupdate: sled::Tree, // LastOneTimeKeyUpdate = Count
|
||||
pub(super) keychangeid_userid: sled::Tree, // KeyChangeId = UserId/RoomId + 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: Arc<dyn Tree>, // ToDeviceId = UserId + DeviceId + Count
|
||||
pub(super) todeviceid_events: sled::Tree, // ToDeviceId = UserId + DeviceId + Count
|
||||
}
|
||||
|
||||
impl Users {
|
||||
/// Check if a user has an account on this homeserver.
|
||||
pub fn exists(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self.userid_password.get(user_id.as_bytes())?.is_some())
|
||||
Ok(self.userid_password.contains_key(user_id.to_string())?)
|
||||
}
|
||||
|
||||
/// Check if account is deactivated
|
||||
pub fn is_deactivated(&self, user_id: &UserId) -> Result<bool> {
|
||||
Ok(self
|
||||
.userid_password
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"User does not exist.",
|
||||
@@ -50,20 +55,20 @@ impl Users {
|
||||
}
|
||||
|
||||
/// Create a new user account on this homeserver.
|
||||
pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
||||
pub fn create(&self, user_id: &UserId, password: &str) -> Result<()> {
|
||||
self.set_password(user_id, password)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the number of users registered on this server.
|
||||
pub fn count(&self) -> Result<usize> {
|
||||
Ok(self.userid_password.iter().count())
|
||||
pub fn count(&self) -> usize {
|
||||
self.userid_password.iter().count()
|
||||
}
|
||||
|
||||
/// Find out which user an access token belongs to.
|
||||
pub fn find_from_token(&self, token: &str) -> Result<Option<(UserId, String)>> {
|
||||
self.token_userdeviceid
|
||||
.get(token.as_bytes())?
|
||||
.get(token)?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
let mut parts = bytes.split(|&b| b == 0xff);
|
||||
let user_bytes = parts.next().ok_or_else(|| {
|
||||
@@ -88,19 +93,21 @@ impl Users {
|
||||
}
|
||||
|
||||
/// Returns an iterator over all users on this homeserver.
|
||||
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> + '_ {
|
||||
self.userid_password.iter().map(|(bytes, _)| {
|
||||
UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> {
|
||||
self.userid_password.iter().keys().map(|bytes| {
|
||||
Ok(
|
||||
UserId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| {
|
||||
Error::bad_database("User ID in userid_password is invalid unicode.")
|
||||
})?)
|
||||
.map_err(|_| Error::bad_database("User ID in userid_password is invalid."))
|
||||
.map_err(|_| Error::bad_database("User ID in userid_password is invalid."))?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the password hash for the given user.
|
||||
pub fn password_hash(&self, user_id: &UserId) -> Result<Option<String>> {
|
||||
self.userid_password
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||
Error::bad_database("Password hash in db is not valid string.")
|
||||
@@ -109,11 +116,9 @@ impl Users {
|
||||
}
|
||||
|
||||
/// Hash and set the user's password to the Argon2 hash
|
||||
pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
||||
if let Some(password) = password {
|
||||
pub fn set_password(&self, user_id: &UserId, password: &str) -> Result<()> {
|
||||
if let Ok(hash) = utils::calculate_hash(&password) {
|
||||
self.userid_password
|
||||
.insert(user_id.as_bytes(), hash.as_bytes())?;
|
||||
self.userid_password.insert(user_id.to_string(), &*hash)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::BadRequest(
|
||||
@@ -121,16 +126,12 @@ impl Users {
|
||||
"Password does not meet the requirements.",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
self.userid_password.insert(user_id.as_bytes(), b"")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the displayname of a user on this homeserver.
|
||||
pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> {
|
||||
self.userid_displayname
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||
Error::bad_database("Displayname in db is invalid.")
|
||||
@@ -142,9 +143,9 @@ impl Users {
|
||||
pub fn set_displayname(&self, user_id: &UserId, displayname: Option<String>) -> Result<()> {
|
||||
if let Some(displayname) = displayname {
|
||||
self.userid_displayname
|
||||
.insert(user_id.as_bytes(), displayname.as_bytes())?;
|
||||
.insert(user_id.to_string(), &*displayname)?;
|
||||
} else {
|
||||
self.userid_displayname.remove(user_id.as_bytes())?;
|
||||
self.userid_displayname.remove(user_id.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -153,7 +154,7 @@ impl Users {
|
||||
/// Get a the avatar_url of a user.
|
||||
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<MxcUri>> {
|
||||
self.userid_avatarurl
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.map(|bytes| {
|
||||
let s = utils::string_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Avatar URL in db is invalid."))?;
|
||||
@@ -166,9 +167,9 @@ impl Users {
|
||||
pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<MxcUri>) -> Result<()> {
|
||||
if let Some(avatar_url) = avatar_url {
|
||||
self.userid_avatarurl
|
||||
.insert(user_id.as_bytes(), avatar_url.to_string().as_bytes())?;
|
||||
.insert(user_id.to_string(), avatar_url.to_string().as_str())?;
|
||||
} else {
|
||||
self.userid_avatarurl.remove(user_id.as_bytes())?;
|
||||
self.userid_avatarurl.remove(user_id.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -190,17 +191,19 @@ impl Users {
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
self.userid_devicelistversion
|
||||
.increment(user_id.as_bytes())?;
|
||||
.update_and_fetch(&user_id.as_bytes(), utils::increment)?
|
||||
.expect("utils::increment will always put in a value");
|
||||
|
||||
self.userdeviceid_metadata.insert(
|
||||
&userdeviceid,
|
||||
&serde_json::to_vec(&Device {
|
||||
userdeviceid,
|
||||
serde_json::to_string(&Device {
|
||||
device_id: device_id.into(),
|
||||
display_name: initial_device_display_name,
|
||||
last_seen_ip: None, // TODO
|
||||
last_seen_ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||
last_seen_ts: Some(SystemTime::now()),
|
||||
})
|
||||
.expect("Device::to_string never fails."),
|
||||
.expect("Device::to_string never fails.")
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
self.set_token(user_id, &device_id, token)?;
|
||||
@@ -215,8 +218,7 @@ impl Users {
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
// Remove tokens
|
||||
if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? {
|
||||
self.userdeviceid_token.remove(&userdeviceid)?;
|
||||
if let Some(old_token) = self.userdeviceid_token.remove(&userdeviceid)? {
|
||||
self.token_userdeviceid.remove(&old_token)?;
|
||||
}
|
||||
|
||||
@@ -224,14 +226,15 @@ impl Users {
|
||||
let mut prefix = userdeviceid.clone();
|
||||
prefix.push(0xff);
|
||||
|
||||
for (key, _) in self.todeviceid_events.scan_prefix(prefix) {
|
||||
self.todeviceid_events.remove(&key)?;
|
||||
for key in self.todeviceid_events.scan_prefix(&prefix).keys() {
|
||||
self.todeviceid_events.remove(key?)?;
|
||||
}
|
||||
|
||||
// TODO: Remove onetimekeys
|
||||
|
||||
self.userid_devicelistversion
|
||||
.increment(user_id.as_bytes())?;
|
||||
.update_and_fetch(&user_id.as_bytes(), utils::increment)?
|
||||
.expect("utils::increment will always put in a value");
|
||||
|
||||
self.userdeviceid_metadata.remove(&userdeviceid)?;
|
||||
|
||||
@@ -239,18 +242,16 @@ impl Users {
|
||||
}
|
||||
|
||||
/// Returns an iterator over all device ids of this user.
|
||||
pub fn all_device_ids<'a>(
|
||||
&'a self,
|
||||
user_id: &UserId,
|
||||
) -> impl Iterator<Item = Result<Box<DeviceId>>> + 'a {
|
||||
pub fn all_device_ids(&self, user_id: &UserId) -> impl Iterator<Item = Result<Box<DeviceId>>> {
|
||||
let mut prefix = user_id.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
// All devices have metadata
|
||||
self.userdeviceid_metadata
|
||||
.scan_prefix(prefix)
|
||||
.map(|(bytes, _)| {
|
||||
.keys()
|
||||
.map(|bytes| {
|
||||
Ok(utils::string_from_bytes(
|
||||
&bytes
|
||||
&*bytes?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("UserDevice ID in db is invalid."))?,
|
||||
@@ -271,15 +272,13 @@ impl Users {
|
||||
|
||||
// Remove old token
|
||||
if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? {
|
||||
self.token_userdeviceid.remove(&old_token)?;
|
||||
self.token_userdeviceid.remove(old_token)?;
|
||||
// It will be removed from userdeviceid_token by the insert later
|
||||
}
|
||||
|
||||
// Assign token to user device combination
|
||||
self.userdeviceid_token
|
||||
.insert(&userdeviceid, token.as_bytes())?;
|
||||
self.token_userdeviceid
|
||||
.insert(token.as_bytes(), &userdeviceid)?;
|
||||
self.userdeviceid_token.insert(&userdeviceid, &*token)?;
|
||||
self.token_userdeviceid.insert(token, userdeviceid)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -311,7 +310,8 @@ impl Users {
|
||||
|
||||
self.onetimekeyid_onetimekeys.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(&one_time_key_value).expect("OneTimeKey::to_vec always works"),
|
||||
&*serde_json::to_string(&one_time_key_value)
|
||||
.expect("OneTimeKey::to_string always works"),
|
||||
)?;
|
||||
|
||||
self.userid_lastonetimekeyupdate
|
||||
@@ -351,9 +351,10 @@ impl Users {
|
||||
.insert(&user_id.as_bytes(), &globals.next_count()?.to_be_bytes())?;
|
||||
|
||||
self.onetimekeyid_onetimekeys
|
||||
.scan_prefix(prefix)
|
||||
.scan_prefix(&prefix)
|
||||
.next()
|
||||
.map(|(key, value)| {
|
||||
.map(|r| {
|
||||
let (key, value) = r?;
|
||||
self.onetimekeyid_onetimekeys.remove(&key)?;
|
||||
|
||||
Ok((
|
||||
@@ -383,13 +384,14 @@ impl Users {
|
||||
|
||||
let mut counts = BTreeMap::new();
|
||||
|
||||
for algorithm in
|
||||
self.onetimekeyid_onetimekeys
|
||||
.scan_prefix(userdeviceid)
|
||||
.map(|(bytes, _)| {
|
||||
for algorithm in self
|
||||
.onetimekeyid_onetimekeys
|
||||
.scan_prefix(&userdeviceid)
|
||||
.keys()
|
||||
.map(|bytes| {
|
||||
Ok::<_, Error>(
|
||||
serde_json::from_slice::<DeviceKeyId>(
|
||||
&*bytes.rsplit(|&b| b == 0xff).next().ok_or_else(|| {
|
||||
&*bytes?.rsplit(|&b| b == 0xff).next().ok_or_else(|| {
|
||||
Error::bad_database("OneTimeKey ID in db is invalid.")
|
||||
})?,
|
||||
)
|
||||
@@ -418,7 +420,7 @@ impl Users {
|
||||
|
||||
self.keyid_key.insert(
|
||||
&userdeviceid,
|
||||
&serde_json::to_vec(&device_keys).expect("DeviceKeys::to_vec always works"),
|
||||
&*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"),
|
||||
)?;
|
||||
|
||||
self.mark_device_key_update(user_id, rooms, globals)?;
|
||||
@@ -459,11 +461,11 @@ impl Users {
|
||||
|
||||
self.keyid_key.insert(
|
||||
&master_key_key,
|
||||
&serde_json::to_vec(&master_key).expect("CrossSigningKey::to_vec always works"),
|
||||
&*serde_json::to_string(&master_key).expect("CrossSigningKey::to_string always works"),
|
||||
)?;
|
||||
|
||||
self.userid_masterkeyid
|
||||
.insert(user_id.as_bytes(), &master_key_key)?;
|
||||
.insert(&*user_id.to_string(), master_key_key)?;
|
||||
|
||||
// Self-signing key
|
||||
if let Some(self_signing_key) = self_signing_key {
|
||||
@@ -485,12 +487,12 @@ impl Users {
|
||||
|
||||
self.keyid_key.insert(
|
||||
&self_signing_key_key,
|
||||
&serde_json::to_vec(&self_signing_key)
|
||||
.expect("CrossSigningKey::to_vec always works"),
|
||||
&*serde_json::to_string(&self_signing_key)
|
||||
.expect("CrossSigningKey::to_string always works"),
|
||||
)?;
|
||||
|
||||
self.userid_selfsigningkeyid
|
||||
.insert(user_id.as_bytes(), &self_signing_key_key)?;
|
||||
.insert(&*user_id.to_string(), self_signing_key_key)?;
|
||||
}
|
||||
|
||||
// User-signing key
|
||||
@@ -513,12 +515,12 @@ impl Users {
|
||||
|
||||
self.keyid_key.insert(
|
||||
&user_signing_key_key,
|
||||
&serde_json::to_vec(&user_signing_key)
|
||||
.expect("CrossSigningKey::to_vec always works"),
|
||||
&*serde_json::to_string(&user_signing_key)
|
||||
.expect("CrossSigningKey::to_string always works"),
|
||||
)?;
|
||||
|
||||
self.userid_usersigningkeyid
|
||||
.insert(user_id.as_bytes(), &user_signing_key_key)?;
|
||||
.insert(&*user_id.to_string(), user_signing_key_key)?;
|
||||
}
|
||||
|
||||
self.mark_device_key_update(user_id, rooms, globals)?;
|
||||
@@ -560,7 +562,8 @@ impl Users {
|
||||
|
||||
self.keyid_key.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(&cross_signing_key).expect("CrossSigningKey::to_vec always works"),
|
||||
&*serde_json::to_string(&cross_signing_key)
|
||||
.expect("CrossSigningKey::to_string always works"),
|
||||
)?;
|
||||
|
||||
// TODO: Should we notify about this change?
|
||||
@@ -570,26 +573,36 @@ impl Users {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn keys_changed<'a>(
|
||||
&'a self,
|
||||
pub fn keys_changed(
|
||||
&self,
|
||||
user_or_room_id: &str,
|
||||
from: u64,
|
||||
to: Option<u64>,
|
||||
) -> impl Iterator<Item = Result<UserId>> + 'a {
|
||||
) -> impl Iterator<Item = Result<UserId>> {
|
||||
let mut prefix = user_or_room_id.as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
let mut start = prefix.clone();
|
||||
start.extend_from_slice(&(from + 1).to_be_bytes());
|
||||
|
||||
let mut end = prefix.clone();
|
||||
end.extend_from_slice(&to.unwrap_or(u64::MAX).to_be_bytes());
|
||||
|
||||
self.keychangeid_userid
|
||||
.iter_from(&start, false)
|
||||
.range(start..end)
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.map(|(_, bytes)| {
|
||||
Ok(
|
||||
UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||
Error::bad_database("User ID in devicekeychangeid_userid is invalid unicode.")
|
||||
Error::bad_database(
|
||||
"User ID in devicekeychangeid_userid is invalid unicode.",
|
||||
)
|
||||
})?)
|
||||
.map_err(|_| Error::bad_database("User ID in devicekeychangeid_userid is invalid."))
|
||||
.map_err(|_| {
|
||||
Error::bad_database("User ID in devicekeychangeid_userid is invalid.")
|
||||
})?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -613,13 +626,13 @@ impl Users {
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(&count);
|
||||
|
||||
self.keychangeid_userid.insert(&key, user_id.as_bytes())?;
|
||||
self.keychangeid_userid.insert(key, &*user_id.to_string())?;
|
||||
}
|
||||
|
||||
let mut key = user_id.as_bytes().to_vec();
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(&count);
|
||||
self.keychangeid_userid.insert(&key, user_id.as_bytes())?;
|
||||
self.keychangeid_userid.insert(key, &*user_id.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -633,22 +646,23 @@ impl Users {
|
||||
key.push(0xff);
|
||||
key.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| {
|
||||
self.keyid_key.get(key)?.map_or(Ok(None), |bytes| {
|
||||
Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
|
||||
Error::bad_database("DeviceKeys in db are invalid.")
|
||||
})?))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_master_key<F: Fn(&UserId) -> bool>(
|
||||
pub fn get_master_key(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
allowed_signatures: F,
|
||||
sender_id: &UserId,
|
||||
) -> Result<Option<CrossSigningKey>> {
|
||||
// TODO: hide some signatures
|
||||
self.userid_masterkeyid
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.map_or(Ok(None), |key| {
|
||||
self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| {
|
||||
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.")
|
||||
@@ -659,7 +673,7 @@ impl Users {
|
||||
cross_signing_key.signatures = cross_signing_key
|
||||
.signatures
|
||||
.into_iter()
|
||||
.filter(|(user, _)| allowed_signatures(user))
|
||||
.filter(|(user, _)| user == user_id || user == sender_id)
|
||||
.collect();
|
||||
|
||||
Ok(Some(cross_signing_key))
|
||||
@@ -667,15 +681,15 @@ impl Users {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_self_signing_key<F: Fn(&UserId) -> bool>(
|
||||
pub fn get_self_signing_key(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
allowed_signatures: F,
|
||||
sender_id: &UserId,
|
||||
) -> Result<Option<CrossSigningKey>> {
|
||||
self.userid_selfsigningkeyid
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.map_or(Ok(None), |key| {
|
||||
self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| {
|
||||
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.")
|
||||
@@ -686,7 +700,7 @@ impl Users {
|
||||
cross_signing_key.signatures = cross_signing_key
|
||||
.signatures
|
||||
.into_iter()
|
||||
.filter(|(user, _)| user == user_id || allowed_signatures(user))
|
||||
.filter(|(user, _)| user == user_id || user == sender_id)
|
||||
.collect();
|
||||
|
||||
Ok(Some(cross_signing_key))
|
||||
@@ -696,9 +710,9 @@ impl Users {
|
||||
|
||||
pub fn get_user_signing_key(&self, user_id: &UserId) -> Result<Option<CrossSigningKey>> {
|
||||
self.userid_usersigningkeyid
|
||||
.get(user_id.as_bytes())?
|
||||
.get(user_id.to_string())?
|
||||
.map_or(Ok(None), |key| {
|
||||
self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| {
|
||||
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.")
|
||||
})?))
|
||||
@@ -711,7 +725,7 @@ impl Users {
|
||||
sender: &UserId,
|
||||
target_user_id: &UserId,
|
||||
target_device_id: &DeviceId,
|
||||
event_type: &str,
|
||||
event_type: &EventType,
|
||||
content: serde_json::Value,
|
||||
globals: &super::globals::Globals,
|
||||
) -> Result<()> {
|
||||
@@ -722,13 +736,13 @@ impl Users {
|
||||
key.extend_from_slice(&globals.next_count()?.to_be_bytes());
|
||||
|
||||
let mut json = serde_json::Map::new();
|
||||
json.insert("type".to_owned(), event_type.to_owned().into());
|
||||
json.insert("type".to_owned(), event_type.to_string().into());
|
||||
json.insert("sender".to_owned(), sender.to_string().into());
|
||||
json.insert("content".to_owned(), content);
|
||||
|
||||
self.todeviceid_events.insert(
|
||||
&key,
|
||||
&serde_json::to_vec(&json).expect("Map::to_vec always works"),
|
||||
&*serde_json::to_string(&json).expect("Map::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -747,9 +761,9 @@ impl Users {
|
||||
prefix.extend_from_slice(device_id.as_bytes());
|
||||
prefix.push(0xff);
|
||||
|
||||
for (_, value) in self.todeviceid_events.scan_prefix(prefix) {
|
||||
for value in self.todeviceid_events.scan_prefix(&prefix).values() {
|
||||
events.push(
|
||||
serde_json::from_slice(&value)
|
||||
serde_json::from_slice(&*value?)
|
||||
.map_err(|_| Error::bad_database("Event in todeviceid_events is invalid."))?,
|
||||
);
|
||||
}
|
||||
@@ -774,9 +788,10 @@ impl Users {
|
||||
|
||||
for (key, _) in self
|
||||
.todeviceid_events
|
||||
.iter_from(&last, true)
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.map(|(key, _)| {
|
||||
.range(&*prefix..=&*last)
|
||||
.keys()
|
||||
.map(|key| {
|
||||
let key = key?;
|
||||
Ok::<_, Error>((
|
||||
key.clone(),
|
||||
utils::u64_from_bytes(&key[key.len() - mem::size_of::<u64>()..key.len()])
|
||||
@@ -786,7 +801,7 @@ impl Users {
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(|&(_, count)| count <= until)
|
||||
{
|
||||
self.todeviceid_events.remove(&key)?;
|
||||
self.todeviceid_events.remove(key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -806,11 +821,14 @@ impl Users {
|
||||
assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
|
||||
|
||||
self.userid_devicelistversion
|
||||
.increment(user_id.as_bytes())?;
|
||||
.update_and_fetch(&user_id.as_bytes(), utils::increment)?
|
||||
.expect("utils::increment will always put in a value");
|
||||
|
||||
self.userdeviceid_metadata.insert(
|
||||
&userdeviceid,
|
||||
&serde_json::to_vec(device).expect("Device::to_string always works"),
|
||||
userdeviceid,
|
||||
serde_json::to_string(device)
|
||||
.expect("Device::to_string always works")
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -845,18 +863,17 @@ impl Users {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_devices_metadata<'a>(
|
||||
&'a self,
|
||||
user_id: &UserId,
|
||||
) -> impl Iterator<Item = Result<Device>> + 'a {
|
||||
pub fn all_devices_metadata(&self, user_id: &UserId) -> impl Iterator<Item = Result<Device>> {
|
||||
let mut key = user_id.as_bytes().to_vec();
|
||||
key.push(0xff);
|
||||
|
||||
self.userdeviceid_metadata
|
||||
.scan_prefix(key)
|
||||
.map(|(_, bytes)| {
|
||||
serde_json::from_slice::<Device>(&bytes)
|
||||
.map_err(|_| Error::bad_database("Device in userdeviceid_metadata is invalid."))
|
||||
.values()
|
||||
.map(|bytes| {
|
||||
Ok(serde_json::from_slice::<Device>(&bytes?).map_err(|_| {
|
||||
Error::bad_database("Device in userdeviceid_metadata is invalid.")
|
||||
})?)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -870,7 +887,7 @@ impl Users {
|
||||
// Set the password to "" to indicate a deactivated account. Hashes will never result in an
|
||||
// empty string, so the user will not be able to log in again. Systems like changing the
|
||||
// password without logging in should check if the account is deactivated.
|
||||
self.userid_password.insert(user_id.as_bytes(), &[])?;
|
||||
self.userid_password.insert(user_id.to_string(), "")?;
|
||||
|
||||
// TODO: Unhook 3PID
|
||||
Ok(())
|
||||
|
||||
+16
-48
@@ -1,11 +1,5 @@
|
||||
use log::{error, warn};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::{Error as RumaError, ErrorKind},
|
||||
r0::uiaa::UiaaInfo,
|
||||
},
|
||||
ServerName,
|
||||
};
|
||||
use log::error;
|
||||
use ruma::api::client::{error::ErrorKind, r0::uiaa::UiaaInfo};
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
@@ -16,25 +10,18 @@ use {
|
||||
response::{self, Responder},
|
||||
Request,
|
||||
},
|
||||
ruma::api::client::r0::uiaa::UiaaResponse,
|
||||
ruma::api::client::{error::Error as RumaError, r0::uiaa::UiaaResponse},
|
||||
};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[cfg(feature = "sled")]
|
||||
#[error("There was a problem with the connection to the sled database.")]
|
||||
#[error("There was a problem with the connection to the database.")]
|
||||
SledError {
|
||||
#[from]
|
||||
source: sled::Error,
|
||||
},
|
||||
#[cfg(feature = "rocksdb")]
|
||||
#[error("There was a problem with the connection to the rocksdb database: {source}")]
|
||||
RocksDbError {
|
||||
#[from]
|
||||
source: rocksdb::Error,
|
||||
},
|
||||
#[error("Could not generate an image.")]
|
||||
ImageError {
|
||||
#[from]
|
||||
@@ -46,13 +33,6 @@ pub enum Error {
|
||||
source: reqwest::Error,
|
||||
},
|
||||
#[error("{0}")]
|
||||
FederationError(Box<ServerName>, RumaError),
|
||||
#[error("Could not do this io: {source}")]
|
||||
IoError {
|
||||
#[from]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("{0}")]
|
||||
BadServerResponse(&'static str),
|
||||
#[error("{0}")]
|
||||
BadConfig(&'static str),
|
||||
@@ -61,6 +41,7 @@ pub enum Error {
|
||||
BadDatabase(&'static str),
|
||||
#[error("uiaa")]
|
||||
Uiaa(UiaaInfo),
|
||||
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ErrorKind, &'static str),
|
||||
#[error("{0}")]
|
||||
@@ -79,16 +60,14 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn to_response(&self) -> RumaResponse<UiaaResponse> {
|
||||
if let Self::Uiaa(uiaainfo) = self {
|
||||
return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone()));
|
||||
}
|
||||
|
||||
if let Self::FederationError(origin, error) = self {
|
||||
let mut error = error.clone();
|
||||
error.message = format!("Answer from {}: {}", origin, error.message);
|
||||
return RumaResponse(UiaaResponse::MatrixError(error));
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
impl<'r, 'o> Responder<'r, 'o> for Error
|
||||
where
|
||||
'o: 'r,
|
||||
{
|
||||
fn respond_to(self, r: &'r Request<'_>) -> response::Result<'o> {
|
||||
if let Self::Uiaa(uiaainfo) = &self {
|
||||
return RumaResponse::from(UiaaResponse::AuthResponse(uiaainfo.clone())).respond_to(r);
|
||||
}
|
||||
|
||||
let message = format!("{}", self);
|
||||
@@ -113,22 +92,11 @@ impl Error {
|
||||
_ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
|
||||
warn!("{}: {}", status_code, message);
|
||||
|
||||
RumaResponse(UiaaResponse::MatrixError(RumaError {
|
||||
RumaResponse::from(RumaError {
|
||||
kind,
|
||||
message,
|
||||
status_code,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
impl<'r, 'o> Responder<'r, 'o> for Error
|
||||
where
|
||||
'o: 'r,
|
||||
{
|
||||
fn respond_to(self, r: &'r Request<'_>) -> response::Result<'o> {
|
||||
self.to_response().respond_to(r)
|
||||
})
|
||||
.respond_to(r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#![allow(clippy::suspicious_else_formatting)]
|
||||
#![deny(clippy::dbg_macro)]
|
||||
|
||||
pub mod appservice_server;
|
||||
pub mod client_server;
|
||||
mod database;
|
||||
|
||||
+6
-15
@@ -1,6 +1,5 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![allow(clippy::suspicious_else_formatting)]
|
||||
#![deny(clippy::dbg_macro)]
|
||||
|
||||
pub mod appservice_server;
|
||||
pub mod client_server;
|
||||
@@ -12,8 +11,6 @@ mod pdu;
|
||||
mod ruma_wrapper;
|
||||
mod utils;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use database::Config;
|
||||
pub use database::Database;
|
||||
pub use error::{Error, Result};
|
||||
@@ -33,7 +30,7 @@ use rocket::{
|
||||
use tracing::span;
|
||||
use tracing_subscriber::{prelude::*, Registry};
|
||||
|
||||
fn setup_rocket(config: Figment, data: Arc<Database>) -> rocket::Rocket<rocket::Build> {
|
||||
fn setup_rocket(config: Figment, data: Database) -> rocket::Rocket<rocket::Build> {
|
||||
rocket::custom(config)
|
||||
.manage(data)
|
||||
.mount(
|
||||
@@ -71,7 +68,6 @@ fn setup_rocket(config: Figment, data: Arc<Database>) -> rocket::Rocket<rocket::
|
||||
client_server::get_avatar_url_route,
|
||||
client_server::get_profile_route,
|
||||
client_server::set_presence_route,
|
||||
client_server::get_presence_route,
|
||||
client_server::upload_keys_route,
|
||||
client_server::get_keys_route,
|
||||
client_server::claim_keys_route,
|
||||
@@ -153,8 +149,6 @@ fn setup_rocket(config: Figment, data: Arc<Database>) -> rocket::Rocket<rocket::
|
||||
server_server::send_transaction_message_route,
|
||||
server_server::get_event_route,
|
||||
server_server::get_missing_events_route,
|
||||
server_server::get_event_authorization_route,
|
||||
server_server::get_room_state_route,
|
||||
server_server::get_room_state_ids_route,
|
||||
server_server::create_join_event_template_route,
|
||||
server_server::create_join_event_route,
|
||||
@@ -162,8 +156,6 @@ fn setup_rocket(config: Figment, data: Arc<Database>) -> rocket::Rocket<rocket::
|
||||
server_server::get_devices_route,
|
||||
server_server::get_room_information_route,
|
||||
server_server::get_profile_information_route,
|
||||
server_server::get_keys_route,
|
||||
server_server::claim_keys_route,
|
||||
],
|
||||
)
|
||||
.register(
|
||||
@@ -201,6 +193,8 @@ async fn main() {
|
||||
.await
|
||||
.expect("config is valid");
|
||||
|
||||
db.sending.start_handler(&db);
|
||||
|
||||
if config.allow_jaeger {
|
||||
let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
|
||||
.with_service_name("conduit")
|
||||
@@ -211,17 +205,14 @@ async fn main() {
|
||||
|
||||
let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
|
||||
let _enter = root.enter();
|
||||
|
||||
let rocket = setup_rocket(raw_config, db);
|
||||
rocket.launch().await.unwrap();
|
||||
} else {
|
||||
std::env::set_var("RUST_LOG", config.log);
|
||||
tracing_subscriber::fmt::init();
|
||||
std::env::set_var("CONDUIT_LOG", config.log);
|
||||
pretty_env_logger::init_custom_env("CONDUIT_LOG");
|
||||
}
|
||||
|
||||
let rocket = setup_rocket(raw_config, db);
|
||||
rocket.launch().await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
fn not_found_catcher(_: &Request<'_>) -> String {
|
||||
|
||||
+12
-14
@@ -2,17 +2,15 @@ use crate::Error;
|
||||
use log::error;
|
||||
use ruma::{
|
||||
events::{
|
||||
pdu::EventHash, room::member::MemberEventContent, AnyEphemeralRoomEvent, AnyRoomEvent,
|
||||
AnyStateEvent, AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, EventType,
|
||||
StateEvent,
|
||||
pdu::EventHash, room::member::MemberEventContent, AnyEvent, AnyRoomEvent, AnyStateEvent,
|
||||
AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, EventType, StateEvent,
|
||||
},
|
||||
serde::{CanonicalJsonObject, CanonicalJsonValue, Raw},
|
||||
state_res, EventId, MilliSecondsSinceUnixEpoch, RoomId, RoomVersionId, ServerName,
|
||||
ServerSigningKeyId, UInt, UserId,
|
||||
EventId, RoomId, RoomVersionId, ServerName, ServerSigningKeyId, UInt, UserId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::{cmp::Ordering, collections::BTreeMap, convert::TryFrom};
|
||||
use std::{cmp::Ordering, collections::BTreeMap, convert::TryFrom, time::UNIX_EPOCH};
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct PduEvent {
|
||||
@@ -74,7 +72,9 @@ impl PduEvent {
|
||||
|
||||
self.unsigned.insert(
|
||||
"redacted_because".to_owned(),
|
||||
serde_json::to_value(reason).expect("to_value(PduEvent) always works"),
|
||||
serde_json::to_string(reason)
|
||||
.expect("PduEvent::to_string always works")
|
||||
.into(),
|
||||
);
|
||||
|
||||
self.content = new_content.into();
|
||||
@@ -105,7 +105,7 @@ impl PduEvent {
|
||||
|
||||
/// This only works for events that are also AnyRoomEvents.
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn to_any_event(&self) -> Raw<AnyEphemeralRoomEvent> {
|
||||
pub fn to_any_event(&self) -> Raw<AnyEvent> {
|
||||
let mut json = json!({
|
||||
"content": self.content,
|
||||
"type": self.kind,
|
||||
@@ -213,10 +213,7 @@ impl PduEvent {
|
||||
pub fn convert_to_outgoing_federation_event(
|
||||
mut pdu_json: CanonicalJsonObject,
|
||||
) -> Raw<ruma::events::pdu::Pdu> {
|
||||
if let Some(unsigned) = pdu_json
|
||||
.get_mut("unsigned")
|
||||
.and_then(|val| val.as_object_mut())
|
||||
{
|
||||
if let Some(CanonicalJsonValue::Object(unsigned)) = pdu_json.get_mut("unsigned") {
|
||||
unsigned.remove("transaction_id");
|
||||
}
|
||||
|
||||
@@ -267,9 +264,10 @@ impl state_res::Event for PduEvent {
|
||||
fn content(&self) -> serde_json::Value {
|
||||
self.content.clone()
|
||||
}
|
||||
fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
|
||||
MilliSecondsSinceUnixEpoch(self.origin_server_ts)
|
||||
fn origin_server_ts(&self) -> std::time::SystemTime {
|
||||
UNIX_EPOCH + std::time::Duration::from_millis(self.origin_server_ts.into())
|
||||
}
|
||||
|
||||
fn state_key(&self) -> Option<String> {
|
||||
self.state_key.clone()
|
||||
}
|
||||
|
||||
+53
-96
@@ -1,15 +1,14 @@
|
||||
use crate::Error;
|
||||
use ruma::{
|
||||
api::{client::r0::uiaa::UiaaResponse, OutgoingResponse},
|
||||
api::OutgoingResponse,
|
||||
identifiers::{DeviceId, UserId},
|
||||
signatures::CanonicalJsonValue,
|
||||
Outgoing, ServerName,
|
||||
Outgoing,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use {
|
||||
crate::{server_server, Database},
|
||||
crate::{server_server, utils},
|
||||
log::{debug, warn},
|
||||
rocket::{
|
||||
data::{self, ByteUnit, Data, FromData},
|
||||
@@ -19,11 +18,14 @@ use {
|
||||
tokio::io::AsyncReadExt,
|
||||
Request, State,
|
||||
},
|
||||
ruma::api::{AuthScheme, IncomingRequest},
|
||||
ruma::{
|
||||
api::{AuthScheme, IncomingRequest},
|
||||
signatures::CanonicalJsonValue,
|
||||
ServerName,
|
||||
},
|
||||
std::collections::BTreeMap,
|
||||
std::convert::TryFrom,
|
||||
std::io::Cursor,
|
||||
std::sync::Arc,
|
||||
};
|
||||
|
||||
/// This struct converts rocket requests into ruma structs by converting them into http requests
|
||||
@@ -32,9 +34,8 @@ pub struct Ruma<T: Outgoing> {
|
||||
pub body: T::Incoming,
|
||||
pub sender_user: Option<UserId>,
|
||||
pub sender_device: Option<Box<DeviceId>>,
|
||||
pub sender_servername: Option<Box<ServerName>>,
|
||||
// This is None when body is not a valid string
|
||||
pub json_body: Option<CanonicalJsonValue>,
|
||||
pub json_body: Option<Box<serde_json::value::RawValue>>,
|
||||
pub from_appservice: bool,
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ where
|
||||
async fn from_data(request: &'a Request<'_>, data: Data) -> data::Outcome<Self, Self::Error> {
|
||||
let metadata = T::Incoming::METADATA;
|
||||
let db = request
|
||||
.guard::<State<'_, Arc<Database>>>()
|
||||
.guard::<State<'_, crate::Database>>()
|
||||
.await
|
||||
.expect("database was loaded");
|
||||
|
||||
@@ -57,7 +58,7 @@ where
|
||||
let token = request
|
||||
.headers()
|
||||
.get_one("Authorization")
|
||||
.and_then(|s| s.get(7..)) // Split off "Bearer "
|
||||
.map(|s| s[7..].to_owned()) // Split off "Bearer "
|
||||
.or_else(|| request.query_value("access_token").and_then(|r| r.ok()));
|
||||
|
||||
let limit = db.globals.max_request_size();
|
||||
@@ -65,15 +66,9 @@ where
|
||||
let mut body = Vec::new();
|
||||
handle.read_to_end(&mut body).await.unwrap();
|
||||
|
||||
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&body).ok();
|
||||
|
||||
let (sender_user, sender_device, sender_servername, from_appservice) = if let Some((
|
||||
_id,
|
||||
registration,
|
||||
)) = db
|
||||
let (sender_user, sender_device, from_appservice) = if let Some((_id, registration)) = db
|
||||
.appservice
|
||||
.iter_all()
|
||||
.unwrap()
|
||||
.filter_map(|r| r.ok())
|
||||
.find(|(_id, registration)| {
|
||||
registration
|
||||
@@ -107,10 +102,10 @@ where
|
||||
}
|
||||
|
||||
// TODO: Check if appservice is allowed to be that user
|
||||
(Some(user_id), None, None, true)
|
||||
(Some(user_id), None, true)
|
||||
}
|
||||
AuthScheme::ServerSignatures => (None, None, None, true),
|
||||
AuthScheme::None => (None, None, None, true),
|
||||
AuthScheme::ServerSignatures => (None, None, true),
|
||||
AuthScheme::None => (None, None, true),
|
||||
}
|
||||
} else {
|
||||
match metadata.authentication {
|
||||
@@ -119,12 +114,9 @@ where
|
||||
match db.users.find_from_token(&token).unwrap() {
|
||||
// Unknown Token
|
||||
None => return Failure((Status::raw(581), ())),
|
||||
Some((user_id, device_id)) => (
|
||||
Some(user_id),
|
||||
Some(Box::<DeviceId>::from(device_id)),
|
||||
None,
|
||||
false,
|
||||
),
|
||||
Some((user_id, device_id)) => {
|
||||
(Some(user_id), Some(device_id.into()), false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Missing Token
|
||||
@@ -133,12 +125,10 @@ where
|
||||
}
|
||||
AuthScheme::ServerSignatures => {
|
||||
// Get origin from header
|
||||
let x_matrix = match request
|
||||
.headers()
|
||||
.get_one("Authorization")
|
||||
.and_then(|s| s.get(9..)) // Split off "X-Matrix " and parse the rest
|
||||
.map(|s| {
|
||||
s.split_terminator(',')
|
||||
let x_matrix = match request.headers().get_one("Authorization").map(|s| {
|
||||
// Split off "X-Matrix " and parse the rest
|
||||
s[9..]
|
||||
.split_terminator(',')
|
||||
.map(|field| {
|
||||
let mut splits = field.splitn(2, '=');
|
||||
(splits.next(), splits.next().map(|s| s.trim_matches('"')))
|
||||
@@ -197,10 +187,12 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let json_body = serde_json::from_slice::<CanonicalJsonValue>(&body);
|
||||
|
||||
let mut request_map = BTreeMap::<String, CanonicalJsonValue>::new();
|
||||
|
||||
if let Some(json_body) = &json_body {
|
||||
request_map.insert("content".to_owned(), json_body.clone());
|
||||
if let Ok(json_body) = json_body {
|
||||
request_map.insert("content".to_owned(), json_body);
|
||||
};
|
||||
|
||||
request_map.insert(
|
||||
@@ -235,8 +227,11 @@ where
|
||||
CanonicalJsonValue::Object(signatures),
|
||||
);
|
||||
|
||||
let keys =
|
||||
match server_server::fetch_signing_keys(&db, &origin, vec![key.to_owned()])
|
||||
let keys = match server_server::fetch_signing_keys(
|
||||
&db,
|
||||
&origin,
|
||||
vec![&key.to_owned()],
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(b) => b,
|
||||
@@ -252,7 +247,7 @@ where
|
||||
pub_key_map.insert(origin.as_str().to_owned(), keys);
|
||||
|
||||
match ruma::signatures::verify_json(&pub_key_map, &request_map) {
|
||||
Ok(()) => (None, None, Some(origin), false),
|
||||
Ok(()) => (None, None, false),
|
||||
Err(e) => {
|
||||
warn!("Failed to verify json request from {}: {}", origin, e);
|
||||
|
||||
@@ -265,7 +260,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthScheme::None => (None, None, None, false),
|
||||
AuthScheme::None => (None, None, false),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -276,35 +271,6 @@ where
|
||||
http_request = http_request.header(header.name.as_str(), &*header.value);
|
||||
}
|
||||
|
||||
if let Some(json_body) = json_body.as_mut().and_then(|val| val.as_object_mut()) {
|
||||
let user_id = sender_user.clone().unwrap_or_else(|| {
|
||||
UserId::parse_with_server_name("", db.globals.server_name())
|
||||
.expect("we know this is valid")
|
||||
});
|
||||
|
||||
if let Some(CanonicalJsonValue::Object(initial_request)) = json_body
|
||||
.get("auth")
|
||||
.and_then(|auth| auth.as_object())
|
||||
.and_then(|auth| auth.get("session"))
|
||||
.and_then(|session| session.as_str())
|
||||
.and_then(|session| {
|
||||
db.uiaa
|
||||
.get_uiaa_request(
|
||||
&user_id,
|
||||
&sender_device.clone().unwrap_or_else(|| "".into()),
|
||||
session,
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
{
|
||||
for (key, value) in initial_request {
|
||||
json_body.entry(key).or_insert(value);
|
||||
}
|
||||
}
|
||||
body = serde_json::to_vec(json_body).expect("value to bytes can't fail");
|
||||
}
|
||||
|
||||
let http_request = http_request.body(&*body).unwrap();
|
||||
debug!("{:?}", http_request);
|
||||
match <T::Incoming as IncomingRequest>::try_from_http_request(http_request) {
|
||||
@@ -312,9 +278,11 @@ where
|
||||
body: t,
|
||||
sender_user,
|
||||
sender_device,
|
||||
sender_servername,
|
||||
// TODO: Can we avoid parsing it again? (We only need this for append_pdu)
|
||||
json_body: utils::string_from_bytes(&body)
|
||||
.ok()
|
||||
.and_then(|s| serde_json::value::RawValue::from_string(s).ok()),
|
||||
from_appservice,
|
||||
json_body,
|
||||
}),
|
||||
Err(e) => {
|
||||
warn!("{:?}", e);
|
||||
@@ -335,8 +303,22 @@ impl<T: Outgoing> Deref for Ruma<T> {
|
||||
/// This struct converts ruma responses into rocket http responses.
|
||||
pub type ConduitResult<T> = std::result::Result<RumaResponse<T>, Error>;
|
||||
|
||||
pub fn response<T: OutgoingResponse>(response: RumaResponse<T>) -> response::Result<'static> {
|
||||
let http_response = response
|
||||
pub struct RumaResponse<T: OutgoingResponse>(pub T);
|
||||
|
||||
impl<T: OutgoingResponse> From<T> for RumaResponse<T> {
|
||||
fn from(t: T) -> Self {
|
||||
Self(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
impl<'r, 'o, T> Responder<'r, 'o> for RumaResponse<T>
|
||||
where
|
||||
T: Send + OutgoingResponse,
|
||||
'o: 'r,
|
||||
{
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
||||
let http_response = self
|
||||
.0
|
||||
.try_into_http_response::<Vec<u8>>()
|
||||
.map_err(|_| Status::InternalServerError)?;
|
||||
@@ -366,29 +348,4 @@ pub fn response<T: OutgoingResponse>(response: RumaResponse<T>) -> response::Res
|
||||
response.raw_header("Access-Control-Max-Age", "86400");
|
||||
response.ok()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RumaResponse<T>(pub T);
|
||||
|
||||
impl<T> From<T> for RumaResponse<T> {
|
||||
fn from(t: T) -> Self {
|
||||
Self(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for RumaResponse<UiaaResponse> {
|
||||
fn from(t: Error) -> Self {
|
||||
t.to_response()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
impl<'r, 'o, T> Responder<'r, 'o> for RumaResponse<T>
|
||||
where
|
||||
'o: 'r,
|
||||
T: OutgoingResponse,
|
||||
{
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
||||
response(self)
|
||||
}
|
||||
}
|
||||
|
||||
+173
-542
File diff suppressed because it is too large
Load Diff
+7
-11
@@ -15,15 +15,6 @@ pub fn millis_since_unix_epoch() -> u64 {
|
||||
.as_millis() as u64
|
||||
}
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
pub fn increment_rocksdb(
|
||||
_new_key: &[u8],
|
||||
old: Option<&[u8]>,
|
||||
_operands: &mut rocksdb::MergeOperands,
|
||||
) -> Option<Vec<u8>> {
|
||||
increment(old)
|
||||
}
|
||||
|
||||
pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
||||
let number = match old.map(|bytes| bytes.try_into()) {
|
||||
Some(Ok(bytes)) => {
|
||||
@@ -36,7 +27,8 @@ pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
||||
Some(number.to_be_bytes().to_vec())
|
||||
}
|
||||
|
||||
pub fn generate_keypair() -> Vec<u8> {
|
||||
pub fn generate_keypair(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
||||
Some(old.map(|s| s.to_vec()).unwrap_or_else(|| {
|
||||
let mut value = random_string(8).as_bytes().to_vec();
|
||||
value.push(0xff);
|
||||
value.extend_from_slice(
|
||||
@@ -44,6 +36,7 @@ pub fn generate_keypair() -> Vec<u8> {
|
||||
.expect("Ed25519KeyPair generation always works (?)"),
|
||||
);
|
||||
value
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parses the bytes into an u64.
|
||||
@@ -84,7 +77,9 @@ pub fn common_elements(
|
||||
let mut other_iterators = iterators.map(|i| i.peekable()).collect::<Vec<_>>();
|
||||
|
||||
Some(first_iterator.filter(move |target| {
|
||||
other_iterators.iter_mut().all(|it| {
|
||||
other_iterators
|
||||
.iter_mut()
|
||||
.map(|it| {
|
||||
while let Some(element) = it.peek() {
|
||||
match check_order(element, target) {
|
||||
Ordering::Greater => return false, // We went too far
|
||||
@@ -97,6 +92,7 @@ pub fn common_elements(
|
||||
}
|
||||
false
|
||||
})
|
||||
.all(|b| b)
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user