Compare commits

..
6 Commits
Author SHA1 Message Date
Jonas Platte 42a7a9f795 WIP 2021-04-29 20:50:09 +02:00
Jonas Platte 54a0f21347 WIP 2021-04-29 20:17:14 +02:00
Jonas Platte 54f4fd2cc8 Remove explicit setting of content-type header
`try_into_http_request` takes care of this already.
2021-04-29 20:16:55 +02:00
Jonas Platte ac5dd5e251 Fix formatting 2021-04-29 20:16:52 +02:00
Jonas Platte 6510ffa0fa Fix clippy warnings 2021-04-29 20:16:49 +02:00
Jonas Platte caa186ad22 Bump ruma 2021-04-29 20:15:51 +02:00
66 changed files with 2430 additions and 4358 deletions
-15
View File
@@ -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
-48
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -14
View File
@@ -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
View File
@@ -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="" \
+2 -27
View File
@@ -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)
+2 -4
View File
@@ -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
+10 -10
View File
@@ -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
+33
View File
@@ -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"
+4 -1
View File
@@ -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
+34 -46
View File
@@ -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
CONDUIT_SERVER_NAME="$RET" ROCKET_SERVER_NAME="$RET"
db_get matrix-conduit/address db_get matrix-conduit/address
CONDUIT_ADDRESS="$RET" ROCKET_ADDRESS="$RET"
db_get matrix-conduit/port db_get matrix-conduit/port
CONDUIT_PORT="$RET" ROCKET_PORT="$RET"
mkdir -p "$CONDUIT_CONFIG_PATH" cat >"$CONDUIT_CONFIG_FILE" << EOF
cat > "$CONDUIT_CONFIG_FILE" << EOF # Conduit homeserver Debian configuration
[global] #
# The server_name is the name of this server. It is used as a suffix for user # Conduit is an application based on the Rocket web framework.
# and room ids. Examples: matrix.org, conduit.rs # Configuration of Conduit happens via Debconf (of which the resulting config
# The Conduit server needs to be reachable at https://your.server.name/ on port # is in this file) and optionally by uncommenting and tweaking the variables in
# 443 (client-server) and 8448 (federation) OR you can create /.well-known # /etc/matrix-conduit/local.
# 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
-5
View File
@@ -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."
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+13 -19
View File
@@ -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
+37 -29
View File
@@ -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,11 +22,13 @@ 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 mut parts = http_request.uri().clone().into_parts();
let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned(); let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned();
let symbol = if old_path_and_query.contains('?') { let symbol = if old_path_and_query.contains('?') {
@@ -33,34 +37,38 @@ where
"?" "?"
}; };
parts.path_and_query = Some( parts.path_and_query =
(old_path_and_query + symbol + "access_token=" + hs_token) Some((old_path_and_query + symbol + "access_token=" + hs_token).parse()?);
.parse() *http_request.uri_mut() = parts.try_into()?;
.unwrap(),
);
*http_request.uri_mut() = parts.try_into().expect("our manipulation is always valid");
let mut reqwest_request = reqwest::Request::try_from(http_request) Ok(())
.expect("all http requests are valid reqwest requests"); },
)
.await
.map_err(|e| {
warn!("Appservice error: {}", e);
Error::BadServerResponse("Server returned an error.")
})?;
*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"),
); );
+30 -42
View File
@@ -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));
} }
+11 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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>,
} }
+2 -2
View File
@@ -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");
+7 -19
View File
@@ -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));
} }
+17 -44
View File
@@ -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
Ok::<_, Error>(
serde_json::from_str( serde_json::from_str(
&serde_json::to_string(&c) &serde_json::to_string(&c)
.expect("PublicRoomsChunk::to_string always works"), .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<_>>();
+95 -130
View File
@@ -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,
for (user_id, device_ids) in &body.device_keys {
if device_ids.is_empty() {
let mut container = BTreeMap::new();
for device_id in db.users.all_device_ids(user_id) {
let device_id = device_id?;
if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? {
let metadata = db
.users
.get_device_metadata(user_id, &device_id)?
.ok_or_else(|| {
Error::bad_database("all_device_keys contained nonexistent device.")
})?;
keys.unsigned = UnsignedDeviceInfo {
device_display_name: metadata.display_name,
};
container.insert(device_id, keys);
}
}
device_keys.insert(user_id.clone(), container);
} else {
for device_id in device_ids {
let mut container = BTreeMap::new();
if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? {
let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or(
Error::BadRequest(
ErrorKind::InvalidParam,
"Tried to get keys for nonexistent device.",
),
)?; )?;
Ok(response.into()) keys.unsigned = UnsignedDeviceInfo {
device_display_name: metadata.display_name,
};
container.insert(device_id.clone(), keys);
}
device_keys.insert(user_id.clone(), container);
}
}
if let Some(master_key) = db.users.get_master_key(user_id, sender_user)? {
master_keys.insert(user_id.clone(), master_key);
}
if let Some(self_signing_key) = db.users.get_self_signing_key(user_id, sender_user)? {
self_signing_keys.insert(user_id.clone(), self_signing_key);
}
if user_id == sender_user {
if let Some(user_signing_key) = db.users.get_user_signing_key(sender_user)? {
user_signing_keys.insert(user_id.clone(), user_signing_key);
}
}
}
Ok(get_keys::Response {
master_keys,
self_signing_keys,
user_signing_keys,
device_keys,
failures: BTreeMap::new(),
}
.into())
} }
#[cfg_attr( #[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,
})
}
+17 -36
View File
@@ -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
.create(
mxc.clone(), mxc.clone(),
&db.globals, &body.filename.as_deref(),
&body
.filename
.as_ref()
.map(|filename| "inline; filename=".to_owned() + filename)
.as_deref(),
&body.content_type.as_deref(), &body.content_type.as_deref(),
&body.file, &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,
&db.globals,
&get_content_response.content_disposition.as_deref(), &get_content_response.content_disposition.as_deref(),
&get_content_response.content_type.as_deref(), &get_content_response.content_type.as_deref(),
&get_content_response.file, &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
.get_thumbnail(
mxc.clone(), mxc.clone(),
&db.globals,
body.width body.width
.try_into() .try_into()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
body.height body.height
.try_into() .try_into()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, .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,
&db.globals,
&None, &None,
&get_thumbnail_response.content_type, &get_thumbnail_response.content_type,
body.width.try_into().expect("all UInts are valid u32s"), body.width.try_into().expect("all UInts are valid u32s"),
body.height.try_into().expect("all UInts are valid u32s"), body.height.try_into().expect("all UInts are valid u32s"),
&get_thumbnail_response.file, &get_thumbnail_response.file,
) )?;
.await?;
Ok(get_thumbnail_response.into()) Ok(get_thumbnail_response.into())
} else { } else {
+75 -74
View File
@@ -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,9 +244,8 @@ 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."))?;
@@ -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| {
Ok::<_, Error>(
serde_json::from_value::<Raw<CreateEventContent>>(create_event.content.clone()) serde_json::from_value::<Raw<CreateEventContent>>(create_event.content.clone())
.expect("Raw::from_value always works.") .expect("Raw::from_value always works.")
.deserialize() .deserialize()
.map_err(|_| Error::bad_database("Invalid PowerLevels event in db.")) .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"),
+8 -4
View File
@@ -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,7 +56,12 @@ 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(
body.json_body
.as_ref()
.ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?
.get(),
)
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?, .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
unsigned: Some(unsigned), unsigned: Some(unsigned),
state_key: None, state_key: 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");
+3 -3
View File
@@ -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("/<_..>")]
+4 -49
View File
@@ -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!();
}
}
+8 -10
View File
@@ -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
View File
@@ -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");
+27 -21
View File
@@ -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(
ruma::events::receipt::ReceiptEvent {
content: ruma::events::receipt::ReceiptEventContent(receipt_content), content: ruma::events::receipt::ReceiptEventContent(receipt_content),
room_id: body.room_id.clone(), 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(
ruma::events::receipt::ReceiptEvent {
content: ruma::events::receipt::ReceiptEventContent(receipt_content), content: ruma::events::receipt::ReceiptEventContent(receipt_content),
room_id: body.room_id.clone(), room_id: body.room_id.clone(),
}), },
)),
&db.globals, &db.globals,
)?; )?;
+1 -2
View File
@@ -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
View File
@@ -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()
+9 -37
View File
@@ -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,26 +13,18 @@ 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();
for room_id in room_ids {
if !db.rooms.is_joined(sender_user, &room_id)? { if !db.rooms.is_joined(sender_user, &room_id)? {
return Err(Error::BadRequest( return Err(Error::BadRequest(
ErrorKind::Forbidden, ErrorKind::Forbidden,
@@ -41,13 +32,6 @@ pub async fn search_events_route(
)); ));
} }
let search = db
.rooms
.search_pdus(&room_id, &search_criteria.search_term)?;
searches.push(search.0.peekable());
}
let skip = match body.next_batch.as_ref().map(|s| s.parse()) { let skip = match body.next_batch.as_ref().map(|s| s.parse()) {
Some(Ok(s)) => s, Some(Ok(s)) => s,
Some(Err(_)) => { Some(Err(_)) => {
@@ -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())
+6 -6
View File
@@ -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,14 +195,16 @@ 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) {
if let Ok(device_id) = device_id {
db.users.remove_device(&sender_user, &device_id)?; db.users.remove_device(&sender_user, &device_id)?;
} }
}
db.flush().await?; db.flush().await?;
+10 -12
View File
@@ -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())
+52 -183
View File
@@ -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| {
Ok::<_, Error>(
since_shortstatehash since_shortstatehash
.map(|since_shortstatehash| db.rooms.state_full(since_shortstatehash)) .map(|since_shortstatehash| db.rooms.state_full(since_shortstatehash))
.transpose() .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)
} }
+6 -6
View File
@@ -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");
+11 -11
View File
@@ -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) => {
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,
)?, )?
}
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 -3
View File
@@ -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");
+6 -12
View File
@@ -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;
+80 -183
View File
@@ -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
// 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.admin.start_handler(db.clone(), admin_receiver);
}
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
-329
View File
@@ -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();
})
}
}
+31 -28
View File
@@ -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()
.map(|(k, _)| {
k.rsplit(|&b| b == 0xff) k.rsplit(|&b| b == 0xff)
.next() .next()
.map(|current_event_type| current_event_type == kind.as_ref().as_bytes()) .map(|current_event_type| {
current_event_type == kind.as_ref().as_bytes()
})
.unwrap_or(false) .unwrap_or(false)
})) })
.unwrap_or(false)
})
.map(|r| Ok(r?))
} }
} }
+5 -11
View File
@@ -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,7 +73,7 @@ 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 ({}): {}",
@@ -84,9 +81,6 @@ impl Admin {
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
View File
@@ -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"),
)) ))
})) })
} }
} }
+65 -116
View File
@@ -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();
for item in self.servertimeout_signingkey.scan_prefix(origin.as_bytes()) {
let (k, bytes) = item?;
let valid_until = k
.splitn(2, |&b| b == 0xff)
.nth(1)
.map(crate::utils::u64_from_bytes)
.ok_or_else(|| Error::bad_database("Invalid signing keys."))?
.map_err(|_| Error::bad_database("Invalid signing key valid until bytes"))?;
// If these keys are still valid use em!
if valid_until > now {
let btree: BTreeMap<_, _> = serde_json::from_slice(&bytes)
.map_err(|_| Error::bad_database("Invalid BTreeMap<> of signing keys"))?;
response.extend(btree);
} }
pub fn database_version(&self) -> Result<u64> {
self.globals.get(b"version")?.map_or(Ok(0), |version| {
utils::u64_from_bytes(&version)
.map_err(|_| Error::bad_database("Database version id is invalid."))
})
} }
Ok(response)
pub fn bump_database_version(&self, new_version: u64) -> Result<()> {
self.globals
.insert(b"version", &new_version.to_be_bytes())?;
Ok(())
}
pub fn get_media_folder(&self) -> PathBuf {
let mut r = PathBuf::new();
r.push(self.config.database_path.clone());
r.push("media");
r
}
pub fn get_media_file(&self, key: &[u8]) -> PathBuf {
let mut r = PathBuf::new();
r.push(self.config.database_path.clone());
r.push("media");
r.push(base64::encode_config(key, base64::URL_SAFE_NO_PAD));
r
} }
} }
+67 -57
View File
@@ -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,11 +117,9 @@ 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."))
}) })
} }
@@ -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)
.map(|(key, value)| {
let mut parts = key.rsplit(|&b| b == 0xff); 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()
.ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?,
)
.map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?, .map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?,
) )
.map_err(|_| { .map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid room id."))?;
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
View File
@@ -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
View File
@@ -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());
+283 -307
View File
File diff suppressed because it is too large Load Diff
+58 -114
View File
@@ -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,9 +122,7 @@ 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)?
.map_or(Ok(None), |v| {
Ok(Some(utils::u64_from_bytes(&v).map_err(|_| { Ok(Some(utils::u64_from_bytes(&v).map_err(|_| {
Error::bad_database("Invalid private read marker bytes") Error::bad_database("Invalid private read marker bytes")
})?)) })?))
@@ -214,10 +191,11 @@ impl RoomEdus {
// Maybe there are multiple ones from calling roomtyping_add multiple times // 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| {
Ok::<_, Error>(
UserId::try_from(utils::string_from_bytes(&user_id?).map_err(|_| {
Error::bad_database("User ID in typingid_userid is invalid unicode.") Error::bad_database("User ID in typingid_userid is invalid unicode.")
})?) })?)
.map_err(|_| Error::bad_database("User ID in typingid_userid is invalid.")) .map_err(|_| Error::bad_database("User ID in typingid_userid is invalid."))?,
)
}) })
{ {
user_ids.insert(user_id?); 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(
+251 -346
View File
@@ -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,9 +28,62 @@ pub enum OutgoingKind {
Normal(Box<ServerName>), Normal(Box<ServerName>),
} }
impl OutgoingKind { #[derive(Clone)]
pub fn get_prefix(&self) -> Vec<u8> { pub struct Sending {
let mut prefix = match self { /// The state for a given state hash.
pub(super) servernamepduids: sled::Tree, // ServernamePduId = (+ / $)SenderKey / ServerName / UserId + PduId
pub(super) servercurrentpdus: sled::Tree, // ServerCurrentPdus = (+ / $)ServerName / UserId + PduId
pub(super) maximum_requests: Arc<Semaphore>,
}
enum TransactionStatus {
Running,
Failed(u32, Instant), // number of times failed, time of last failure
Retrying(u32), // number of times failed
}
impl Sending {
pub fn start_handler(&self, db: &Database) {
let servernamepduids = self.servernamepduids.clone();
let servercurrentpdus = self.servercurrentpdus.clone();
let db = db.clone();
tokio::spawn(async move {
let mut futures = FuturesUnordered::new();
// Retry requests we could not finish yet
let mut subscriber = servernamepduids.watch_prefix(b"");
let mut current_transaction_status = HashMap::<Vec<u8>, TransactionStatus>::new();
let mut initial_transactions = HashMap::<OutgoingKind, Vec<Vec<u8>>>::new();
for (key, outgoing_kind, pdu) in servercurrentpdus
.iter()
.filter_map(|r| r.ok())
.filter_map(|(key, _)| {
Self::parse_servercurrentpdus(&key)
.ok()
.map(|(k, p)| (key, k, p.to_vec()))
})
{
let entry = initial_transactions
.entry(outgoing_kind.clone())
.or_insert_with(Vec::new);
if entry.len() > 30 {
warn!(
"Dropping some current pdu: {:?} {:?} {:?}",
key, outgoing_kind, pdu
);
servercurrentpdus.remove(key).unwrap();
continue;
}
entry.push(pdu);
}
for (outgoing_kind, pdus) in initial_transactions {
let mut prefix = match &outgoing_kind {
OutgoingKind::Appservice(server) => { OutgoingKind::Appservice(server) => {
let mut p = b"+".to_vec(); let mut p = b"+".to_vec();
p.extend_from_slice(server.as_bytes()); p.extend_from_slice(server.as_bytes());
@@ -63,71 +103,8 @@ impl OutgoingKind {
} }
}; };
prefix.push(0xff); prefix.push(0xff);
current_transaction_status.insert(prefix, TransactionStatus::Running);
prefix futures.push(Self::handle_event(outgoing_kind.clone(), pdus, &db));
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum SendingEventType {
Pdu(Vec<u8>),
Edu(Vec<u8>),
}
pub struct Sending {
/// The state for a given state hash.
pub(super) servername_educount: Arc<dyn Tree>, // EduCount: Count of last EDU sync
pub(super) servernamepduids: Arc<dyn Tree>, // ServernamePduId = (+ / $)SenderKey / ServerName / UserId + PduId
pub(super) servercurrentevents: Arc<dyn Tree>, // ServerCurrentEvents = (+ / $)ServerName / UserId + PduId / (*)EduEvent
pub(super) maximum_requests: Arc<Semaphore>,
pub sender: mpsc::UnboundedSender<Vec<u8>>,
}
enum TransactionStatus {
Running,
Failed(u32, Instant), // number of times failed, time of last failure
Retrying(u32), // number of times failed
}
impl Sending {
pub fn start_handler(&self, db: Arc<Database>, mut receiver: mpsc::UnboundedReceiver<Vec<u8>>) {
tokio::spawn(async move {
let mut futures = FuturesUnordered::new();
let mut current_transaction_status = HashMap::<Vec<u8>, TransactionStatus>::new();
// Retry requests we could not finish yet
let mut initial_transactions = HashMap::<OutgoingKind, Vec<SendingEventType>>::new();
for (key, outgoing_kind, event) in
db.sending
.servercurrentevents
.iter()
.filter_map(|(key, _)| {
Self::parse_servercurrentevent(&key)
.ok()
.map(|(k, e)| (key, k, e))
})
{
let entry = initial_transactions
.entry(outgoing_kind.clone())
.or_insert_with(Vec::new);
if entry.len() > 30 {
warn!(
"Dropping some current events: {:?} {:?} {:?}",
key, outgoing_kind, event
);
db.sending.servercurrentevents.remove(&key).unwrap();
continue;
}
entry.push(event);
}
for (outgoing_kind, events) in initial_transactions {
current_transaction_status
.insert(outgoing_kind.get_prefix(), TransactionStatus::Running);
futures.push(Self::handle_events(outgoing_kind.clone(), events, &db));
} }
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(&current_key, &[]).unwrap();
SendingEventType::Edu(b) => { servernamepduids.remove(&current_key).unwrap();
current_key.extend_from_slice(&b);
db.sending.servercurrentevents.insert(&current_key, &[]).unwrap();
db.sending.servernamepduids.remove(&current_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,45 +205,57 @@ 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();
}
}
}
}
}
});
}
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 retry = false;
if let Some((outgoing_kind, prefix, pdu_id)) = Self::parse_servercurrentpdus(&servernamepduid)
.ok()
.map(|(outgoing_kind, pdu_id)| {
let mut prefix = match &outgoing_kind {
OutgoingKind::Appservice(serv) => {
let mut p = b"+".to_vec();
p.extend_from_slice(serv.as_bytes());
p
},
OutgoingKind::Push(user, pushkey) => {
let mut p = b"$".to_vec();
p.extend_from_slice(&user);
p.push(0xff);
p.extend_from_slice(&pushkey);
p
},
OutgoingKind::Normal(serv) => {
let mut p = vec![];
p.extend_from_slice(serv.as_bytes());
p
},
};
prefix.push(0xff);
(outgoing_kind, prefix, pdu_id)
})
.filter(|(_, prefix, _)| {
let entry = current_transaction_status.entry(prefix.clone());
let mut allow = true; let mut allow = true;
let prefix = outgoing_kind.get_prefix(); entry.and_modify(|e| match e {
let entry = current_transaction_status.entry(prefix.clone());
entry
.and_modify(|e| match e {
TransactionStatus::Running | TransactionStatus::Retrying(_) => { TransactionStatus::Running | TransactionStatus::Retrying(_) => {
allow = false; // already running allow = false; // already running
} },
TransactionStatus::Failed(tries, time) => { TransactionStatus::Failed(tries, time) => {
// Fail if a request has failed recently (exponential backoff) // Fail if a request has failed recently (exponential backoff)
let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries); let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries);
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) { if min_elapsed_duration > Duration::from_secs(60*60*24) {
min_elapsed_duration = Duration::from_secs(60 * 60 * 24); min_elapsed_duration = Duration::from_secs(60*60*24);
} }
if time.elapsed() < min_elapsed_duration { if time.elapsed() < min_elapsed_duration {
@@ -238,132 +265,53 @@ impl Sending {
*e = TransactionStatus::Retrying(*tries); *e = TransactionStatus::Retrying(*tries);
} }
} }
}).or_insert(TransactionStatus::Running);
allow
}) })
.or_insert(TransactionStatus::Running); {
let mut pdus = Vec::new();
if !allow {
return Ok(None);
}
let mut events = Vec::new();
if retry { if retry {
// We retry the previous transaction // We retry the previous transaction
for (key, _) in db.sending.servercurrentevents.scan_prefix(prefix) { for pdu in servercurrentpdus
if let Ok((_, e)) = Self::parse_servercurrentevent(&key) { .scan_prefix(&prefix)
events.push(e); .filter_map(|r| r.ok())
} .filter_map(|(key, _)| {
Self::parse_servercurrentpdus(&key)
.ok()
.map(|(_, p)| p.to_vec())
})
{
pdus.push(pdu);
} }
} else { } else {
for (e, full_key) in new_events { servercurrentpdus.insert(&key, &[]).unwrap();
db.sending.servercurrentevents.insert(&full_key, &[])?; servernamepduids.remove(&key).unwrap();
pdus.push(pdu_id.to_vec());
// 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);
} }
futures.push(
if let OutgoingKind::Normal(server_name) = outgoing_kind { Self::handle_event(
if let Ok((select_edus, last_count)) = Self::select_edus(db, server_name) { outgoing_kind,
events.extend_from_slice(&select_edus); pdus,
db.sending &db,
.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) => {
pdus.push(
db.rooms db.rooms
.get_pdu_from_id(&pdu_id) .get_pdu_from_id(pdu_id)
.map_err(|e| (kind.clone(), e))? .map_err(|e| (pushkey.clone(), e))?
.ok_or_else(|| { .ok_or_else(|| {
( (
kind.clone(), pushkey.clone(),
Error::bad_database( Error::bad_database(
"[Push] Event in servernamepduids not found in db.", "[Push] Event in servernamepduids not found in db.",
), ),
) )
})?, })?,
); )
} })
SendingEventType::Edu(_) => { .filter_map(|r| r.ok())
// Push gateways don't need EDUs (?) .collect::<Vec<_>>();
}
}
}
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())
},
) )
}) })
} }
+6 -8
View File
@@ -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
View File
@@ -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)
} }
} }
+143 -126
View File
@@ -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(
UserId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| {
Error::bad_database("User ID in userid_password is invalid unicode.") 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,11 +116,9 @@ 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 self.userid_password.insert(user_id.to_string(), &*hash)?;
.insert(user_id.as_bytes(), hash.as_bytes())?;
Ok(()) Ok(())
} else { } else {
Err(Error::BadRequest( Err(Error::BadRequest(
@@ -121,16 +126,12 @@ impl Users {
"Password does not meet the requirements.", "Password does not meet the requirements.",
)) ))
} }
} else {
self.userid_password.insert(user_id.as_bytes(), b"")?;
Ok(())
}
} }
/// Returns the displayname of a user on this homeserver. /// 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,13 +384,14 @@ 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()
.map(|bytes| {
Ok::<_, Error>( Ok::<_, Error>(
serde_json::from_slice::<DeviceKeyId>( serde_json::from_slice::<DeviceKeyId>(
&*bytes.rsplit(|&b| b == 0xff).next().ok_or_else(|| { &*bytes?.rsplit(|&b| b == 0xff).next().ok_or_else(|| {
Error::bad_database("OneTimeKey ID in db is invalid.") Error::bad_database("OneTimeKey ID in db is invalid.")
})?, })?,
) )
@@ -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)| {
Ok(
UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| { UserId::try_from(utils::string_from_bytes(&bytes).map_err(|_| {
Error::bad_database("User ID in devicekeychangeid_userid is invalid unicode.") Error::bad_database(
"User ID in devicekeychangeid_userid is invalid unicode.",
)
})?) })?)
.map_err(|_| Error::bad_database("User ID in devicekeychangeid_userid is invalid.")) .map_err(|_| {
Error::bad_database("User ID in devicekeychangeid_userid is invalid.")
})?,
)
}) })
} }
@@ -613,13 +626,13 @@ impl Users {
key.push(0xff); key.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
View File
@@ -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)
} }
} }
-2
View File
@@ -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;
+6 -15
View File
@@ -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); let rocket = setup_rocket(raw_config, db);
rocket.launch().await.unwrap(); rocket.launch().await.unwrap();
}
} }
#[catch(404)] #[catch(404)]
+12 -14
View File
@@ -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()
} }
+53 -96
View File
@@ -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,12 +125,10 @@ 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| {
s.split_terminator(',')
.map(|field| { .map(|field| {
let mut splits = field.splitn(2, '='); let mut splits = field.splitn(2, '=');
(splits.next(), splits.next().map(|s| s.trim_matches('"'))) (splits.next(), splits.next().map(|s| s.trim_matches('"')))
@@ -197,10 +187,12 @@ where
} }
}; };
let json_body = serde_json::from_slice::<CanonicalJsonValue>(&body);
let mut request_map = BTreeMap::<String, CanonicalJsonValue>::new(); 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,8 +227,11 @@ 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,
&origin,
vec![&key.to_owned()],
)
.await .await
{ {
Ok(b) => b, Ok(b) => b,
@@ -252,7 +247,7 @@ where
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,8 +303,22 @@ 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
impl<T: OutgoingResponse> From<T> for RumaResponse<T> {
fn from(t: T) -> Self {
Self(t)
}
}
#[cfg(feature = "conduit_bin")]
impl<'r, 'o, T> Responder<'r, 'o> for RumaResponse<T>
where
T: Send + OutgoingResponse,
'o: 'r,
{
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
let http_response = self
.0 .0
.try_into_http_response::<Vec<u8>>() .try_into_http_response::<Vec<u8>>()
.map_err(|_| Status::InternalServerError)?; .map_err(|_| Status::InternalServerError)?;
@@ -365,30 +347,5 @@ pub fn response<T: OutgoingResponse>(response: RumaResponse<T>) -> response::Res
); );
response.raw_header("Access-Control-Max-Age", "86400"); response.raw_header("Access-Control-Max-Age", "86400");
response.ok() response.ok()
}
#[derive(Clone)]
pub struct RumaResponse<T>(pub T);
impl<T> From<T> for RumaResponse<T> {
fn from(t: T) -> Self {
Self(t)
}
}
impl From<Error> for RumaResponse<UiaaResponse> {
fn from(t: Error) -> Self {
t.to_response()
}
}
#[cfg(feature = "conduit_bin")]
impl<'r, 'o, T> Responder<'r, 'o> for RumaResponse<T>
where
'o: 'r,
T: OutgoingResponse,
{
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
response(self)
} }
} }
+173 -542
View File
File diff suppressed because it is too large Load Diff
+7 -11
View File
@@ -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,7 +27,8 @@ 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>> {
Some(old.map(|s| s.to_vec()).unwrap_or_else(|| {
let mut value = random_string(8).as_bytes().to_vec(); let mut value = random_string(8).as_bytes().to_vec();
value.push(0xff); value.push(0xff);
value.extend_from_slice( value.extend_from_slice(
@@ -44,6 +36,7 @@ pub fn generate_keypair() -> Vec<u8> {
.expect("Ed25519KeyPair generation always works (?)"), .expect("Ed25519KeyPair generation always works (?)"),
); );
value value
}))
} }
/// Parses the bytes into an u64. /// Parses the bytes into an u64.
@@ -84,7 +77,9 @@ pub fn common_elements(
let mut other_iterators = iterators.map(|i| i.peekable()).collect::<Vec<_>>(); 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
.iter_mut()
.map(|it| {
while let Some(element) = it.peek() { while let Some(element) = it.peek() {
match check_order(element, target) { match check_order(element, target) {
Ordering::Greater => return false, // We went too far Ordering::Greater => return false, // We went too far
@@ -97,6 +92,7 @@ pub fn common_elements(
} }
false false
}) })
.all(|b| b)
})) }))
} }