Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42a7a9f795 | ||
|
|
54a0f21347 | ||
|
|
54f4fd2cc8 | ||
|
|
ac5dd5e251 | ||
|
|
6510ffa0fa | ||
|
|
caa186ad22 |
@@ -1,28 +1,13 @@
|
|||||||
image: "rust:latest"
|
image: "rust:latest"
|
||||||
|
|
||||||
default:
|
|
||||||
tags: [docker]
|
|
||||||
|
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- target
|
|
||||||
- cargohome
|
|
||||||
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
CARGO_HOME: "cargohome"
|
|
||||||
FF_USE_FASTZIP: 1
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mkdir -p $CARGO_HOME && echo "using $CARGO_HOME to cache cargo deps"
|
|
||||||
- apt-get update -yqq
|
- apt-get update -yqq
|
||||||
- apt-get install -yqq --no-install-recommends build-essential libssl-dev pkg-config
|
- apt-get install -yqq --no-install-recommends build-essential libssl-dev pkg-config
|
||||||
- rustup component add clippy rustfmt
|
|
||||||
|
|
||||||
test:cargo:
|
test:cargo:
|
||||||
script:
|
script:
|
||||||
- rustc --version && cargo --version # Print version info for debugging
|
- rustc --version && cargo --version # Print version info for debugging
|
||||||
- cargo test --workspace --verbose --locked
|
- 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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
|
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]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.50"
|
version = "0.1.50"
|
||||||
@@ -103,25 +124,6 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
|
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]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -139,15 +141,6 @@ dependencies = [
|
|||||||
"constant_time_eq",
|
"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]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.6.1"
|
version = "3.6.1"
|
||||||
@@ -181,15 +174,6 @@ dependencies = [
|
|||||||
"jobserver",
|
"jobserver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cexpr"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
@@ -215,17 +199,6 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "color_quant"
|
name = "color_quant"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -243,16 +216,14 @@ dependencies = [
|
|||||||
"image",
|
"image",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"log",
|
"log",
|
||||||
"lru-cache",
|
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"opentelemetry-jaeger",
|
"opentelemetry-jaeger",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ring",
|
"ring",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocksdb",
|
|
||||||
"ruma",
|
"ruma",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -261,6 +232,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sled",
|
"sled",
|
||||||
|
"state-res",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -270,12 +242,6 @@ dependencies = [
|
|||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-oid"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "279bc8fc53f788a75c7804af68237d1fce02cde1e275a886a4b320604dc2aeda"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -321,15 +287,6 @@ version = "0.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -363,19 +320,6 @@ dependencies = [
|
|||||||
"lazy_static",
|
"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]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -392,15 +336,6 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "der"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2eeb9d92785d1facb50567852ce75d0858630630e7eabea59cf7eb7474051087"
|
|
||||||
dependencies = [
|
|
||||||
"const-oid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.13"
|
version = "0.99.13"
|
||||||
@@ -443,15 +378,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
@@ -484,29 +410,6 @@ version = "0.4.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@@ -701,16 +604,6 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
@@ -924,15 +817,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indoc"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136"
|
|
||||||
dependencies = [
|
|
||||||
"unindent",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inlinable_string"
|
name = "inlinable_string"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
@@ -1049,40 +933,12 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazycell"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.93"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
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]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
@@ -1227,16 +1083,6 @@ dependencies = [
|
|||||||
"version_check",
|
"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]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -1314,12 +1160,6 @@ version = "1.7.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "opaque-debug"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -1338,7 +1178,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1418,12 +1258,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "peeking_take_while"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem"
|
name = "pem"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -1473,17 +1307,6 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
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]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.16.8"
|
version = "0.16.8"
|
||||||
@@ -1571,19 +1394,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -1591,19 +1401,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.3.0",
|
"rand_chacha",
|
||||||
"rand_core 0.6.2",
|
"rand_core",
|
||||||
"rand_hc 0.3.0",
|
"rand_hc",
|
||||||
]
|
|
||||||
|
|
||||||
[[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",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1613,16 +1413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core 0.6.2",
|
"rand_core",
|
||||||
]
|
|
||||||
|
|
||||||
[[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",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1634,22 +1425,13 @@ dependencies = [
|
|||||||
"getrandom 0.2.2",
|
"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]]
|
[[package]]
|
||||||
name = "rand_hc"
|
name = "rand_hc"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core 0.6.2",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1814,7 +1596,7 @@ dependencies = [
|
|||||||
"num_cpus",
|
"num_cpus",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
"rocket_codegen",
|
"rocket_codegen",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
@@ -1870,25 +1652,16 @@ dependencies = [
|
|||||||
"uncased",
|
"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]]
|
[[package]]
|
||||||
name = "ruma"
|
name = "ruma"
|
||||||
version = "0.1.2"
|
version = "0.0.3"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assign",
|
"assign",
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-api",
|
"ruma-api",
|
||||||
"ruma-appservice-api",
|
"ruma-appservice-api",
|
||||||
|
"ruma-client",
|
||||||
"ruma-client-api",
|
"ruma-client-api",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
"ruma-events",
|
"ruma-events",
|
||||||
@@ -1898,13 +1671,12 @@ dependencies = [
|
|||||||
"ruma-push-gateway-api",
|
"ruma-push-gateway-api",
|
||||||
"ruma-serde",
|
"ruma-serde",
|
||||||
"ruma-signatures",
|
"ruma-signatures",
|
||||||
"ruma-state-res",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-api"
|
name = "ruma-api"
|
||||||
version = "0.17.0"
|
version = "0.17.0-alpha.4"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http",
|
"http",
|
||||||
@@ -1919,8 +1691,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-api-macros"
|
name = "ruma-api-macros"
|
||||||
version = "0.17.0"
|
version = "0.17.0-alpha.4"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -1930,8 +1702,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-appservice-api"
|
name = "ruma-appservice-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0-alpha.3"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ruma-api",
|
"ruma-api",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -1942,10 +1714,31 @@ dependencies = [
|
|||||||
"serde_json",
|
"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]]
|
[[package]]
|
||||||
name = "ruma-client-api"
|
name = "ruma-client-api"
|
||||||
version = "0.10.2"
|
version = "0.10.0-alpha.3"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assign",
|
"assign",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1964,11 +1757,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-common"
|
name = "ruma-common"
|
||||||
version = "0.5.3"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"js_int",
|
"js_int",
|
||||||
|
"maplit",
|
||||||
"ruma-identifiers",
|
"ruma-identifiers",
|
||||||
"ruma-serde",
|
"ruma-serde",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1979,10 +1773,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-events"
|
name = "ruma-events"
|
||||||
version = "0.22.2"
|
version = "0.22.0-alpha.3"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc",
|
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
"ruma-events-macros",
|
"ruma-events-macros",
|
||||||
@@ -1994,8 +1787,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-events-macros"
|
name = "ruma-events-macros"
|
||||||
version = "0.22.2"
|
version = "0.22.0-alpha.3"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -2005,8 +1798,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-federation-api"
|
name = "ruma-federation-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0-alpha.2"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-api",
|
"ruma-api",
|
||||||
@@ -2020,11 +1813,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers"
|
name = "ruma-identifiers"
|
||||||
version = "0.19.2"
|
version = "0.19.0"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"paste",
|
"paste",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
"ruma-identifiers-macros",
|
"ruma-identifiers-macros",
|
||||||
"ruma-identifiers-validation",
|
"ruma-identifiers-validation",
|
||||||
"ruma-serde",
|
"ruma-serde",
|
||||||
@@ -2034,9 +1827,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers-macros"
|
name = "ruma-identifiers-macros"
|
||||||
version = "0.19.2"
|
version = "0.19.0"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"ruma-identifiers-validation",
|
"ruma-identifiers-validation",
|
||||||
"syn",
|
"syn",
|
||||||
@@ -2044,13 +1838,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers-validation"
|
name = "ruma-identifiers-validation"
|
||||||
version = "0.4.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identity-service-api"
|
name = "ruma-identity-service-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0-alpha.1"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-api",
|
"ruma-api",
|
||||||
@@ -2058,12 +1852,13 @@ dependencies = [
|
|||||||
"ruma-identifiers",
|
"ruma-identifiers",
|
||||||
"ruma-serde",
|
"ruma-serde",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-push-gateway-api"
|
name = "ruma-push-gateway-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0-alpha.1"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-api",
|
"ruma-api",
|
||||||
@@ -2077,8 +1872,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-serde"
|
name = "ruma-serde"
|
||||||
version = "0.4.0"
|
version = "0.3.1"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
@@ -2091,8 +1886,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-serde-macros"
|
name = "ruma-serde-macros"
|
||||||
version = "0.4.0"
|
version = "0.3.1"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -2102,41 +1897,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-signatures"
|
name = "ruma-signatures"
|
||||||
version = "0.7.2"
|
version = "0.7.0"
|
||||||
source = "git+https://github.com/ruma/ruma?rev=5a7e2cddcf257e367465cced51442c91e8f557c9#5a7e2cddcf257e367465cced51442c91e8f557c9"
|
source = "git+https://github.com/ruma/ruma?rev=097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2#097f20c837fcbaef1d9c18c30d3ec5a24a6e4ec2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"ed25519-dalek",
|
"ring",
|
||||||
"pkcs8",
|
|
||||||
"rand 0.7.3",
|
|
||||||
"ruma-identifiers",
|
"ruma-identifiers",
|
||||||
"ruma-serde",
|
"ruma-serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
|
||||||
"thiserror",
|
|
||||||
"tracing",
|
|
||||||
"untrusted",
|
"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]]
|
[[package]]
|
||||||
name = "rust-argon2"
|
name = "rust-argon2"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -2149,12 +1920,6 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-hash"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -2273,18 +2038,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.126"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.126"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2332,19 +2097,6 @@ version = "0.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
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]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -2354,12 +2106,6 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -2369,12 +2115,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signature"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0f0242b8e50dd9accdd56170e94ca1ebd223b098eb9c83539a6e367d0f36ae68"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simple_asn1"
|
name = "simple_asn1"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -2442,15 +2182,6 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spki"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9dae7e047abc519c96350e9484a96c6bf1492348af912fd3446dd2dc323f6268"
|
|
||||||
dependencies = [
|
|
||||||
"der",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable-pattern"
|
name = "stable-pattern"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2477,6 +2208,20 @@ dependencies = [
|
|||||||
"loom",
|
"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]]
|
[[package]]
|
||||||
name = "stdweb"
|
name = "stdweb"
|
||||||
version = "0.4.20"
|
version = "0.4.20"
|
||||||
@@ -2526,12 +2271,6 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "subtle"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.70"
|
version = "1.0.70"
|
||||||
@@ -2543,18 +2282,6 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"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]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.2.0"
|
version = "3.2.0"
|
||||||
@@ -2563,7 +2290,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
"redox_syscall 0.2.6",
|
"redox_syscall 0.2.6",
|
||||||
"remove_dir_all",
|
"remove_dir_all",
|
||||||
"winapi",
|
"winapi",
|
||||||
@@ -2764,9 +2491,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.26"
|
version = "0.1.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
|
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@@ -2787,9 +2514,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.18"
|
version = "0.1.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
|
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
@@ -2867,7 +2594,7 @@ dependencies = [
|
|||||||
"ipnet",
|
"ipnet",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
@@ -2911,12 +2638,6 @@ dependencies = [
|
|||||||
"unchecked-index",
|
"unchecked-index",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ubyte"
|
name = "ubyte"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@@ -2972,12 +2693,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unindent"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -3194,27 +2909,6 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
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]]
|
[[package]]
|
||||||
name = "zstd"
|
name = "zstd"
|
||||||
version = "0.5.4+zstd.1.4.7"
|
version = "0.5.4+zstd.1.4.7"
|
||||||
|
|||||||
+12
-14
@@ -8,7 +8,6 @@ repository = "https://gitlab.com/famedly/conduit"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
rust = "1.50"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# 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
|
# Used to handle requests
|
||||||
# TODO: This can become optional as soon as proper configs are supported
|
# 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/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
|
# 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 = { 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 = { 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/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
|
# Used for long polling and federation sender, should be the same as rocket::tokio
|
||||||
tokio = "1.2.0"
|
tokio = "1.2.0"
|
||||||
# Used for storing data permanently
|
# Used for storing data permanently
|
||||||
sled = { version = "0.34.6", features = ["compression", "no_metrics"], optional = true }
|
sled = { version = "0.34.6", features = ["compression", "no_metrics"] }
|
||||||
rocksdb = { version = "0.16.0", features = ["multi-threaded-cf"], optional = true }
|
|
||||||
#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] }
|
#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
|
# 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"
|
tracing-opentelemetry = "0.11.0"
|
||||||
opentelemetry-jaeger = "0.11.0"
|
opentelemetry-jaeger = "0.11.0"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
lru-cache = "0.1.2"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["conduit_bin", "backend_sled"]
|
default = ["conduit_bin"]
|
||||||
backend_sled = ["sled"]
|
|
||||||
backend_rocksdb = ["rocksdb"]
|
|
||||||
conduit_bin = [] # TODO: add rocket to this when it is optional
|
conduit_bin = [] # TODO: add rocket to this when it is optional
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
@@ -102,16 +103,13 @@ instead of a server that has high scalability."""
|
|||||||
section = "net"
|
section = "net"
|
||||||
priority = "optional"
|
priority = "optional"
|
||||||
assets = [
|
assets = [
|
||||||
|
["debian/env.local", "etc/matrix-conduit/local", "644"],
|
||||||
["debian/README.Debian", "usr/share/doc/matrix-conduit/", "644"],
|
["debian/README.Debian", "usr/share/doc/matrix-conduit/", "644"],
|
||||||
["README.md", "usr/share/doc/matrix-conduit/", "644"],
|
["README.md", "usr/share/doc/matrix-conduit/", "644"],
|
||||||
["target/release/conduit", "usr/sbin/matrix-conduit", "755"],
|
["target/release/conduit", "usr/sbin/matrix-conduit", "755"],
|
||||||
]
|
]
|
||||||
conf-files = [
|
conf-files = [
|
||||||
"/etc/matrix-conduit/conduit.toml"
|
"/etc/matrix-conduit/local"
|
||||||
]
|
]
|
||||||
maintainer-scripts = "debian/"
|
maintainer-scripts = "debian/"
|
||||||
systemd-units = { unit-name = "matrix-conduit" }
|
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
|
## 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:
|
what you need. Now copy the right url:
|
||||||
- x84_64: `https://conduit.rs/master/x86_64/conduit-bin`
|
- x84_64: `https://conduit.rs/master/x86_64/conduit-bin`
|
||||||
- armv7: `https://conduit.rs/master/armv7/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
|
$ 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
|
## Adding a Conduit user
|
||||||
|
|
||||||
While Conduit can run as any user it is usually better to use dedicated users for different services.
|
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_encryption = true
|
||||||
allow_federation = true
|
allow_federation = true
|
||||||
|
|
||||||
trusted_servers = ["matrix.org"]
|
|
||||||
|
|
||||||
#cache_capacity = 1073741824 # in bytes, 1024 * 1024 * 1024
|
#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
|
#workers = 4 # default: cpu core count * 2
|
||||||
|
|
||||||
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
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
|
```bash
|
||||||
$ sudo systemctl enable conduit
|
$ 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 \
|
RUN if [[ $LOCAL == "true" ]]; then \
|
||||||
cargo install --path . ; \
|
cargo install --path . ; \
|
||||||
else \
|
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
|
fi
|
||||||
|
|
||||||
########################## RUNTIME IMAGE ##########################
|
########################## RUNTIME IMAGE ##########################
|
||||||
@@ -40,9 +40,7 @@ FROM alpine:3.12
|
|||||||
|
|
||||||
ARG CREATED
|
ARG CREATED
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ARG GIT_REF=origin/master
|
ARG GIT_REF=HEAD
|
||||||
|
|
||||||
ENV CONDUIT_CONFIG="/srv/conduit/conduit.toml"
|
|
||||||
|
|
||||||
# Labels according to https://github.com/opencontainers/image-spec/blob/master/annotations.md
|
# Labels according to https://github.com/opencontainers/image-spec/blob/master/annotations.md
|
||||||
# including a custom label specifying the build command
|
# 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.description="A Matrix homeserver written in Rust" \
|
||||||
org.opencontainers.image.url="https://conduit.rs/" \
|
org.opencontainers.image.url="https://conduit.rs/" \
|
||||||
org.opencontainers.image.revision=${GIT_REF} \
|
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.licenses="Apache-2.0" \
|
||||||
org.opencontainers.image.documentation="" \
|
org.opencontainers.image.documentation="" \
|
||||||
org.opencontainers.image.ref.name="" \
|
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.
|
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
|
- [Ruma](https://www.ruma.io): Useful structures for endpoint requests and
|
||||||
responses that can be (de)serialized
|
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
|
Download or compile a Conduit binary, set up the config and call it from somewhere like a systemd script. [Read
|
||||||
more](DEPLOY.md)
|
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
|
##### Deploy using Docker
|
||||||
|
|
||||||
Pull and run the docker image with
|
Pull and run the docker image with
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
docker pull matrixconduit/matrix-conduit:latest
|
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)
|
Or build and run it with docker or docker-compose. [Read more](docker/README.md)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# YOU NEED TO EDIT THIS
|
# YOU NEED TO EDIT THIS
|
||||||
#server_name = "your.server.name"
|
#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"
|
database_path = "/var/lib/conduit/conduit.db"
|
||||||
|
|
||||||
# The port Conduit will be running on. You need to set up a reverse proxy in
|
# 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
|
# Enable jaeger to support monitoring and troubleshooting through jaeger
|
||||||
#allow_jaeger = false
|
#allow_jaeger = false
|
||||||
|
|
||||||
trusted_servers = ["matrix.org"]
|
|
||||||
|
|
||||||
#cache_capacity = 1073741824 # in bytes, 1024 * 1024 * 1024
|
#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"
|
#log = "info,state_res=warn,rocket=off,_=off,sled=off"
|
||||||
#workers = 4 # default: cpu core count * 2
|
#workers = 4 # default: cpu core count * 2
|
||||||
|
|
||||||
|
|||||||
Vendored
+10
-10
@@ -4,25 +4,25 @@ Conduit for Debian
|
|||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
When installed, Debconf generates the configuration of the homeserver
|
When installed, Debconf handles the configuration of the homeserver (host)name,
|
||||||
(host)name, the address and port it listens on. This configuration ends up in
|
the address and port it listens on. These configuration variables end up in
|
||||||
/etc/matrix-conduit/conduit.toml.
|
/etc/matrix-conduit/debian.
|
||||||
|
|
||||||
You can tweak more detailed settings by uncommenting and setting the variables
|
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
|
in /etc/matrix-conduit/local. This involves settings such as the maximum file
|
||||||
file size for download/upload, enabling federation, etc.
|
size for download/upload, enabling federation, etc.
|
||||||
|
|
||||||
Running
|
Running
|
||||||
-------
|
-------
|
||||||
|
|
||||||
The package uses the matrix-conduit.service systemd unit file to start and
|
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.
|
environment before running the server.
|
||||||
|
|
||||||
This package assumes by default that Conduit will be placed behind a reverse
|
This package assumes by default that Conduit is placed behind a reverse proxy
|
||||||
proxy such as Apache or nginx. This default deployment entails just listening
|
such as Apache or nginx. This default deployment entails just listening on
|
||||||
on 127.0.0.1 and the free port 6167 and is reachable via a client using the URL
|
127.0.0.1 and the free port 14004 and is reachable via a client using the URL
|
||||||
http://localhost:6167.
|
http://localhost:14004.
|
||||||
|
|
||||||
At a later stage this packaging may support also setting up TLS and running
|
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
|
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
|
SystemCallErrorNumber=EPERM
|
||||||
StateDirectory=matrix-conduit
|
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
|
ExecStart=/usr/sbin/matrix-conduit
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|||||||
Vendored
+38
-50
@@ -4,8 +4,8 @@ set -e
|
|||||||
. /usr/share/debconf/confmodule
|
. /usr/share/debconf/confmodule
|
||||||
|
|
||||||
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
||||||
CONDUIT_CONFIG_FILE="${CONDUIT_CONFIG_PATH}/conduit.toml"
|
CONDUIT_CONFIG_FILE="$CONDUIT_CONFIG_PATH/debian"
|
||||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit/conduit_db
|
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
configure)
|
configure)
|
||||||
@@ -13,7 +13,7 @@ case "$1" in
|
|||||||
if ! getent passwd _matrix-conduit > /dev/null ; then
|
if ! getent passwd _matrix-conduit > /dev/null ; then
|
||||||
echo 'Adding system user for the Conduit Matrix homeserver' 1>&2
|
echo 'Adding system user for the Conduit Matrix homeserver' 1>&2
|
||||||
adduser --system --group --quiet \
|
adduser --system --group --quiet \
|
||||||
--home "$CONDUIT_DATABASE_PATH" \
|
--home $CONDUIT_DATABASE_PATH \
|
||||||
--disabled-login \
|
--disabled-login \
|
||||||
--force-badname \
|
--force-badname \
|
||||||
_matrix-conduit
|
_matrix-conduit
|
||||||
@@ -25,60 +25,48 @@ case "$1" in
|
|||||||
chown _matrix-conduit "$CONDUIT_DATABASE_PATH"
|
chown _matrix-conduit "$CONDUIT_DATABASE_PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -e "$CONDUIT_CONFIG_FILE" ]; then
|
# Write the debconf values in the config.
|
||||||
# Write the debconf values in the config.
|
db_get matrix-conduit/hostname
|
||||||
db_get matrix-conduit/hostname
|
ROCKET_SERVER_NAME="$RET"
|
||||||
CONDUIT_SERVER_NAME="$RET"
|
db_get matrix-conduit/address
|
||||||
db_get matrix-conduit/address
|
ROCKET_ADDRESS="$RET"
|
||||||
CONDUIT_ADDRESS="$RET"
|
db_get matrix-conduit/port
|
||||||
db_get matrix-conduit/port
|
ROCKET_PORT="$RET"
|
||||||
CONDUIT_PORT="$RET"
|
cat >"$CONDUIT_CONFIG_FILE" << EOF
|
||||||
mkdir -p "$CONDUIT_CONFIG_PATH"
|
# Conduit homeserver Debian configuration
|
||||||
cat > "$CONDUIT_CONFIG_FILE" << EOF
|
#
|
||||||
[global]
|
# Conduit is an application based on the Rocket web framework.
|
||||||
# The server_name is the name of this server. It is used as a suffix for user
|
# Configuration of Conduit happens via Debconf (of which the resulting config
|
||||||
# and room ids. Examples: matrix.org, conduit.rs
|
# is in this file) and optionally by uncommenting and tweaking the variables in
|
||||||
# The Conduit server needs to be reachable at https://your.server.name/ on port
|
# /etc/matrix-conduit/local.
|
||||||
# 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}"
|
|
||||||
|
|
||||||
# This is the only directory where Conduit will save its data.
|
# THIS FILE IS GENERATED BY DEBCONF AND WILL BE OVERRIDDEN!
|
||||||
database_path = "${CONDUIT_DATABASE_PATH}"
|
#
|
||||||
|
# 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
|
# 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.
|
# 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
|
# The port of the Matrix homeserver.
|
||||||
# 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.
|
# This port is could be any available port if accessed by a reverse proxy.
|
||||||
port = ${CONDUIT_PORT}
|
# By default the server listens on port 8000.
|
||||||
|
ROCKET_PORT="$ROCKET_PORT"
|
||||||
|
|
||||||
# Max size for uploads
|
# THIS FILE IS GENERATED BY DEBCONF AND WILL BE OVERRIDDEN!
|
||||||
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
|
|
||||||
EOF
|
EOF
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
Vendored
-5
@@ -1,16 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
. /usr/share/debconf/confmodule
|
|
||||||
|
|
||||||
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
||||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit
|
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
purge)
|
purge)
|
||||||
# Remove debconf changes from the db
|
|
||||||
db_purge
|
|
||||||
|
|
||||||
# Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior
|
# Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior
|
||||||
# "configuration files must be preserved when the package is removed, and
|
# "configuration files must be preserved when the package is removed, and
|
||||||
# only deleted when the package is purged."
|
# 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
|
Template: matrix-conduit/port
|
||||||
Type: string
|
Type: string
|
||||||
Default: 6167
|
Default: 14004
|
||||||
Description: The port of the Matrix homeserver
|
Description: The port of the Matrix homeserver
|
||||||
This port is most often just accessed by a reverse proxy.
|
This port is most often just accessed by a reverse proxy.
|
||||||
|
|||||||
+21
-26
@@ -5,48 +5,43 @@ services:
|
|||||||
homeserver:
|
homeserver:
|
||||||
### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image,
|
### If you already built the Conduit image with 'docker build' or want to use the Docker Hub image,
|
||||||
### then you are ready to go.
|
### 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
|
### 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:
|
### 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
|
### 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:
|
# build:
|
||||||
context: .
|
# context: .
|
||||||
args:
|
# args:
|
||||||
CREATED: '2021-03-16T08:18:27Z'
|
# CREATED:
|
||||||
VERSION: '0.1.0'
|
# VERSION:
|
||||||
LOCAL: 'false'
|
# LOCAL: 'false'
|
||||||
GIT_REF: origin/master
|
# GIT_REF: HEAD
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 8448:8000
|
- 8448:8000
|
||||||
volumes:
|
volumes:
|
||||||
- db:/srv/conduit/.local/share/conduit
|
- db:/srv/conduit/.local/share/conduit
|
||||||
### Uncomment if you want to use conduit.toml to configure Conduit
|
### Uncomment if you want to use Rocket.toml to configure Conduit
|
||||||
### Note: Set env vars will override conduit.toml values
|
### Note: Set env vars will override Rocket.toml values
|
||||||
# - ./conduit.toml:/srv/conduit/conduit.toml
|
# - ./Rocket.toml:/srv/conduit/Rocket.toml
|
||||||
environment:
|
environment:
|
||||||
CONDUIT_SERVER_NAME: localhost:8000 # replace with your own name
|
ROCKET_SERVER_NAME: localhost:8000 # replace with your own name
|
||||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
|
||||||
### Uncomment and change values as desired
|
### Uncomment and change values as desired
|
||||||
# CONDUIT_ADDRESS: 127.0.0.1
|
# ROCKET_LOG: normal # Available levels are: off, debug, normal, critical
|
||||||
# CONDUIT_PORT: 8000
|
# ROCKET_PORT: 8000
|
||||||
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if you want to configure purely by env vars, set this to an empty string ''
|
# ROCKET_REGISTRATION_DISABLED: 'true'
|
||||||
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
# ROCKET_ENCRYPTION_DISABLED: 'true'
|
||||||
# CONDUIT_LOG: info # default is: "info,rocket=off,_=off,sled=off"
|
# ROCKET_FEDERATION_ENABLED: 'true'
|
||||||
# CONDUIT_ALLOW_JAEGER: 'false'
|
# ROCKET_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||||
# CONDUIT_ALLOW_REGISTRATION : 'false'
|
# ROCKET_WORKERS: 10
|
||||||
# CONDUIT_ALLOW_ENCRYPTION: 'false'
|
# ROCKET_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||||
# 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
|
|
||||||
|
|
||||||
### Uncomment if you want to use your own Element-Web App.
|
### 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
|
### 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
|
### Domain or Subdomain for the communication between Element and Conduit
|
||||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||||
# element-web:
|
# element-web:
|
||||||
# image: vectorim/element-web:latest
|
# image: vectorim/riot-web:latest
|
||||||
# restart: unless-stopped
|
# restart: unless-stopped
|
||||||
# ports:
|
# ports:
|
||||||
# - 8009:80
|
# - 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 LOCAL=false
|
||||||
ARG CREATED
|
ARG CREATED
|
||||||
ARG VERSION
|
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')`
|
- **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)`
|
- **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`.
|
- **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
|
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
|
After building the image you can simply run it with
|
||||||
|
|
||||||
``` bash
|
``` 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).
|
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.
|
||||||
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.
|
|
||||||
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.
|
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:
|
# CREATED:
|
||||||
# VERSION:
|
# VERSION:
|
||||||
# LOCAL: 'false'
|
# LOCAL: 'false'
|
||||||
# GIT_REF: origin/master
|
# GIT_REF: HEAD
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- db:/srv/conduit/.local/share/conduit
|
- db:/srv/conduit/.local/share/conduit
|
||||||
### Uncomment if you want to use conduit.toml to configure Conduit
|
### Uncomment if you want to use Rocket.toml to configure Conduit
|
||||||
### Note: Set env vars will override conduit.toml values
|
### Note: Set env vars will override Rocket.toml values
|
||||||
# - ./conduit.toml:/srv/conduit/conduit.toml
|
# - ./Rocket.toml:/srv/conduit/Rocket.toml
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
environment:
|
environment:
|
||||||
CONDUIT_SERVER_NAME: localhost:8000 # replace with your own name
|
ROCKET_SERVER_NAME: localhost:8000 # replace with your own name
|
||||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
|
||||||
### Uncomment and change values as desired
|
### Uncomment and change values as desired
|
||||||
# CONDUIT_ADDRESS: 127.0.0.1
|
# ROCKET_LOG: normal # Available levels are: off, debug, normal, critical
|
||||||
# CONDUIT_PORT: 8000
|
# ROCKET_PORT: 8000
|
||||||
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if want to configure purely by env vars, set this to an empty string ''
|
# ROCKET_REGISTRATION_DISABLED: 'true'
|
||||||
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
# ROCKET_ENCRYPTION_DISABLED: 'true'
|
||||||
# CONDUIT_LOG: info # default is: "info,rocket=off,_=off,sled=off"
|
# ROCKET_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||||
# CONDUIT_ALLOW_JAEGER: 'false'
|
# ROCKET_WORKERS: 10
|
||||||
# CONDUIT_ALLOW_REGISTRATION : 'false'
|
# ROCKET_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||||
# 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
|
|
||||||
|
|
||||||
### Uncomment if you want to use your own Element-Web App.
|
### 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
|
### 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
|
### Domain or Subdomain for the communication between Element and Conduit
|
||||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||||
# element-web:
|
# element-web:
|
||||||
# image: vectorim/element-web:latest
|
# image: vectorim/riot-web:latest
|
||||||
# restart: unless-stopped
|
# restart: unless-stopped
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./element_config.json:/app/config.json
|
# - ./element_config.json:/app/config.json
|
||||||
|
|||||||
+44
-36
@@ -1,11 +1,13 @@
|
|||||||
use crate::{utils, Error, Result};
|
use crate::{utils, Error, Result};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use ruma::api::{IncomingResponse, OutgoingRequest, SendAccessToken};
|
use ruma::{
|
||||||
|
api::{IncomingResponse, OutgoingRequest, SendAccessToken},
|
||||||
|
client::HttpClientExt,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
mem,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,47 +22,53 @@ where
|
|||||||
let destination = registration.get("url").unwrap().as_str().unwrap();
|
let destination = registration.get("url").unwrap().as_str().unwrap();
|
||||||
let hs_token = registration.get("hs_token").unwrap().as_str().unwrap();
|
let hs_token = registration.get("hs_token").unwrap().as_str().unwrap();
|
||||||
|
|
||||||
let mut http_request = request
|
globals
|
||||||
.try_into_http_request::<BytesMut>(&destination, SendAccessToken::IfRequired(""))
|
.reqwest_client()
|
||||||
.unwrap()
|
.send_customized_matrix_request(
|
||||||
.map(|body| body.freeze());
|
&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('?') {
|
||||||
|
"&"
|
||||||
|
} else {
|
||||||
|
"?"
|
||||||
|
};
|
||||||
|
|
||||||
let mut parts = http_request.uri().clone().into_parts();
|
parts.path_and_query =
|
||||||
let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned();
|
Some((old_path_and_query + symbol + "access_token=" + hs_token).parse()?);
|
||||||
let symbol = if old_path_and_query.contains('?') {
|
*http_request.uri_mut() = parts.try_into()?;
|
||||||
"&"
|
|
||||||
} else {
|
|
||||||
"?"
|
|
||||||
};
|
|
||||||
|
|
||||||
parts.path_and_query = Some(
|
Ok(())
|
||||||
(old_path_and_query + symbol + "access_token=" + hs_token)
|
},
|
||||||
.parse()
|
)
|
||||||
.unwrap(),
|
.await
|
||||||
);
|
.map_err(|e| {
|
||||||
*http_request.uri_mut() = parts.try_into().expect("our manipulation is always valid");
|
warn!("Appservice error: {}", e);
|
||||||
|
Error::BadServerResponse("Server returned an error.")
|
||||||
let mut reqwest_request = reqwest::Request::try_from(http_request)
|
})?;
|
||||||
.expect("all http requests are valid reqwest requests");
|
|
||||||
|
|
||||||
*reqwest_request.timeout_mut() = Some(Duration::from_secs(30));
|
*reqwest_request.timeout_mut() = Some(Duration::from_secs(30));
|
||||||
|
|
||||||
let url = reqwest_request.url().clone();
|
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
|
// Because reqwest::Response -> http::Response is complicated:
|
||||||
let status = response.status();
|
let status = reqwest_response.status();
|
||||||
let mut http_response_builder = http::Response::builder()
|
let mut http_response = http::Response::builder().status(status);
|
||||||
.status(status)
|
let headers = http_response.headers_mut().unwrap();
|
||||||
.version(response.version());
|
|
||||||
mem::swap(
|
|
||||||
response.headers_mut(),
|
|
||||||
http_response_builder
|
|
||||||
.headers_mut()
|
|
||||||
.expect("http::response::Builder is usable"),
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
warn!("server error: {}", e);
|
||||||
Vec::new().into()
|
Vec::new().into()
|
||||||
}); // TODO: handle timeout
|
}); // TODO: handle timeout
|
||||||
@@ -76,7 +84,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let response = T::IncomingResponse::try_from_http_response(
|
let response = T::IncomingResponse::try_from_http_response(
|
||||||
http_response_builder
|
http_response
|
||||||
.body(body)
|
.body(body)
|
||||||
.expect("reqwest body is valid http 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 super::{State, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::{pdu::PduBuilder, utils, ConduitResult, Database, Error, Ruma};
|
use crate::{pdu::PduBuilder, utils, ConduitResult, Database, Error, Ruma};
|
||||||
@@ -42,7 +42,7 @@ const GUEST_NAME_LENGTH: usize = 10;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_register_available_route(
|
pub async fn get_register_available_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_username_availability::Request<'_>>,
|
body: Ruma<get_username_availability::Request<'_>>,
|
||||||
) -> ConduitResult<get_username_availability::Response> {
|
) -> ConduitResult<get_username_availability::Response> {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
@@ -85,10 +85,10 @@ pub async fn get_register_available_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn register_route(
|
pub async fn register_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<register::Request<'_>>,
|
body: Ruma<register::Request<'_>>,
|
||||||
) -> ConduitResult<register::Response> {
|
) -> ConduitResult<register::Response> {
|
||||||
if !db.globals.allow_registration() && !body.from_appservice {
|
if !db.globals.allow_registration() {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"Registration has been disabled.",
|
"Registration has been disabled.",
|
||||||
@@ -143,28 +143,16 @@ pub async fn register_route(
|
|||||||
|
|
||||||
if !body.from_appservice {
|
if !body.from_appservice {
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = db.uiaa.try_auth(
|
let (worked, uiaainfo) =
|
||||||
&UserId::parse_with_server_name("", db.globals.server_name())
|
db.uiaa
|
||||||
.expect("we know this is valid"),
|
.try_auth(&user_id, "".into(), auth, &uiaainfo, &db.users, &db.globals)?;
|
||||||
"".into(),
|
|
||||||
auth,
|
|
||||||
&uiaainfo,
|
|
||||||
&db.users,
|
|
||||||
&db.globals,
|
|
||||||
)?;
|
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
// Success!
|
// Success!
|
||||||
} else {
|
} else {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
db.uiaa.create(
|
db.uiaa.create(&user_id, "".into(), &uiaainfo)?;
|
||||||
&UserId::parse_with_server_name("", db.globals.server_name())
|
|
||||||
.expect("we know this is valid"),
|
|
||||||
"".into(),
|
|
||||||
&uiaainfo,
|
|
||||||
&body.json_body.expect("body is json"),
|
|
||||||
)?;
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,11 +167,12 @@ pub async fn register_route(
|
|||||||
let password = if is_guest {
|
let password = if is_guest {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
body.password.as_deref()
|
body.password.clone()
|
||||||
};
|
}
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
db.users.create(&user_id, password)?;
|
db.users.create(&user_id, &password)?;
|
||||||
|
|
||||||
// Initial data
|
// Initial data
|
||||||
db.account_data.update(
|
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 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
|
// Create a user for the server
|
||||||
let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name())
|
let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name())
|
||||||
.expect("@conduit:server_name is valid");
|
.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());
|
let room_id = RoomId::new(db.globals.server_name());
|
||||||
|
|
||||||
@@ -286,8 +275,18 @@ pub async fn register_route(
|
|||||||
event_type: EventType::RoomPowerLevels,
|
event_type: EventType::RoomPowerLevels,
|
||||||
content: serde_json::to_value(
|
content: serde_json::to_value(
|
||||||
ruma::events::room::power_levels::PowerLevelsEventContent {
|
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::default()
|
users_default: 0.into(),
|
||||||
|
notifications: ruma::events::room::power_levels::NotificationPowerLevels {
|
||||||
|
room: 50.into(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
@@ -496,7 +495,7 @@ pub async fn register_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn change_password_route(
|
pub async fn change_password_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<change_password::Request<'_>>,
|
body: Ruma<change_password::Request<'_>>,
|
||||||
) -> ConduitResult<change_password::Response> {
|
) -> ConduitResult<change_password::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -527,17 +526,11 @@ pub async fn change_password_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else {
|
} else {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
db.uiaa.create(
|
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||||
&sender_user,
|
|
||||||
&sender_device,
|
|
||||||
&uiaainfo,
|
|
||||||
&body.json_body.expect("body is json"),
|
|
||||||
)?;
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.users
|
db.users.set_password(&sender_user, &body.new_password)?;
|
||||||
.set_password(&sender_user, Some(&body.new_password))?;
|
|
||||||
|
|
||||||
if body.logout_devices {
|
if body.logout_devices {
|
||||||
// Logout all devices except the current one
|
// 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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn deactivate_route(
|
pub async fn deactivate_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<deactivate::Request<'_>>,
|
body: Ruma<deactivate::Request<'_>>,
|
||||||
) -> ConduitResult<deactivate::Response> {
|
) -> ConduitResult<deactivate::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -619,12 +612,7 @@ pub async fn deactivate_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else {
|
} else {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
db.uiaa.create(
|
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||||
&sender_user,
|
|
||||||
&sender_device,
|
|
||||||
&uiaainfo,
|
|
||||||
&body.json_body.expect("body is json"),
|
|
||||||
)?;
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -24,7 +22,7 @@ use rocket::{delete, get, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn create_alias_route(
|
pub async fn create_alias_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_alias::Request<'_>>,
|
body: Ruma<create_alias::Request<'_>>,
|
||||||
) -> ConduitResult<create_alias::Response> {
|
) -> ConduitResult<create_alias::Response> {
|
||||||
if db.rooms.id_from_alias(&body.room_alias)?.is_some() {
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_alias_route(
|
pub async fn delete_alias_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_alias::Request<'_>>,
|
body: Ruma<delete_alias::Request<'_>>,
|
||||||
) -> ConduitResult<delete_alias::Response> {
|
) -> ConduitResult<delete_alias::Response> {
|
||||||
db.rooms.set_alias(&body.room_alias, None, &db.globals)?;
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_alias_route(
|
pub async fn get_alias_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_alias::Request<'_>>,
|
body: Ruma<get_alias::Request<'_>>,
|
||||||
) -> ConduitResult<get_alias::Response> {
|
) -> ConduitResult<get_alias::Response> {
|
||||||
get_alias_helper(&db, &body.room_alias).await
|
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)? {
|
match db.rooms.id_from_alias(&room_alias)? {
|
||||||
Some(r) => room_id = Some(r),
|
Some(r) => room_id = Some(r),
|
||||||
None => {
|
None => {
|
||||||
let iter = db.appservice.iter_all()?;
|
for (_id, registration) in db.appservice.iter_all().filter_map(|r| r.ok()) {
|
||||||
for (_id, registration) in iter.filter_map(|r| r.ok()) {
|
|
||||||
let aliases = registration
|
let aliases = registration
|
||||||
.get("namespaces")
|
.get("namespaces")
|
||||||
.and_then(|ns| ns.get("aliases"))
|
.and_then(|ns| ns.get("aliases"))
|
||||||
@@ -97,7 +94,13 @@ pub async fn get_alias_helper(
|
|||||||
.map_or_else(Vec::new, |aliases| {
|
.map_or_else(Vec::new, |aliases| {
|
||||||
aliases
|
aliases
|
||||||
.iter()
|
.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<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+15
-17
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
@@ -21,7 +19,7 @@ use rocket::{delete, get, post, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn create_backup_route(
|
pub async fn create_backup_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_backup::Request>,
|
body: Ruma<create_backup::Request>,
|
||||||
) -> ConduitResult<create_backup::Response> {
|
) -> ConduitResult<create_backup::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn update_backup_route(
|
pub async fn update_backup_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<update_backup::Request<'_>>,
|
body: Ruma<update_backup::Request<'_>>,
|
||||||
) -> ConduitResult<update_backup::Response> {
|
) -> ConduitResult<update_backup::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_latest_backup_route(
|
pub async fn get_latest_backup_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_latest_backup::Request>,
|
body: Ruma<get_latest_backup::Request>,
|
||||||
) -> ConduitResult<get_latest_backup::Response> {
|
) -> ConduitResult<get_latest_backup::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_backup_route(
|
pub async fn get_backup_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_backup::Request<'_>>,
|
body: Ruma<get_backup::Request<'_>>,
|
||||||
) -> ConduitResult<get_backup::Response> {
|
) -> ConduitResult<get_backup::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_backup_route(
|
pub async fn delete_backup_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_backup::Request<'_>>,
|
body: Ruma<delete_backup::Request<'_>>,
|
||||||
) -> ConduitResult<delete_backup::Response> {
|
) -> ConduitResult<delete_backup::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn add_backup_keys_route(
|
pub async fn add_backup_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<add_backup_keys::Request<'_>>,
|
body: Ruma<add_backup_keys::Request<'_>>,
|
||||||
) -> ConduitResult<add_backup_keys::Response> {
|
) -> ConduitResult<add_backup_keys::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn add_backup_key_sessions_route(
|
pub async fn add_backup_key_sessions_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<add_backup_key_sessions::Request<'_>>,
|
body: Ruma<add_backup_key_sessions::Request<'_>>,
|
||||||
) -> ConduitResult<add_backup_key_sessions::Response> {
|
) -> ConduitResult<add_backup_key_sessions::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn add_backup_key_session_route(
|
pub async fn add_backup_key_session_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<add_backup_key_session::Request<'_>>,
|
body: Ruma<add_backup_key_session::Request<'_>>,
|
||||||
) -> ConduitResult<add_backup_key_session::Response> {
|
) -> ConduitResult<add_backup_key_session::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_backup_keys_route(
|
pub async fn get_backup_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_backup_keys::Request<'_>>,
|
body: Ruma<get_backup_keys::Request<'_>>,
|
||||||
) -> ConduitResult<get_backup_keys::Response> {
|
) -> ConduitResult<get_backup_keys::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_backup_key_sessions_route(
|
pub async fn get_backup_key_sessions_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_backup_key_sessions::Request<'_>>,
|
body: Ruma<get_backup_key_sessions::Request<'_>>,
|
||||||
) -> ConduitResult<get_backup_key_sessions::Response> {
|
) -> ConduitResult<get_backup_key_sessions::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let sessions = db
|
let sessions = db
|
||||||
.key_backups
|
.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())
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_backup_key_session_route(
|
pub async fn get_backup_key_session_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_backup_key_session::Request<'_>>,
|
body: Ruma<get_backup_key_session::Request<'_>>,
|
||||||
) -> ConduitResult<get_backup_key_session::Response> {
|
) -> ConduitResult<get_backup_key_session::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_backup_keys_route(
|
pub async fn delete_backup_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_backup_keys::Request<'_>>,
|
body: Ruma<delete_backup_keys::Request<'_>>,
|
||||||
) -> ConduitResult<delete_backup_keys::Response> {
|
) -> ConduitResult<delete_backup_keys::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_backup_key_sessions_route(
|
pub async fn delete_backup_key_sessions_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_backup_key_sessions::Request<'_>>,
|
body: Ruma<delete_backup_key_sessions::Request<'_>>,
|
||||||
) -> ConduitResult<delete_backup_key_sessions::Response> {
|
) -> ConduitResult<delete_backup_key_sessions::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_backup_key_session_route(
|
pub async fn delete_backup_key_session_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_backup_key_session::Request<'_>>,
|
body: Ruma<delete_backup_key_session::Request<'_>>,
|
||||||
) -> ConduitResult<delete_backup_key_session::Response> {
|
) -> ConduitResult<delete_backup_key_session::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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 super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -10,11 +8,11 @@ use ruma::{
|
|||||||
set_room_account_data,
|
set_room_account_data,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events::{AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent},
|
events::{custom::CustomEventContent, AnyBasicEventContent, BasicEvent},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::{json, value::RawValue as RawJsonValue};
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, put};
|
use rocket::{get, put};
|
||||||
@@ -25,12 +23,12 @@ use rocket::{get, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_global_account_data_route(
|
pub async fn set_global_account_data_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_global_account_data::Request<'_>>,
|
body: Ruma<set_global_account_data::Request<'_>>,
|
||||||
) -> ConduitResult<set_global_account_data::Response> {
|
) -> ConduitResult<set_global_account_data::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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."))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
||||||
|
|
||||||
let event_type = body.event_type.to_string();
|
let event_type = body.event_type.to_string();
|
||||||
@@ -39,10 +37,9 @@ pub async fn set_global_account_data_route(
|
|||||||
None,
|
None,
|
||||||
sender_user,
|
sender_user,
|
||||||
event_type.clone().into(),
|
event_type.clone().into(),
|
||||||
&json!({
|
&BasicEvent {
|
||||||
"type": event_type,
|
content: CustomEventContent { event_type, data },
|
||||||
"content": data,
|
},
|
||||||
}),
|
|
||||||
&db.globals,
|
&db.globals,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -60,12 +57,12 @@ pub async fn set_global_account_data_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_room_account_data_route(
|
pub async fn set_room_account_data_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_room_account_data::Request<'_>>,
|
body: Ruma<set_room_account_data::Request<'_>>,
|
||||||
) -> ConduitResult<set_room_account_data::Response> {
|
) -> ConduitResult<set_room_account_data::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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."))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
||||||
|
|
||||||
let event_type = body.event_type.to_string();
|
let event_type = body.event_type.to_string();
|
||||||
@@ -74,10 +71,9 @@ pub async fn set_room_account_data_route(
|
|||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
sender_user,
|
sender_user,
|
||||||
event_type.clone().into(),
|
event_type.clone().into(),
|
||||||
&json!({
|
&BasicEvent {
|
||||||
"type": event_type,
|
content: CustomEventContent { event_type, data },
|
||||||
"content": data,
|
},
|
||||||
}),
|
|
||||||
&db.globals,
|
&db.globals,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -92,7 +88,7 @@ pub async fn set_room_account_data_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_global_account_data_route(
|
pub async fn get_global_account_data_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_global_account_data::Request<'_>>,
|
body: Ruma<get_global_account_data::Request<'_>>,
|
||||||
) -> ConduitResult<get_global_account_data::Response> {
|
) -> ConduitResult<get_global_account_data::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||||
db.flush().await?;
|
db.flush().await?;
|
||||||
|
|
||||||
let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get())
|
let account_data = serde_json::from_str::<ExtractEventContent>(event.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
.expect("event to contain a content field")
|
||||||
.content;
|
.content;
|
||||||
|
|
||||||
Ok(get_global_account_data::Response { account_data }.into())
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_room_account_data_route(
|
pub async fn get_room_account_data_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_room_account_data::Request<'_>>,
|
body: Ruma<get_room_account_data::Request<'_>>,
|
||||||
) -> ConduitResult<get_room_account_data::Response> {
|
) -> ConduitResult<get_room_account_data::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||||
db.flush().await?;
|
db.flush().await?;
|
||||||
|
|
||||||
let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get())
|
let account_data = serde_json::from_str::<ExtractEventContent>(event.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
.expect("event to contain a content field")
|
||||||
.content;
|
.content;
|
||||||
|
|
||||||
Ok(get_room_account_data::Response { account_data }.into())
|
Ok(get_room_account_data::Response { account_data }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct ExtractRoomEventContent {
|
struct ExtractEventContent {
|
||||||
content: Raw<AnyRoomAccountDataEventContent>,
|
content: Raw<AnyBasicEventContent>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ExtractGlobalEventContent {
|
|
||||||
content: Raw<AnyGlobalAccountDataEventContent>,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::api::client::{error::ErrorKind, r0::context::get_context};
|
use ruma::api::client::{error::ErrorKind, r0::context::get_context};
|
||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::get;
|
use rocket::get;
|
||||||
@@ -12,7 +12,7 @@ use rocket::get;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_context_route(
|
pub async fn get_context_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_context::Request<'_>>,
|
body: Ruma<get_context::Request<'_>>,
|
||||||
) -> ConduitResult<get_context::Response> {
|
) -> ConduitResult<get_context::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
@@ -20,7 +18,7 @@ use rocket::{delete, get, post, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_devices_route(
|
pub async fn get_devices_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_devices::Request>,
|
body: Ruma<get_devices::Request>,
|
||||||
) -> ConduitResult<get_devices::Response> {
|
) -> ConduitResult<get_devices::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_device_route(
|
pub async fn get_device_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_device::Request<'_>>,
|
body: Ruma<get_device::Request<'_>>,
|
||||||
) -> ConduitResult<get_device::Response> {
|
) -> ConduitResult<get_device::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn update_device_route(
|
pub async fn update_device_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<update_device::Request<'_>>,
|
body: Ruma<update_device::Request<'_>>,
|
||||||
) -> ConduitResult<update_device::Response> {
|
) -> ConduitResult<update_device::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_device_route(
|
pub async fn delete_device_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_device::Request<'_>>,
|
body: Ruma<delete_device::Request<'_>>,
|
||||||
) -> ConduitResult<delete_device::Response> {
|
) -> ConduitResult<delete_device::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -117,12 +115,7 @@ pub async fn delete_device_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else {
|
} else {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
db.uiaa.create(
|
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||||
&sender_user,
|
|
||||||
&sender_device,
|
|
||||||
&uiaainfo,
|
|
||||||
&body.json_body.expect("body is json"),
|
|
||||||
)?;
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +132,7 @@ pub async fn delete_device_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_devices_route(
|
pub async fn delete_devices_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_devices::Request<'_>>,
|
body: Ruma<delete_devices::Request<'_>>,
|
||||||
) -> ConduitResult<delete_devices::Response> {
|
) -> ConduitResult<delete_devices::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -171,12 +164,7 @@ pub async fn delete_devices_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else {
|
} else {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
db.uiaa.create(
|
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||||
&sender_user,
|
|
||||||
&sender_device,
|
|
||||||
&uiaainfo,
|
|
||||||
&body.json_body.expect("body is json"),
|
|
||||||
)?;
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Result, Ruma};
|
use crate::{ConduitResult, Database, Error, Result, Ruma};
|
||||||
use log::info;
|
use log::info;
|
||||||
@@ -35,7 +33,7 @@ use rocket::{get, post, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_public_rooms_filtered_route(
|
pub async fn get_public_rooms_filtered_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_public_rooms_filtered::Request<'_>>,
|
body: Ruma<get_public_rooms_filtered::Request<'_>>,
|
||||||
) -> ConduitResult<get_public_rooms_filtered::Response> {
|
) -> ConduitResult<get_public_rooms_filtered::Response> {
|
||||||
get_public_rooms_filtered_helper(
|
get_public_rooms_filtered_helper(
|
||||||
@@ -55,7 +53,7 @@ pub async fn get_public_rooms_filtered_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_public_rooms_route(
|
pub async fn get_public_rooms_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_public_rooms::Request<'_>>,
|
body: Ruma<get_public_rooms::Request<'_>>,
|
||||||
) -> ConduitResult<get_public_rooms::Response> {
|
) -> ConduitResult<get_public_rooms::Response> {
|
||||||
let response = get_public_rooms_filtered_helper(
|
let response = get_public_rooms_filtered_helper(
|
||||||
@@ -84,7 +82,7 @@ pub async fn get_public_rooms_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_room_visibility_route(
|
pub async fn set_room_visibility_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_room_visibility::Request<'_>>,
|
body: Ruma<set_room_visibility::Request<'_>>,
|
||||||
) -> ConduitResult<set_room_visibility::Response> {
|
) -> ConduitResult<set_room_visibility::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_room_visibility_route(
|
pub async fn get_room_visibility_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_room_visibility::Request<'_>>,
|
body: Ruma<get_room_visibility::Request<'_>>,
|
||||||
) -> ConduitResult<get_room_visibility::Response> {
|
) -> ConduitResult<get_room_visibility::Response> {
|
||||||
Ok(get_room_visibility::Response {
|
Ok(get_room_visibility::Response {
|
||||||
@@ -135,7 +133,9 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
filter: &IncomingFilter,
|
filter: &IncomingFilter,
|
||||||
_network: &IncomingRoomNetwork,
|
_network: &IncomingRoomNetwork,
|
||||||
) -> ConduitResult<get_public_rooms_filtered::Response> {
|
) -> 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
|
let response = db
|
||||||
.sending
|
.sending
|
||||||
@@ -160,12 +160,15 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.map(|c| {
|
.map(|c| {
|
||||||
// Convert ruma::api::federation::directory::get_public_rooms::v1::PublicRoomsChunk
|
// Convert ruma::api::federation::directory::get_public_rooms::v1::PublicRoomsChunk
|
||||||
// to ruma::api::client::r0::directory::PublicRoomsChunk
|
// to ruma::api::client::r0::directory::PublicRoomsChunk
|
||||||
serde_json::from_str(
|
Ok::<_, Error>(
|
||||||
&serde_json::to_string(&c)
|
serde_json::from_str(
|
||||||
.expect("PublicRoomsChunk::to_string always works"),
|
&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(),
|
.collect(),
|
||||||
prev_batch: response.prev_batch,
|
prev_batch: response.prev_batch,
|
||||||
next_batch: response.next_batch,
|
next_batch: response.next_batch,
|
||||||
@@ -214,7 +217,7 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.map_or(Ok::<_, Error>(None), |s| {
|
.map_or(Ok::<_, Error>(None), |s| {
|
||||||
Ok(serde_json::from_value::<
|
Ok(serde_json::from_value::<
|
||||||
Raw<canonical_alias::CanonicalAliasEventContent>,
|
Raw<canonical_alias::CanonicalAliasEventContent>,
|
||||||
>(s.content.clone())
|
>(s.content)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
@@ -227,7 +230,7 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.room_state_get(&room_id, &EventType::RoomName, "")?
|
.room_state_get(&room_id, &EventType::RoomName, "")?
|
||||||
.map_or(Ok::<_, Error>(None), |s| {
|
.map_or(Ok::<_, Error>(None), |s| {
|
||||||
Ok(serde_json::from_value::<Raw<name::NameEventContent>>(
|
Ok(serde_json::from_value::<Raw<name::NameEventContent>>(
|
||||||
s.content.clone(),
|
s.content,
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -244,7 +247,7 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.map_or(Ok::<_, Error>(None), |s| {
|
.map_or(Ok::<_, Error>(None), |s| {
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
serde_json::from_value::<Raw<topic::TopicEventContent>>(
|
serde_json::from_value::<Raw<topic::TopicEventContent>>(
|
||||||
s.content.clone(),
|
s.content,
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -260,7 +263,7 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.map_or(Ok::<_, Error>(false), |s| {
|
.map_or(Ok::<_, Error>(false), |s| {
|
||||||
Ok(serde_json::from_value::<
|
Ok(serde_json::from_value::<
|
||||||
Raw<history_visibility::HistoryVisibilityEventContent>,
|
Raw<history_visibility::HistoryVisibilityEventContent>,
|
||||||
>(s.content.clone())
|
>(s.content)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
@@ -277,7 +280,7 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.map_or(Ok::<_, Error>(false), |s| {
|
.map_or(Ok::<_, Error>(false), |s| {
|
||||||
Ok(
|
Ok(
|
||||||
serde_json::from_value::<Raw<guest_access::GuestAccessEventContent>>(
|
serde_json::from_value::<Raw<guest_access::GuestAccessEventContent>>(
|
||||||
s.content.clone(),
|
s.content,
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -294,7 +297,7 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
.map(|s| {
|
.map(|s| {
|
||||||
Ok::<_, Error>(
|
Ok::<_, Error>(
|
||||||
serde_json::from_value::<Raw<avatar::AvatarEventContent>>(
|
serde_json::from_value::<Raw<avatar::AvatarEventContent>>(
|
||||||
s.content.clone(),
|
s.content,
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -312,36 +315,6 @@ pub async fn get_public_rooms_filtered_helper(
|
|||||||
Ok(chunk)
|
Ok(chunk)
|
||||||
})
|
})
|
||||||
.filter_map(|r: Result<_>| r.ok()) // Filter out buggy rooms
|
.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
|
// We need to collect all, so we can sort by member count
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|||||||
+96
-131
@@ -1,5 +1,5 @@
|
|||||||
use super::{State, SESSION_ID_LENGTH};
|
use super::{State, SESSION_ID_LENGTH};
|
||||||
use crate::{utils, ConduitResult, Database, Error, Result, Ruma};
|
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -12,12 +12,8 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
encryption::UnsignedDeviceInfo,
|
encryption::UnsignedDeviceInfo,
|
||||||
DeviceId, DeviceKeyAlgorithm, UserId,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::{BTreeMap, HashSet},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, post};
|
use rocket::{get, post};
|
||||||
@@ -28,7 +24,7 @@ use rocket::{get, post};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn upload_keys_route(
|
pub async fn upload_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<upload_keys::Request>,
|
body: Ruma<upload_keys::Request>,
|
||||||
) -> ConduitResult<upload_keys::Response> {
|
) -> ConduitResult<upload_keys::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_keys_route(
|
pub async fn get_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_keys::Request<'_>>,
|
body: Ruma<get_keys::Request<'_>>,
|
||||||
) -> ConduitResult<get_keys::Response> {
|
) -> ConduitResult<get_keys::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let response = get_keys_helper(
|
let mut master_keys = BTreeMap::new();
|
||||||
Some(sender_user),
|
let mut self_signing_keys = BTreeMap::new();
|
||||||
&body.device_keys,
|
let mut user_signing_keys = BTreeMap::new();
|
||||||
|u| u == sender_user,
|
let mut device_keys = BTreeMap::new();
|
||||||
&db,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(response.into())
|
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.",
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
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(
|
#[cfg_attr(
|
||||||
@@ -98,14 +154,32 @@ pub async fn get_keys_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn claim_keys_route(
|
pub async fn claim_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<claim_keys::Request>,
|
body: Ruma<claim_keys::Request>,
|
||||||
) -> ConduitResult<claim_keys::Response> {
|
) -> 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?;
|
db.flush().await?;
|
||||||
|
|
||||||
Ok(response.into())
|
Ok(claim_keys::Response {
|
||||||
|
failures: BTreeMap::new(),
|
||||||
|
one_time_keys,
|
||||||
|
}
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -114,7 +188,7 @@ pub async fn claim_keys_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn upload_signing_keys_route(
|
pub async fn upload_signing_keys_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<upload_signing_keys::Request<'_>>,
|
body: Ruma<upload_signing_keys::Request<'_>>,
|
||||||
) -> ConduitResult<upload_signing_keys::Response> {
|
) -> ConduitResult<upload_signing_keys::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -146,12 +220,7 @@ pub async fn upload_signing_keys_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else {
|
} else {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
db.uiaa.create(
|
db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?;
|
||||||
&sender_user,
|
|
||||||
&sender_device,
|
|
||||||
&uiaainfo,
|
|
||||||
&body.json_body.expect("body is json"),
|
|
||||||
)?;
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +246,7 @@ pub async fn upload_signing_keys_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn upload_signatures_route(
|
pub async fn upload_signatures_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<upload_signatures::Request>,
|
body: Ruma<upload_signatures::Request>,
|
||||||
) -> ConduitResult<upload_signatures::Response> {
|
) -> ConduitResult<upload_signatures::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_key_changes_route(
|
pub async fn get_key_changes_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_key_changes::Request<'_>>,
|
body: Ruma<get_key_changes::Request<'_>>,
|
||||||
) -> ConduitResult<get_key_changes::Response> {
|
) -> ConduitResult<get_key_changes::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -282,107 +351,3 @@ pub async fn get_key_changes_route(
|
|||||||
}
|
}
|
||||||
.into())
|
.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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+37
-56
@@ -7,14 +7,14 @@ use ruma::api::client::{
|
|||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, post};
|
use rocket::{get, post};
|
||||||
use std::{convert::TryInto, sync::Arc};
|
use std::convert::TryInto;
|
||||||
|
|
||||||
const MXC_LENGTH: usize = 32;
|
const MXC_LENGTH: usize = 32;
|
||||||
|
|
||||||
#[cfg_attr(feature = "conduit_bin", get("/_matrix/media/r0/config"))]
|
#[cfg_attr(feature = "conduit_bin", get("/_matrix/media/r0/config"))]
|
||||||
#[tracing::instrument(skip(db))]
|
#[tracing::instrument(skip(db))]
|
||||||
pub async fn get_media_config_route(
|
pub async fn get_media_config_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
) -> ConduitResult<get_media_config::Response> {
|
) -> ConduitResult<get_media_config::Response> {
|
||||||
Ok(get_media_config::Response {
|
Ok(get_media_config::Response {
|
||||||
upload_size: db.globals.max_request_size().into(),
|
upload_size: db.globals.max_request_size().into(),
|
||||||
@@ -28,7 +28,7 @@ pub async fn get_media_config_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn create_content_route(
|
pub async fn create_content_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_content::Request<'_>>,
|
body: Ruma<create_content::Request<'_>>,
|
||||||
) -> ConduitResult<create_content::Response> {
|
) -> ConduitResult<create_content::Response> {
|
||||||
let mxc = format!(
|
let mxc = format!(
|
||||||
@@ -36,20 +36,12 @@ pub async fn create_content_route(
|
|||||||
db.globals.server_name(),
|
db.globals.server_name(),
|
||||||
utils::random_string(MXC_LENGTH)
|
utils::random_string(MXC_LENGTH)
|
||||||
);
|
);
|
||||||
|
db.media.create(
|
||||||
db.media
|
mxc.clone(),
|
||||||
.create(
|
&body.filename.as_deref(),
|
||||||
mxc.clone(),
|
&body.content_type.as_deref(),
|
||||||
&db.globals,
|
&body.file,
|
||||||
&body
|
)?;
|
||||||
.filename
|
|
||||||
.as_ref()
|
|
||||||
.map(|filename| "inline; filename=".to_owned() + filename)
|
|
||||||
.as_deref(),
|
|
||||||
&body.content_type.as_deref(),
|
|
||||||
&body.file,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
db.flush().await?;
|
db.flush().await?;
|
||||||
|
|
||||||
@@ -66,21 +58,21 @@ pub async fn create_content_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_content_route(
|
pub async fn get_content_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_content::Request<'_>>,
|
body: Ruma<get_content::Request<'_>>,
|
||||||
) -> ConduitResult<get_content::Response> {
|
) -> ConduitResult<get_content::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
content_disposition,
|
filename,
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
file,
|
||||||
}) = db.media.get(&db.globals, &mxc).await?
|
}) = db.media.get(&mxc)?
|
||||||
{
|
{
|
||||||
Ok(get_content::Response {
|
Ok(get_content::Response {
|
||||||
file,
|
file,
|
||||||
content_type,
|
content_type,
|
||||||
content_disposition,
|
content_disposition: filename,
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
} else if &*body.server_name != db.globals.server_name() && body.allow_remote {
|
} else if &*body.server_name != db.globals.server_name() && body.allow_remote {
|
||||||
@@ -97,15 +89,12 @@ pub async fn get_content_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
db.media
|
db.media.create(
|
||||||
.create(
|
mxc,
|
||||||
mxc,
|
&get_content_response.content_disposition.as_deref(),
|
||||||
&db.globals,
|
&get_content_response.content_type.as_deref(),
|
||||||
&get_content_response.content_disposition.as_deref(),
|
&get_content_response.file,
|
||||||
&get_content_response.content_type.as_deref(),
|
)?;
|
||||||
&get_content_response.file,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(get_content_response.into())
|
Ok(get_content_response.into())
|
||||||
} else {
|
} else {
|
||||||
@@ -119,27 +108,22 @@ pub async fn get_content_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_content_thumbnail_route(
|
pub async fn get_content_thumbnail_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_content_thumbnail::Request<'_>>,
|
body: Ruma<get_content_thumbnail::Request<'_>>,
|
||||||
) -> ConduitResult<get_content_thumbnail::Response> {
|
) -> ConduitResult<get_content_thumbnail::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
content_type, file, ..
|
content_type, file, ..
|
||||||
}) = db
|
}) = db.media.get_thumbnail(
|
||||||
.media
|
mxc.clone(),
|
||||||
.get_thumbnail(
|
body.width
|
||||||
mxc.clone(),
|
.try_into()
|
||||||
&db.globals,
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||||
body.width
|
body.height
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
.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())
|
Ok(get_content_thumbnail::Response { file, content_type }.into())
|
||||||
} else if &*body.server_name != db.globals.server_name() && body.allow_remote {
|
} else if &*body.server_name != db.globals.server_name() && body.allow_remote {
|
||||||
let get_thumbnail_response = db
|
let get_thumbnail_response = db
|
||||||
@@ -158,17 +142,14 @@ pub async fn get_content_thumbnail_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
db.media
|
db.media.upload_thumbnail(
|
||||||
.upload_thumbnail(
|
mxc,
|
||||||
mxc,
|
&None,
|
||||||
&db.globals,
|
&get_thumbnail_response.content_type,
|
||||||
&None,
|
body.width.try_into().expect("all UInts are valid u32s"),
|
||||||
&get_thumbnail_response.content_type,
|
body.height.try_into().expect("all UInts are valid u32s"),
|
||||||
body.width.try_into().expect("all UInts are valid u32s"),
|
&get_thumbnail_response.file,
|
||||||
body.height.try_into().expect("all UInts are valid u32s"),
|
)?;
|
||||||
&get_thumbnail_response.file,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(get_thumbnail_response.into())
|
Ok(get_thumbnail_response.into())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
pdu::{PduBuilder, PduEvent},
|
pdu::{PduBuilder, PduEvent},
|
||||||
server_server, utils, ConduitResult, Database, Error, Result, Ruma,
|
server_server, utils, ConduitResult, Database, Error, Result, Ruma,
|
||||||
};
|
};
|
||||||
use log::{debug, error, warn};
|
use log::{error, warn};
|
||||||
use member::{MemberEventContent, MembershipState};
|
use member::{MemberEventContent, MembershipState};
|
||||||
use rocket::futures;
|
use rocket::futures;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -25,14 +25,13 @@ use ruma::{
|
|||||||
EventType,
|
EventType,
|
||||||
},
|
},
|
||||||
serde::{to_canonical_value, CanonicalJsonObject, CanonicalJsonValue, Raw},
|
serde::{to_canonical_value, CanonicalJsonObject, CanonicalJsonValue, Raw},
|
||||||
state_res::{self, EventMap, RoomVersion},
|
|
||||||
uint, EventId, RoomId, RoomVersionId, ServerName, UserId,
|
uint, EventId, RoomId, RoomVersionId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
|
use state_res::EventMap;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{btree_map::Entry, BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
@@ -44,7 +43,7 @@ use rocket::{get, post};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn join_room_by_id_route(
|
pub async fn join_room_by_id_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<join_room_by_id::Request<'_>>,
|
body: Ruma<join_room_by_id::Request<'_>>,
|
||||||
) -> ConduitResult<join_room_by_id::Response> {
|
) -> ConduitResult<join_room_by_id::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn join_room_by_id_or_alias_route(
|
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<'_>>,
|
body: Ruma<join_room_by_id_or_alias::Request<'_>>,
|
||||||
) -> ConduitResult<join_room_by_id_or_alias::Response> {
|
) -> ConduitResult<join_room_by_id_or_alias::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn leave_room_route(
|
pub async fn leave_room_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<leave_room::Request<'_>>,
|
body: Ruma<leave_room::Request<'_>>,
|
||||||
) -> ConduitResult<leave_room::Response> {
|
) -> ConduitResult<leave_room::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn invite_user_route(
|
pub async fn invite_user_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<invite_user::Request<'_>>,
|
body: Ruma<invite_user::Request<'_>>,
|
||||||
) -> ConduitResult<invite_user::Response> {
|
) -> ConduitResult<invite_user::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn kick_user_route(
|
pub async fn kick_user_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<kick_user::Request<'_>>,
|
body: Ruma<kick_user::Request<'_>>,
|
||||||
) -> ConduitResult<kick_user::Response> {
|
) -> ConduitResult<kick_user::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -189,8 +188,7 @@ pub async fn kick_user_route(
|
|||||||
ErrorKind::BadState,
|
ErrorKind::BadState,
|
||||||
"Cannot kick member that's not in the room.",
|
"Cannot kick member that's not in the room.",
|
||||||
))?
|
))?
|
||||||
.content
|
.content,
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
.expect("Raw::from_value always works")
|
.expect("Raw::from_value always works")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -223,7 +221,7 @@ pub async fn kick_user_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn ban_user_route(
|
pub async fn ban_user_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<ban_user::Request<'_>>,
|
body: Ruma<ban_user::Request<'_>>,
|
||||||
) -> ConduitResult<ban_user::Response> {
|
) -> ConduitResult<ban_user::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -246,12 +244,11 @@ pub async fn ban_user_route(
|
|||||||
third_party_invite: None,
|
third_party_invite: None,
|
||||||
}),
|
}),
|
||||||
|event| {
|
|event| {
|
||||||
let mut event = serde_json::from_value::<Raw<member::MemberEventContent>>(
|
let mut event =
|
||||||
event.content.clone(),
|
serde_json::from_value::<Raw<member::MemberEventContent>>(event.content)
|
||||||
)
|
.expect("Raw::from_value always works")
|
||||||
.expect("Raw::from_value always works")
|
.deserialize()
|
||||||
.deserialize()
|
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
|
||||||
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
|
|
||||||
event.membership = ruma::events::room::member::MembershipState::Ban;
|
event.membership = ruma::events::room::member::MembershipState::Ban;
|
||||||
Ok(event)
|
Ok(event)
|
||||||
},
|
},
|
||||||
@@ -281,7 +278,7 @@ pub async fn ban_user_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn unban_user_route(
|
pub async fn unban_user_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<unban_user::Request<'_>>,
|
body: Ruma<unban_user::Request<'_>>,
|
||||||
) -> ConduitResult<unban_user::Response> {
|
) -> ConduitResult<unban_user::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -297,8 +294,7 @@ pub async fn unban_user_route(
|
|||||||
ErrorKind::BadState,
|
ErrorKind::BadState,
|
||||||
"Cannot unban a user who is not banned.",
|
"Cannot unban a user who is not banned.",
|
||||||
))?
|
))?
|
||||||
.content
|
.content,
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -330,7 +326,7 @@ pub async fn unban_user_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn forget_room_route(
|
pub async fn forget_room_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<forget_room::Request<'_>>,
|
body: Ruma<forget_room::Request<'_>>,
|
||||||
) -> ConduitResult<forget_room::Response> {
|
) -> ConduitResult<forget_room::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn joined_rooms_route(
|
pub async fn joined_rooms_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<joined_rooms::Request>,
|
body: Ruma<joined_rooms::Request>,
|
||||||
) -> ConduitResult<joined_rooms::Response> {
|
) -> ConduitResult<joined_rooms::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_member_events_route(
|
pub async fn get_member_events_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_member_events::Request<'_>>,
|
body: Ruma<get_member_events::Request<'_>>,
|
||||||
) -> ConduitResult<get_member_events::Response> {
|
) -> ConduitResult<get_member_events::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn joined_members_route(
|
pub async fn joined_members_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<joined_members::Request<'_>>,
|
body: Ruma<joined_members::Request<'_>>,
|
||||||
) -> ConduitResult<joined_members::Response> {
|
) -> ConduitResult<joined_members::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -549,6 +545,12 @@ async fn join_room_by_id_helper(
|
|||||||
)
|
)
|
||||||
.await?;
|
.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())
|
let pdu = PduEvent::from_id_val(&event_id, join_event.clone())
|
||||||
.map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?;
|
.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)?;
|
db.rooms.add_pdu_outlier(&event_id, &value)?;
|
||||||
if let Some(state_key) = &pdu.state_key {
|
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());
|
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.
|
// 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 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(
|
db.rooms.append_pdu(
|
||||||
&pdu,
|
&pdu,
|
||||||
utils::to_canonical_object(&pdu).expect("Pdu is valid canonical object"),
|
utils::to_canonical_object(&pdu).expect("Pdu is valid canonical object"),
|
||||||
count,
|
db.globals.next_count()?,
|
||||||
&pdu_id,
|
pdu_id.into(),
|
||||||
&[pdu.event_id.clone()],
|
&[pdu.event_id.clone()],
|
||||||
db,
|
db,
|
||||||
)?;
|
)?;
|
||||||
@@ -667,41 +694,9 @@ async fn validate_and_add_event_id(
|
|||||||
db: &Database,
|
db: &Database,
|
||||||
) -> Result<(EventId, CanonicalJsonObject)> {
|
) -> Result<(EventId, CanonicalJsonObject)> {
|
||||||
let mut value = serde_json::from_str::<CanonicalJsonObject>(pdu.json().get()).map_err(|e| {
|
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")
|
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?;
|
server_server::fetch_required_signing_keys(&value, pub_key_map, db).await?;
|
||||||
if let Err(e) = ruma::signatures::verify_event(
|
if let Err(e) = ruma::signatures::verify_event(
|
||||||
@@ -711,11 +706,17 @@ async fn validate_and_add_event_id(
|
|||||||
&value,
|
&value,
|
||||||
room_version,
|
room_version,
|
||||||
) {
|
) {
|
||||||
warn!("Event {} failed verification {:?} {}", event_id, pdu, e);
|
warn!("Event failed verification: {}", e);
|
||||||
back_off(event_id);
|
|
||||||
return Err(Error::BadServerResponse("Event failed verification."));
|
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(
|
value.insert(
|
||||||
"event_id".to_owned(),
|
"event_id".to_owned(),
|
||||||
CanonicalJsonValue::String(event_id.as_str().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
|
let create_event_content = create_event
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|create_event| {
|
.map(|create_event| {
|
||||||
serde_json::from_value::<Raw<CreateEventContent>>(create_event.content.clone())
|
Ok::<_, Error>(
|
||||||
.expect("Raw::from_value always works.")
|
serde_json::from_value::<Raw<CreateEventContent>>(create_event.content.clone())
|
||||||
.deserialize()
|
.expect("Raw::from_value always works.")
|
||||||
.map_err(|_| Error::bad_database("Invalid PowerLevels event in db."))
|
.deserialize()
|
||||||
|
.map_err(|_| Error::bad_database("Invalid PowerLevels event in db."))?,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let create_prev_event = if prev_events.len() == 1
|
let create_prev_event = if prev_events.len() == 1
|
||||||
&& Some(&prev_events[0]) == create_event.as_ref().map(|c| &c.event_id)
|
&& Some(&prev_events[0]) == create_event.as_ref().map(|c| &c.event_id)
|
||||||
{
|
{
|
||||||
create_event
|
create_event.map(Arc::new)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// If there was no create event yet, assume we are creating a version 6 room right now
|
// If there was no create event yet, assume we are creating a version 6 room right now
|
||||||
let room_version_id = create_event_content
|
let room_version = create_event_content.map_or(RoomVersionId::Version6, |create_event| {
|
||||||
.map_or(RoomVersionId::Version6, |create_event| {
|
create_event.room_version
|
||||||
create_event.room_version
|
});
|
||||||
});
|
|
||||||
let room_version = RoomVersion::new(&room_version_id).expect("room version is supported");
|
|
||||||
|
|
||||||
let content = serde_json::to_value(MemberEventContent {
|
let content = serde_json::to_value(MemberEventContent {
|
||||||
avatar_url: None,
|
avatar_url: None,
|
||||||
@@ -795,10 +796,10 @@ pub async fn invite_helper(
|
|||||||
let mut unsigned = BTreeMap::new();
|
let mut unsigned = BTreeMap::new();
|
||||||
|
|
||||||
if let Some(prev_pdu) = db.rooms.room_state_get(room_id, &kind, &state_key)? {
|
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(
|
unsigned.insert(
|
||||||
"prev_sender".to_owned(),
|
"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 {
|
if !auth_check {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::InvalidParam,
|
||||||
"Event is not authorized.",
|
"Event is not authorized.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -862,7 +863,7 @@ pub async fn invite_helper(
|
|||||||
db.globals.server_name().as_str(),
|
db.globals.server_name().as_str(),
|
||||||
db.globals.keypair(),
|
db.globals.keypair(),
|
||||||
&mut pdu_json,
|
&mut pdu_json,
|
||||||
&room_version_id,
|
&room_version,
|
||||||
)
|
)
|
||||||
.expect("event is valid, we just created it");
|
.expect("event is valid, we just created it");
|
||||||
|
|
||||||
@@ -946,7 +947,7 @@ pub async fn invite_helper(
|
|||||||
membership: member::MembershipState::Invite,
|
membership: member::MembershipState::Invite,
|
||||||
displayname: db.users.displayname(&user_id)?,
|
displayname: db.users.displayname(&user_id)?,
|
||||||
avatar_url: db.users.avatar_url(&user_id)?,
|
avatar_url: db.users.avatar_url(&user_id)?,
|
||||||
is_direct: Some(is_direct),
|
is_direct: None,
|
||||||
third_party_invite: None,
|
third_party_invite: None,
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use ruma::{
|
|||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
@@ -23,7 +22,7 @@ use rocket::{get, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn send_message_event_route(
|
pub async fn send_message_event_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<send_message_event::Request<'_>>,
|
body: Ruma<send_message_event::Request<'_>>,
|
||||||
) -> ConduitResult<send_message_event::Response> {
|
) -> ConduitResult<send_message_event::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -57,8 +56,13 @@ pub async fn send_message_event_route(
|
|||||||
let event_id = db.rooms.build_and_append_pdu(
|
let event_id = db.rooms.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: EventType::from(&body.event_type),
|
event_type: EventType::from(&body.event_type),
|
||||||
content: serde_json::from_str(body.body.body.json().get())
|
content: serde_json::from_str(
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
|
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),
|
unsigned: Some(unsigned),
|
||||||
state_key: None,
|
state_key: None,
|
||||||
redacts: None,
|
redacts: None,
|
||||||
@@ -86,7 +90,7 @@ pub async fn send_message_event_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_message_events_route(
|
pub async fn get_message_events_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_message_events::Request<'_>>,
|
body: Ruma<get_message_events::Request<'_>>,
|
||||||
) -> ConduitResult<get_message_events::Response> {
|
) -> ConduitResult<get_message_events::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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,
|
ruma::api::client::r0::to_device::send_event_to_device,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DEVICE_ID_LENGTH: usize = 10;
|
const DEVICE_ID_LENGTH: usize = 10;
|
||||||
pub const TOKEN_LENGTH: usize = 256;
|
const TOKEN_LENGTH: usize = 256;
|
||||||
pub const SESSION_ID_LENGTH: usize = 256;
|
const SESSION_ID_LENGTH: usize = 256;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
#[options("/<_..>")]
|
#[options("/<_..>")]
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use super::State;
|
use super::State;
|
||||||
use crate::{utils, ConduitResult, Database, Ruma};
|
use crate::{utils, ConduitResult, Database, Ruma};
|
||||||
use ruma::api::client::r0::presence::{get_presence, set_presence};
|
use ruma::api::client::r0::presence::set_presence;
|
||||||
use std::{convert::TryInto, sync::Arc, time::Duration};
|
use std::convert::TryInto;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, put};
|
use rocket::put;
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "conduit_bin",
|
feature = "conduit_bin",
|
||||||
@@ -12,7 +12,7 @@ use rocket::{get, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_presence_route(
|
pub async fn set_presence_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_presence::Request<'_>>,
|
body: Ruma<set_presence::Request<'_>>,
|
||||||
) -> ConduitResult<set_presence::Response> {
|
) -> ConduitResult<set_presence::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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())
|
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")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, put};
|
use rocket::{get, put};
|
||||||
use std::{convert::TryInto, sync::Arc};
|
use std::convert::TryInto;
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "conduit_bin",
|
feature = "conduit_bin",
|
||||||
@@ -21,7 +21,7 @@ use std::{convert::TryInto, sync::Arc};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_displayname_route(
|
pub async fn set_displayname_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_display_name::Request<'_>>,
|
body: Ruma<set_display_name::Request<'_>>,
|
||||||
) -> ConduitResult<set_display_name::Response> {
|
) -> ConduitResult<set_display_name::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -53,8 +53,7 @@ pub async fn set_displayname_route(
|
|||||||
room.",
|
room.",
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.content
|
.content,
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -108,7 +107,7 @@ pub async fn set_displayname_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_displayname_route(
|
pub async fn get_displayname_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_display_name::Request<'_>>,
|
body: Ruma<get_display_name::Request<'_>>,
|
||||||
) -> ConduitResult<get_display_name::Response> {
|
) -> ConduitResult<get_display_name::Response> {
|
||||||
Ok(get_display_name::Response {
|
Ok(get_display_name::Response {
|
||||||
@@ -123,7 +122,7 @@ pub async fn get_displayname_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_avatar_url_route(
|
pub async fn set_avatar_url_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_avatar_url::Request<'_>>,
|
body: Ruma<set_avatar_url::Request<'_>>,
|
||||||
) -> ConduitResult<set_avatar_url::Response> {
|
) -> ConduitResult<set_avatar_url::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -155,8 +154,7 @@ pub async fn set_avatar_url_route(
|
|||||||
room.",
|
room.",
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.content
|
.content,
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
.expect("from_value::<Raw<..>> can never fail")
|
.expect("from_value::<Raw<..>> can never fail")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -210,7 +208,7 @@ pub async fn set_avatar_url_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_avatar_url_route(
|
pub async fn get_avatar_url_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_avatar_url::Request<'_>>,
|
body: Ruma<get_avatar_url::Request<'_>>,
|
||||||
) -> ConduitResult<get_avatar_url::Response> {
|
) -> ConduitResult<get_avatar_url::Response> {
|
||||||
Ok(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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_profile_route(
|
pub async fn get_profile_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_profile::Request<'_>>,
|
body: Ruma<get_profile::Request<'_>>,
|
||||||
) -> ConduitResult<get_profile::Response> {
|
) -> ConduitResult<get_profile::Response> {
|
||||||
if !db.users.exists(&body.user_id)? {
|
if !db.users.exists(&body.user_id)? {
|
||||||
|
|||||||
+10
-12
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -24,7 +22,7 @@ use rocket::{delete, get, post, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_pushrules_all_route(
|
pub async fn get_pushrules_all_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_pushrules_all::Request>,
|
body: Ruma<get_pushrules_all::Request>,
|
||||||
) -> ConduitResult<get_pushrules_all::Response> {
|
) -> ConduitResult<get_pushrules_all::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_pushrule_route(
|
pub async fn get_pushrule_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_pushrule::Request<'_>>,
|
body: Ruma<get_pushrule::Request<'_>>,
|
||||||
) -> ConduitResult<get_pushrule::Response> {
|
) -> ConduitResult<get_pushrule::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, req))]
|
||||||
pub async fn set_pushrule_route(
|
pub async fn set_pushrule_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
req: Ruma<set_pushrule::Request<'_>>,
|
req: Ruma<set_pushrule::Request<'_>>,
|
||||||
) -> ConduitResult<set_pushrule::Response> {
|
) -> ConduitResult<set_pushrule::Response> {
|
||||||
let sender_user = req.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_pushrule_actions_route(
|
pub async fn get_pushrule_actions_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_pushrule_actions::Request<'_>>,
|
body: Ruma<get_pushrule_actions::Request<'_>>,
|
||||||
) -> ConduitResult<get_pushrule_actions::Response> {
|
) -> ConduitResult<get_pushrule_actions::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_pushrule_actions_route(
|
pub async fn set_pushrule_actions_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_pushrule_actions::Request<'_>>,
|
body: Ruma<set_pushrule_actions::Request<'_>>,
|
||||||
) -> ConduitResult<set_pushrule_actions::Response> {
|
) -> ConduitResult<set_pushrule_actions::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_pushrule_enabled_route(
|
pub async fn get_pushrule_enabled_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_pushrule_enabled::Request<'_>>,
|
body: Ruma<get_pushrule_enabled::Request<'_>>,
|
||||||
) -> ConduitResult<get_pushrule_enabled::Response> {
|
) -> ConduitResult<get_pushrule_enabled::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_pushrule_enabled_route(
|
pub async fn set_pushrule_enabled_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_pushrule_enabled::Request<'_>>,
|
body: Ruma<set_pushrule_enabled::Request<'_>>,
|
||||||
) -> ConduitResult<set_pushrule_enabled::Response> {
|
) -> ConduitResult<set_pushrule_enabled::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_pushrule_route(
|
pub async fn delete_pushrule_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_pushrule::Request<'_>>,
|
body: Ruma<delete_pushrule::Request<'_>>,
|
||||||
) -> ConduitResult<delete_pushrule::Response> {
|
) -> ConduitResult<delete_pushrule::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_pushers_route(
|
pub async fn get_pushers_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_pushers::Request>,
|
body: Ruma<get_pushers::Request>,
|
||||||
) -> ConduitResult<get_pushers::Response> {
|
) -> ConduitResult<get_pushers::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_pushers_route(
|
pub async fn set_pushers_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_pusher::Request>,
|
body: Ruma<set_pusher::Request>,
|
||||||
) -> ConduitResult<set_pusher::Response> {
|
) -> ConduitResult<set_pusher::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|||||||
@@ -5,14 +5,12 @@ use ruma::{
|
|||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
r0::{read_marker::set_read_marker, receipt::create_receipt},
|
r0::{read_marker::set_read_marker, receipt::create_receipt},
|
||||||
},
|
},
|
||||||
events::{AnyEphemeralRoomEvent, EventType},
|
events::{AnyEphemeralRoomEvent, AnyEvent, EventType},
|
||||||
receipt::ReceiptType,
|
|
||||||
MilliSecondsSinceUnixEpoch,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::post;
|
use rocket::post;
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{collections::BTreeMap, time::SystemTime};
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "conduit_bin",
|
feature = "conduit_bin",
|
||||||
@@ -20,7 +18,7 @@ use std::{collections::BTreeMap, sync::Arc};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn set_read_marker_route(
|
pub async fn set_read_marker_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<set_read_marker::Request<'_>>,
|
body: Ruma<set_read_marker::Request<'_>>,
|
||||||
) -> ConduitResult<set_read_marker::Response> {
|
) -> ConduitResult<set_read_marker::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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 {
|
content: ruma::events::fully_read::FullyReadEventContent {
|
||||||
event_id: body.fully_read.clone(),
|
event_id: body.fully_read.clone(),
|
||||||
},
|
},
|
||||||
|
room_id: body.room_id.clone(),
|
||||||
};
|
};
|
||||||
db.account_data.update(
|
db.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
@@ -55,23 +54,26 @@ pub async fn set_read_marker_route(
|
|||||||
user_receipts.insert(
|
user_receipts.insert(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
ruma::events::receipt::Receipt {
|
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();
|
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(
|
db.rooms.edus.readreceipt_update(
|
||||||
&sender_user,
|
&sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
AnyEphemeralRoomEvent::Receipt(ruma::events::receipt::ReceiptEvent {
|
AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
|
||||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
ruma::events::receipt::ReceiptEvent {
|
||||||
room_id: body.room_id.clone(),
|
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||||
}),
|
room_id: body.room_id.clone(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
&db.globals,
|
&db.globals,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,7 @@ pub async fn set_read_marker_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn create_receipt_route(
|
pub async fn create_receipt_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_receipt::Request<'_>>,
|
body: Ruma<create_receipt::Request<'_>>,
|
||||||
) -> ConduitResult<create_receipt::Response> {
|
) -> ConduitResult<create_receipt::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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(
|
user_receipts.insert(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
ruma::events::receipt::Receipt {
|
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();
|
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(
|
db.rooms.edus.readreceipt_update(
|
||||||
&sender_user,
|
&sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
AnyEphemeralRoomEvent::Receipt(ruma::events::receipt::ReceiptEvent {
|
AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
|
||||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
ruma::events::receipt::ReceiptEvent {
|
||||||
room_id: body.room_id.clone(),
|
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||||
}),
|
room_id: body.room_id.clone(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
&db.globals,
|
&db.globals,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use ruma::{
|
|||||||
api::client::r0::redact::redact_event,
|
api::client::r0::redact::redact_event,
|
||||||
events::{room::redaction, EventType},
|
events::{room::redaction, EventType},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::put;
|
use rocket::put;
|
||||||
@@ -15,7 +14,7 @@ use rocket::put;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn redact_event_route(
|
pub async fn redact_event_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<redact_event::Request<'_>>,
|
body: Ruma<redact_event::Request<'_>>,
|
||||||
) -> ConduitResult<redact_event::Response> {
|
) -> ConduitResult<redact_event::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|||||||
+28
-28
@@ -1,5 +1,6 @@
|
|||||||
use super::State;
|
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 log::info;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
@@ -13,7 +14,7 @@ use ruma::{
|
|||||||
serde::Raw,
|
serde::Raw,
|
||||||
RoomAliasId, RoomId, RoomVersionId,
|
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")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, post};
|
use rocket::{get, post};
|
||||||
@@ -24,7 +25,7 @@ use rocket::{get, post};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn create_room_route(
|
pub async fn create_room_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_room::Request<'_>>,
|
body: Ruma<create_room::Request<'_>>,
|
||||||
) -> ConduitResult<create_room::Response> {
|
) -> ConduitResult<create_room::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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 =
|
let power_levels_content = if let Some(power_levels) = &body.power_level_content_override {
|
||||||
serde_json::to_value(ruma::events::room::power_levels::PowerLevelsEventContent {
|
serde_json::from_str(power_levels.json().get()).map_err(|_| {
|
||||||
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(|_| {
|
|
||||||
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
|
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
|
||||||
})?;
|
})?
|
||||||
|
} else {
|
||||||
for (key, value) in json {
|
serde_json::to_value(ruma::events::room::power_levels::PowerLevelsEventContent {
|
||||||
power_levels_content[key] = value;
|
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(
|
db.rooms.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: EventType::RoomPowerLevels,
|
event_type: EventType::RoomPowerLevels,
|
||||||
@@ -294,7 +296,7 @@ pub async fn create_room_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_room_event_route(
|
pub async fn get_room_event_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_room_event::Request<'_>>,
|
body: Ruma<get_room_event::Request<'_>>,
|
||||||
) -> ConduitResult<get_room_event::Response> {
|
) -> ConduitResult<get_room_event::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn upgrade_room_route(
|
pub async fn upgrade_room_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<upgrade_room::Request<'_>>,
|
body: Ruma<upgrade_room::Request<'_>>,
|
||||||
_room_id: String,
|
_room_id: String,
|
||||||
) -> ConduitResult<upgrade_room::Response> {
|
) -> ConduitResult<upgrade_room::Response> {
|
||||||
@@ -362,8 +364,7 @@ pub async fn upgrade_room_route(
|
|||||||
db.rooms
|
db.rooms
|
||||||
.room_state_get(&body.room_id, &EventType::RoomCreate, "")?
|
.room_state_get(&body.room_id, &EventType::RoomCreate, "")?
|
||||||
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?
|
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?
|
||||||
.content
|
.content,
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
.expect("Raw::from_value always works")
|
.expect("Raw::from_value always works")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
@@ -464,8 +465,7 @@ pub async fn upgrade_room_route(
|
|||||||
db.rooms
|
db.rooms
|
||||||
.room_state_get(&body.room_id, &EventType::RoomPowerLevels, "")?
|
.room_state_get(&body.room_id, &EventType::RoomPowerLevels, "")?
|
||||||
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?
|
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?
|
||||||
.content
|
.content,
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
.expect("database contains invalid PDU")
|
.expect("database contains invalid PDU")
|
||||||
.deserialize()
|
.deserialize()
|
||||||
|
|||||||
+14
-42
@@ -1,7 +1,6 @@
|
|||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::api::client::{error::ErrorKind, r0::search::search_events};
|
use ruma::api::client::{error::ErrorKind, r0::search::search_events};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::post;
|
use rocket::post;
|
||||||
@@ -14,38 +13,23 @@ use std::collections::BTreeMap;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn search_events_route(
|
pub async fn search_events_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<search_events::Request<'_>>,
|
body: Ruma<search_events::Request<'_>>,
|
||||||
) -> ConduitResult<search_events::Response> {
|
) -> ConduitResult<search_events::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
|
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(|| {
|
let room_id = filter.rooms.as_ref().unwrap().first().unwrap();
|
||||||
db.rooms
|
|
||||||
.rooms_joined(&sender_user)
|
|
||||||
.filter_map(|r| r.ok())
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
let limit = filter.limit.map_or(10, |l| u64::from(l) as usize);
|
let limit = filter.limit.map_or(10, |l| u64::from(l) as usize);
|
||||||
|
|
||||||
let mut searches = Vec::new();
|
if !db.rooms.is_joined(sender_user, &room_id)? {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
for room_id in room_ids {
|
ErrorKind::Forbidden,
|
||||||
if !db.rooms.is_joined(sender_user, &room_id)? {
|
"You don't have permission to view this room.",
|
||||||
return Err(Error::BadRequest(
|
));
|
||||||
ErrorKind::Forbidden,
|
|
||||||
"You don't have permission to view this room.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
let skip = match body.next_batch.as_ref().map(|s| s.parse()) {
|
||||||
@@ -59,20 +43,12 @@ pub async fn search_events_route(
|
|||||||
None => 0, // Default to the start
|
None => 0, // Default to the start
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let search = db
|
||||||
for _ in 0..skip + limit {
|
.rooms
|
||||||
if let Some(s) = searches
|
.search_pdus(&room_id, &search_criteria.search_term)?;
|
||||||
.iter_mut()
|
|
||||||
.map(|s| (s.peek().cloned(), s))
|
|
||||||
.max_by_key(|(peek, _)| peek.clone())
|
|
||||||
.and_then(|(_, i)| i.next())
|
|
||||||
{
|
|
||||||
results.push(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let results = results
|
let results = search
|
||||||
.iter()
|
.0
|
||||||
.map(|result| {
|
.map(|result| {
|
||||||
Ok::<_, Error>(SearchResult {
|
Ok::<_, Error>(SearchResult {
|
||||||
context: EventContextResult {
|
context: EventContextResult {
|
||||||
@@ -107,11 +83,7 @@ pub async fn search_events_route(
|
|||||||
next_batch,
|
next_batch,
|
||||||
results,
|
results,
|
||||||
state: BTreeMap::new(), // TODO
|
state: BTreeMap::new(), // TODO
|
||||||
highlights: search_criteria
|
highlights: search.1,
|
||||||
.search_term
|
|
||||||
.split_terminator(|c: char| !c.is_alphanumeric())
|
|
||||||
.map(str::to_lowercase)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.into())
|
.into())
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::{State, DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
use super::{State, DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||||
use log::info;
|
use log::info;
|
||||||
@@ -52,7 +50,7 @@ pub async fn get_login_types_route() -> ConduitResult<get_login_types::Response>
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn login_route(
|
pub async fn login_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<login::Request<'_>>,
|
body: Ruma<login::Request<'_>>,
|
||||||
) -> ConduitResult<login::Response> {
|
) -> ConduitResult<login::Response> {
|
||||||
// Validate login method
|
// Validate login method
|
||||||
@@ -169,7 +167,7 @@ pub async fn login_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn logout_route(
|
pub async fn logout_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<logout::Request>,
|
body: Ruma<logout::Request>,
|
||||||
) -> ConduitResult<logout::Response> {
|
) -> ConduitResult<logout::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -197,13 +195,15 @@ pub async fn logout_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn logout_all_route(
|
pub async fn logout_all_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<logout_all::Request>,
|
body: Ruma<logout_all::Request>,
|
||||||
) -> ConduitResult<logout_all::Response> {
|
) -> ConduitResult<logout_all::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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) {
|
||||||
db.users.remove_device(&sender_user, &device_id)?;
|
if let Ok(device_id) = device_id {
|
||||||
|
db.users.remove_device(&sender_user, &device_id)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.flush().await?;
|
db.flush().await?;
|
||||||
|
|||||||
+10
-12
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{pdu::PduBuilder, ConduitResult, Database, Error, Result, Ruma};
|
use crate::{pdu::PduBuilder, ConduitResult, Database, Error, Result, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -27,7 +25,7 @@ use rocket::{get, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn send_state_event_for_key_route(
|
pub async fn send_state_event_for_key_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<send_state_event::Request<'_>>,
|
body: Ruma<send_state_event::Request<'_>>,
|
||||||
) -> ConduitResult<send_state_event::Response> {
|
) -> ConduitResult<send_state_event::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn send_state_event_for_empty_key_route(
|
pub async fn send_state_event_for_empty_key_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<send_state_event::Request<'_>>,
|
body: Ruma<send_state_event::Request<'_>>,
|
||||||
) -> ConduitResult<send_state_event::Response> {
|
) -> ConduitResult<send_state_event::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_state_events_route(
|
pub async fn get_state_events_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_state_events::Request<'_>>,
|
body: Ruma<get_state_events::Request<'_>>,
|
||||||
) -> ConduitResult<get_state_events::Response> {
|
) -> ConduitResult<get_state_events::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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
|
db.rooms
|
||||||
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
||||||
.map(|event| {
|
.map(|event| {
|
||||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content.clone())
|
serde_json::from_value::<HistoryVisibilityEventContent>(event.content)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database(
|
Error::bad_database(
|
||||||
"Invalid room history visibility event in database.",
|
"Invalid room history visibility event in database.",
|
||||||
@@ -126,7 +124,7 @@ pub async fn get_state_events_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_state_events_for_key_route(
|
pub async fn get_state_events_for_key_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_state_events_for_key::Request<'_>>,
|
body: Ruma<get_state_events_for_key::Request<'_>>,
|
||||||
) -> ConduitResult<get_state_events_for_key::Response> {
|
) -> ConduitResult<get_state_events_for_key::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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
|
db.rooms
|
||||||
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
||||||
.map(|event| {
|
.map(|event| {
|
||||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content.clone())
|
serde_json::from_value::<HistoryVisibilityEventContent>(event.content)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database(
|
Error::bad_database(
|
||||||
"Invalid room history visibility event in 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 {
|
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"))?,
|
.map_err(|_| Error::bad_database("Invalid event content in database"))?,
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
@@ -177,7 +175,7 @@ pub async fn get_state_events_for_key_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_state_events_for_empty_key_route(
|
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<'_>>,
|
body: Ruma<get_state_events_for_key::Request<'_>>,
|
||||||
) -> ConduitResult<get_state_events_for_key::Response> {
|
) -> ConduitResult<get_state_events_for_key::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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
|
db.rooms
|
||||||
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
.room_state_get(&body.room_id, &EventType::RoomHistoryVisibility, "")?
|
||||||
.map(|event| {
|
.map(|event| {
|
||||||
serde_json::from_value::<HistoryVisibilityEventContent>(event.content.clone())
|
serde_json::from_value::<HistoryVisibilityEventContent>(event.content)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database(
|
Error::bad_database(
|
||||||
"Invalid room history visibility event in 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 {
|
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"))?,
|
.map_err(|_| Error::bad_database("Invalid event content in database"))?,
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
|
|||||||
+54
-185
@@ -1,22 +1,19 @@
|
|||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Result, Ruma, RumaResponse};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use log::error;
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::r0::{sync::sync_events, uiaa::UiaaResponse},
|
api::client::r0::sync::sync_events,
|
||||||
events::{room::member::MembershipState, AnySyncEphemeralRoomEvent, EventType},
|
events::{room::member::MembershipState, AnySyncEphemeralRoomEvent, EventType},
|
||||||
serde::Raw,
|
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")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, tokio};
|
use rocket::{get, tokio};
|
||||||
|
use std::{
|
||||||
|
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/sync`
|
/// # `GET /_matrix/client/r0/sync`
|
||||||
///
|
///
|
||||||
@@ -35,136 +32,23 @@ use rocket::{get, tokio};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn sync_events_route(
|
pub async fn sync_events_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<sync_events::Request<'_>>,
|
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_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.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 {
|
// TODO: match body.set_presence {
|
||||||
db.rooms.edus.ping_presence(&sender_user)?;
|
db.rooms.edus.ping_presence(&sender_user)?;
|
||||||
|
|
||||||
// Setup watchers, so if there's no response, we can wait for them
|
// 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 = db.globals.current_count()?.to_string();
|
||||||
let next_batch_string = next_batch.to_string();
|
|
||||||
|
|
||||||
let mut joined_rooms = BTreeMap::new();
|
let mut joined_rooms = BTreeMap::new();
|
||||||
let since = since
|
let since = body
|
||||||
|
.since
|
||||||
.clone()
|
.clone()
|
||||||
.and_then(|string| string.parse().ok())
|
.and_then(|string| string.parse().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
@@ -186,23 +70,13 @@ async fn sync_helper(
|
|||||||
|
|
||||||
let mut non_timeline_pdus = db
|
let mut non_timeline_pdus = db
|
||||||
.rooms
|
.rooms
|
||||||
.pdus_until(&sender_user, &room_id, u64::MAX)
|
.pdus_since(&sender_user, &room_id, since)?
|
||||||
.filter_map(|r| {
|
.filter_map(|r| r.ok()); // Filter out buggy events
|
||||||
// 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)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Take the last 10 events for the timeline
|
// Take the last 10 events for the timeline
|
||||||
let timeline_pdus = non_timeline_pdus
|
let timeline_pdus = non_timeline_pdus
|
||||||
.by_ref()
|
.by_ref()
|
||||||
|
.rev()
|
||||||
.take(10)
|
.take(10)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -228,11 +102,10 @@ async fn sync_helper(
|
|||||||
// since and the current room state, meaning there should be no updates.
|
// 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
|
// 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.
|
// 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
|
let pdus_after_since = db
|
||||||
.rooms
|
.rooms
|
||||||
.pdus_after(&sender_user, &room_id, since)
|
.pdus_after(sender_user, &room_id, since)
|
||||||
.next()
|
.next()
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
@@ -261,9 +134,11 @@ async fn sync_helper(
|
|||||||
let since_state = since_shortstatehash
|
let since_state = since_shortstatehash
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|since_shortstatehash| {
|
.map(|since_shortstatehash| {
|
||||||
since_shortstatehash
|
Ok::<_, Error>(
|
||||||
.map(|since_shortstatehash| db.rooms.state_full(since_shortstatehash))
|
since_shortstatehash
|
||||||
.transpose()
|
.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) {
|
match (since_membership, current_membership) {
|
||||||
(MembershipState::Leave, MembershipState::Join) => {
|
(MembershipState::Leave, MembershipState::Join) => {
|
||||||
// A new user joined an encrypted room
|
// 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);
|
device_list_updates.insert(user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Remove, this should never happen here, right?
|
|
||||||
(MembershipState::Join, MembershipState::Leave) => {
|
(MembershipState::Join, MembershipState::Leave) => {
|
||||||
// Write down users that have left encrypted rooms we are in
|
// Write down users that have left encrypted rooms we are in
|
||||||
left_encrypted_users.insert(user_id);
|
left_encrypted_users.insert(user_id);
|
||||||
@@ -368,15 +242,14 @@ async fn sync_helper(
|
|||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
db.rooms
|
db.rooms
|
||||||
.room_members(&room_id)
|
.room_members(&room_id)
|
||||||
.flatten()
|
.filter_map(|user_id| Some(user_id.ok()?))
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Don't send key updates from the sender to the sender
|
// Don't send key updates from the sender to the sender
|
||||||
&sender_user != user_id
|
sender_user != user_id
|
||||||
})
|
})
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Only send keys if the sender doesn't share an encrypted room with the target already
|
// 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)
|
!share_encrypted_room(&db, sender_user, user_id, &room_id)
|
||||||
.unwrap_or(false)
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -394,13 +267,15 @@ async fn sync_helper(
|
|||||||
|
|
||||||
for hero in db
|
for hero in db
|
||||||
.rooms
|
.rooms
|
||||||
.all_pdus(&sender_user, &room_id)
|
.all_pdus(&sender_user, &room_id)?
|
||||||
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
||||||
.filter(|(_, pdu)| pdu.kind == EventType::RoomMember)
|
.filter(|(_, pdu)| pdu.kind == EventType::RoomMember)
|
||||||
.map(|(_, pdu)| {
|
.map(|(_, pdu)| {
|
||||||
let content = serde_json::from_value::<
|
let content = serde_json::from_value::<
|
||||||
ruma::events::room::member::MemberEventContent,
|
Raw<ruma::events::room::member::MemberEventContent>,
|
||||||
>(pdu.content.clone())
|
>(pdu.content.clone())
|
||||||
|
.expect("Raw::from_value always works")
|
||||||
|
.deserialize()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database("Invalid member event in database.")
|
Error::bad_database("Invalid member event in database.")
|
||||||
})?;
|
})?;
|
||||||
@@ -426,10 +301,9 @@ async fn sync_helper(
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Filter out buggy users
|
.filter_map(|u| u.ok()) // Filter out buggy users
|
||||||
.filter_map(|u| u.ok())
|
|
||||||
// Filter for possible heroes
|
// Filter for possible heroes
|
||||||
.flatten()
|
.filter_map(|u| u)
|
||||||
{
|
{
|
||||||
if heroes.contains(&hero) || hero == sender_user.as_str() {
|
if heroes.contains(&hero) || hero == sender_user.as_str() {
|
||||||
continue;
|
continue;
|
||||||
@@ -450,7 +324,7 @@ async fn sync_helper(
|
|||||||
|
|
||||||
let state_events = if joined_since_last_sync {
|
let state_events = if joined_since_last_sync {
|
||||||
current_state
|
current_state
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|(_, pdu)| pdu.to_sync_state_event())
|
.map(|(_, pdu)| pdu.to_sync_state_event())
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
@@ -530,9 +404,8 @@ async fn sync_helper(
|
|||||||
let mut edus = db
|
let mut edus = db
|
||||||
.rooms
|
.rooms
|
||||||
.edus
|
.edus
|
||||||
.readreceipts_since(&room_id, since)
|
.readreceipts_since(&room_id, since)?
|
||||||
.filter_map(|r| r.ok()) // Filter out buggy events
|
.filter_map(|r| r.ok()) // Filter out buggy events
|
||||||
.map(|(_, _, v)| v)
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if db.rooms.edus.last_typing_update(&room_id, &db.globals)? > since {
|
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 {
|
let joined_room = sync_events::JoinedRoom {
|
||||||
account_data: sync_events::RoomAccountData {
|
account_data: sync_events::AccountData {
|
||||||
events: db
|
events: db
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(Some(&room_id), &sender_user, since)?
|
.changes_since(Some(&room_id), &sender_user, since)?
|
||||||
@@ -632,10 +505,10 @@ async fn sync_helper(
|
|||||||
left_rooms.insert(
|
left_rooms.insert(
|
||||||
room_id.clone(),
|
room_id.clone(),
|
||||||
sync_events::LeftRoom {
|
sync_events::LeftRoom {
|
||||||
account_data: sync_events::RoomAccountData { events: Vec::new() },
|
account_data: sync_events::AccountData { events: Vec::new() },
|
||||||
timeline: sync_events::Timeline {
|
timeline: sync_events::Timeline {
|
||||||
limited: false,
|
limited: false,
|
||||||
prev_batch: Some(next_batch_string.clone()),
|
prev_batch: Some(next_batch.clone()),
|
||||||
events: Vec::new(),
|
events: Vec::new(),
|
||||||
},
|
},
|
||||||
state: sync_events::State {
|
state: sync_events::State {
|
||||||
@@ -668,7 +541,7 @@ async fn sync_helper(
|
|||||||
for user_id in left_encrypted_users {
|
for user_id in left_encrypted_users {
|
||||||
let still_share_encrypted_room = db
|
let still_share_encrypted_room = db
|
||||||
.rooms
|
.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(|r| r.ok())
|
||||||
.filter_map(|other_room_id| {
|
.filter_map(|other_room_id| {
|
||||||
Some(
|
Some(
|
||||||
@@ -688,10 +561,10 @@ async fn sync_helper(
|
|||||||
|
|
||||||
// Remove all to-device events the device received *last time*
|
// Remove all to-device events the device received *last time*
|
||||||
db.users
|
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 {
|
let response = sync_events::Response {
|
||||||
next_batch: next_batch_string,
|
next_batch,
|
||||||
rooms: sync_events::Rooms {
|
rooms: sync_events::Rooms {
|
||||||
leave: left_rooms,
|
leave: left_rooms,
|
||||||
join: joined_rooms,
|
join: joined_rooms,
|
||||||
@@ -703,7 +576,7 @@ async fn sync_helper(
|
|||||||
.map(|(_, v)| Raw::from(v))
|
.map(|(_, v)| Raw::from(v))
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
account_data: sync_events::GlobalAccountData {
|
account_data: sync_events::AccountData {
|
||||||
events: db
|
events: db
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(None, &sender_user, since)?
|
.changes_since(None, &sender_user, since)?
|
||||||
@@ -719,22 +592,20 @@ async fn sync_helper(
|
|||||||
changed: device_list_updates.into_iter().collect(),
|
changed: device_list_updates.into_iter().collect(),
|
||||||
left: device_list_left.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
|
|| since == 0
|
||||||
{
|
{
|
||||||
db.users.count_one_time_keys(&sender_user, &sender_device)?
|
db.users.count_one_time_keys(sender_user, sender_device)?
|
||||||
} else {
|
} else {
|
||||||
BTreeMap::new()
|
BTreeMap::new()
|
||||||
},
|
},
|
||||||
to_device: sync_events::ToDevice {
|
to_device: sync_events::ToDevice {
|
||||||
events: db
|
events: db.users.get_to_device_events(sender_user, sender_device)?,
|
||||||
.users
|
|
||||||
.get_to_device_events(&sender_user, &sender_device)?,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Retry the endpoint instead of returning (waiting for #118)
|
// TODO: Retry the endpoint instead of returning (waiting for #118)
|
||||||
if !full_state
|
if !body.full_state
|
||||||
&& response.rooms.is_empty()
|
&& response.rooms.is_empty()
|
||||||
&& response.presence.is_empty()
|
&& response.presence.is_empty()
|
||||||
&& response.account_data.is_empty()
|
&& response.account_data.is_empty()
|
||||||
@@ -744,15 +615,14 @@ async fn sync_helper(
|
|||||||
{
|
{
|
||||||
// Hang a few seconds so requests are not spammed
|
// Hang a few seconds so requests are not spammed
|
||||||
// Stop hanging if new info arrives
|
// 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 {
|
if duration.as_secs() > 30 {
|
||||||
duration = Duration::from_secs(30);
|
duration = Duration::from_secs(30);
|
||||||
}
|
}
|
||||||
let _ = tokio::time::timeout(duration, watcher).await;
|
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))]
|
#[tracing::instrument(skip(db))]
|
||||||
@@ -761,10 +631,9 @@ fn share_encrypted_room(
|
|||||||
sender_user: &UserId,
|
sender_user: &UserId,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
ignore_room: &RoomId,
|
ignore_room: &RoomId,
|
||||||
) -> Result<bool> {
|
) -> bool {
|
||||||
Ok(db
|
db.rooms
|
||||||
.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(|r| r.ok())
|
||||||
.filter(|room_id| room_id != ignore_room)
|
.filter(|room_id| room_id != ignore_room)
|
||||||
.filter_map(|other_room_id| {
|
.filter_map(|other_room_id| {
|
||||||
@@ -775,5 +644,5 @@ fn share_encrypted_room(
|
|||||||
.is_some(),
|
.is_some(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.any(|encrypted| encrypted))
|
.any(|encrypted| encrypted)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use ruma::{
|
|||||||
api::client::r0::tag::{create_tag, delete_tag, get_tags},
|
api::client::r0::tag::{create_tag, delete_tag, get_tags},
|
||||||
events::EventType,
|
events::EventType,
|
||||||
};
|
};
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{delete, get, put};
|
use rocket::{delete, get, put};
|
||||||
@@ -15,7 +15,7 @@ use rocket::{delete, get, put};
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn update_tag_route(
|
pub async fn update_tag_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_tag::Request<'_>>,
|
body: Ruma<create_tag::Request<'_>>,
|
||||||
) -> ConduitResult<create_tag::Response> {
|
) -> ConduitResult<create_tag::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -31,7 +31,7 @@ pub async fn update_tag_route(
|
|||||||
tags_event
|
tags_event
|
||||||
.content
|
.content
|
||||||
.tags
|
.tags
|
||||||
.insert(body.tag.clone().into(), body.tag_info.clone());
|
.insert(body.tag.to_string(), body.tag_info.clone());
|
||||||
|
|
||||||
db.account_data.update(
|
db.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
@@ -52,7 +52,7 @@ pub async fn update_tag_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn delete_tag_route(
|
pub async fn delete_tag_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<delete_tag::Request<'_>>,
|
body: Ruma<delete_tag::Request<'_>>,
|
||||||
) -> ConduitResult<delete_tag::Response> {
|
) -> ConduitResult<delete_tag::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
tags_event.content.tags.remove(&body.tag.clone().into());
|
tags_event.content.tags.remove(&body.tag);
|
||||||
|
|
||||||
db.account_data.update(
|
db.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
@@ -86,7 +86,7 @@ pub async fn delete_tag_route(
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn get_tags_route(
|
pub async fn get_tags_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<get_tags::Request<'_>>,
|
body: Ruma<get_tags::Request<'_>>,
|
||||||
) -> ConduitResult<get_tags::Response> {
|
) -> ConduitResult<get_tags::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Error, Ruma};
|
use crate::{ConduitResult, Database, Error, Ruma};
|
||||||
use ruma::{
|
use ruma::api::client::{
|
||||||
api::client::{error::ErrorKind, r0::to_device::send_event_to_device},
|
error::ErrorKind,
|
||||||
to_device::DeviceIdOrAllDevices,
|
r0::to_device::{self, send_event_to_device},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
@@ -16,7 +14,7 @@ use rocket::put;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn send_event_to_device_route(
|
pub async fn send_event_to_device_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<send_event_to_device::Request<'_>>,
|
body: Ruma<send_event_to_device::Request<'_>>,
|
||||||
) -> ConduitResult<send_event_to_device::Response> {
|
) -> ConduitResult<send_event_to_device::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
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_user_id, map) in &body.messages {
|
||||||
for (target_device_id_maybe, event) in map {
|
for (target_device_id_maybe, event) in map {
|
||||||
match target_device_id_maybe {
|
match target_device_id_maybe {
|
||||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => db.users.add_to_device_event(
|
to_device::DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||||
sender_user,
|
db.users.add_to_device_event(
|
||||||
&target_user_id,
|
sender_user,
|
||||||
&target_device_id,
|
&target_user_id,
|
||||||
&body.event_type,
|
&target_device_id,
|
||||||
event.deserialize_as().map_err(|_| {
|
&body.event_type,
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
serde_json::from_str(event.get()).map_err(|_| {
|
||||||
})?,
|
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||||
&db.globals,
|
})?,
|
||||||
)?,
|
&db.globals,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
|
||||||
DeviceIdOrAllDevices::AllDevices => {
|
to_device::DeviceIdOrAllDevices::AllDevices => {
|
||||||
for target_device_id in db.users.all_device_ids(&target_user_id) {
|
for target_device_id in db.users.all_device_ids(&target_user_id) {
|
||||||
db.users.add_to_device_event(
|
db.users.add_to_device_event(
|
||||||
sender_user,
|
sender_user,
|
||||||
&target_user_id,
|
&target_user_id,
|
||||||
&target_device_id?,
|
&target_device_id?,
|
||||||
&body.event_type,
|
&body.event_type,
|
||||||
event.deserialize_as().map_err(|_| {
|
serde_json::from_str(event.get()).map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||||
})?,
|
})?,
|
||||||
&db.globals,
|
&db.globals,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{utils, ConduitResult, Database, Ruma};
|
use crate::{utils, ConduitResult, Database, Ruma};
|
||||||
use create_typing_event::Typing;
|
use create_typing_event::Typing;
|
||||||
@@ -14,7 +12,7 @@ use rocket::put;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub fn create_typing_event_route(
|
pub fn create_typing_event_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<create_typing_event::Request<'_>>,
|
body: Ruma<create_typing_event::Request<'_>>,
|
||||||
) -> ConduitResult<create_typing_event::Response> {
|
) -> ConduitResult<create_typing_event::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
use crate::{ConduitResult, Database, Ruma};
|
use crate::{ConduitResult, Database, Ruma};
|
||||||
use ruma::api::client::r0::user_directory::search_users;
|
use ruma::api::client::r0::user_directory::search_users;
|
||||||
@@ -13,7 +11,7 @@ use rocket::post;
|
|||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip(db, body))]
|
#[tracing::instrument(skip(db, body))]
|
||||||
pub async fn search_users_route(
|
pub async fn search_users_route(
|
||||||
db: State<'_, Arc<Database>>,
|
db: State<'_, Database>,
|
||||||
body: Ruma<search_users::Request<'_>>,
|
body: Ruma<search_users::Request<'_>>,
|
||||||
) -> ConduitResult<search_users::Response> {
|
) -> ConduitResult<search_users::Response> {
|
||||||
let limit = u64::from(body.limit) as usize;
|
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| {
|
let mut users = db.users.iter().filter_map(|user_id| {
|
||||||
// Filter out buggy users (they should not exist, but you never know...)
|
// Filter out buggy users (they should not exist, but you never know...)
|
||||||
let user_id = user_id.ok()?;
|
let user_id = user_id.ok()?;
|
||||||
|
if db.users.is_deactivated(&user_id).ok()? {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let user = search_users::User {
|
let user = search_users::User {
|
||||||
user_id: user_id.clone(),
|
user_id: user_id.clone(),
|
||||||
@@ -28,18 +29,11 @@ pub async fn search_users_route(
|
|||||||
avatar_url: db.users.avatar_url(&user_id).ok()?,
|
avatar_url: db.users.avatar_url(&user_id).ok()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !user
|
if !user.user_id.to_string().contains(&body.search_term)
|
||||||
.user_id
|
|
||||||
.to_string()
|
|
||||||
.to_lowercase()
|
|
||||||
.contains(&body.search_term.to_lowercase())
|
|
||||||
&& user
|
&& user
|
||||||
.display_name
|
.display_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.filter(|name| {
|
.filter(|name| name.contains(&body.search_term))
|
||||||
name.to_lowercase()
|
|
||||||
.contains(&body.search_term.to_lowercase())
|
|
||||||
})
|
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
+81
-184
@@ -1,5 +1,3 @@
|
|||||||
pub mod abstraction;
|
|
||||||
|
|
||||||
pub mod account_data;
|
pub mod account_data;
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod appservice;
|
pub mod appservice;
|
||||||
@@ -13,18 +11,16 @@ pub mod transaction_ids;
|
|||||||
pub mod uiaa;
|
pub mod uiaa;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|
||||||
use crate::{utils, Error, Result};
|
use crate::{Error, Result};
|
||||||
use abstraction::DatabaseEngine;
|
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use log::error;
|
use futures::StreamExt;
|
||||||
use lru_cache::LruCache;
|
use log::info;
|
||||||
use rocket::futures::{channel::mpsc, stream::FuturesUnordered, StreamExt};
|
use rocket::futures::{self, channel::mpsc};
|
||||||
use ruma::{DeviceId, ServerName, UserId};
|
use ruma::{DeviceId, ServerName, UserId};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::{self, remove_dir_all},
|
fs::remove_dir_all,
|
||||||
io::Write,
|
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
@@ -71,19 +67,14 @@ fn default_max_request_size() -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_max_concurrent_requests() -> u16 {
|
fn default_max_concurrent_requests() -> u16 {
|
||||||
100
|
4
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_log() -> String {
|
fn default_log() -> String {
|
||||||
"info,state_res=warn,rocket=off,_=off,sled=off".to_owned()
|
"info,state_res=warn,rocket=off,_=off,sled=off".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sled")]
|
#[derive(Clone)]
|
||||||
pub type Engine = abstraction::SledEngine;
|
|
||||||
|
|
||||||
#[cfg(feature = "rocksdb")]
|
|
||||||
pub type Engine = abstraction::RocksDbEngine;
|
|
||||||
|
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub globals: globals::Globals,
|
pub globals: globals::Globals,
|
||||||
pub users: users::Users,
|
pub users: users::Users,
|
||||||
@@ -97,6 +88,7 @@ pub struct Database {
|
|||||||
pub admin: admin::Admin,
|
pub admin: admin::Admin,
|
||||||
pub appservice: appservice::Appservice,
|
pub appservice: appservice::Appservice,
|
||||||
pub pusher: pusher::PushData,
|
pub pusher: pusher::PushData,
|
||||||
|
pub _db: sled::Db,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
@@ -113,213 +105,118 @@ impl Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Load an existing database or create a new one.
|
/// Load an existing database or create a new one.
|
||||||
pub async fn load_or_create(config: Config) -> Result<Arc<Self>> {
|
pub async fn load_or_create(config: Config) -> Result<Self> {
|
||||||
let builder = Engine::open(&config)?;
|
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 {
|
info!("Opened sled database at {}", config.database_path);
|
||||||
eprintln!("ERROR: Max request size is less than 1KB. Please increase it.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let (admin_sender, admin_receiver) = mpsc::unbounded();
|
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 {
|
users: users::Users {
|
||||||
userid_password: builder.open_tree("userid_password")?,
|
userid_password: db.open_tree("userid_password")?,
|
||||||
userid_displayname: builder.open_tree("userid_displayname")?,
|
userid_displayname: db.open_tree("userid_displayname")?,
|
||||||
userid_avatarurl: builder.open_tree("userid_avatarurl")?,
|
userid_avatarurl: db.open_tree("userid_avatarurl")?,
|
||||||
userdeviceid_token: builder.open_tree("userdeviceid_token")?,
|
userdeviceid_token: db.open_tree("userdeviceid_token")?,
|
||||||
userdeviceid_metadata: builder.open_tree("userdeviceid_metadata")?,
|
userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?,
|
||||||
userid_devicelistversion: builder.open_tree("userid_devicelistversion")?,
|
userid_devicelistversion: db.open_tree("userid_devicelistversion")?,
|
||||||
token_userdeviceid: builder.open_tree("token_userdeviceid")?,
|
token_userdeviceid: db.open_tree("token_userdeviceid")?,
|
||||||
onetimekeyid_onetimekeys: builder.open_tree("onetimekeyid_onetimekeys")?,
|
onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?,
|
||||||
userid_lastonetimekeyupdate: builder.open_tree("userid_lastonetimekeyupdate")?,
|
userid_lastonetimekeyupdate: db.open_tree("userid_lastonetimekeyupdate")?,
|
||||||
keychangeid_userid: builder.open_tree("keychangeid_userid")?,
|
keychangeid_userid: db.open_tree("keychangeid_userid")?,
|
||||||
keyid_key: builder.open_tree("keyid_key")?,
|
keyid_key: db.open_tree("keyid_key")?,
|
||||||
userid_masterkeyid: builder.open_tree("userid_masterkeyid")?,
|
userid_masterkeyid: db.open_tree("userid_masterkeyid")?,
|
||||||
userid_selfsigningkeyid: builder.open_tree("userid_selfsigningkeyid")?,
|
userid_selfsigningkeyid: db.open_tree("userid_selfsigningkeyid")?,
|
||||||
userid_usersigningkeyid: builder.open_tree("userid_usersigningkeyid")?,
|
userid_usersigningkeyid: db.open_tree("userid_usersigningkeyid")?,
|
||||||
todeviceid_events: builder.open_tree("todeviceid_events")?,
|
todeviceid_events: db.open_tree("todeviceid_events")?,
|
||||||
},
|
},
|
||||||
uiaa: uiaa::Uiaa {
|
uiaa: uiaa::Uiaa {
|
||||||
userdevicesessionid_uiaainfo: builder.open_tree("userdevicesessionid_uiaainfo")?,
|
userdeviceid_uiaainfo: db.open_tree("userdeviceid_uiaainfo")?,
|
||||||
userdevicesessionid_uiaarequest: builder
|
|
||||||
.open_tree("userdevicesessionid_uiaarequest")?,
|
|
||||||
},
|
},
|
||||||
rooms: rooms::Rooms {
|
rooms: rooms::Rooms {
|
||||||
edus: rooms::RoomEdus {
|
edus: rooms::RoomEdus {
|
||||||
readreceiptid_readreceipt: builder.open_tree("readreceiptid_readreceipt")?,
|
readreceiptid_readreceipt: db.open_tree("readreceiptid_readreceipt")?,
|
||||||
roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, // "Private" read receipt
|
roomuserid_privateread: db.open_tree("roomuserid_privateread")?, // "Private" read receipt
|
||||||
roomuserid_lastprivatereadupdate: builder
|
roomuserid_lastprivatereadupdate: db
|
||||||
.open_tree("roomuserid_lastprivatereadupdate")?,
|
.open_tree("roomuserid_lastprivatereadupdate")?,
|
||||||
typingid_userid: builder.open_tree("typingid_userid")?,
|
typingid_userid: db.open_tree("typingid_userid")?,
|
||||||
roomid_lasttypingupdate: builder.open_tree("roomid_lasttypingupdate")?,
|
roomid_lasttypingupdate: db.open_tree("roomid_lasttypingupdate")?,
|
||||||
presenceid_presence: builder.open_tree("presenceid_presence")?,
|
presenceid_presence: db.open_tree("presenceid_presence")?,
|
||||||
userid_lastpresenceupdate: builder.open_tree("userid_lastpresenceupdate")?,
|
userid_lastpresenceupdate: db.open_tree("userid_lastpresenceupdate")?,
|
||||||
},
|
},
|
||||||
pduid_pdu: builder.open_tree("pduid_pdu")?,
|
pduid_pdu: db.open_tree("pduid_pdu")?,
|
||||||
eventid_pduid: builder.open_tree("eventid_pduid")?,
|
eventid_pduid: db.open_tree("eventid_pduid")?,
|
||||||
roomid_pduleaves: builder.open_tree("roomid_pduleaves")?,
|
roomid_pduleaves: db.open_tree("roomid_pduleaves")?,
|
||||||
|
|
||||||
alias_roomid: builder.open_tree("alias_roomid")?,
|
alias_roomid: db.open_tree("alias_roomid")?,
|
||||||
aliasid_alias: builder.open_tree("aliasid_alias")?,
|
aliasid_alias: db.open_tree("aliasid_alias")?,
|
||||||
publicroomids: builder.open_tree("publicroomids")?,
|
publicroomids: db.open_tree("publicroomids")?,
|
||||||
|
|
||||||
tokenids: builder.open_tree("tokenids")?,
|
tokenids: db.open_tree("tokenids")?,
|
||||||
|
|
||||||
roomserverids: builder.open_tree("roomserverids")?,
|
roomserverids: db.open_tree("roomserverids")?,
|
||||||
serverroomids: builder.open_tree("serverroomids")?,
|
userroomid_joined: db.open_tree("userroomid_joined")?,
|
||||||
userroomid_joined: builder.open_tree("userroomid_joined")?,
|
roomuserid_joined: db.open_tree("roomuserid_joined")?,
|
||||||
roomuserid_joined: builder.open_tree("roomuserid_joined")?,
|
roomuseroncejoinedids: db.open_tree("roomuseroncejoinedids")?,
|
||||||
roomuseroncejoinedids: builder.open_tree("roomuseroncejoinedids")?,
|
userroomid_invitestate: db.open_tree("userroomid_invitestate")?,
|
||||||
userroomid_invitestate: builder.open_tree("userroomid_invitestate")?,
|
roomuserid_invitecount: db.open_tree("roomuserid_invitecount")?,
|
||||||
roomuserid_invitecount: builder.open_tree("roomuserid_invitecount")?,
|
userroomid_leftstate: db.open_tree("userroomid_leftstate")?,
|
||||||
userroomid_leftstate: builder.open_tree("userroomid_leftstate")?,
|
roomuserid_leftcount: db.open_tree("roomuserid_leftcount")?,
|
||||||
roomuserid_leftcount: builder.open_tree("roomuserid_leftcount")?,
|
|
||||||
|
|
||||||
userroomid_notificationcount: builder.open_tree("userroomid_notificationcount")?,
|
userroomid_notificationcount: db.open_tree("userroomid_notificationcount")?,
|
||||||
userroomid_highlightcount: builder.open_tree("userroomid_highlightcount")?,
|
userroomid_highlightcount: db.open_tree("userroomid_highlightcount")?,
|
||||||
|
|
||||||
statekey_shortstatekey: builder.open_tree("statekey_shortstatekey")?,
|
statekey_shortstatekey: db.open_tree("statekey_shortstatekey")?,
|
||||||
stateid_shorteventid: builder.open_tree("stateid_shorteventid")?,
|
stateid_shorteventid: db.open_tree("stateid_shorteventid")?,
|
||||||
eventid_shorteventid: builder.open_tree("eventid_shorteventid")?,
|
eventid_shorteventid: db.open_tree("eventid_shorteventid")?,
|
||||||
shorteventid_eventid: builder.open_tree("shorteventid_eventid")?,
|
shorteventid_eventid: db.open_tree("shorteventid_eventid")?,
|
||||||
shorteventid_shortstatehash: builder.open_tree("shorteventid_shortstatehash")?,
|
shorteventid_shortstatehash: db.open_tree("shorteventid_shortstatehash")?,
|
||||||
roomid_shortstatehash: builder.open_tree("roomid_shortstatehash")?,
|
roomid_shortstatehash: db.open_tree("roomid_shortstatehash")?,
|
||||||
statehash_shortstatehash: builder.open_tree("statehash_shortstatehash")?,
|
statehash_shortstatehash: db.open_tree("statehash_shortstatehash")?,
|
||||||
|
|
||||||
eventid_outlierpdu: builder.open_tree("eventid_outlierpdu")?,
|
eventid_outlierpdu: db.open_tree("eventid_outlierpdu")?,
|
||||||
prevevent_parent: builder.open_tree("prevevent_parent")?,
|
prevevent_parent: db.open_tree("prevevent_parent")?,
|
||||||
pdu_cache: RwLock::new(LruCache::new(1_000_000)),
|
|
||||||
},
|
},
|
||||||
account_data: account_data::AccountData {
|
account_data: account_data::AccountData {
|
||||||
roomuserdataid_accountdata: builder.open_tree("roomuserdataid_accountdata")?,
|
roomuserdataid_accountdata: db.open_tree("roomuserdataid_accountdata")?,
|
||||||
},
|
},
|
||||||
media: media::Media {
|
media: media::Media {
|
||||||
mediaid_file: builder.open_tree("mediaid_file")?,
|
mediaid_file: db.open_tree("mediaid_file")?,
|
||||||
},
|
},
|
||||||
key_backups: key_backups::KeyBackups {
|
key_backups: key_backups::KeyBackups {
|
||||||
backupid_algorithm: builder.open_tree("backupid_algorithm")?,
|
backupid_algorithm: db.open_tree("backupid_algorithm")?,
|
||||||
backupid_etag: builder.open_tree("backupid_etag")?,
|
backupid_etag: db.open_tree("backupid_etag")?,
|
||||||
backupkeyid_backup: builder.open_tree("backupkeyid_backup")?,
|
backupkeyid_backup: db.open_tree("backupkeyid_backup")?,
|
||||||
},
|
},
|
||||||
transaction_ids: transaction_ids::TransactionIds {
|
transaction_ids: transaction_ids::TransactionIds {
|
||||||
userdevicetxnid_response: builder.open_tree("userdevicetxnid_response")?,
|
userdevicetxnid_response: db.open_tree("userdevicetxnid_response")?,
|
||||||
},
|
},
|
||||||
sending: sending::Sending {
|
sending: sending::Sending {
|
||||||
servername_educount: builder.open_tree("servername_educount")?,
|
servernamepduids: db.open_tree("servernamepduids")?,
|
||||||
servernamepduids: builder.open_tree("servernamepduids")?,
|
servercurrentpdus: db.open_tree("servercurrentpdus")?,
|
||||||
servercurrentevents: builder.open_tree("servercurrentevents")?,
|
|
||||||
maximum_requests: Arc::new(Semaphore::new(config.max_concurrent_requests as usize)),
|
maximum_requests: Arc::new(Semaphore::new(config.max_concurrent_requests as usize)),
|
||||||
sender: sending_sender,
|
|
||||||
},
|
},
|
||||||
admin: admin::Admin {
|
admin: admin::Admin {
|
||||||
sender: admin_sender,
|
sender: admin_sender,
|
||||||
},
|
},
|
||||||
appservice: appservice::Appservice {
|
appservice: appservice::Appservice {
|
||||||
cached_registrations: Arc::new(RwLock::new(HashMap::new())),
|
cached_registrations: Arc::new(RwLock::new(HashMap::new())),
|
||||||
id_appserviceregistrations: builder.open_tree("id_appserviceregistrations")?,
|
id_appserviceregistrations: db.open_tree("id_appserviceregistrations")?,
|
||||||
},
|
|
||||||
pusher: pusher::PushData {
|
|
||||||
senderkey_pusher: builder.open_tree("senderkey_pusher")?,
|
|
||||||
},
|
},
|
||||||
|
pusher: pusher::PushData::new(&db)?,
|
||||||
globals: globals::Globals::load(
|
globals: globals::Globals::load(
|
||||||
builder.open_tree("global")?,
|
db.open_tree("global")?,
|
||||||
builder.open_tree("server_signingkeys")?,
|
db.open_tree("servertimeout_signingkey")?,
|
||||||
config,
|
config,
|
||||||
)?,
|
)?,
|
||||||
});
|
_db: db,
|
||||||
|
};
|
||||||
|
|
||||||
// MIGRATIONS
|
db.admin.start_handler(db.clone(), admin_receiver);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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);
|
|
||||||
|
|
||||||
Ok(db)
|
Ok(db)
|
||||||
}
|
}
|
||||||
@@ -333,7 +230,7 @@ impl Database {
|
|||||||
userdeviceid_prefix.extend_from_slice(device_id.as_bytes());
|
userdeviceid_prefix.extend_from_slice(device_id.as_bytes());
|
||||||
userdeviceid_prefix.push(0xff);
|
userdeviceid_prefix.push(0xff);
|
||||||
|
|
||||||
let mut futures = FuturesUnordered::new();
|
let mut futures = futures::stream::FuturesUnordered::new();
|
||||||
|
|
||||||
// Return when *any* user changed his key
|
// Return when *any* user changed his key
|
||||||
// TODO: only send for user they share a room with
|
// 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 crate::{utils, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::error::ErrorKind,
|
api::client::error::ErrorKind,
|
||||||
events::{AnyEphemeralRoomEvent, EventType},
|
events::{AnyEvent as EduEvent, EventType},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
RoomId, UserId,
|
RoomId, UserId,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use std::{collections::HashMap, convert::TryFrom, sync::Arc};
|
use sled::IVec;
|
||||||
|
use std::{collections::HashMap, convert::TryFrom};
|
||||||
use super::abstraction::Tree;
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct AccountData {
|
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 {
|
impl AccountData {
|
||||||
@@ -34,8 +34,9 @@ impl AccountData {
|
|||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
// Remove old entry
|
// Remove old entry
|
||||||
if let Some((old_key, _)) = self.find_event(room_id, user_id, &event_type)? {
|
if let Some(previous) = self.find_event(room_id, user_id, &event_type) {
|
||||||
self.roomuserdataid_accountdata.remove(&old_key)?;
|
let (old_key, _) = previous?;
|
||||||
|
self.roomuserdataid_accountdata.remove(old_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut key = prefix;
|
let mut key = prefix;
|
||||||
@@ -51,10 +52,8 @@ impl AccountData {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.roomuserdataid_accountdata.insert(
|
self.roomuserdataid_accountdata
|
||||||
&key,
|
.insert(key, &*json.to_string())?;
|
||||||
&serde_json::to_vec(&json).expect("to_vec always works on json values"),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -66,8 +65,9 @@ impl AccountData {
|
|||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
kind: EventType,
|
kind: EventType,
|
||||||
) -> Result<Option<T>> {
|
) -> Result<Option<T>> {
|
||||||
self.find_event(room_id, user_id, &kind)?
|
self.find_event(room_id, user_id, &kind)
|
||||||
.map(|(_, v)| {
|
.map(|r| {
|
||||||
|
let (_, v) = r?;
|
||||||
serde_json::from_slice(&v).map_err(|_| Error::bad_database("could not deserialize"))
|
serde_json::from_slice(&v).map_err(|_| Error::bad_database("could not deserialize"))
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
@@ -80,7 +80,7 @@ impl AccountData {
|
|||||||
room_id: Option<&RoomId>,
|
room_id: Option<&RoomId>,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
since: u64,
|
since: u64,
|
||||||
) -> Result<HashMap<EventType, Raw<AnyEphemeralRoomEvent>>> {
|
) -> Result<HashMap<EventType, Raw<EduEvent>>> {
|
||||||
let mut userdata = HashMap::new();
|
let mut userdata = HashMap::new();
|
||||||
|
|
||||||
let mut prefix = room_id
|
let mut prefix = room_id
|
||||||
@@ -98,7 +98,8 @@ impl AccountData {
|
|||||||
|
|
||||||
for r in self
|
for r in self
|
||||||
.roomuserdataid_accountdata
|
.roomuserdataid_accountdata
|
||||||
.iter_from(&first_possible, false)
|
.range(&*first_possible..)
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
Ok::<_, Error>((
|
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."))?,
|
||||||
)
|
)
|
||||||
.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.")
|
Error::bad_database("Database contains invalid account data.")
|
||||||
})?,
|
})?,
|
||||||
))
|
))
|
||||||
@@ -127,7 +128,7 @@ impl AccountData {
|
|||||||
room_id: Option<&RoomId>,
|
room_id: Option<&RoomId>,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
kind: &EventType,
|
kind: &EventType,
|
||||||
) -> Result<Option<(Box<[u8]>, Box<[u8]>)>> {
|
) -> Option<Result<(IVec, IVec)>> {
|
||||||
let mut prefix = room_id
|
let mut prefix = room_id
|
||||||
.map(|r| r.to_string())
|
.map(|r| r.to_string())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@@ -136,21 +137,23 @@ impl AccountData {
|
|||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
prefix.extend_from_slice(&user_id.as_bytes());
|
prefix.extend_from_slice(&user_id.as_bytes());
|
||||||
prefix.push(0xff);
|
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();
|
let kind = kind.clone();
|
||||||
|
|
||||||
Ok(self
|
self.roomuserdataid_accountdata
|
||||||
.roomuserdataid_accountdata
|
.scan_prefix(prefix)
|
||||||
.iter_from(&last_possible_key, true)
|
.rev()
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.find(move |r| {
|
||||||
.find(move |(k, _)| {
|
r.as_ref()
|
||||||
k.rsplit(|&b| b == 0xff)
|
.map(|(k, _)| {
|
||||||
.next()
|
k.rsplit(|&b| b == 0xff)
|
||||||
.map(|current_event_type| current_event_type == kind.as_ref().as_bytes())
|
.next()
|
||||||
|
.map(|current_event_type| {
|
||||||
|
current_event_type == kind.as_ref().as_bytes()
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}))
|
})
|
||||||
|
.map(|r| Ok(r?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-18
@@ -1,9 +1,6 @@
|
|||||||
use std::{
|
use std::convert::{TryFrom, TryInto};
|
||||||
convert::{TryFrom, TryInto},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{pdu::PduBuilder, Database};
|
use crate::pdu::PduBuilder;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use rocket::futures::{channel::mpsc, stream::StreamExt};
|
use rocket::futures::{channel::mpsc, stream::StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -25,7 +22,7 @@ pub struct Admin {
|
|||||||
impl Admin {
|
impl Admin {
|
||||||
pub fn start_handler(
|
pub fn start_handler(
|
||||||
&self,
|
&self,
|
||||||
db: Arc<Database>,
|
db: super::Database,
|
||||||
mut receiver: mpsc::UnboundedReceiver<AdminCommand>,
|
mut receiver: mpsc::UnboundedReceiver<AdminCommand>,
|
||||||
) {
|
) {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -76,17 +73,14 @@ impl Admin {
|
|||||||
db.appservice.register_appservice(yaml).unwrap(); // TODO handle error
|
db.appservice.register_appservice(yaml).unwrap(); // TODO handle error
|
||||||
}
|
}
|
||||||
AdminCommand::ListAppservices => {
|
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 count = appservices.len();
|
||||||
let output = format!(
|
let output = format!(
|
||||||
"Appservices ({}): {}",
|
"Appservices ({}): {}",
|
||||||
count,
|
count,
|
||||||
appservices.into_iter().filter_map(|r| r.ok()).collect::<Vec<_>>().join(", ")
|
appservices.into_iter().filter_map(|r| r.ok()).collect::<Vec<_>>().join(", ")
|
||||||
);
|
);
|
||||||
send_message(message::MessageEventContent::text_plain(output));
|
send_message(message::MessageEventContent::text_plain(output));
|
||||||
} else {
|
|
||||||
send_message(message::MessageEventContent::text_plain("Failed to get appservices."));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
AdminCommand::SendMessage(message) => {
|
AdminCommand::SendMessage(message) => {
|
||||||
send_message(message);
|
send_message(message);
|
||||||
@@ -99,6 +93,6 @@ impl Admin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&self, command: AdminCommand) {
|
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},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct Appservice {
|
pub struct Appservice {
|
||||||
pub(super) cached_registrations: Arc<RwLock<HashMap<String, serde_yaml::Value>>>,
|
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 {
|
impl Appservice {
|
||||||
pub fn register_appservice(&self, yaml: serde_yaml::Value) -> Result<()> {
|
pub fn register_appservice(&self, yaml: serde_yaml::Value) -> Result<()> {
|
||||||
// TODO: Rumaify
|
// TODO: Rumaify
|
||||||
let id = yaml.get("id").unwrap().as_str().unwrap();
|
let id = yaml.get("id").unwrap().as_str().unwrap();
|
||||||
self.id_appserviceregistrations.insert(
|
self.id_appserviceregistrations
|
||||||
id.as_bytes(),
|
.insert(id, serde_yaml::to_string(&yaml).unwrap().as_bytes())?;
|
||||||
serde_yaml::to_string(&yaml).unwrap().as_bytes(),
|
|
||||||
)?;
|
|
||||||
self.cached_registrations
|
self.cached_registrations
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -34,37 +31,37 @@ impl Appservice {
|
|||||||
.get(id)
|
.get(id)
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|| {
|
|| {
|
||||||
self.id_appserviceregistrations
|
Ok(self
|
||||||
.get(id.as_bytes())?
|
.id_appserviceregistrations
|
||||||
|
.get(id)?
|
||||||
.map(|bytes| {
|
.map(|bytes| {
|
||||||
serde_yaml::from_slice(&bytes).map_err(|_| {
|
Ok::<_, Error>(serde_yaml::from_slice(&bytes).map_err(|_| {
|
||||||
Error::bad_database(
|
Error::bad_database(
|
||||||
"Invalid registration bytes in id_appserviceregistrations.",
|
"Invalid registration bytes in id_appserviceregistrations.",
|
||||||
)
|
)
|
||||||
})
|
})?)
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()?)
|
||||||
},
|
},
|
||||||
|r| Ok(Some(r.clone())),
|
|r| Ok(Some(r.clone())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_ids(&self) -> Result<impl Iterator<Item = Result<String>> + Send + Sync + '_> {
|
pub fn iter_ids(&self) -> impl Iterator<Item = Result<String>> {
|
||||||
Ok(self.id_appserviceregistrations.iter().map(|(id, _)| {
|
self.id_appserviceregistrations.iter().keys().map(|id| {
|
||||||
utils::string_from_bytes(&id)
|
Ok(utils::string_from_bytes(&id?).map_err(|_| {
|
||||||
.map_err(|_| Error::bad_database("Invalid id bytes in id_appserviceregistrations."))
|
Error::bad_database("Invalid id bytes in id_appserviceregistrations.")
|
||||||
}))
|
})?)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_all(
|
pub fn iter_all(&self) -> impl Iterator<Item = Result<(String, serde_yaml::Value)>> + '_ {
|
||||||
&self,
|
self.iter_ids().filter_map(|id| id.ok()).map(move |id| {
|
||||||
) -> Result<impl Iterator<Item = Result<(String, serde_yaml::Value)>> + '_ + Send + Sync> {
|
|
||||||
Ok(self.iter_ids()?.filter_map(|id| id.ok()).map(move |id| {
|
|
||||||
Ok((
|
Ok((
|
||||||
id.clone(),
|
id.clone(),
|
||||||
self.get_registration(&id)?
|
self.get_registration(&id)?
|
||||||
.expect("iter_ids only returns appservices that exist"),
|
.expect("iter_ids only returns appservices that exist"),
|
||||||
))
|
))
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-117
@@ -1,52 +1,32 @@
|
|||||||
use crate::{database::Config, utils, ConduitResult, Error, Result};
|
use crate::{database::Config, utils, Error, Result};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::federation::discovery::{ServerSigningKeys, VerifyKey},
|
||||||
client::r0::sync::sync_events,
|
ServerName, ServerSigningKeyId,
|
||||||
federation::discovery::{ServerSigningKeys, VerifyKey},
|
|
||||||
},
|
|
||||||
DeviceId, EventId, MilliSecondsSinceUnixEpoch, ServerName, ServerSigningKeyId, UserId,
|
|
||||||
};
|
};
|
||||||
use rustls::{ServerCertVerifier, WebPKIVerifier};
|
use rustls::{ServerCertVerifier, WebPKIVerifier};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
fs,
|
|
||||||
path::PathBuf,
|
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
time::{Duration, Instant},
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tokio::sync::Semaphore;
|
|
||||||
use trust_dns_resolver::TokioAsyncResolver;
|
use trust_dns_resolver::TokioAsyncResolver;
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
pub const COUNTER: &str = "c";
|
||||||
|
|
||||||
pub const COUNTER: &[u8] = b"c";
|
|
||||||
|
|
||||||
type WellKnownMap = HashMap<Box<ServerName>, (String, String)>;
|
type WellKnownMap = HashMap<Box<ServerName>, (String, String)>;
|
||||||
type TlsNameMap = HashMap<String, webpki::DNSName>;
|
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 struct Globals {
|
||||||
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
||||||
pub tls_name_override: Arc<RwLock<TlsNameMap>>,
|
pub tls_name_override: Arc<RwLock<TlsNameMap>>,
|
||||||
pub(super) globals: Arc<dyn Tree>,
|
pub(super) globals: sled::Tree,
|
||||||
config: Config,
|
config: Config,
|
||||||
keypair: Arc<ruma::signatures::Ed25519KeyPair>,
|
keypair: Arc<ruma::signatures::Ed25519KeyPair>,
|
||||||
reqwest_client: reqwest::Client,
|
reqwest_client: reqwest::Client,
|
||||||
dns_resolver: TokioAsyncResolver,
|
dns_resolver: TokioAsyncResolver,
|
||||||
jwt_decoding_key: Option<jsonwebtoken::DecodingKey<'static>>,
|
jwt_decoding_key: Option<jsonwebtoken::DecodingKey<'static>>,
|
||||||
pub(super) server_signingkeys: Arc<dyn Tree>,
|
pub(super) servertimeout_signingkey: sled::Tree, // ServerName + Timeout Timestamp -> algorithm:key + pubkey
|
||||||
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
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MatrixServerVerifier {
|
struct MatrixServerVerifier {
|
||||||
@@ -84,20 +64,15 @@ impl ServerCertVerifier for MatrixServerVerifier {
|
|||||||
|
|
||||||
impl Globals {
|
impl Globals {
|
||||||
pub fn load(
|
pub fn load(
|
||||||
globals: Arc<dyn Tree>,
|
globals: sled::Tree,
|
||||||
server_signingkeys: Arc<dyn Tree>,
|
servertimeout_signingkey: sled::Tree,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let keypair_bytes = globals.get(b"keypair")?.map_or_else(
|
let bytes = &*globals
|
||||||
|| {
|
.update_and_fetch("keypair", utils::generate_keypair)?
|
||||||
let keypair = utils::generate_keypair();
|
.expect("utils::generate_keypair always returns Some");
|
||||||
globals.insert(b"keypair", &keypair)?;
|
|
||||||
Ok::<_, Error>(keypair)
|
|
||||||
},
|
|
||||||
|s| Ok(s.to_vec()),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
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(
|
let keypair = utils::string_from_bytes(
|
||||||
// 1. version
|
// 1. version
|
||||||
@@ -114,7 +89,7 @@ impl Globals {
|
|||||||
.map(|key| (version, key))
|
.map(|key| (version, key))
|
||||||
})
|
})
|
||||||
.and_then(|(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."))
|
.map_err(|_| Error::bad_database("Private or public keys are invalid."))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,7 +97,7 @@ impl Globals {
|
|||||||
Ok(k) => k,
|
Ok(k) => k,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Keypair invalid. Deleting...");
|
error!("Keypair invalid. Deleting...");
|
||||||
globals.remove(b"keypair")?;
|
globals.remove("keypair")?;
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -150,7 +125,7 @@ impl Globals {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static());
|
.map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static());
|
||||||
|
|
||||||
let s = Self {
|
Ok(Self {
|
||||||
globals,
|
globals,
|
||||||
config,
|
config,
|
||||||
keypair: Arc::new(keypair),
|
keypair: Arc::new(keypair),
|
||||||
@@ -160,17 +135,9 @@ impl Globals {
|
|||||||
})?,
|
})?,
|
||||||
actual_destination_cache: Arc::new(RwLock::new(WellKnownMap::new())),
|
actual_destination_cache: Arc::new(RwLock::new(WellKnownMap::new())),
|
||||||
tls_name_override,
|
tls_name_override,
|
||||||
server_signingkeys,
|
servertimeout_signingkey,
|
||||||
jwt_decoding_key,
|
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.
|
/// Returns this server's keypair.
|
||||||
@@ -184,14 +151,19 @@ impl Globals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_count(&self) -> Result<u64> {
|
pub fn next_count(&self) -> Result<u64> {
|
||||||
utils::u64_from_bytes(&self.globals.increment(COUNTER)?)
|
Ok(utils::u64_from_bytes(
|
||||||
.map_err(|_| Error::bad_database("Count has invalid 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> {
|
pub fn current_count(&self) -> Result<u64> {
|
||||||
self.globals.get(COUNTER)?.map_or(Ok(0_u64), |bytes| {
|
self.globals.get(COUNTER)?.map_or(Ok(0_u64), |bytes| {
|
||||||
utils::u64_from_bytes(&bytes)
|
Ok(utils::u64_from_bytes(&bytes)
|
||||||
.map_err(|_| Error::bad_database("Count has invalid 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.
|
/// Remove the outdated keys and insert the new ones.
|
||||||
///
|
///
|
||||||
/// This doesn't actually check that the keys provided are newer than the old set.
|
/// 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<()> {
|
pub fn add_signing_key(&self, origin: &ServerName, keys: &ServerSigningKeys) -> Result<()> {
|
||||||
// Not atomic, but this is not critical
|
let mut key1 = origin.as_bytes().to_vec();
|
||||||
let signingkeys = self.server_signingkeys.get(origin.as_bytes())?;
|
key1.push(0xff);
|
||||||
|
|
||||||
let mut keys = signingkeys
|
let mut key2 = key1.clone();
|
||||||
.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 ServerSigningKeys {
|
let ts = keys
|
||||||
verify_keys,
|
.valid_until_ts
|
||||||
old_verify_keys,
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
..
|
.expect("time is valid")
|
||||||
} = new_keys;
|
.as_millis() as u64;
|
||||||
|
|
||||||
keys.verify_keys.extend(verify_keys.into_iter());
|
key1.extend_from_slice(&ts.to_be_bytes());
|
||||||
keys.old_verify_keys.extend(old_verify_keys.into_iter());
|
key2.extend_from_slice(&(ts + 1).to_be_bytes());
|
||||||
|
|
||||||
self.server_signingkeys.insert(
|
self.servertimeout_signingkey.insert(
|
||||||
origin.as_bytes(),
|
key1,
|
||||||
&serde_json::to_vec(&keys).expect("serversigningkeys can be serialized"),
|
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(())
|
Ok(())
|
||||||
@@ -264,49 +237,25 @@ impl Globals {
|
|||||||
&self,
|
&self,
|
||||||
origin: &ServerName,
|
origin: &ServerName,
|
||||||
) -> Result<BTreeMap<ServerSigningKeyId, VerifyKey>> {
|
) -> Result<BTreeMap<ServerSigningKeyId, VerifyKey>> {
|
||||||
let signingkeys = self
|
let mut response = BTreeMap::new();
|
||||||
.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);
|
|
||||||
|
|
||||||
Ok(signingkeys)
|
let now = crate::utils::millis_since_unix_epoch();
|
||||||
}
|
|
||||||
|
|
||||||
pub fn database_version(&self) -> Result<u64> {
|
for item in self.servertimeout_signingkey.scan_prefix(origin.as_bytes()) {
|
||||||
self.globals.get(b"version")?.map_or(Ok(0), |version| {
|
let (k, bytes) = item?;
|
||||||
utils::u64_from_bytes(&version)
|
let valid_until = k
|
||||||
.map_err(|_| Error::bad_database("Database version id is invalid."))
|
.splitn(2, |&b| b == 0xff)
|
||||||
})
|
.nth(1)
|
||||||
}
|
.map(crate::utils::u64_from_bytes)
|
||||||
|
.ok_or_else(|| Error::bad_database("Invalid signing keys."))?
|
||||||
pub fn bump_database_version(&self, new_version: u64) -> Result<()> {
|
.map_err(|_| Error::bad_database("Invalid signing key valid until bytes"))?;
|
||||||
self.globals
|
// If these keys are still valid use em!
|
||||||
.insert(b"version", &new_version.to_be_bytes())?;
|
if valid_until > now {
|
||||||
Ok(())
|
let btree: BTreeMap<_, _> = serde_json::from_slice(&bytes)
|
||||||
}
|
.map_err(|_| Error::bad_database("Invalid BTreeMap<> of signing keys"))?;
|
||||||
|
response.extend(btree);
|
||||||
pub fn get_media_folder(&self) -> PathBuf {
|
}
|
||||||
let mut r = PathBuf::new();
|
}
|
||||||
r.push(self.config.database_path.clone());
|
Ok(response)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-65
@@ -6,14 +6,13 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
RoomId, UserId,
|
RoomId, UserId,
|
||||||
};
|
};
|
||||||
use std::{collections::BTreeMap, convert::TryFrom, sync::Arc};
|
use std::{collections::BTreeMap, convert::TryFrom};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct KeyBackups {
|
pub struct KeyBackups {
|
||||||
pub(super) backupid_algorithm: Arc<dyn Tree>, // BackupId = UserId + Version(Count)
|
pub(super) backupid_algorithm: sled::Tree, // BackupId = UserId + Version(Count)
|
||||||
pub(super) backupid_etag: Arc<dyn Tree>, // BackupId = UserId + Version(Count)
|
pub(super) backupid_etag: sled::Tree, // BackupId = UserId + Version(Count)
|
||||||
pub(super) backupkeyid_backup: Arc<dyn Tree>, // BackupKeyId = UserId + Version + RoomId + SessionId
|
pub(super) backupkeyid_backup: sled::Tree, // BackupKeyId = UserId + Version + RoomId + SessionId
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyBackups {
|
impl KeyBackups {
|
||||||
@@ -31,7 +30,8 @@ impl KeyBackups {
|
|||||||
|
|
||||||
self.backupid_algorithm.insert(
|
self.backupid_algorithm.insert(
|
||||||
&key,
|
&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
|
self.backupid_etag
|
||||||
.insert(&key, &globals.next_count()?.to_be_bytes())?;
|
.insert(&key, &globals.next_count()?.to_be_bytes())?;
|
||||||
@@ -48,8 +48,13 @@ impl KeyBackups {
|
|||||||
|
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
|
|
||||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
for outdated_key in self
|
||||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
.backupkeyid_backup
|
||||||
|
.scan_prefix(&key)
|
||||||
|
.keys()
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
|
{
|
||||||
|
self.backupkeyid_backup.remove(outdated_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -75,9 +80,8 @@ impl KeyBackups {
|
|||||||
|
|
||||||
self.backupid_algorithm.insert(
|
self.backupid_algorithm.insert(
|
||||||
&key,
|
&key,
|
||||||
&serde_json::to_string(backup_metadata)
|
&*serde_json::to_string(backup_metadata)
|
||||||
.expect("BackupAlgorithm::to_string always works")
|
.expect("BackupAlgorithm::to_string always works"),
|
||||||
.as_bytes(),
|
|
||||||
)?;
|
)?;
|
||||||
self.backupid_etag
|
self.backupid_etag
|
||||||
.insert(&key, &globals.next_count()?.to_be_bytes())?;
|
.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)>> {
|
pub fn get_latest_backup(&self, user_id: &UserId) -> Result<Option<(String, BackupAlgorithm)>> {
|
||||||
let mut prefix = user_id.as_bytes().to_vec();
|
let mut prefix = user_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
let mut last_possible_key = prefix.clone();
|
|
||||||
last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes());
|
|
||||||
|
|
||||||
self.backupid_algorithm
|
self.backupid_algorithm
|
||||||
.iter_from(&last_possible_key, true)
|
.scan_prefix(&prefix)
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.last()
|
||||||
.next()
|
.map_or(Ok(None), |r| {
|
||||||
.map_or(Ok(None), |(key, value)| {
|
let (key, value) = r?;
|
||||||
let version = utils::string_from_bytes(
|
let version = utils::string_from_bytes(
|
||||||
key.rsplit(|&b| b == 0xff)
|
key.rsplit(|&b| b == 0xff)
|
||||||
.next()
|
.next()
|
||||||
@@ -116,12 +117,10 @@ impl KeyBackups {
|
|||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(version.as_bytes());
|
key.extend_from_slice(version.as_bytes());
|
||||||
|
|
||||||
self.backupid_algorithm
|
self.backupid_algorithm.get(key)?.map_or(Ok(None), |bytes| {
|
||||||
.get(&key)?
|
Ok(serde_json::from_slice(&bytes)
|
||||||
.map_or(Ok(None), |bytes| {
|
.map_err(|_| Error::bad_database("Algorithm in backupid_algorithm is invalid."))?)
|
||||||
serde_json::from_slice(&bytes)
|
})
|
||||||
.map_err(|_| Error::bad_database("Algorithm in backupid_algorithm is invalid."))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_key(
|
pub fn add_key(
|
||||||
@@ -154,7 +153,7 @@ impl KeyBackups {
|
|||||||
|
|
||||||
self.backupkeyid_backup.insert(
|
self.backupkeyid_backup.insert(
|
||||||
&key,
|
&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(())
|
Ok(())
|
||||||
@@ -165,7 +164,7 @@ impl KeyBackups {
|
|||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
prefix.extend_from_slice(version.as_bytes());
|
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> {
|
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();
|
let mut rooms = BTreeMap::<RoomId, RoomKeyBackup>::new();
|
||||||
|
|
||||||
for result in self
|
for result in self.backupkeyid_backup.scan_prefix(&prefix).map(|r| {
|
||||||
.backupkeyid_backup
|
let (key, value) = r?;
|
||||||
.scan_prefix(prefix)
|
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||||
.map(|(key, value)| {
|
|
||||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
|
||||||
|
|
||||||
let session_id =
|
let session_id = utils::string_from_bytes(
|
||||||
utils::string_from_bytes(&parts.next().ok_or_else(|| {
|
&parts
|
||||||
Error::bad_database("backupkeyid_backup key is invalid.")
|
.next()
|
||||||
})?)
|
.ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?,
|
||||||
.map_err(|_| {
|
)
|
||||||
Error::bad_database("backupkeyid_backup session_id is invalid.")
|
.map_err(|_| Error::bad_database("backupkeyid_backup session_id is invalid."))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
let room_id = RoomId::try_from(
|
let room_id = RoomId::try_from(
|
||||||
utils::string_from_bytes(&parts.next().ok_or_else(|| {
|
utils::string_from_bytes(
|
||||||
Error::bad_database("backupkeyid_backup key is invalid.")
|
&parts
|
||||||
})?)
|
.next()
|
||||||
.map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?,
|
.ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?,
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?,
|
||||||
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(|_| {
|
let key_data = serde_json::from_slice(&value).map_err(|_| {
|
||||||
Error::bad_database("KeyBackupData in backupkeyid_backup is invalid.")
|
Error::bad_database("KeyBackupData in backupkeyid_backup is invalid.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok::<_, Error>((room_id, session_id, key_data))
|
Ok::<_, Error>((room_id, session_id, key_data))
|
||||||
})
|
}) {
|
||||||
{
|
|
||||||
let (room_id, session_id, key_data) = result?;
|
let (room_id, session_id, key_data) = result?;
|
||||||
rooms
|
rooms
|
||||||
.entry(room_id)
|
.entry(room_id)
|
||||||
@@ -244,7 +239,7 @@ impl KeyBackups {
|
|||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
version: &str,
|
version: &str,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<BTreeMap<String, KeyBackupData>> {
|
) -> BTreeMap<String, KeyBackupData> {
|
||||||
let mut prefix = user_id.as_bytes().to_vec();
|
let mut prefix = user_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
prefix.extend_from_slice(version.as_bytes());
|
prefix.extend_from_slice(version.as_bytes());
|
||||||
@@ -252,10 +247,10 @@ impl KeyBackups {
|
|||||||
prefix.extend_from_slice(room_id.as_bytes());
|
prefix.extend_from_slice(room_id.as_bytes());
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
Ok(self
|
self.backupkeyid_backup
|
||||||
.backupkeyid_backup
|
.scan_prefix(&prefix)
|
||||||
.scan_prefix(prefix)
|
.map(|r| {
|
||||||
.map(|(key, value)| {
|
let (key, value) = r?;
|
||||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||||
|
|
||||||
let session_id =
|
let session_id =
|
||||||
@@ -273,7 +268,7 @@ impl KeyBackups {
|
|||||||
Ok::<_, Error>((session_id, key_data))
|
Ok::<_, Error>((session_id, key_data))
|
||||||
})
|
})
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.collect())
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_session(
|
pub fn get_session(
|
||||||
@@ -307,8 +302,13 @@ impl KeyBackups {
|
|||||||
key.extend_from_slice(&version.as_bytes());
|
key.extend_from_slice(&version.as_bytes());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
|
|
||||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
for outdated_key in self
|
||||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
.backupkeyid_backup
|
||||||
|
.scan_prefix(&key)
|
||||||
|
.keys()
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
|
{
|
||||||
|
self.backupkeyid_backup.remove(outdated_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -327,8 +327,13 @@ impl KeyBackups {
|
|||||||
key.extend_from_slice(&room_id.as_bytes());
|
key.extend_from_slice(&room_id.as_bytes());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
|
|
||||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
for outdated_key in self
|
||||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
.backupkeyid_backup
|
||||||
|
.scan_prefix(&key)
|
||||||
|
.keys()
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
|
{
|
||||||
|
self.backupkeyid_backup.remove(outdated_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -349,8 +354,13 @@ impl KeyBackups {
|
|||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(&session_id.as_bytes());
|
key.extend_from_slice(&session_id.as_bytes());
|
||||||
|
|
||||||
for (outdated_key, _) in self.backupkeyid_backup.scan_prefix(key) {
|
for outdated_key in self
|
||||||
self.backupkeyid_backup.remove(&outdated_key)?;
|
.backupkeyid_backup
|
||||||
|
.scan_prefix(&key)
|
||||||
|
.keys()
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
|
{
|
||||||
|
self.backupkeyid_backup.remove(outdated_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+48
-95
@@ -1,28 +1,25 @@
|
|||||||
use crate::database::globals::Globals;
|
|
||||||
use image::{imageops::FilterType, GenericImageView};
|
use image::{imageops::FilterType, GenericImageView};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
|
||||||
use crate::{utils, Error, Result};
|
use crate::{utils, Error, Result};
|
||||||
use std::{mem, sync::Arc};
|
use std::mem;
|
||||||
use tokio::{fs::File, io::AsyncReadExt, io::AsyncWriteExt};
|
|
||||||
|
|
||||||
pub struct FileMeta {
|
pub struct FileMeta {
|
||||||
pub content_disposition: Option<String>,
|
pub filename: Option<String>,
|
||||||
pub content_type: Option<String>,
|
pub content_type: Option<String>,
|
||||||
pub file: Vec<u8>,
|
pub file: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Media {
|
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 {
|
impl Media {
|
||||||
/// Uploads a file.
|
/// Uploads or replaces a file.
|
||||||
pub async fn create(
|
pub fn create(
|
||||||
&self,
|
&self,
|
||||||
mxc: String,
|
mxc: String,
|
||||||
globals: &Globals,
|
filename: &Option<&str>,
|
||||||
content_disposition: &Option<&str>,
|
|
||||||
content_type: &Option<&str>,
|
content_type: &Option<&str>,
|
||||||
file: &[u8],
|
file: &[u8],
|
||||||
) -> Result<()> {
|
) -> 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()); // 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.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(
|
key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default());
|
||||||
content_disposition
|
|
||||||
.as_ref()
|
|
||||||
.map(|f| f.as_bytes())
|
|
||||||
.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(
|
key.extend_from_slice(
|
||||||
content_type
|
content_type
|
||||||
@@ -45,20 +37,16 @@ impl Media {
|
|||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let path = globals.get_media_file(&key);
|
self.mediaid_file.insert(key, file)?;
|
||||||
let mut f = File::create(path).await?;
|
|
||||||
f.write_all(file).await?;
|
|
||||||
|
|
||||||
self.mediaid_file.insert(&key, &[])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uploads or replaces a file thumbnail.
|
/// Uploads or replaces a file thumbnail.
|
||||||
pub async fn upload_thumbnail(
|
pub fn upload_thumbnail(
|
||||||
&self,
|
&self,
|
||||||
mxc: String,
|
mxc: String,
|
||||||
globals: &Globals,
|
filename: &Option<String>,
|
||||||
content_disposition: &Option<String>,
|
|
||||||
content_type: &Option<String>,
|
content_type: &Option<String>,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
@@ -69,12 +57,7 @@ impl Media {
|
|||||||
key.extend_from_slice(&width.to_be_bytes());
|
key.extend_from_slice(&width.to_be_bytes());
|
||||||
key.extend_from_slice(&height.to_be_bytes());
|
key.extend_from_slice(&height.to_be_bytes());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(
|
key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default());
|
||||||
content_disposition
|
|
||||||
.as_ref()
|
|
||||||
.map(|f| f.as_bytes())
|
|
||||||
.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(
|
key.extend_from_slice(
|
||||||
content_type
|
content_type
|
||||||
@@ -83,59 +66,48 @@ impl Media {
|
|||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let path = globals.get_media_file(&key);
|
self.mediaid_file.insert(key, file)?;
|
||||||
let mut f = File::create(path).await?;
|
|
||||||
f.write_all(file).await?;
|
|
||||||
|
|
||||||
self.mediaid_file.insert(&key, &[])?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Downloads a file.
|
/// 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();
|
let mut prefix = mxc.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
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()); // 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.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
let mut iter = self.mediaid_file.scan_prefix(prefix);
|
if let Some(r) = self.mediaid_file.scan_prefix(&prefix).next() {
|
||||||
if let Some((key, _)) = iter.next() {
|
let (key, file) = r?;
|
||||||
let path = globals.get_media_file(&key);
|
|
||||||
let mut file = Vec::new();
|
|
||||||
File::open(path).await?.read_to_end(&mut file).await?;
|
|
||||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||||
|
|
||||||
let content_type = parts
|
let content_type = parts
|
||||||
.next()
|
.next()
|
||||||
.map(|bytes| {
|
.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.")
|
Error::bad_database("Content type in mediaid_file is invalid unicode.")
|
||||||
})
|
})?)
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let content_disposition_bytes = parts
|
let filename_bytes = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
|
.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
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(
|
Some(utils::string_from_bytes(filename_bytes).map_err(|_| {
|
||||||
utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
|
Error::bad_database("Filename in mediaid_file is invalid unicode.")
|
||||||
Error::bad_database(
|
})?)
|
||||||
"Content Disposition in mediaid_file is invalid unicode.",
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
filename,
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
file: file.to_vec(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@@ -165,13 +137,7 @@ impl Media {
|
|||||||
/// - Server creates the thumbnail and sends it to the user
|
/// - 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.
|
/// For width,height <= 96 the server uses another thumbnailing algorithm which crops the image afterwards.
|
||||||
pub async fn get_thumbnail(
|
pub fn get_thumbnail(&self, mxc: String, width: u32, height: u32) -> Result<Option<FileMeta>> {
|
||||||
&self,
|
|
||||||
mxc: String,
|
|
||||||
globals: &Globals,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
) -> Result<Option<FileMeta>> {
|
|
||||||
let (width, height, crop) = self
|
let (width, height, crop) = self
|
||||||
.thumbnail_properties(width, height)
|
.thumbnail_properties(width, height)
|
||||||
.unwrap_or((0, 0, false)); // 0, 0 because that's the original file
|
.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.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
|
||||||
original_prefix.push(0xff);
|
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
|
// Using saved thumbnail
|
||||||
let path = globals.get_media_file(&key);
|
let (key, file) = r?;
|
||||||
let mut file = Vec::new();
|
|
||||||
File::open(path).await?.read_to_end(&mut file).await?;
|
|
||||||
let mut parts = key.rsplit(|&b| b == 0xff);
|
let mut parts = key.rsplit(|&b| b == 0xff);
|
||||||
|
|
||||||
let content_type = parts
|
let content_type = parts
|
||||||
.next()
|
.next()
|
||||||
.map(|bytes| {
|
.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.")
|
Error::bad_database("Content type in mediaid_file is invalid unicode.")
|
||||||
})
|
})?)
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let content_disposition_bytes = parts
|
let filename_bytes = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
|
.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
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(
|
Some(
|
||||||
utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
|
utils::string_from_bytes(filename_bytes)
|
||||||
Error::bad_database("Content Disposition in db is invalid.")
|
.map_err(|_| Error::bad_database("Filename in db is invalid."))?,
|
||||||
})?,
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
filename,
|
||||||
content_type,
|
content_type,
|
||||||
file: file.to_vec(),
|
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
|
// 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 mut parts = key.rsplit(|&b| b == 0xff);
|
||||||
|
|
||||||
let content_type = parts
|
let content_type = parts
|
||||||
.next()
|
.next()
|
||||||
.map(|bytes| {
|
.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.")
|
Error::bad_database("Content type in mediaid_file is invalid unicode.")
|
||||||
})
|
})?)
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let content_disposition_bytes = parts
|
let filename_bytes = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
|
.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
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(
|
Some(utils::string_from_bytes(filename_bytes).map_err(|_| {
|
||||||
utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
|
Error::bad_database("Filename in mediaid_file is invalid unicode.")
|
||||||
Error::bad_database(
|
})?)
|
||||||
"Content Disposition in mediaid_file is invalid unicode.",
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(image) = image::load_from_memory(&file) {
|
if let Ok(image) = image::load_from_memory(&file) {
|
||||||
@@ -262,7 +219,7 @@ impl Media {
|
|||||||
let original_height = image.height();
|
let original_height = image.height();
|
||||||
if width > original_width || height > original_height {
|
if width > original_width || height > original_height {
|
||||||
return Ok(Some(FileMeta {
|
return Ok(Some(FileMeta {
|
||||||
content_disposition,
|
filename,
|
||||||
content_type,
|
content_type,
|
||||||
file: file.to_vec(),
|
file: file.to_vec(),
|
||||||
}));
|
}));
|
||||||
@@ -326,21 +283,17 @@ impl Media {
|
|||||||
widthheight,
|
widthheight,
|
||||||
);
|
);
|
||||||
|
|
||||||
let path = globals.get_media_file(&thumbnail_key);
|
self.mediaid_file.insert(thumbnail_key, &*thumbnail_bytes)?;
|
||||||
let mut f = File::create(path).await?;
|
|
||||||
f.write_all(&thumbnail_bytes).await?;
|
|
||||||
|
|
||||||
self.mediaid_file.insert(&thumbnail_key, &[])?;
|
|
||||||
|
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
filename,
|
||||||
content_type,
|
content_type,
|
||||||
file: thumbnail_bytes.to_vec(),
|
file: thumbnail_bytes.to_vec(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
// Couldn't parse file to generate thumbnail, send original
|
// Couldn't parse file to generate thumbnail, send original
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
filename,
|
||||||
content_type,
|
content_type,
|
||||||
file: file.to_vec(),
|
file: file.to_vec(),
|
||||||
}))
|
}))
|
||||||
|
|||||||
+44
-36
@@ -14,17 +14,23 @@ use ruma::{
|
|||||||
push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak},
|
push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak},
|
||||||
uint, UInt, UserId,
|
uint, UInt, UserId,
|
||||||
};
|
};
|
||||||
|
use sled::IVec;
|
||||||
|
|
||||||
use std::{convert::TryFrom, fmt::Debug, mem, sync::Arc};
|
use std::{convert::TryFrom, fmt::Debug};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct PushData {
|
pub struct PushData {
|
||||||
/// UserId + pushkey -> Pusher
|
/// UserId + pushkey -> Pusher
|
||||||
pub(super) senderkey_pusher: Arc<dyn Tree>,
|
pub(super) senderkey_pusher: sled::Tree,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PushData {
|
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<()> {
|
pub fn set_pusher(&self, sender: &UserId, pusher: set_pusher::Pusher) -> Result<()> {
|
||||||
let mut key = sender.as_bytes().to_vec();
|
let mut key = sender.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
@@ -34,14 +40,14 @@ impl PushData {
|
|||||||
if pusher.kind.is_none() {
|
if pusher.kind.is_none() {
|
||||||
return self
|
return self
|
||||||
.senderkey_pusher
|
.senderkey_pusher
|
||||||
.remove(&key)
|
.remove(key)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(Into::into);
|
.map_err(Into::into);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.senderkey_pusher.insert(
|
self.senderkey_pusher.insert(
|
||||||
&key,
|
key,
|
||||||
&serde_json::to_vec(&pusher).expect("Pusher is valid JSON value"),
|
&*serde_json::to_string(&pusher).expect("Pusher is valid JSON string"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -51,8 +57,8 @@ impl PushData {
|
|||||||
self.senderkey_pusher
|
self.senderkey_pusher
|
||||||
.get(senderkey)?
|
.get(senderkey)?
|
||||||
.map(|push| {
|
.map(|push| {
|
||||||
serde_json::from_slice(&*push)
|
Ok(serde_json::from_slice(&*push)
|
||||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
.map_err(|_| Error::bad_database("Invalid Pusher in db."))?)
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
@@ -63,21 +69,23 @@ impl PushData {
|
|||||||
|
|
||||||
self.senderkey_pusher
|
self.senderkey_pusher
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(prefix)
|
||||||
.map(|(_, push)| {
|
.values()
|
||||||
serde_json::from_slice(&*push)
|
.map(|push| {
|
||||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
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()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pusher_senderkeys<'a>(
|
pub fn get_pusher_senderkeys(&self, sender: &UserId) -> impl Iterator<Item = Result<IVec>> {
|
||||||
&'a self,
|
|
||||||
sender: &UserId,
|
|
||||||
) -> impl Iterator<Item = Box<[u8]>> + 'a {
|
|
||||||
let mut prefix = sender.as_bytes().to_vec();
|
let mut prefix = sender.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
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));
|
//*reqwest_request.timeout_mut() = Some(Duration::from_secs(5));
|
||||||
|
|
||||||
let url = reqwest_request.url().clone();
|
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 {
|
// Because reqwest::Response -> http::Response is complicated:
|
||||||
Ok(mut response) => {
|
match reqwest_response {
|
||||||
// reqwest::Response -> http::Response conversion
|
Ok(mut reqwest_response) => {
|
||||||
let status = response.status();
|
let status = reqwest_response.status();
|
||||||
let mut http_response_builder = http::Response::builder()
|
let mut http_response = http::Response::builder().status(status);
|
||||||
.status(status)
|
let headers = http_response.headers_mut().unwrap();
|
||||||
.version(response.version());
|
|
||||||
mem::swap(
|
|
||||||
response.headers_mut(),
|
|
||||||
http_response_builder
|
|
||||||
.headers_mut()
|
|
||||||
.expect("http::response::Builder is usable"),
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
warn!("server error {}", e);
|
||||||
Vec::new().into()
|
Vec::new().into()
|
||||||
}); // TODO: handle timeout
|
}); // TODO: handle timeout
|
||||||
@@ -138,7 +147,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let response = T::IncomingResponse::try_from_http_response(
|
let response = T::IncomingResponse::try_from_http_response(
|
||||||
http_response_builder
|
http_response
|
||||||
.body(body)
|
.body(body)
|
||||||
.expect("reqwest body is valid http body"),
|
.expect("reqwest body is valid http body"),
|
||||||
);
|
);
|
||||||
@@ -203,7 +212,7 @@ pub fn get_actions<'a>(
|
|||||||
.rooms
|
.rooms
|
||||||
.room_state_get(&pdu.room_id, &EventType::RoomPowerLevels, "")?
|
.room_state_get(&pdu.room_id, &EventType::RoomPowerLevels, "")?
|
||||||
.map(|ev| {
|
.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"))
|
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
|
||||||
})
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
@@ -286,8 +295,7 @@ async fn send_notice(
|
|||||||
} else {
|
} else {
|
||||||
notifi.sender = Some(&event.sender);
|
notifi.sender = Some(&event.sender);
|
||||||
notifi.event_type = Some(&event.kind);
|
notifi.event_type = Some(&event.kind);
|
||||||
let content = serde_json::value::to_raw_value(&event.content).ok();
|
notifi.content = serde_json::value::to_raw_value(&event.content).ok();
|
||||||
notifi.content = content.as_deref();
|
|
||||||
|
|
||||||
if event.kind == EventType::RoomMember {
|
if event.kind == EventType::RoomMember {
|
||||||
notifi.user_is_target = event.state_key.as_deref() == Some(event.sender.as_str());
|
notifi.user_is_target = event.state_key.as_deref() == Some(event.sender.as_str());
|
||||||
|
|||||||
+338
-362
File diff suppressed because it is too large
Load Diff
+64
-120
@@ -1,29 +1,28 @@
|
|||||||
use crate::{database::abstraction::Tree, utils, Error, Result};
|
use crate::{utils, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{
|
events::{
|
||||||
presence::{PresenceEvent, PresenceEventContent},
|
presence::{PresenceEvent, PresenceEventContent},
|
||||||
AnyEphemeralRoomEvent, SyncEphemeralRoomEvent,
|
AnyEvent as EduEvent, SyncEphemeralRoomEvent,
|
||||||
},
|
},
|
||||||
presence::PresenceState,
|
presence::PresenceState,
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
signatures::CanonicalJsonObject,
|
|
||||||
RoomId, UInt, UserId,
|
RoomId, UInt, UserId,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
mem,
|
mem,
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct RoomEdus {
|
pub struct RoomEdus {
|
||||||
pub(in super::super) readreceiptid_readreceipt: Arc<dyn Tree>, // ReadReceiptId = RoomId + Count + UserId
|
pub(in super::super) readreceiptid_readreceipt: sled::Tree, // ReadReceiptId = RoomId + Count + UserId
|
||||||
pub(in super::super) roomuserid_privateread: Arc<dyn Tree>, // RoomUserId = Room + User, PrivateRead = Count
|
pub(in super::super) roomuserid_privateread: sled::Tree, // RoomUserId = Room + User, PrivateRead = Count
|
||||||
pub(in super::super) roomuserid_lastprivatereadupdate: Arc<dyn Tree>, // LastPrivateReadUpdate = Count
|
pub(in super::super) roomuserid_lastprivatereadupdate: sled::Tree, // LastPrivateReadUpdate = Count
|
||||||
pub(in super::super) typingid_userid: Arc<dyn Tree>, // TypingId = RoomId + TimeoutTime + Count
|
pub(in super::super) typingid_userid: sled::Tree, // TypingId = RoomId + TimeoutTime + Count
|
||||||
pub(in super::super) roomid_lasttypingupdate: Arc<dyn Tree>, // LastRoomTypingUpdate = Count
|
pub(in super::super) roomid_lasttypingupdate: sled::Tree, // LastRoomTypingUpdate = Count
|
||||||
pub(in super::super) presenceid_presence: Arc<dyn Tree>, // PresenceId = RoomId + Count + UserId
|
pub(in super::super) presenceid_presence: sled::Tree, // PresenceId = RoomId + Count + UserId
|
||||||
pub(in super::super) userid_lastpresenceupdate: Arc<dyn Tree>, // LastPresenceUpdate = Count
|
pub(in super::super) userid_lastpresenceupdate: sled::Tree, // LastPresenceUpdate = Count
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RoomEdus {
|
impl RoomEdus {
|
||||||
@@ -32,21 +31,21 @@ impl RoomEdus {
|
|||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
event: AnyEphemeralRoomEvent,
|
event: EduEvent,
|
||||||
globals: &super::super::globals::Globals,
|
globals: &super::super::globals::Globals,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
let mut last_possible_key = prefix.clone();
|
|
||||||
last_possible_key.extend_from_slice(&u64::MAX.to_be_bytes());
|
|
||||||
|
|
||||||
// Remove old entry
|
// Remove old entry
|
||||||
if let Some((old, _)) = self
|
if let Some(old) = self
|
||||||
.readreceiptid_readreceipt
|
.readreceiptid_readreceipt
|
||||||
.iter_from(&last_possible_key, true)
|
.scan_prefix(&prefix)
|
||||||
.take_while(|(key, _)| key.starts_with(&prefix))
|
.keys()
|
||||||
.find(|(key, _)| {
|
.rev()
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
|
.take_while(|key| key.starts_with(&prefix))
|
||||||
|
.find(|key| {
|
||||||
key.rsplit(|&b| b == 0xff)
|
key.rsplit(|&b| b == 0xff)
|
||||||
.next()
|
.next()
|
||||||
.expect("rsplit always returns an element")
|
.expect("rsplit always returns an element")
|
||||||
@@ -54,7 +53,7 @@ impl RoomEdus {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
// This is the old room_latest
|
// This is the old room_latest
|
||||||
self.readreceiptid_readreceipt.remove(&old)?;
|
self.readreceiptid_readreceipt.remove(old)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut room_latest_id = prefix;
|
let mut room_latest_id = prefix;
|
||||||
@@ -63,8 +62,8 @@ impl RoomEdus {
|
|||||||
room_latest_id.extend_from_slice(&user_id.as_bytes());
|
room_latest_id.extend_from_slice(&user_id.as_bytes());
|
||||||
|
|
||||||
self.readreceiptid_readreceipt.insert(
|
self.readreceiptid_readreceipt.insert(
|
||||||
&room_latest_id,
|
room_latest_id,
|
||||||
&serde_json::to_vec(&event).expect("EduEvent::to_string always works"),
|
&*serde_json::to_string(&event).expect("EduEvent::to_string always works"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
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`.
|
/// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn readreceipts_since<'a>(
|
pub fn readreceipts_since(
|
||||||
&'a self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
since: u64,
|
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();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
let prefix2 = prefix.clone();
|
|
||||||
|
|
||||||
let mut first_possible_edu = 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
|
first_possible_edu.extend_from_slice(&(since + 1).to_be_bytes()); // +1 so we don't send the event at since
|
||||||
|
|
||||||
self.readreceiptid_readreceipt
|
Ok(self
|
||||||
.iter_from(&first_possible_edu, false)
|
.readreceiptid_readreceipt
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix2))
|
.range(&*first_possible_edu..)
|
||||||
.map(move |(k, v)| {
|
.filter_map(|r| r.ok())
|
||||||
let count =
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
utils::u64_from_bytes(&k[prefix.len()..prefix.len() + mem::size_of::<u64>()])
|
.map(|(_, v)| {
|
||||||
.map_err(|_| Error::bad_database("Invalid readreceiptid count in db."))?;
|
Ok(serde_json::from_slice(&v).map_err(|_| {
|
||||||
let user_id = UserId::try_from(
|
Error::bad_database("Read receipt in roomlatestid_roomlatest is invalid.")
|
||||||
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"),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a private read marker at `count`.
|
/// Sets a private read marker at `count`.
|
||||||
@@ -143,13 +122,11 @@ impl RoomEdus {
|
|||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(&user_id.as_bytes());
|
key.extend_from_slice(&user_id.as_bytes());
|
||||||
|
|
||||||
self.roomuserid_privateread
|
self.roomuserid_privateread.get(key)?.map_or(Ok(None), |v| {
|
||||||
.get(&key)?
|
Ok(Some(utils::u64_from_bytes(&v).map_err(|_| {
|
||||||
.map_or(Ok(None), |v| {
|
Error::bad_database("Invalid private read marker bytes")
|
||||||
Ok(Some(utils::u64_from_bytes(&v).map_err(|_| {
|
})?))
|
||||||
Error::bad_database("Invalid private read marker bytes")
|
})
|
||||||
})?))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the count of the last typing update in this room.
|
/// Returns the count of the last typing update in this room.
|
||||||
@@ -214,10 +191,11 @@ impl RoomEdus {
|
|||||||
// Maybe there are multiple ones from calling roomtyping_add multiple times
|
// Maybe there are multiple ones from calling roomtyping_add multiple times
|
||||||
for outdated_edu in self
|
for outdated_edu in self
|
||||||
.typingid_userid
|
.typingid_userid
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(&prefix)
|
||||||
.filter(|(_, v)| &**v == user_id.as_bytes())
|
.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;
|
found_outdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,8 +223,10 @@ impl RoomEdus {
|
|||||||
// Find all outdated edus before inserting a new one
|
// Find all outdated edus before inserting a new one
|
||||||
for outdated_edu in self
|
for outdated_edu in self
|
||||||
.typingid_userid
|
.typingid_userid
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(&prefix)
|
||||||
.map(|(key, _)| {
|
.keys()
|
||||||
|
.map(|key| {
|
||||||
|
let key = key?;
|
||||||
Ok::<_, Error>((
|
Ok::<_, Error>((
|
||||||
key.clone(),
|
key.clone(),
|
||||||
utils::u64_from_bytes(
|
utils::u64_from_bytes(
|
||||||
@@ -261,7 +241,7 @@ impl RoomEdus {
|
|||||||
.take_while(|&(_, timestamp)| timestamp < current_timestamp)
|
.take_while(|&(_, timestamp)| timestamp < current_timestamp)
|
||||||
{
|
{
|
||||||
// This is an outdated edu (time > 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;
|
found_outdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,11 +285,14 @@ impl RoomEdus {
|
|||||||
for user_id in self
|
for user_id in self
|
||||||
.typingid_userid
|
.typingid_userid
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(prefix)
|
||||||
.map(|(_, user_id)| {
|
.values()
|
||||||
UserId::try_from(utils::string_from_bytes(&user_id).map_err(|_| {
|
.map(|user_id| {
|
||||||
Error::bad_database("User ID in typingid_userid is invalid unicode.")
|
Ok::<_, Error>(
|
||||||
})?)
|
UserId::try_from(utils::string_from_bytes(&user_id?).map_err(|_| {
|
||||||
.map_err(|_| Error::bad_database("User ID in typingid_userid is invalid."))
|
Error::bad_database("User ID in typingid_userid is invalid unicode.")
|
||||||
|
})?)
|
||||||
|
.map_err(|_| Error::bad_database("User ID in typingid_userid is invalid."))?,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
user_ids.insert(user_id?);
|
user_ids.insert(user_id?);
|
||||||
@@ -344,12 +327,12 @@ impl RoomEdus {
|
|||||||
presence_id.extend_from_slice(&presence.sender.as_bytes());
|
presence_id.extend_from_slice(&presence.sender.as_bytes());
|
||||||
|
|
||||||
self.presenceid_presence.insert(
|
self.presenceid_presence.insert(
|
||||||
&presence_id,
|
presence_id,
|
||||||
&serde_json::to_vec(&presence).expect("PresenceEvent can be serialized"),
|
&*serde_json::to_string(&presence).expect("PresenceEvent can be serialized"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.userid_lastpresenceupdate.insert(
|
self.userid_lastpresenceupdate.insert(
|
||||||
user_id.as_bytes(),
|
&user_id.as_bytes(),
|
||||||
&utils::millis_since_unix_epoch().to_be_bytes(),
|
&utils::millis_since_unix_epoch().to_be_bytes(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -379,47 +362,6 @@ impl RoomEdus {
|
|||||||
.transpose()
|
.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.
|
/// Sets all users to offline who have been quiet for too long.
|
||||||
pub fn presence_maintain(
|
pub fn presence_maintain(
|
||||||
&self,
|
&self,
|
||||||
@@ -431,6 +373,7 @@ impl RoomEdus {
|
|||||||
for (user_id_bytes, last_timestamp) in self
|
for (user_id_bytes, last_timestamp) in self
|
||||||
.userid_lastpresenceupdate
|
.userid_lastpresenceupdate
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
.filter_map(|(k, bytes)| {
|
.filter_map(|(k, bytes)| {
|
||||||
Some((
|
Some((
|
||||||
k,
|
k,
|
||||||
@@ -460,8 +403,8 @@ impl RoomEdus {
|
|||||||
presence_id.extend_from_slice(&user_id_bytes);
|
presence_id.extend_from_slice(&user_id_bytes);
|
||||||
|
|
||||||
self.presenceid_presence.insert(
|
self.presenceid_presence.insert(
|
||||||
&presence_id,
|
presence_id,
|
||||||
&serde_json::to_vec(&PresenceEvent {
|
&*serde_json::to_string(&PresenceEvent {
|
||||||
content: PresenceEventContent {
|
content: PresenceEventContent {
|
||||||
avatar_url: None,
|
avatar_url: None,
|
||||||
currently_active: None,
|
currently_active: None,
|
||||||
@@ -507,7 +450,8 @@ impl RoomEdus {
|
|||||||
|
|
||||||
for (key, value) in self
|
for (key, value) in self
|
||||||
.presenceid_presence
|
.presenceid_presence
|
||||||
.iter_from(&*first_possible_edu, false)
|
.range(&*first_possible_edu..)
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
.take_while(|(key, _)| key.starts_with(&prefix))
|
.take_while(|(key, _)| key.starts_with(&prefix))
|
||||||
{
|
{
|
||||||
let user_id = UserId::try_from(
|
let user_id = UserId::try_from(
|
||||||
|
|||||||
+271
-366
@@ -1,9 +1,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::HashMap,
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -12,28 +12,15 @@ use crate::{
|
|||||||
use federation::transactions::send_transaction_message;
|
use federation::transactions::send_transaction_message;
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
use rocket::futures::{
|
use rocket::futures::stream::{FuturesUnordered, StreamExt};
|
||||||
channel::mpsc,
|
|
||||||
stream::{FuturesUnordered, StreamExt},
|
|
||||||
};
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{appservice, federation, OutgoingRequest},
|
||||||
appservice,
|
events::{push_rules, EventType},
|
||||||
federation::{
|
push, ServerName, UInt, UserId,
|
||||||
self,
|
|
||||||
transactions::edu::{Edu, ReceiptContent, ReceiptData, ReceiptMap},
|
|
||||||
},
|
|
||||||
OutgoingRequest,
|
|
||||||
},
|
|
||||||
events::{push_rules, AnySyncEphemeralRoomEvent, EventType},
|
|
||||||
push,
|
|
||||||
receipt::ReceiptType,
|
|
||||||
MilliSecondsSinceUnixEpoch, ServerName, UInt, UserId,
|
|
||||||
};
|
};
|
||||||
|
use sled::IVec;
|
||||||
use tokio::{select, sync::Semaphore};
|
use tokio::{select, sync::Semaphore};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum OutgoingKind {
|
pub enum OutgoingKind {
|
||||||
Appservice(Box<ServerName>),
|
Appservice(Box<ServerName>),
|
||||||
@@ -41,46 +28,12 @@ pub enum OutgoingKind {
|
|||||||
Normal(Box<ServerName>),
|
Normal(Box<ServerName>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutgoingKind {
|
#[derive(Clone)]
|
||||||
pub fn get_prefix(&self) -> Vec<u8> {
|
|
||||||
let mut prefix = match self {
|
|
||||||
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::new();
|
|
||||||
p.extend_from_slice(server.as_bytes());
|
|
||||||
p
|
|
||||||
}
|
|
||||||
};
|
|
||||||
prefix.push(0xff);
|
|
||||||
|
|
||||||
prefix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum SendingEventType {
|
|
||||||
Pdu(Vec<u8>),
|
|
||||||
Edu(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Sending {
|
pub struct Sending {
|
||||||
/// The state for a given state hash.
|
/// The state for a given state hash.
|
||||||
pub(super) servername_educount: Arc<dyn Tree>, // EduCount: Count of last EDU sync
|
pub(super) servernamepduids: sled::Tree, // ServernamePduId = (+ / $)SenderKey / ServerName / UserId + PduId
|
||||||
pub(super) servernamepduids: Arc<dyn Tree>, // ServernamePduId = (+ / $)SenderKey / ServerName / UserId + PduId
|
pub(super) servercurrentpdus: sled::Tree, // ServerCurrentPdus = (+ / $)ServerName / UserId + PduId
|
||||||
pub(super) servercurrentevents: Arc<dyn Tree>, // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / (*)EduEvent
|
|
||||||
pub(super) maximum_requests: Arc<Semaphore>,
|
pub(super) maximum_requests: Arc<Semaphore>,
|
||||||
pub sender: mpsc::UnboundedSender<Vec<u8>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TransactionStatus {
|
enum TransactionStatus {
|
||||||
@@ -90,23 +43,28 @@ enum TransactionStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Sending {
|
impl Sending {
|
||||||
pub fn start_handler(&self, db: Arc<Database>, mut receiver: mpsc::UnboundedReceiver<Vec<u8>>) {
|
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 {
|
tokio::spawn(async move {
|
||||||
let mut futures = FuturesUnordered::new();
|
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 current_transaction_status = HashMap::<Vec<u8>, TransactionStatus>::new();
|
||||||
|
|
||||||
// Retry requests we could not finish yet
|
let mut initial_transactions = HashMap::<OutgoingKind, Vec<Vec<u8>>>::new();
|
||||||
let mut initial_transactions = HashMap::<OutgoingKind, Vec<SendingEventType>>::new();
|
for (key, outgoing_kind, pdu) in servercurrentpdus
|
||||||
for (key, outgoing_kind, event) in
|
.iter()
|
||||||
db.sending
|
.filter_map(|r| r.ok())
|
||||||
.servercurrentevents
|
.filter_map(|(key, _)| {
|
||||||
.iter()
|
Self::parse_servercurrentpdus(&key)
|
||||||
.filter_map(|(key, _)| {
|
.ok()
|
||||||
Self::parse_servercurrentevent(&key)
|
.map(|(k, p)| (key, k, p.to_vec()))
|
||||||
.ok()
|
})
|
||||||
.map(|(k, e)| (key, k, e))
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
let entry = initial_transactions
|
let entry = initial_transactions
|
||||||
.entry(outgoing_kind.clone())
|
.entry(outgoing_kind.clone())
|
||||||
@@ -114,20 +72,39 @@ impl Sending {
|
|||||||
|
|
||||||
if entry.len() > 30 {
|
if entry.len() > 30 {
|
||||||
warn!(
|
warn!(
|
||||||
"Dropping some current events: {:?} {:?} {:?}",
|
"Dropping some current pdu: {:?} {:?} {:?}",
|
||||||
key, outgoing_kind, event
|
key, outgoing_kind, pdu
|
||||||
);
|
);
|
||||||
db.sending.servercurrentevents.remove(&key).unwrap();
|
servercurrentpdus.remove(key).unwrap();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.push(event);
|
entry.push(pdu);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (outgoing_kind, events) in initial_transactions {
|
for (outgoing_kind, pdus) in initial_transactions {
|
||||||
current_transaction_status
|
let mut prefix = match &outgoing_kind {
|
||||||
.insert(outgoing_kind.get_prefix(), TransactionStatus::Running);
|
OutgoingKind::Appservice(server) => {
|
||||||
futures.push(Self::handle_events(outgoing_kind.clone(), events, &db));
|
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::new();
|
||||||
|
p.extend_from_slice(server.as_bytes());
|
||||||
|
p
|
||||||
|
}
|
||||||
|
};
|
||||||
|
prefix.push(0xff);
|
||||||
|
current_transaction_status.insert(prefix, TransactionStatus::Running);
|
||||||
|
futures.push(Self::handle_event(outgoing_kind.clone(), pdus, &db));
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -135,42 +112,58 @@ impl Sending {
|
|||||||
Some(response) = futures.next() => {
|
Some(response) = futures.next() => {
|
||||||
match response {
|
match response {
|
||||||
Ok(outgoing_kind) => {
|
Ok(outgoing_kind) => {
|
||||||
let prefix = outgoing_kind.get_prefix();
|
let mut prefix = match &outgoing_kind {
|
||||||
for (key, _) in db.sending.servercurrentevents
|
OutgoingKind::Appservice(server) => {
|
||||||
.scan_prefix(prefix.clone())
|
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
|
// Find events that have been added since starting the last request
|
||||||
let new_events = db.sending.servernamepduids
|
let new_pdus = servernamepduids
|
||||||
.scan_prefix(prefix.clone())
|
.scan_prefix(&prefix)
|
||||||
.map(|(k, _)| {
|
.keys()
|
||||||
SendingEventType::Pdu(k[prefix.len()..].to_vec())
|
.filter_map(|r| r.ok())
|
||||||
|
.map(|k| {
|
||||||
|
k[prefix.len()..].to_vec()
|
||||||
})
|
})
|
||||||
.take(30)
|
.take(30)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// TODO: find edus
|
if !new_pdus.is_empty() {
|
||||||
|
for pdu_id in &new_pdus {
|
||||||
if !new_events.is_empty() {
|
|
||||||
// Insert pdus we found
|
|
||||||
for event in &new_events {
|
|
||||||
let mut current_key = prefix.clone();
|
let mut current_key = prefix.clone();
|
||||||
match event {
|
current_key.extend_from_slice(pdu_id);
|
||||||
SendingEventType::Pdu(b) |
|
servercurrentpdus.insert(¤t_key, &[]).unwrap();
|
||||||
SendingEventType::Edu(b) => {
|
servernamepduids.remove(¤t_key).unwrap();
|
||||||
current_key.extend_from_slice(&b);
|
|
||||||
db.sending.servercurrentevents.insert(¤t_key, &[]).unwrap();
|
|
||||||
db.sending.servernamepduids.remove(¤t_key).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
futures.push(
|
futures.push(
|
||||||
Self::handle_events(
|
Self::handle_event(
|
||||||
outgoing_kind.clone(),
|
outgoing_kind.clone(),
|
||||||
new_events,
|
new_pdus,
|
||||||
&db,
|
&db,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -179,7 +172,29 @@ impl Sending {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err((outgoing_kind, _)) => {
|
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::Running => TransactionStatus::Failed(1, Instant::now()),
|
||||||
TransactionStatus::Retrying(n) => TransactionStatus::Failed(*n+1, Instant::now()),
|
TransactionStatus::Retrying(n) => TransactionStatus::Failed(*n+1, Instant::now()),
|
||||||
TransactionStatus::Failed(_, _) => {
|
TransactionStatus::Failed(_, _) => {
|
||||||
@@ -190,15 +205,98 @@ impl Sending {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
Some(key) = receiver.next() => {
|
Some(event) = &mut subscriber => {
|
||||||
if let Ok((outgoing_kind, event)) = Self::parse_servercurrentevent(&key) {
|
if let sled::Event::Insert { key, .. } = event {
|
||||||
if let Ok(Some(events)) = Self::select_events(
|
// New sled version:
|
||||||
&outgoing_kind,
|
//for (_tree, key, value_opt) in &event {
|
||||||
vec![(event, key)],
|
// if value_opt.is_none() {
|
||||||
&mut current_transaction_status,
|
// continue;
|
||||||
&db
|
// }
|
||||||
) {
|
|
||||||
futures.push(Self::handle_events(outgoing_kind, events, &db));
|
let servernamepduid = key.clone();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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 {
|
||||||
|
allow = false;
|
||||||
|
} else {
|
||||||
|
retry = true;
|
||||||
|
*e = TransactionStatus::Retrying(*tries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).or_insert(TransactionStatus::Running);
|
||||||
|
|
||||||
|
allow
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let mut pdus = Vec::new();
|
||||||
|
|
||||||
|
if retry {
|
||||||
|
// We retry the previous transaction
|
||||||
|
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 {
|
||||||
|
servercurrentpdus.insert(&key, &[]).unwrap();
|
||||||
|
servernamepduids.remove(&key).unwrap();
|
||||||
|
pdus.push(pdu_id.to_vec());
|
||||||
|
}
|
||||||
|
futures.push(
|
||||||
|
Self::handle_event(
|
||||||
|
outgoing_kind,
|
||||||
|
pdus,
|
||||||
|
&db,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,163 +305,13 @@ impl Sending {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
let mut allow = true;
|
|
||||||
|
|
||||||
let prefix = outgoing_kind.get_prefix();
|
|
||||||
let entry = current_transaction_status.entry(prefix.clone());
|
|
||||||
|
|
||||||
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);
|
|
||||||
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 {
|
|
||||||
allow = false;
|
|
||||||
} else {
|
|
||||||
retry = true;
|
|
||||||
*e = TransactionStatus::Retrying(*tries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert(TransactionStatus::Running);
|
|
||||||
|
|
||||||
if !allow {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut events = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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))]
|
#[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();
|
let mut key = b"$".to_vec();
|
||||||
key.extend_from_slice(&senderkey);
|
key.extend_from_slice(&senderkey);
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(pdu_id);
|
key.extend_from_slice(pdu_id);
|
||||||
self.servernamepduids.insert(&key, b"")?;
|
self.servernamepduids.insert(key, b"")?;
|
||||||
self.sender.unbounded_send(key).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -373,8 +321,7 @@ impl Sending {
|
|||||||
let mut key = server.as_bytes().to_vec();
|
let mut key = server.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(pdu_id);
|
key.extend_from_slice(pdu_id);
|
||||||
self.servernamepduids.insert(&key, b"")?;
|
self.servernamepduids.insert(key, b"")?;
|
||||||
self.sender.unbounded_send(key).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -385,14 +332,13 @@ impl Sending {
|
|||||||
key.extend_from_slice(appservice_id.as_bytes());
|
key.extend_from_slice(appservice_id.as_bytes());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(pdu_id);
|
key.extend_from_slice(pdu_id);
|
||||||
self.servernamepduids.insert(&key, b"")?;
|
self.servernamepduids.insert(key, b"")?;
|
||||||
self.sender.unbounded_send(key).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[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
|
// We only hash the pdu's event ids, not the whole pdu
|
||||||
let bytes = keys.join(&0xff);
|
let bytes = keys.join(&0xff);
|
||||||
let hash = digest::digest(&digest::SHA256, &bytes);
|
let hash = digest::digest(&digest::SHA256, &bytes);
|
||||||
@@ -400,37 +346,33 @@ impl Sending {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(db))]
|
#[tracing::instrument(skip(db))]
|
||||||
async fn handle_events(
|
async fn handle_event(
|
||||||
kind: OutgoingKind,
|
kind: OutgoingKind,
|
||||||
events: Vec<SendingEventType>,
|
pdu_ids: Vec<Vec<u8>>,
|
||||||
db: &Database,
|
db: &Database,
|
||||||
) -> std::result::Result<OutgoingKind, (OutgoingKind, Error)> {
|
) -> std::result::Result<OutgoingKind, (OutgoingKind, Error)> {
|
||||||
match &kind {
|
match &kind {
|
||||||
OutgoingKind::Appservice(server) => {
|
OutgoingKind::Appservice(server) => {
|
||||||
let mut pdu_jsons = Vec::new();
|
let pdu_jsons = pdu_ids
|
||||||
|
.iter()
|
||||||
for event in &events {
|
.map(|pdu_id| {
|
||||||
match event {
|
Ok::<_, (Box<ServerName>, Error)>(
|
||||||
SendingEventType::Pdu(pdu_id) => {
|
db.rooms
|
||||||
pdu_jsons.push(db.rooms
|
.get_pdu_from_id(pdu_id)
|
||||||
.get_pdu_from_id(&pdu_id)
|
.map_err(|e| (server.clone(), e))?
|
||||||
.map_err(|e| (kind.clone(), e))?
|
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
(
|
(
|
||||||
kind.clone(),
|
server.clone(),
|
||||||
Error::bad_database(
|
Error::bad_database(
|
||||||
"[Appservice] Event in servernamepduids not found in db.",
|
"[Appservice] Event in servernamepduids not found in db.",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.to_room_event())
|
.to_any_event(),
|
||||||
}
|
)
|
||||||
SendingEventType::Edu(_) => {
|
})
|
||||||
// Appservices don't need EDUs (?)
|
.filter_map(|r| r.ok())
|
||||||
}
|
.collect::<Vec<_>>();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let permit = db.sending.maximum_requests.acquire().await;
|
let permit = db.sending.maximum_requests.acquire().await;
|
||||||
|
|
||||||
let response = appservice_server::send_request(
|
let response = appservice_server::send_request(
|
||||||
@@ -442,14 +384,7 @@ impl Sending {
|
|||||||
appservice::event::push_events::v1::Request {
|
appservice::event::push_events::v1::Request {
|
||||||
events: &pdu_jsons,
|
events: &pdu_jsons,
|
||||||
txn_id: &base64::encode_config(
|
txn_id: &base64::encode_config(
|
||||||
Self::calculate_hash(
|
Self::calculate_hash(&pdu_ids),
|
||||||
&events
|
|
||||||
.iter()
|
|
||||||
.map(|e| match e {
|
|
||||||
SendingEventType::Edu(b) | SendingEventType::Pdu(b) => &**b,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
),
|
|
||||||
base64::URL_SAFE_NO_PAD,
|
base64::URL_SAFE_NO_PAD,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -463,30 +398,25 @@ impl Sending {
|
|||||||
response
|
response
|
||||||
}
|
}
|
||||||
OutgoingKind::Push(user, pushkey) => {
|
OutgoingKind::Push(user, pushkey) => {
|
||||||
let mut pdus = Vec::new();
|
let pdus = pdu_ids
|
||||||
|
.iter()
|
||||||
for event in &events {
|
.map(|pdu_id| {
|
||||||
match event {
|
Ok::<_, (Vec<u8>, Error)>(
|
||||||
SendingEventType::Pdu(pdu_id) => {
|
db.rooms
|
||||||
pdus.push(
|
.get_pdu_from_id(pdu_id)
|
||||||
db.rooms
|
.map_err(|e| (pushkey.clone(), e))?
|
||||||
.get_pdu_from_id(&pdu_id)
|
.ok_or_else(|| {
|
||||||
.map_err(|e| (kind.clone(), e))?
|
(
|
||||||
.ok_or_else(|| {
|
pushkey.clone(),
|
||||||
(
|
Error::bad_database(
|
||||||
kind.clone(),
|
"[Push] Event in servernamepduids not found in db.",
|
||||||
Error::bad_database(
|
),
|
||||||
"[Push] Event in servernamepduids not found in db.",
|
)
|
||||||
),
|
})?,
|
||||||
)
|
)
|
||||||
})?,
|
})
|
||||||
);
|
.filter_map(|r| r.ok())
|
||||||
}
|
.collect::<Vec<_>>();
|
||||||
SendingEventType::Edu(_) => {
|
|
||||||
// Push gateways don't need EDUs (?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for pdu in pdus {
|
for pdu in pdus {
|
||||||
// Redacted events are not notification targets (we don't send push for them)
|
// Redacted events are not notification targets (we don't send push for them)
|
||||||
@@ -497,13 +427,13 @@ impl Sending {
|
|||||||
let userid =
|
let userid =
|
||||||
UserId::try_from(utils::string_from_bytes(user).map_err(|_| {
|
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."),
|
Error::bad_database("Invalid push user string in db."),
|
||||||
)
|
)
|
||||||
})?)
|
})?)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
(
|
(
|
||||||
kind.clone(),
|
OutgoingKind::Push(user.clone(), pushkey.clone()),
|
||||||
Error::bad_database("Invalid push user id in db."),
|
Error::bad_database("Invalid push user id in db."),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
@@ -554,17 +484,15 @@ impl Sending {
|
|||||||
Ok(OutgoingKind::Push(user.clone(), pushkey.clone()))
|
Ok(OutgoingKind::Push(user.clone(), pushkey.clone()))
|
||||||
}
|
}
|
||||||
OutgoingKind::Normal(server) => {
|
OutgoingKind::Normal(server) => {
|
||||||
let mut edu_jsons = Vec::new();
|
let pdu_jsons = pdu_ids
|
||||||
let mut pdu_jsons = Vec::new();
|
.iter()
|
||||||
|
.map(|pdu_id| {
|
||||||
for event in &events {
|
Ok::<_, (OutgoingKind, Error)>(
|
||||||
match event {
|
|
||||||
SendingEventType::Pdu(pdu_id) => {
|
|
||||||
// TODO: check room version and remove event_id if needed
|
// 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(
|
PduEvent::convert_to_outgoing_federation_event(
|
||||||
db.rooms
|
db.rooms
|
||||||
.get_pdu_json_from_id(&pdu_id)
|
.get_pdu_json_from_id(pdu_id)
|
||||||
.map_err(|e| (OutgoingKind::Normal(server.clone()), e))?
|
.map_err(|e| (OutgoingKind::Normal(server.clone()), e))?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
(
|
(
|
||||||
@@ -578,15 +506,11 @@ impl Sending {
|
|||||||
.json()
|
.json()
|
||||||
.get(),
|
.get(),
|
||||||
)
|
)
|
||||||
.expect("Raw<..> is always valid"));
|
.expect("Raw<..> is always valid"),
|
||||||
}
|
)
|
||||||
SendingEventType::Edu(edu) => {
|
})
|
||||||
edu_jsons.push(
|
.filter_map(|r| r.ok())
|
||||||
serde_json::from_slice(edu).expect("Raw<..> is always valid"),
|
.collect::<Vec<_>>();
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let permit = db.sending.maximum_requests.acquire().await;
|
let permit = db.sending.maximum_requests.acquire().await;
|
||||||
|
|
||||||
@@ -596,17 +520,10 @@ impl Sending {
|
|||||||
send_transaction_message::v1::Request {
|
send_transaction_message::v1::Request {
|
||||||
origin: db.globals.server_name(),
|
origin: db.globals.server_name(),
|
||||||
pdus: &pdu_jsons,
|
pdus: &pdu_jsons,
|
||||||
edus: &edu_jsons,
|
edus: &[],
|
||||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
origin_server_ts: SystemTime::now(),
|
||||||
transaction_id: &base64::encode_config(
|
transaction_id: &base64::encode_config(
|
||||||
Self::calculate_hash(
|
Self::calculate_hash(&pdu_ids),
|
||||||
&events
|
|
||||||
.iter()
|
|
||||||
.map(|e| match e {
|
|
||||||
SendingEventType::Edu(b) | SendingEventType::Pdu(b) => &**b,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
),
|
|
||||||
base64::URL_SAFE_NO_PAD,
|
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
|
// Appservices start with a plus
|
||||||
Ok::<_, Error>(if key.starts_with(b"+") {
|
Ok::<_, Error>(if key.starts_with(b"+") {
|
||||||
let mut parts = key[1..].splitn(2, |&b| b == 0xff);
|
let mut parts = key[1..].splitn(2, |&b| b == 0xff);
|
||||||
|
|
||||||
let server = parts.next().expect("splitn always returns one element");
|
let server = parts.next().expect("splitn always returns one element");
|
||||||
let event = parts
|
let pdu = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||||
let server = utils::string_from_bytes(&server).map_err(|_| {
|
let server = utils::string_from_bytes(&server).map_err(|_| {
|
||||||
@@ -646,11 +563,7 @@ impl Sending {
|
|||||||
OutgoingKind::Appservice(Box::<ServerName>::try_from(server).map_err(|_| {
|
OutgoingKind::Appservice(Box::<ServerName>::try_from(server).map_err(|_| {
|
||||||
Error::bad_database("Invalid server string in server_currenttransaction")
|
Error::bad_database("Invalid server string in server_currenttransaction")
|
||||||
})?),
|
})?),
|
||||||
if event.starts_with(b"*") {
|
IVec::from(pdu),
|
||||||
SendingEventType::Edu(event[1..].to_vec())
|
|
||||||
} else {
|
|
||||||
SendingEventType::Pdu(event.to_vec())
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
} else if key.starts_with(b"$") {
|
} else if key.starts_with(b"$") {
|
||||||
let mut parts = key[1..].splitn(3, |&b| b == 0xff);
|
let mut parts = key[1..].splitn(3, |&b| b == 0xff);
|
||||||
@@ -659,22 +572,18 @@ impl Sending {
|
|||||||
let pushkey = parts
|
let pushkey = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||||
let event = parts
|
let pdu = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||||
(
|
(
|
||||||
OutgoingKind::Push(user.to_vec(), pushkey.to_vec()),
|
OutgoingKind::Push(user.to_vec(), pushkey.to_vec()),
|
||||||
if event.starts_with(b"*") {
|
IVec::from(pdu),
|
||||||
SendingEventType::Edu(event[1..].to_vec())
|
|
||||||
} else {
|
|
||||||
SendingEventType::Pdu(event.to_vec())
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let mut parts = key.splitn(2, |&b| b == 0xff);
|
let mut parts = key.splitn(2, |&b| b == 0xff);
|
||||||
|
|
||||||
let server = parts.next().expect("splitn always returns one element");
|
let server = parts.next().expect("splitn always returns one element");
|
||||||
let event = parts
|
let pdu = parts
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||||
let server = utils::string_from_bytes(&server).map_err(|_| {
|
let server = utils::string_from_bytes(&server).map_err(|_| {
|
||||||
@@ -685,11 +594,7 @@ impl Sending {
|
|||||||
OutgoingKind::Normal(Box::<ServerName>::try_from(server).map_err(|_| {
|
OutgoingKind::Normal(Box::<ServerName>::try_from(server).map_err(|_| {
|
||||||
Error::bad_database("Invalid server string in server_currenttransaction")
|
Error::bad_database("Invalid server string in server_currenttransaction")
|
||||||
})?),
|
})?),
|
||||||
if event.starts_with(b"*") {
|
IVec::from(pdu),
|
||||||
SendingEventType::Edu(event[1..].to_vec())
|
|
||||||
} else {
|
|
||||||
SendingEventType::Pdu(event.to_vec())
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use ruma::{DeviceId, UserId};
|
use ruma::{DeviceId, UserId};
|
||||||
|
use sled::IVec;
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct TransactionIds {
|
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 {
|
impl TransactionIds {
|
||||||
@@ -23,7 +21,7 @@ impl TransactionIds {
|
|||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(txn_id.as_bytes());
|
key.extend_from_slice(txn_id.as_bytes());
|
||||||
|
|
||||||
self.userdevicetxnid_response.insert(&key, data)?;
|
self.userdevicetxnid_response.insert(key, data)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -33,7 +31,7 @@ impl TransactionIds {
|
|||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
device_id: Option<&DeviceId>,
|
device_id: Option<&DeviceId>,
|
||||||
txn_id: &str,
|
txn_id: &str,
|
||||||
) -> Result<Option<Vec<u8>>> {
|
) -> Result<Option<IVec>> {
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default());
|
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());
|
key.extend_from_slice(txn_id.as_bytes());
|
||||||
|
|
||||||
// If there's no entry, this is a new transaction
|
// 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::{Error, Result};
|
||||||
|
|
||||||
use crate::{client_server::SESSION_ID_LENGTH, utils, Error, Result};
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
r0::uiaa::{IncomingAuthData, UiaaInfo},
|
r0::uiaa::{IncomingAuthData, UiaaInfo},
|
||||||
},
|
},
|
||||||
signatures::CanonicalJsonValue,
|
|
||||||
DeviceId, UserId,
|
DeviceId, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct Uiaa {
|
pub struct Uiaa {
|
||||||
pub(super) userdevicesessionid_uiaainfo: Arc<dyn Tree>, // User-interactive authentication
|
pub(super) userdeviceid_uiaainfo: sled::Tree, // User-interactive authentication
|
||||||
pub(super) userdevicesessionid_uiaarequest: Arc<dyn Tree>, // UiaaRequest = canonical json value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Uiaa {
|
impl Uiaa {
|
||||||
@@ -24,20 +19,8 @@ impl Uiaa {
|
|||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
device_id: &DeviceId,
|
device_id: &DeviceId,
|
||||||
uiaainfo: &UiaaInfo,
|
uiaainfo: &UiaaInfo,
|
||||||
json_body: &CanonicalJsonValue,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.set_uiaa_request(
|
self.update_uiaa_session(user_id, device_id, Some(uiaainfo))
|
||||||
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),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_auth(
|
pub fn try_auth(
|
||||||
@@ -57,13 +40,11 @@ impl Uiaa {
|
|||||||
{
|
{
|
||||||
let mut uiaainfo = session
|
let mut uiaainfo = session
|
||||||
.as_ref()
|
.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()))?;
|
.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
|
// Find out what the user completed
|
||||||
match &**kind {
|
match &**kind {
|
||||||
"m.login.password" => {
|
"m.login.password" => {
|
||||||
@@ -149,96 +130,35 @@ impl Uiaa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !completed {
|
if !completed {
|
||||||
self.update_uiaa_session(
|
self.update_uiaa_session(user_id, device_id, Some(&uiaainfo))?;
|
||||||
user_id,
|
|
||||||
device_id,
|
|
||||||
uiaainfo.session.as_ref().expect("session is always set"),
|
|
||||||
Some(&uiaainfo),
|
|
||||||
)?;
|
|
||||||
return Ok((false, uiaainfo));
|
return Ok((false, uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
// UIAA was successful! Remove this session and return true
|
// UIAA was successful! Remove this session and return true
|
||||||
self.update_uiaa_session(
|
self.update_uiaa_session(user_id, device_id, None)?;
|
||||||
user_id,
|
|
||||||
device_id,
|
|
||||||
uiaainfo.session.as_ref().expect("session is always set"),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
Ok((true, uiaainfo))
|
Ok((true, uiaainfo))
|
||||||
} else {
|
} else {
|
||||||
panic!("FallbackAcknowledgement is not supported yet");
|
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(
|
fn update_uiaa_session(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
device_id: &DeviceId,
|
device_id: &DeviceId,
|
||||||
session: &str,
|
|
||||||
uiaainfo: Option<&UiaaInfo>,
|
uiaainfo: Option<&UiaaInfo>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut userdevicesessionid = user_id.as_bytes().to_vec();
|
let mut userdeviceid = user_id.as_bytes().to_vec();
|
||||||
userdevicesessionid.push(0xff);
|
userdeviceid.push(0xff);
|
||||||
userdevicesessionid.extend_from_slice(device_id.as_bytes());
|
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||||
userdevicesessionid.push(0xff);
|
|
||||||
userdevicesessionid.extend_from_slice(session.as_bytes());
|
|
||||||
|
|
||||||
if let Some(uiaainfo) = uiaainfo {
|
if let Some(uiaainfo) = uiaainfo {
|
||||||
self.userdevicesessionid_uiaainfo.insert(
|
self.userdeviceid_uiaainfo.insert(
|
||||||
&userdevicesessionid,
|
&userdeviceid,
|
||||||
&serde_json::to_vec(&uiaainfo).expect("UiaaInfo::to_vec always works"),
|
&*serde_json::to_string(&uiaainfo).expect("UiaaInfo::to_string always works"),
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
self.userdevicesessionid_uiaainfo
|
self.userdeviceid_uiaainfo.remove(&userdeviceid)?;
|
||||||
.remove(&userdevicesessionid)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -250,16 +170,14 @@ impl Uiaa {
|
|||||||
device_id: &DeviceId,
|
device_id: &DeviceId,
|
||||||
session: &str,
|
session: &str,
|
||||||
) -> Result<UiaaInfo> {
|
) -> Result<UiaaInfo> {
|
||||||
let mut userdevicesessionid = user_id.as_bytes().to_vec();
|
let mut userdeviceid = user_id.as_bytes().to_vec();
|
||||||
userdevicesessionid.push(0xff);
|
userdeviceid.push(0xff);
|
||||||
userdevicesessionid.extend_from_slice(device_id.as_bytes());
|
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||||
userdevicesessionid.push(0xff);
|
|
||||||
userdevicesessionid.extend_from_slice(session.as_bytes());
|
|
||||||
|
|
||||||
let uiaainfo = serde_json::from_slice::<UiaaInfo>(
|
let uiaainfo = serde_json::from_slice::<UiaaInfo>(
|
||||||
&self
|
&self
|
||||||
.userdevicesessionid_uiaainfo
|
.userdeviceid_uiaainfo
|
||||||
.get(&userdevicesessionid)?
|
.get(&userdeviceid)?
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"UIAA session does not exist.",
|
"UIAA session does not exist.",
|
||||||
@@ -267,6 +185,18 @@ impl Uiaa {
|
|||||||
)
|
)
|
||||||
.map_err(|_| Error::bad_database("UiaaInfo in userdeviceid_uiaainfo is invalid."))?;
|
.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)
|
Ok(uiaainfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+161
-144
@@ -1,47 +1,52 @@
|
|||||||
use crate::{utils, Error, Result};
|
use crate::{utils, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, r0::device::Device},
|
api::client::{
|
||||||
encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
|
error::ErrorKind,
|
||||||
|
r0::{
|
||||||
|
device::Device,
|
||||||
|
keys::{CrossSigningKey, OneTimeKey},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
encryption::DeviceKeys,
|
||||||
events::{AnyToDeviceEvent, EventType},
|
events::{AnyToDeviceEvent, EventType},
|
||||||
identifiers::MxcUri,
|
identifiers::MxcUri,
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, UInt, UserId,
|
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UInt, UserId,
|
||||||
};
|
};
|
||||||
use std::{collections::BTreeMap, convert::TryFrom, mem, sync::Arc};
|
use std::{collections::BTreeMap, convert::TryFrom, mem, time::SystemTime};
|
||||||
|
|
||||||
use super::abstraction::Tree;
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Users {
|
pub struct Users {
|
||||||
pub(super) userid_password: Arc<dyn Tree>,
|
pub(super) userid_password: sled::Tree,
|
||||||
pub(super) userid_displayname: Arc<dyn Tree>,
|
pub(super) userid_displayname: sled::Tree,
|
||||||
pub(super) userid_avatarurl: Arc<dyn Tree>,
|
pub(super) userid_avatarurl: sled::Tree,
|
||||||
pub(super) userdeviceid_token: Arc<dyn Tree>,
|
pub(super) userdeviceid_token: sled::Tree,
|
||||||
pub(super) userdeviceid_metadata: Arc<dyn Tree>, // This is also used to check if a device exists
|
pub(super) userdeviceid_metadata: sled::Tree, // This is also used to check if a device exists
|
||||||
pub(super) userid_devicelistversion: Arc<dyn Tree>, // DevicelistVersion = u64
|
pub(super) userid_devicelistversion: sled::Tree, // DevicelistVersion = u64
|
||||||
pub(super) token_userdeviceid: Arc<dyn Tree>,
|
pub(super) token_userdeviceid: sled::Tree,
|
||||||
|
|
||||||
pub(super) onetimekeyid_onetimekeys: Arc<dyn Tree>, // OneTimeKeyId = UserId + DeviceKeyId
|
pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + DeviceKeyId
|
||||||
pub(super) userid_lastonetimekeyupdate: Arc<dyn Tree>, // LastOneTimeKeyUpdate = Count
|
pub(super) userid_lastonetimekeyupdate: sled::Tree, // LastOneTimeKeyUpdate = Count
|
||||||
pub(super) keychangeid_userid: Arc<dyn Tree>, // KeyChangeId = UserId/RoomId + Count
|
pub(super) keychangeid_userid: sled::Tree, // KeyChangeId = UserId/RoomId + Count
|
||||||
pub(super) keyid_key: Arc<dyn Tree>, // KeyId = UserId + KeyId (depends on key type)
|
pub(super) keyid_key: sled::Tree, // KeyId = UserId + KeyId (depends on key type)
|
||||||
pub(super) userid_masterkeyid: Arc<dyn Tree>,
|
pub(super) userid_masterkeyid: sled::Tree,
|
||||||
pub(super) userid_selfsigningkeyid: Arc<dyn Tree>,
|
pub(super) userid_selfsigningkeyid: sled::Tree,
|
||||||
pub(super) userid_usersigningkeyid: Arc<dyn 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 {
|
impl Users {
|
||||||
/// Check if a user has an account on this homeserver.
|
/// Check if a user has an account on this homeserver.
|
||||||
pub fn exists(&self, user_id: &UserId) -> Result<bool> {
|
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
|
/// Check if account is deactivated
|
||||||
pub fn is_deactivated(&self, user_id: &UserId) -> Result<bool> {
|
pub fn is_deactivated(&self, user_id: &UserId) -> Result<bool> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.userid_password
|
.userid_password
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"User does not exist.",
|
"User does not exist.",
|
||||||
@@ -50,20 +55,20 @@ impl Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new user account on this homeserver.
|
/// 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)?;
|
self.set_password(user_id, password)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of users registered on this server.
|
/// Returns the number of users registered on this server.
|
||||||
pub fn count(&self) -> Result<usize> {
|
pub fn count(&self) -> usize {
|
||||||
Ok(self.userid_password.iter().count())
|
self.userid_password.iter().count()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find out which user an access token belongs to.
|
/// Find out which user an access token belongs to.
|
||||||
pub fn find_from_token(&self, token: &str) -> Result<Option<(UserId, String)>> {
|
pub fn find_from_token(&self, token: &str) -> Result<Option<(UserId, String)>> {
|
||||||
self.token_userdeviceid
|
self.token_userdeviceid
|
||||||
.get(token.as_bytes())?
|
.get(token)?
|
||||||
.map_or(Ok(None), |bytes| {
|
.map_or(Ok(None), |bytes| {
|
||||||
let mut parts = bytes.split(|&b| b == 0xff);
|
let mut parts = bytes.split(|&b| b == 0xff);
|
||||||
let user_bytes = parts.next().ok_or_else(|| {
|
let user_bytes = parts.next().ok_or_else(|| {
|
||||||
@@ -88,19 +93,21 @@ impl Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all users on this homeserver.
|
/// Returns an iterator over all users on this homeserver.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> + '_ {
|
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> {
|
||||||
self.userid_password.iter().map(|(bytes, _)| {
|
self.userid_password.iter().keys().map(|bytes| {
|
||||||
UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| {
|
Ok(
|
||||||
Error::bad_database("User ID in userid_password is invalid unicode.")
|
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.
|
/// Returns the password hash for the given user.
|
||||||
pub fn password_hash(&self, user_id: &UserId) -> Result<Option<String>> {
|
pub fn password_hash(&self, user_id: &UserId) -> Result<Option<String>> {
|
||||||
self.userid_password
|
self.userid_password
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.map_or(Ok(None), |bytes| {
|
.map_or(Ok(None), |bytes| {
|
||||||
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
|
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||||
Error::bad_database("Password hash in db is not valid string.")
|
Error::bad_database("Password hash in db is not valid string.")
|
||||||
@@ -109,28 +116,22 @@ impl Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Hash and set the user's password to the Argon2 hash
|
/// Hash and set the user's password to the Argon2 hash
|
||||||
pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
pub fn set_password(&self, user_id: &UserId, password: &str) -> Result<()> {
|
||||||
if let Some(password) = password {
|
if let Ok(hash) = utils::calculate_hash(&password) {
|
||||||
if let Ok(hash) = utils::calculate_hash(&password) {
|
self.userid_password.insert(user_id.to_string(), &*hash)?;
|
||||||
self.userid_password
|
|
||||||
.insert(user_id.as_bytes(), hash.as_bytes())?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Password does not meet the requirements.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.userid_password.insert(user_id.as_bytes(), b"")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Password does not meet the requirements.",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the displayname of a user on this homeserver.
|
/// Returns the displayname of a user on this homeserver.
|
||||||
pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> {
|
pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> {
|
||||||
self.userid_displayname
|
self.userid_displayname
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.map_or(Ok(None), |bytes| {
|
.map_or(Ok(None), |bytes| {
|
||||||
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
|
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||||
Error::bad_database("Displayname in db is invalid.")
|
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<()> {
|
pub fn set_displayname(&self, user_id: &UserId, displayname: Option<String>) -> Result<()> {
|
||||||
if let Some(displayname) = displayname {
|
if let Some(displayname) = displayname {
|
||||||
self.userid_displayname
|
self.userid_displayname
|
||||||
.insert(user_id.as_bytes(), displayname.as_bytes())?;
|
.insert(user_id.to_string(), &*displayname)?;
|
||||||
} else {
|
} else {
|
||||||
self.userid_displayname.remove(user_id.as_bytes())?;
|
self.userid_displayname.remove(user_id.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -153,7 +154,7 @@ impl Users {
|
|||||||
/// Get a the avatar_url of a user.
|
/// Get a the avatar_url of a user.
|
||||||
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<MxcUri>> {
|
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<MxcUri>> {
|
||||||
self.userid_avatarurl
|
self.userid_avatarurl
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.map(|bytes| {
|
.map(|bytes| {
|
||||||
let s = utils::string_from_bytes(&bytes)
|
let s = utils::string_from_bytes(&bytes)
|
||||||
.map_err(|_| Error::bad_database("Avatar URL in db is invalid."))?;
|
.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<()> {
|
pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<MxcUri>) -> Result<()> {
|
||||||
if let Some(avatar_url) = avatar_url {
|
if let Some(avatar_url) = avatar_url {
|
||||||
self.userid_avatarurl
|
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 {
|
} else {
|
||||||
self.userid_avatarurl.remove(user_id.as_bytes())?;
|
self.userid_avatarurl.remove(user_id.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -190,17 +191,19 @@ impl Users {
|
|||||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||||
|
|
||||||
self.userid_devicelistversion
|
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(
|
self.userdeviceid_metadata.insert(
|
||||||
&userdeviceid,
|
userdeviceid,
|
||||||
&serde_json::to_vec(&Device {
|
serde_json::to_string(&Device {
|
||||||
device_id: device_id.into(),
|
device_id: device_id.into(),
|
||||||
display_name: initial_device_display_name,
|
display_name: initial_device_display_name,
|
||||||
last_seen_ip: None, // TODO
|
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)?;
|
self.set_token(user_id, &device_id, token)?;
|
||||||
@@ -215,8 +218,7 @@ impl Users {
|
|||||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||||
|
|
||||||
// Remove tokens
|
// Remove tokens
|
||||||
if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? {
|
if let Some(old_token) = self.userdeviceid_token.remove(&userdeviceid)? {
|
||||||
self.userdeviceid_token.remove(&userdeviceid)?;
|
|
||||||
self.token_userdeviceid.remove(&old_token)?;
|
self.token_userdeviceid.remove(&old_token)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,14 +226,15 @@ impl Users {
|
|||||||
let mut prefix = userdeviceid.clone();
|
let mut prefix = userdeviceid.clone();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
for (key, _) in self.todeviceid_events.scan_prefix(prefix) {
|
for key in self.todeviceid_events.scan_prefix(&prefix).keys() {
|
||||||
self.todeviceid_events.remove(&key)?;
|
self.todeviceid_events.remove(key?)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove onetimekeys
|
// TODO: Remove onetimekeys
|
||||||
|
|
||||||
self.userid_devicelistversion
|
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)?;
|
self.userdeviceid_metadata.remove(&userdeviceid)?;
|
||||||
|
|
||||||
@@ -239,18 +242,16 @@ impl Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all device ids of this user.
|
/// Returns an iterator over all device ids of this user.
|
||||||
pub fn all_device_ids<'a>(
|
pub fn all_device_ids(&self, user_id: &UserId) -> impl Iterator<Item = Result<Box<DeviceId>>> {
|
||||||
&'a self,
|
|
||||||
user_id: &UserId,
|
|
||||||
) -> impl Iterator<Item = Result<Box<DeviceId>>> + 'a {
|
|
||||||
let mut prefix = user_id.as_bytes().to_vec();
|
let mut prefix = user_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
// All devices have metadata
|
// All devices have metadata
|
||||||
self.userdeviceid_metadata
|
self.userdeviceid_metadata
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(prefix)
|
||||||
.map(|(bytes, _)| {
|
.keys()
|
||||||
|
.map(|bytes| {
|
||||||
Ok(utils::string_from_bytes(
|
Ok(utils::string_from_bytes(
|
||||||
&bytes
|
&*bytes?
|
||||||
.rsplit(|&b| b == 0xff)
|
.rsplit(|&b| b == 0xff)
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| Error::bad_database("UserDevice ID in db is invalid."))?,
|
.ok_or_else(|| Error::bad_database("UserDevice ID in db is invalid."))?,
|
||||||
@@ -271,15 +272,13 @@ impl Users {
|
|||||||
|
|
||||||
// Remove old token
|
// Remove old token
|
||||||
if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? {
|
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
|
// It will be removed from userdeviceid_token by the insert later
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign token to user device combination
|
// Assign token to user device combination
|
||||||
self.userdeviceid_token
|
self.userdeviceid_token.insert(&userdeviceid, &*token)?;
|
||||||
.insert(&userdeviceid, token.as_bytes())?;
|
self.token_userdeviceid.insert(token, userdeviceid)?;
|
||||||
self.token_userdeviceid
|
|
||||||
.insert(token.as_bytes(), &userdeviceid)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -311,7 +310,8 @@ impl Users {
|
|||||||
|
|
||||||
self.onetimekeyid_onetimekeys.insert(
|
self.onetimekeyid_onetimekeys.insert(
|
||||||
&key,
|
&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
|
self.userid_lastonetimekeyupdate
|
||||||
@@ -351,9 +351,10 @@ impl Users {
|
|||||||
.insert(&user_id.as_bytes(), &globals.next_count()?.to_be_bytes())?;
|
.insert(&user_id.as_bytes(), &globals.next_count()?.to_be_bytes())?;
|
||||||
|
|
||||||
self.onetimekeyid_onetimekeys
|
self.onetimekeyid_onetimekeys
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(&prefix)
|
||||||
.next()
|
.next()
|
||||||
.map(|(key, value)| {
|
.map(|r| {
|
||||||
|
let (key, value) = r?;
|
||||||
self.onetimekeyid_onetimekeys.remove(&key)?;
|
self.onetimekeyid_onetimekeys.remove(&key)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -383,20 +384,21 @@ impl Users {
|
|||||||
|
|
||||||
let mut counts = BTreeMap::new();
|
let mut counts = BTreeMap::new();
|
||||||
|
|
||||||
for algorithm in
|
for algorithm in self
|
||||||
self.onetimekeyid_onetimekeys
|
.onetimekeyid_onetimekeys
|
||||||
.scan_prefix(userdeviceid)
|
.scan_prefix(&userdeviceid)
|
||||||
.map(|(bytes, _)| {
|
.keys()
|
||||||
Ok::<_, Error>(
|
.map(|bytes| {
|
||||||
serde_json::from_slice::<DeviceKeyId>(
|
Ok::<_, Error>(
|
||||||
&*bytes.rsplit(|&b| b == 0xff).next().ok_or_else(|| {
|
serde_json::from_slice::<DeviceKeyId>(
|
||||||
Error::bad_database("OneTimeKey ID in db is invalid.")
|
&*bytes?.rsplit(|&b| b == 0xff).next().ok_or_else(|| {
|
||||||
})?,
|
Error::bad_database("OneTimeKey ID in db is invalid.")
|
||||||
)
|
})?,
|
||||||
.map_err(|_| Error::bad_database("DeviceKeyId in db is invalid."))?
|
|
||||||
.algorithm(),
|
|
||||||
)
|
)
|
||||||
})
|
.map_err(|_| Error::bad_database("DeviceKeyId in db is invalid."))?
|
||||||
|
.algorithm(),
|
||||||
|
)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
*counts.entry(algorithm?).or_default() += UInt::from(1_u32);
|
*counts.entry(algorithm?).or_default() += UInt::from(1_u32);
|
||||||
}
|
}
|
||||||
@@ -418,7 +420,7 @@ impl Users {
|
|||||||
|
|
||||||
self.keyid_key.insert(
|
self.keyid_key.insert(
|
||||||
&userdeviceid,
|
&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)?;
|
self.mark_device_key_update(user_id, rooms, globals)?;
|
||||||
@@ -459,11 +461,11 @@ impl Users {
|
|||||||
|
|
||||||
self.keyid_key.insert(
|
self.keyid_key.insert(
|
||||||
&master_key_key,
|
&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
|
self.userid_masterkeyid
|
||||||
.insert(user_id.as_bytes(), &master_key_key)?;
|
.insert(&*user_id.to_string(), master_key_key)?;
|
||||||
|
|
||||||
// Self-signing key
|
// Self-signing key
|
||||||
if let Some(self_signing_key) = self_signing_key {
|
if let Some(self_signing_key) = self_signing_key {
|
||||||
@@ -485,12 +487,12 @@ impl Users {
|
|||||||
|
|
||||||
self.keyid_key.insert(
|
self.keyid_key.insert(
|
||||||
&self_signing_key_key,
|
&self_signing_key_key,
|
||||||
&serde_json::to_vec(&self_signing_key)
|
&*serde_json::to_string(&self_signing_key)
|
||||||
.expect("CrossSigningKey::to_vec always works"),
|
.expect("CrossSigningKey::to_string always works"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.userid_selfsigningkeyid
|
self.userid_selfsigningkeyid
|
||||||
.insert(user_id.as_bytes(), &self_signing_key_key)?;
|
.insert(&*user_id.to_string(), self_signing_key_key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User-signing key
|
// User-signing key
|
||||||
@@ -513,12 +515,12 @@ impl Users {
|
|||||||
|
|
||||||
self.keyid_key.insert(
|
self.keyid_key.insert(
|
||||||
&user_signing_key_key,
|
&user_signing_key_key,
|
||||||
&serde_json::to_vec(&user_signing_key)
|
&*serde_json::to_string(&user_signing_key)
|
||||||
.expect("CrossSigningKey::to_vec always works"),
|
.expect("CrossSigningKey::to_string always works"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.userid_usersigningkeyid
|
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)?;
|
self.mark_device_key_update(user_id, rooms, globals)?;
|
||||||
@@ -560,7 +562,8 @@ impl Users {
|
|||||||
|
|
||||||
self.keyid_key.insert(
|
self.keyid_key.insert(
|
||||||
&key,
|
&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?
|
// TODO: Should we notify about this change?
|
||||||
@@ -570,26 +573,36 @@ impl Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn keys_changed<'a>(
|
pub fn keys_changed(
|
||||||
&'a self,
|
&self,
|
||||||
user_or_room_id: &str,
|
user_or_room_id: &str,
|
||||||
from: u64,
|
from: u64,
|
||||||
to: Option<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();
|
let mut prefix = user_or_room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
let mut start = prefix.clone();
|
let mut start = prefix.clone();
|
||||||
start.extend_from_slice(&(from + 1).to_be_bytes());
|
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
|
self.keychangeid_userid
|
||||||
.iter_from(&start, false)
|
.range(start..end)
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(|(_, bytes)| {
|
.map(|(_, bytes)| {
|
||||||
UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| {
|
Ok(
|
||||||
Error::bad_database("User ID in devicekeychangeid_userid is invalid unicode.")
|
UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| {
|
||||||
})?)
|
Error::bad_database(
|
||||||
.map_err(|_| Error::bad_database("User ID in devicekeychangeid_userid is invalid."))
|
"User ID in devicekeychangeid_userid is invalid unicode.",
|
||||||
|
)
|
||||||
|
})?)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("User ID in devicekeychangeid_userid is invalid.")
|
||||||
|
})?,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,13 +626,13 @@ impl Users {
|
|||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(&count);
|
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();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(&count);
|
key.extend_from_slice(&count);
|
||||||
self.keychangeid_userid.insert(&key, user_id.as_bytes())?;
|
self.keychangeid_userid.insert(key, &*user_id.to_string())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -633,22 +646,23 @@ impl Users {
|
|||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(device_id.as_bytes());
|
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(|_| {
|
Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
|
||||||
Error::bad_database("DeviceKeys in db are invalid.")
|
Error::bad_database("DeviceKeys in db are invalid.")
|
||||||
})?))
|
})?))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_master_key<F: Fn(&UserId) -> bool>(
|
pub fn get_master_key(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
allowed_signatures: F,
|
sender_id: &UserId,
|
||||||
) -> Result<Option<CrossSigningKey>> {
|
) -> Result<Option<CrossSigningKey>> {
|
||||||
|
// TODO: hide some signatures
|
||||||
self.userid_masterkeyid
|
self.userid_masterkeyid
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.map_or(Ok(None), |key| {
|
.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)
|
let mut cross_signing_key = serde_json::from_slice::<CrossSigningKey>(&bytes)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database("CrossSigningKey in db is invalid.")
|
Error::bad_database("CrossSigningKey in db is invalid.")
|
||||||
@@ -659,7 +673,7 @@ impl Users {
|
|||||||
cross_signing_key.signatures = cross_signing_key
|
cross_signing_key.signatures = cross_signing_key
|
||||||
.signatures
|
.signatures
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(user, _)| allowed_signatures(user))
|
.filter(|(user, _)| user == user_id || user == sender_id)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Some(cross_signing_key))
|
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,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
allowed_signatures: F,
|
sender_id: &UserId,
|
||||||
) -> Result<Option<CrossSigningKey>> {
|
) -> Result<Option<CrossSigningKey>> {
|
||||||
self.userid_selfsigningkeyid
|
self.userid_selfsigningkeyid
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.map_or(Ok(None), |key| {
|
.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)
|
let mut cross_signing_key = serde_json::from_slice::<CrossSigningKey>(&bytes)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database("CrossSigningKey in db is invalid.")
|
Error::bad_database("CrossSigningKey in db is invalid.")
|
||||||
@@ -686,7 +700,7 @@ impl Users {
|
|||||||
cross_signing_key.signatures = cross_signing_key
|
cross_signing_key.signatures = cross_signing_key
|
||||||
.signatures
|
.signatures
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(user, _)| user == user_id || allowed_signatures(user))
|
.filter(|(user, _)| user == user_id || user == sender_id)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Some(cross_signing_key))
|
Ok(Some(cross_signing_key))
|
||||||
@@ -696,9 +710,9 @@ impl Users {
|
|||||||
|
|
||||||
pub fn get_user_signing_key(&self, user_id: &UserId) -> Result<Option<CrossSigningKey>> {
|
pub fn get_user_signing_key(&self, user_id: &UserId) -> Result<Option<CrossSigningKey>> {
|
||||||
self.userid_usersigningkeyid
|
self.userid_usersigningkeyid
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.to_string())?
|
||||||
.map_or(Ok(None), |key| {
|
.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(|_| {
|
Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
|
||||||
Error::bad_database("CrossSigningKey in db is invalid.")
|
Error::bad_database("CrossSigningKey in db is invalid.")
|
||||||
})?))
|
})?))
|
||||||
@@ -711,7 +725,7 @@ impl Users {
|
|||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
target_user_id: &UserId,
|
target_user_id: &UserId,
|
||||||
target_device_id: &DeviceId,
|
target_device_id: &DeviceId,
|
||||||
event_type: &str,
|
event_type: &EventType,
|
||||||
content: serde_json::Value,
|
content: serde_json::Value,
|
||||||
globals: &super::globals::Globals,
|
globals: &super::globals::Globals,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -722,13 +736,13 @@ impl Users {
|
|||||||
key.extend_from_slice(&globals.next_count()?.to_be_bytes());
|
key.extend_from_slice(&globals.next_count()?.to_be_bytes());
|
||||||
|
|
||||||
let mut json = serde_json::Map::new();
|
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("sender".to_owned(), sender.to_string().into());
|
||||||
json.insert("content".to_owned(), content);
|
json.insert("content".to_owned(), content);
|
||||||
|
|
||||||
self.todeviceid_events.insert(
|
self.todeviceid_events.insert(
|
||||||
&key,
|
&key,
|
||||||
&serde_json::to_vec(&json).expect("Map::to_vec always works"),
|
&*serde_json::to_string(&json).expect("Map::to_string always works"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -747,9 +761,9 @@ impl Users {
|
|||||||
prefix.extend_from_slice(device_id.as_bytes());
|
prefix.extend_from_slice(device_id.as_bytes());
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
for (_, value) in self.todeviceid_events.scan_prefix(prefix) {
|
for value in self.todeviceid_events.scan_prefix(&prefix).values() {
|
||||||
events.push(
|
events.push(
|
||||||
serde_json::from_slice(&value)
|
serde_json::from_slice(&*value?)
|
||||||
.map_err(|_| Error::bad_database("Event in todeviceid_events is invalid."))?,
|
.map_err(|_| Error::bad_database("Event in todeviceid_events is invalid."))?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -774,9 +788,10 @@ impl Users {
|
|||||||
|
|
||||||
for (key, _) in self
|
for (key, _) in self
|
||||||
.todeviceid_events
|
.todeviceid_events
|
||||||
.iter_from(&last, true)
|
.range(&*prefix..=&*last)
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.keys()
|
||||||
.map(|(key, _)| {
|
.map(|key| {
|
||||||
|
let key = key?;
|
||||||
Ok::<_, Error>((
|
Ok::<_, Error>((
|
||||||
key.clone(),
|
key.clone(),
|
||||||
utils::u64_from_bytes(&key[key.len() - mem::size_of::<u64>()..key.len()])
|
utils::u64_from_bytes(&key[key.len() - mem::size_of::<u64>()..key.len()])
|
||||||
@@ -786,7 +801,7 @@ impl Users {
|
|||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.take_while(|&(_, count)| count <= until)
|
.take_while(|&(_, count)| count <= until)
|
||||||
{
|
{
|
||||||
self.todeviceid_events.remove(&key)?;
|
self.todeviceid_events.remove(key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -806,11 +821,14 @@ impl Users {
|
|||||||
assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
|
assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
|
||||||
|
|
||||||
self.userid_devicelistversion
|
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(
|
self.userdeviceid_metadata.insert(
|
||||||
&userdeviceid,
|
userdeviceid,
|
||||||
&serde_json::to_vec(device).expect("Device::to_string always works"),
|
serde_json::to_string(device)
|
||||||
|
.expect("Device::to_string always works")
|
||||||
|
.as_bytes(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -845,18 +863,17 @@ impl Users {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_devices_metadata<'a>(
|
pub fn all_devices_metadata(&self, user_id: &UserId) -> impl Iterator<Item = Result<Device>> {
|
||||||
&'a self,
|
|
||||||
user_id: &UserId,
|
|
||||||
) -> impl Iterator<Item = Result<Device>> + 'a {
|
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
|
|
||||||
self.userdeviceid_metadata
|
self.userdeviceid_metadata
|
||||||
.scan_prefix(key)
|
.scan_prefix(key)
|
||||||
.map(|(_, bytes)| {
|
.values()
|
||||||
serde_json::from_slice::<Device>(&bytes)
|
.map(|bytes| {
|
||||||
.map_err(|_| Error::bad_database("Device in userdeviceid_metadata is invalid."))
|
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
|
// 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
|
// 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.
|
// 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
|
// TODO: Unhook 3PID
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+16
-48
@@ -1,11 +1,5 @@
|
|||||||
use log::{error, warn};
|
use log::error;
|
||||||
use ruma::{
|
use ruma::api::client::{error::ErrorKind, r0::uiaa::UiaaInfo};
|
||||||
api::client::{
|
|
||||||
error::{Error as RumaError, ErrorKind},
|
|
||||||
r0::uiaa::UiaaInfo,
|
|
||||||
},
|
|
||||||
ServerName,
|
|
||||||
};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
@@ -16,25 +10,18 @@ use {
|
|||||||
response::{self, Responder},
|
response::{self, Responder},
|
||||||
Request,
|
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>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[cfg(feature = "sled")]
|
#[error("There was a problem with the connection to the database.")]
|
||||||
#[error("There was a problem with the connection to the sled database.")]
|
|
||||||
SledError {
|
SledError {
|
||||||
#[from]
|
#[from]
|
||||||
source: sled::Error,
|
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.")]
|
#[error("Could not generate an image.")]
|
||||||
ImageError {
|
ImageError {
|
||||||
#[from]
|
#[from]
|
||||||
@@ -46,13 +33,6 @@ pub enum Error {
|
|||||||
source: reqwest::Error,
|
source: reqwest::Error,
|
||||||
},
|
},
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
FederationError(Box<ServerName>, RumaError),
|
|
||||||
#[error("Could not do this io: {source}")]
|
|
||||||
IoError {
|
|
||||||
#[from]
|
|
||||||
source: std::io::Error,
|
|
||||||
},
|
|
||||||
#[error("{0}")]
|
|
||||||
BadServerResponse(&'static str),
|
BadServerResponse(&'static str),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
BadConfig(&'static str),
|
BadConfig(&'static str),
|
||||||
@@ -61,6 +41,7 @@ pub enum Error {
|
|||||||
BadDatabase(&'static str),
|
BadDatabase(&'static str),
|
||||||
#[error("uiaa")]
|
#[error("uiaa")]
|
||||||
Uiaa(UiaaInfo),
|
Uiaa(UiaaInfo),
|
||||||
|
|
||||||
#[error("{0}: {1}")]
|
#[error("{0}: {1}")]
|
||||||
BadRequest(ErrorKind, &'static str),
|
BadRequest(ErrorKind, &'static str),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
@@ -79,16 +60,14 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
#[cfg(feature = "conduit_bin")]
|
||||||
pub fn to_response(&self) -> RumaResponse<UiaaResponse> {
|
impl<'r, 'o> Responder<'r, 'o> for Error
|
||||||
if let Self::Uiaa(uiaainfo) = self {
|
where
|
||||||
return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone()));
|
'o: 'r,
|
||||||
}
|
{
|
||||||
|
fn respond_to(self, r: &'r Request<'_>) -> response::Result<'o> {
|
||||||
if let Self::FederationError(origin, error) = self {
|
if let Self::Uiaa(uiaainfo) = &self {
|
||||||
let mut error = error.clone();
|
return RumaResponse::from(UiaaResponse::AuthResponse(uiaainfo.clone())).respond_to(r);
|
||||||
error.message = format!("Answer from {}: {}", origin, error.message);
|
|
||||||
return RumaResponse(UiaaResponse::MatrixError(error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = format!("{}", self);
|
let message = format!("{}", self);
|
||||||
@@ -113,22 +92,11 @@ impl Error {
|
|||||||
_ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR),
|
_ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
};
|
};
|
||||||
|
|
||||||
warn!("{}: {}", status_code, message);
|
RumaResponse::from(RumaError {
|
||||||
|
|
||||||
RumaResponse(UiaaResponse::MatrixError(RumaError {
|
|
||||||
kind,
|
kind,
|
||||||
message,
|
message,
|
||||||
status_code,
|
status_code,
|
||||||
}))
|
})
|
||||||
}
|
.respond_to(r)
|
||||||
}
|
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#![allow(clippy::suspicious_else_formatting)]
|
#![allow(clippy::suspicious_else_formatting)]
|
||||||
#![deny(clippy::dbg_macro)]
|
|
||||||
|
|
||||||
pub mod appservice_server;
|
pub mod appservice_server;
|
||||||
pub mod client_server;
|
pub mod client_server;
|
||||||
mod database;
|
mod database;
|
||||||
|
|||||||
+8
-17
@@ -1,6 +1,5 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![allow(clippy::suspicious_else_formatting)]
|
#![allow(clippy::suspicious_else_formatting)]
|
||||||
#![deny(clippy::dbg_macro)]
|
|
||||||
|
|
||||||
pub mod appservice_server;
|
pub mod appservice_server;
|
||||||
pub mod client_server;
|
pub mod client_server;
|
||||||
@@ -12,8 +11,6 @@ mod pdu;
|
|||||||
mod ruma_wrapper;
|
mod ruma_wrapper;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use database::Config;
|
use database::Config;
|
||||||
pub use database::Database;
|
pub use database::Database;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
@@ -33,7 +30,7 @@ use rocket::{
|
|||||||
use tracing::span;
|
use tracing::span;
|
||||||
use tracing_subscriber::{prelude::*, Registry};
|
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)
|
rocket::custom(config)
|
||||||
.manage(data)
|
.manage(data)
|
||||||
.mount(
|
.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_avatar_url_route,
|
||||||
client_server::get_profile_route,
|
client_server::get_profile_route,
|
||||||
client_server::set_presence_route,
|
client_server::set_presence_route,
|
||||||
client_server::get_presence_route,
|
|
||||||
client_server::upload_keys_route,
|
client_server::upload_keys_route,
|
||||||
client_server::get_keys_route,
|
client_server::get_keys_route,
|
||||||
client_server::claim_keys_route,
|
client_server::claim_keys_route,
|
||||||
@@ -153,8 +149,6 @@ fn setup_rocket(config: Figment, data: Arc<Database>) -> rocket::Rocket<rocket::
|
|||||||
server_server::send_transaction_message_route,
|
server_server::send_transaction_message_route,
|
||||||
server_server::get_event_route,
|
server_server::get_event_route,
|
||||||
server_server::get_missing_events_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::get_room_state_ids_route,
|
||||||
server_server::create_join_event_template_route,
|
server_server::create_join_event_template_route,
|
||||||
server_server::create_join_event_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_devices_route,
|
||||||
server_server::get_room_information_route,
|
server_server::get_room_information_route,
|
||||||
server_server::get_profile_information_route,
|
server_server::get_profile_information_route,
|
||||||
server_server::get_keys_route,
|
|
||||||
server_server::claim_keys_route,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.register(
|
.register(
|
||||||
@@ -201,6 +193,8 @@ async fn main() {
|
|||||||
.await
|
.await
|
||||||
.expect("config is valid");
|
.expect("config is valid");
|
||||||
|
|
||||||
|
db.sending.start_handler(&db);
|
||||||
|
|
||||||
if config.allow_jaeger {
|
if config.allow_jaeger {
|
||||||
let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
|
let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
|
||||||
.with_service_name("conduit")
|
.with_service_name("conduit")
|
||||||
@@ -211,16 +205,13 @@ async fn main() {
|
|||||||
|
|
||||||
let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
|
let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
|
||||||
let _enter = root.enter();
|
let _enter = root.enter();
|
||||||
|
|
||||||
let rocket = setup_rocket(raw_config, db);
|
|
||||||
rocket.launch().await.unwrap();
|
|
||||||
} else {
|
} else {
|
||||||
std::env::set_var("RUST_LOG", config.log);
|
std::env::set_var("CONDUIT_LOG", config.log);
|
||||||
tracing_subscriber::fmt::init();
|
pretty_env_logger::init_custom_env("CONDUIT_LOG");
|
||||||
|
|
||||||
let rocket = setup_rocket(raw_config, db);
|
|
||||||
rocket.launch().await.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rocket = setup_rocket(raw_config, db);
|
||||||
|
rocket.launch().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[catch(404)]
|
#[catch(404)]
|
||||||
|
|||||||
+12
-14
@@ -2,17 +2,15 @@ use crate::Error;
|
|||||||
use log::error;
|
use log::error;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{
|
events::{
|
||||||
pdu::EventHash, room::member::MemberEventContent, AnyEphemeralRoomEvent, AnyRoomEvent,
|
pdu::EventHash, room::member::MemberEventContent, AnyEvent, AnyRoomEvent, AnyStateEvent,
|
||||||
AnyStateEvent, AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, EventType,
|
AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, EventType, StateEvent,
|
||||||
StateEvent,
|
|
||||||
},
|
},
|
||||||
serde::{CanonicalJsonObject, CanonicalJsonValue, Raw},
|
serde::{CanonicalJsonObject, CanonicalJsonValue, Raw},
|
||||||
state_res, EventId, MilliSecondsSinceUnixEpoch, RoomId, RoomVersionId, ServerName,
|
EventId, RoomId, RoomVersionId, ServerName, ServerSigningKeyId, UInt, UserId,
|
||||||
ServerSigningKeyId, UInt, UserId,
|
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
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)]
|
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||||
pub struct PduEvent {
|
pub struct PduEvent {
|
||||||
@@ -74,7 +72,9 @@ impl PduEvent {
|
|||||||
|
|
||||||
self.unsigned.insert(
|
self.unsigned.insert(
|
||||||
"redacted_because".to_owned(),
|
"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();
|
self.content = new_content.into();
|
||||||
@@ -105,7 +105,7 @@ impl PduEvent {
|
|||||||
|
|
||||||
/// This only works for events that are also AnyRoomEvents.
|
/// This only works for events that are also AnyRoomEvents.
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn to_any_event(&self) -> Raw<AnyEphemeralRoomEvent> {
|
pub fn to_any_event(&self) -> Raw<AnyEvent> {
|
||||||
let mut json = json!({
|
let mut json = json!({
|
||||||
"content": self.content,
|
"content": self.content,
|
||||||
"type": self.kind,
|
"type": self.kind,
|
||||||
@@ -213,10 +213,7 @@ impl PduEvent {
|
|||||||
pub fn convert_to_outgoing_federation_event(
|
pub fn convert_to_outgoing_federation_event(
|
||||||
mut pdu_json: CanonicalJsonObject,
|
mut pdu_json: CanonicalJsonObject,
|
||||||
) -> Raw<ruma::events::pdu::Pdu> {
|
) -> Raw<ruma::events::pdu::Pdu> {
|
||||||
if let Some(unsigned) = pdu_json
|
if let Some(CanonicalJsonValue::Object(unsigned)) = pdu_json.get_mut("unsigned") {
|
||||||
.get_mut("unsigned")
|
|
||||||
.and_then(|val| val.as_object_mut())
|
|
||||||
{
|
|
||||||
unsigned.remove("transaction_id");
|
unsigned.remove("transaction_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,9 +264,10 @@ impl state_res::Event for PduEvent {
|
|||||||
fn content(&self) -> serde_json::Value {
|
fn content(&self) -> serde_json::Value {
|
||||||
self.content.clone()
|
self.content.clone()
|
||||||
}
|
}
|
||||||
fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
|
fn origin_server_ts(&self) -> std::time::SystemTime {
|
||||||
MilliSecondsSinceUnixEpoch(self.origin_server_ts)
|
UNIX_EPOCH + std::time::Duration::from_millis(self.origin_server_ts.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_key(&self) -> Option<String> {
|
fn state_key(&self) -> Option<String> {
|
||||||
self.state_key.clone()
|
self.state_key.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
+84
-127
@@ -1,15 +1,14 @@
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{client::r0::uiaa::UiaaResponse, OutgoingResponse},
|
api::OutgoingResponse,
|
||||||
identifiers::{DeviceId, UserId},
|
identifiers::{DeviceId, UserId},
|
||||||
signatures::CanonicalJsonValue,
|
Outgoing,
|
||||||
Outgoing, ServerName,
|
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use {
|
use {
|
||||||
crate::{server_server, Database},
|
crate::{server_server, utils},
|
||||||
log::{debug, warn},
|
log::{debug, warn},
|
||||||
rocket::{
|
rocket::{
|
||||||
data::{self, ByteUnit, Data, FromData},
|
data::{self, ByteUnit, Data, FromData},
|
||||||
@@ -19,11 +18,14 @@ use {
|
|||||||
tokio::io::AsyncReadExt,
|
tokio::io::AsyncReadExt,
|
||||||
Request, State,
|
Request, State,
|
||||||
},
|
},
|
||||||
ruma::api::{AuthScheme, IncomingRequest},
|
ruma::{
|
||||||
|
api::{AuthScheme, IncomingRequest},
|
||||||
|
signatures::CanonicalJsonValue,
|
||||||
|
ServerName,
|
||||||
|
},
|
||||||
std::collections::BTreeMap,
|
std::collections::BTreeMap,
|
||||||
std::convert::TryFrom,
|
std::convert::TryFrom,
|
||||||
std::io::Cursor,
|
std::io::Cursor,
|
||||||
std::sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This struct converts rocket requests into ruma structs by converting them into http requests
|
/// 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 body: T::Incoming,
|
||||||
pub sender_user: Option<UserId>,
|
pub sender_user: Option<UserId>,
|
||||||
pub sender_device: Option<Box<DeviceId>>,
|
pub sender_device: Option<Box<DeviceId>>,
|
||||||
pub sender_servername: Option<Box<ServerName>>,
|
|
||||||
// This is None when body is not a valid string
|
// 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,
|
pub from_appservice: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ where
|
|||||||
async fn from_data(request: &'a Request<'_>, data: Data) -> data::Outcome<Self, Self::Error> {
|
async fn from_data(request: &'a Request<'_>, data: Data) -> data::Outcome<Self, Self::Error> {
|
||||||
let metadata = T::Incoming::METADATA;
|
let metadata = T::Incoming::METADATA;
|
||||||
let db = request
|
let db = request
|
||||||
.guard::<State<'_, Arc<Database>>>()
|
.guard::<State<'_, crate::Database>>()
|
||||||
.await
|
.await
|
||||||
.expect("database was loaded");
|
.expect("database was loaded");
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ where
|
|||||||
let token = request
|
let token = request
|
||||||
.headers()
|
.headers()
|
||||||
.get_one("Authorization")
|
.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()));
|
.or_else(|| request.query_value("access_token").and_then(|r| r.ok()));
|
||||||
|
|
||||||
let limit = db.globals.max_request_size();
|
let limit = db.globals.max_request_size();
|
||||||
@@ -65,15 +66,9 @@ where
|
|||||||
let mut body = Vec::new();
|
let mut body = Vec::new();
|
||||||
handle.read_to_end(&mut body).await.unwrap();
|
handle.read_to_end(&mut body).await.unwrap();
|
||||||
|
|
||||||
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&body).ok();
|
let (sender_user, sender_device, from_appservice) = if let Some((_id, registration)) = db
|
||||||
|
|
||||||
let (sender_user, sender_device, sender_servername, from_appservice) = if let Some((
|
|
||||||
_id,
|
|
||||||
registration,
|
|
||||||
)) = db
|
|
||||||
.appservice
|
.appservice
|
||||||
.iter_all()
|
.iter_all()
|
||||||
.unwrap()
|
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.find(|(_id, registration)| {
|
.find(|(_id, registration)| {
|
||||||
registration
|
registration
|
||||||
@@ -107,10 +102,10 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check if appservice is allowed to be that user
|
// 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::ServerSignatures => (None, None, true),
|
||||||
AuthScheme::None => (None, None, None, true),
|
AuthScheme::None => (None, None, true),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match metadata.authentication {
|
match metadata.authentication {
|
||||||
@@ -119,12 +114,9 @@ where
|
|||||||
match db.users.find_from_token(&token).unwrap() {
|
match db.users.find_from_token(&token).unwrap() {
|
||||||
// Unknown Token
|
// Unknown Token
|
||||||
None => return Failure((Status::raw(581), ())),
|
None => return Failure((Status::raw(581), ())),
|
||||||
Some((user_id, device_id)) => (
|
Some((user_id, device_id)) => {
|
||||||
Some(user_id),
|
(Some(user_id), Some(device_id.into()), false)
|
||||||
Some(Box::<DeviceId>::from(device_id)),
|
}
|
||||||
None,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Missing Token
|
// Missing Token
|
||||||
@@ -133,18 +125,16 @@ where
|
|||||||
}
|
}
|
||||||
AuthScheme::ServerSignatures => {
|
AuthScheme::ServerSignatures => {
|
||||||
// Get origin from header
|
// Get origin from header
|
||||||
let x_matrix = match request
|
let x_matrix = match request.headers().get_one("Authorization").map(|s| {
|
||||||
.headers()
|
// Split off "X-Matrix " and parse the rest
|
||||||
.get_one("Authorization")
|
s[9..]
|
||||||
.and_then(|s| s.get(9..)) // Split off "X-Matrix " and parse the rest
|
.split_terminator(',')
|
||||||
.map(|s| {
|
.map(|field| {
|
||||||
s.split_terminator(',')
|
let mut splits = field.splitn(2, '=');
|
||||||
.map(|field| {
|
(splits.next(), splits.next().map(|s| s.trim_matches('"')))
|
||||||
let mut splits = field.splitn(2, '=');
|
})
|
||||||
(splits.next(), splits.next().map(|s| s.trim_matches('"')))
|
.collect::<BTreeMap<_, _>>()
|
||||||
})
|
}) {
|
||||||
.collect::<BTreeMap<_, _>>()
|
|
||||||
}) {
|
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => {
|
None => {
|
||||||
warn!("No Authorization header");
|
warn!("No Authorization header");
|
||||||
@@ -197,10 +187,12 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let json_body = serde_json::from_slice::<CanonicalJsonValue>(&body);
|
||||||
|
|
||||||
let mut request_map = BTreeMap::<String, CanonicalJsonValue>::new();
|
let mut request_map = BTreeMap::<String, CanonicalJsonValue>::new();
|
||||||
|
|
||||||
if let Some(json_body) = &json_body {
|
if let Ok(json_body) = json_body {
|
||||||
request_map.insert("content".to_owned(), json_body.clone());
|
request_map.insert("content".to_owned(), json_body);
|
||||||
};
|
};
|
||||||
|
|
||||||
request_map.insert(
|
request_map.insert(
|
||||||
@@ -235,24 +227,27 @@ where
|
|||||||
CanonicalJsonValue::Object(signatures),
|
CanonicalJsonValue::Object(signatures),
|
||||||
);
|
);
|
||||||
|
|
||||||
let keys =
|
let keys = match server_server::fetch_signing_keys(
|
||||||
match server_server::fetch_signing_keys(&db, &origin, vec![key.to_owned()])
|
&db,
|
||||||
.await
|
&origin,
|
||||||
{
|
vec![&key.to_owned()],
|
||||||
Ok(b) => b,
|
)
|
||||||
Err(e) => {
|
.await
|
||||||
warn!("Failed to fetch signing keys: {}", e);
|
{
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to fetch signing keys: {}", e);
|
||||||
|
|
||||||
// Forbidden
|
// Forbidden
|
||||||
return Failure((Status::raw(580), ()));
|
return Failure((Status::raw(580), ()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut pub_key_map = BTreeMap::new();
|
let mut pub_key_map = BTreeMap::new();
|
||||||
pub_key_map.insert(origin.as_str().to_owned(), keys);
|
pub_key_map.insert(origin.as_str().to_owned(), keys);
|
||||||
|
|
||||||
match ruma::signatures::verify_json(&pub_key_map, &request_map) {
|
match ruma::signatures::verify_json(&pub_key_map, &request_map) {
|
||||||
Ok(()) => (None, None, Some(origin), false),
|
Ok(()) => (None, None, false),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to verify json request from {}: {}", origin, 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);
|
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();
|
let http_request = http_request.body(&*body).unwrap();
|
||||||
debug!("{:?}", http_request);
|
debug!("{:?}", http_request);
|
||||||
match <T::Incoming as IncomingRequest>::try_from_http_request(http_request) {
|
match <T::Incoming as IncomingRequest>::try_from_http_request(http_request) {
|
||||||
@@ -312,9 +278,11 @@ where
|
|||||||
body: t,
|
body: t,
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
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,
|
from_appservice,
|
||||||
json_body,
|
|
||||||
}),
|
}),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("{:?}", e);
|
warn!("{:?}", e);
|
||||||
@@ -335,60 +303,49 @@ impl<T: Outgoing> Deref for Ruma<T> {
|
|||||||
/// This struct converts ruma responses into rocket http responses.
|
/// This struct converts ruma responses into rocket http responses.
|
||||||
pub type ConduitResult<T> = std::result::Result<RumaResponse<T>, Error>;
|
pub type ConduitResult<T> = std::result::Result<RumaResponse<T>, Error>;
|
||||||
|
|
||||||
pub fn response<T: OutgoingResponse>(response: RumaResponse<T>) -> response::Result<'static> {
|
pub struct RumaResponse<T: OutgoingResponse>(pub T);
|
||||||
let http_response = response
|
|
||||||
.0
|
|
||||||
.try_into_http_response::<Vec<u8>>()
|
|
||||||
.map_err(|_| Status::InternalServerError)?;
|
|
||||||
|
|
||||||
let mut response = rocket::response::Response::build();
|
impl<T: OutgoingResponse> From<T> for RumaResponse<T> {
|
||||||
|
|
||||||
let status = http_response.status();
|
|
||||||
response.raw_status(status.into(), "");
|
|
||||||
|
|
||||||
for header in http_response.headers() {
|
|
||||||
response.raw_header(header.0.to_string(), header.1.to_str().unwrap().to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let http_body = http_response.into_body();
|
|
||||||
|
|
||||||
response.sized_body(http_body.len(), Cursor::new(http_body));
|
|
||||||
|
|
||||||
response.raw_header("Access-Control-Allow-Origin", "*");
|
|
||||||
response.raw_header(
|
|
||||||
"Access-Control-Allow-Methods",
|
|
||||||
"GET, POST, PUT, DELETE, OPTIONS",
|
|
||||||
);
|
|
||||||
response.raw_header(
|
|
||||||
"Access-Control-Allow-Headers",
|
|
||||||
"Origin, X-Requested-With, Content-Type, Accept, Authorization",
|
|
||||||
);
|
|
||||||
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 {
|
fn from(t: T) -> Self {
|
||||||
Self(t)
|
Self(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for RumaResponse<UiaaResponse> {
|
|
||||||
fn from(t: Error) -> Self {
|
|
||||||
t.to_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
impl<'r, 'o, T> Responder<'r, 'o> for RumaResponse<T>
|
impl<'r, 'o, T> Responder<'r, 'o> for RumaResponse<T>
|
||||||
where
|
where
|
||||||
|
T: Send + OutgoingResponse,
|
||||||
'o: 'r,
|
'o: 'r,
|
||||||
T: OutgoingResponse,
|
|
||||||
{
|
{
|
||||||
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
||||||
response(self)
|
let http_response = self
|
||||||
|
.0
|
||||||
|
.try_into_http_response::<Vec<u8>>()
|
||||||
|
.map_err(|_| Status::InternalServerError)?;
|
||||||
|
|
||||||
|
let mut response = rocket::response::Response::build();
|
||||||
|
|
||||||
|
let status = http_response.status();
|
||||||
|
response.raw_status(status.into(), "");
|
||||||
|
|
||||||
|
for header in http_response.headers() {
|
||||||
|
response.raw_header(header.0.to_string(), header.1.to_str().unwrap().to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
let http_body = http_response.into_body();
|
||||||
|
|
||||||
|
response.sized_body(http_body.len(), Cursor::new(http_body));
|
||||||
|
|
||||||
|
response.raw_header("Access-Control-Allow-Origin", "*");
|
||||||
|
response.raw_header(
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"GET, POST, PUT, DELETE, OPTIONS",
|
||||||
|
);
|
||||||
|
response.raw_header(
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Origin, X-Requested-With, Content-Type, Accept, Authorization",
|
||||||
|
);
|
||||||
|
response.raw_header("Access-Control-Max-Age", "86400");
|
||||||
|
response.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+200
-569
File diff suppressed because it is too large
Load Diff
+24
-28
@@ -15,15 +15,6 @@ pub fn millis_since_unix_epoch() -> u64 {
|
|||||||
.as_millis() as 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>> {
|
pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
||||||
let number = match old.map(|bytes| bytes.try_into()) {
|
let number = match old.map(|bytes| bytes.try_into()) {
|
||||||
Some(Ok(bytes)) => {
|
Some(Ok(bytes)) => {
|
||||||
@@ -36,14 +27,16 @@ pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
|||||||
Some(number.to_be_bytes().to_vec())
|
Some(number.to_be_bytes().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_keypair() -> Vec<u8> {
|
pub fn generate_keypair(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
||||||
let mut value = random_string(8).as_bytes().to_vec();
|
Some(old.map(|s| s.to_vec()).unwrap_or_else(|| {
|
||||||
value.push(0xff);
|
let mut value = random_string(8).as_bytes().to_vec();
|
||||||
value.extend_from_slice(
|
value.push(0xff);
|
||||||
&ruma::signatures::Ed25519KeyPair::generate()
|
value.extend_from_slice(
|
||||||
.expect("Ed25519KeyPair generation always works (?)"),
|
&ruma::signatures::Ed25519KeyPair::generate()
|
||||||
);
|
.expect("Ed25519KeyPair generation always works (?)"),
|
||||||
value
|
);
|
||||||
|
value
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the bytes into an u64.
|
/// Parses the bytes into an u64.
|
||||||
@@ -84,19 +77,22 @@ pub fn common_elements(
|
|||||||
let mut other_iterators = iterators.map(|i| i.peekable()).collect::<Vec<_>>();
|
let mut other_iterators = iterators.map(|i| i.peekable()).collect::<Vec<_>>();
|
||||||
|
|
||||||
Some(first_iterator.filter(move |target| {
|
Some(first_iterator.filter(move |target| {
|
||||||
other_iterators.iter_mut().all(|it| {
|
other_iterators
|
||||||
while let Some(element) = it.peek() {
|
.iter_mut()
|
||||||
match check_order(element, target) {
|
.map(|it| {
|
||||||
Ordering::Greater => return false, // We went too far
|
while let Some(element) = it.peek() {
|
||||||
Ordering::Equal => return true, // Element is in both iters
|
match check_order(element, target) {
|
||||||
Ordering::Less => {
|
Ordering::Greater => return false, // We went too far
|
||||||
// Keep searching
|
Ordering::Equal => return true, // Element is in both iters
|
||||||
it.next();
|
Ordering::Less => {
|
||||||
|
// Keep searching
|
||||||
|
it.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
false
|
||||||
false
|
})
|
||||||
})
|
.all(|b| b)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user