Compare commits

...
This repository has been archived on 2025-08-14. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.

31 commits

Author SHA1 Message Date
strawberry
59e764b4a6 add missing ban reason to ban events
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 18:43:05 -05:00
strawberry
e35627cb54 raise generated passwords to 25 chars
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 14:47:24 -05:00
strawberry
749b0348c9 partially revert keeping track of remote user profiles
this seems to require some more work to properly ignore
dead server errors without breaking the entire room join

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 01:58:06 -05:00
strawberry
5fdcb69ea1 make set avatar, displayname, and blurhash async and forgot another let _
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 01:42:37 -05:00
strawberry
2da710bb3c ignore potential errors when updating user profiles
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 01:24:49 -05:00
strawberry
ed28378c41 update DIFFERENCES.md
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 01:00:52 -05:00
strawberry
bb34cedda1 remove cached destination for a destination if request fails
this can help if users change their well-known or such and we don't want
to keep on hitting the old destination.

from 11357d1f1a

Co-authored-by: Jacob Taylor <jacob@explodie.org>
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 00:52:34 -05:00
strawberry
deac34cc43 oops dedup only works on consecutive elements
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-14 00:03:59 -05:00
strawberry
7d44f1083e don't validate or add signature if room is not v8 or above or not using restricted joins
should resolve https://github.com/matrix-org/matrix-spec/issues/1708 on
for conduwuit until spec clarifies.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 23:44:02 -05:00
strawberry
ac389084f6 replace panics on unknown room versions with errors
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 23:43:53 -05:00
strawberry
932dc35c1f don't allow non-local users to have their creds modified in Deactivate admin cmds
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 21:13:45 -05:00
strawberry
349c804436 dedup servers in get_alias_helper
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 18:23:56 -05:00
strawberry
856eae7686 only follow up to 6 redirects in default reqwest ClientBuilder
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 18:09:37 -05:00
strawberry
a3d9382389 debug log well-known response body and text
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 18:08:50 -05:00
strawberry
d1da1ae790 declare support for Matrix 1.5 in our federation requests
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 18:08:23 -05:00
strawberry
eec672a55a update rocksdb to 8.10.0
https://github.com/rust-rocksdb/rust-rocksdb/pull/852

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 15:18:23 -05:00
strawberry
8d7f00d88f DIFFERENCES.md: suggest servers to join via at room directory
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 14:29:45 -05:00
strawberry
7f5de5968b add all possible workspace clippy lints (with commenting out most for now)
this will be a major pain to work through. for now, let's
just add them and overtime work through these.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 14:29:18 -05:00
strawberry
33f583c906 feat: suggest servers to join at /_matrix/client/v3/directory/room/{roomAlias}
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-13 14:10:04 -05:00
strawberry
163f9113e8 deps updates again
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-12 19:27:16 -05:00
strawberry
fcebcf94b6 oops forgot to bump 1.70.0 in flake and gitlab dockerfile
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 21:30:32 -05:00
strawberry
ab63447043 we also support webp images
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 21:13:10 -05:00
strawberry
6196757ba1 nix: update flake
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:48:14 -05:00
strawberry
c551402b44 add more stuff to DIFFERENCES.md
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:47:59 -05:00
strawberry
36671b6172 the great persy, sled, and heed purge (and bump MSRV to 1.74.1)
these database backends are either unmaintained, broken in conduit, or
incredibly niche for something like conduwuit.

also i want to bump the MSRV.

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:45:48 -05:00
strawberry
eb4944df2a feat: keep track of remote profiles for user directory and local requests
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:39:37 -05:00
strawberry
6c5b13b9db declare explicit support for room filtering (MSC3827)
conduit has supported this for a while now and is a Matrix 1.4 feature

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:26:23 -05:00
strawberry
eae084c1bd bump default_presence_offline_timeout to 30 minutes (too low imo)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:24:09 -05:00
strawberry
74be41c5de bump conduwuit version as we've made a lot of changes
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-11 20:21:50 -05:00
strawberry
0a5095b575 allow HEAD HTTP requests in CORS
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-09 21:02:57 -05:00
strawberry
98f83d1d0c update device lists for user upon logout
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-01-09 19:28:17 -05:00
40 changed files with 507 additions and 902 deletions

View file

@ -24,7 +24,7 @@ jobs:
run: rm -rf "$HOME/.rustup" "$HOME/.cargo"
- name: Install Nix (with flakes and nix-command enabled)
uses: cachix/install-nix-action@v24
uses: cachix/install-nix-action@v25
with:
nix_path: nixpkgs=channel:nixos-unstable

View file

@ -116,7 +116,7 @@ docker build debugging:
cargo check:
stage: test
image: docker.io/rust:1.70.0-bullseye
image: docker.io/rust:1.74.1-bullseye
needs: []
interruptible: true
before_script:

169
Cargo.lock generated
View file

@ -205,9 +205,9 @@ dependencies = [
[[package]]
name = "base64"
version = "0.21.5"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
@ -215,15 +215,6 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.69.1"
@ -347,9 +338,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.13"
version = "4.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445"
dependencies = [
"clap_builder",
"clap_derive",
@ -357,9 +348,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.12"
version = "4.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb"
dependencies = [
"anstyle",
"clap_lex",
@ -391,7 +382,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "conduwuit"
version = "0.7.0-alpha+conduwuit-0.1.1"
version = "0.7.0-alpha+conduwuit-0.1.2"
dependencies = [
"argon2",
"async-trait",
@ -404,7 +395,6 @@ dependencies = [
"directories",
"figment",
"futures-util",
"heed",
"hmac",
"http",
"hyper",
@ -421,7 +411,6 @@ dependencies = [
"opentelemetry-jaeger",
"opentelemetry_sdk",
"parking_lot",
"persy",
"rand",
"regex",
"reqwest",
@ -488,21 +477,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
[[package]]
name = "crc32fast"
version = "1.3.2"
@ -792,16 +766,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "futures-channel"
version = "0.3.28"
@ -991,42 +955,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heed"
version = "0.10.6"
source = "git+https://github.com/timokoesters/heed.git?rev=f6f825da7fb2c758867e05ad973ef800a6fe1d5d#f6f825da7fb2c758867e05ad973ef800a6fe1d5d"
dependencies = [
"bytemuck",
"byteorder",
"heed-traits",
"heed-types",
"libc",
"lmdb-rkv-sys",
"once_cell",
"page_size",
"serde",
"synchronoise",
"url",
]
[[package]]
name = "heed-traits"
version = "0.7.0"
source = "git+https://github.com/timokoesters/heed.git?rev=f6f825da7fb2c758867e05ad973ef800a6fe1d5d#f6f825da7fb2c758867e05ad973ef800a6fe1d5d"
[[package]]
name = "heed-types"
version = "0.7.2"
source = "git+https://github.com/timokoesters/heed.git?rev=f6f825da7fb2c758867e05ad973ef800a6fe1d5d#f6f825da7fb2c758867e05ad973ef800a6fe1d5d"
dependencies = [
"bincode",
"bytemuck",
"byteorder",
"heed-traits",
"serde",
"serde_json",
]
[[package]]
name = "hermit-abi"
version = "0.3.2"
@ -1347,8 +1275,8 @@ dependencies = [
[[package]]
name = "librocksdb-sys"
version = "0.15.0+8.9.1"
source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=66f04df013b6e6bd42b5a8c353406e09a7c7da2a#66f04df013b6e6bd42b5a8c353406e09a7c7da2a"
version = "0.16.0+8.10.0"
source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=8fccdf5473e3e75a5ce0f42e5ff5e89c2012305b#8fccdf5473e3e75a5ce0f42e5ff5e89c2012305b"
dependencies = [
"bindgen",
"bzip2-sys",
@ -1389,17 +1317,6 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lmdb-rkv-sys"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61b9ce6b3be08acefa3003c57b7565377432a89ec24476bbe72e11d101f852fe"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "lock_api"
version = "0.4.10"
@ -1703,16 +1620,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "page_size"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1798,22 +1705,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "persy"
version = "1.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd38c602b23c2f451842d89f27cd5e0d4b292176daf40feeda859c658dcdc76"
dependencies = [
"crc",
"data-encoding",
"fs2",
"linked-hash-map",
"rand",
"thiserror",
"unsigned-varint",
"zigzag",
]
[[package]]
name = "pin-project"
version = "1.1.3"
@ -2119,7 +2010,7 @@ dependencies = [
[[package]]
name = "rocksdb"
version = "0.21.0"
source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=66f04df013b6e6bd42b5a8c353406e09a7c7da2a#66f04df013b6e6bd42b5a8c353406e09a7c7da2a"
source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=8fccdf5473e3e75a5ce0f42e5ff5e89c2012305b#8fccdf5473e3e75a5ce0f42e5ff5e89c2012305b"
dependencies = [
"libc",
"librocksdb-sys",
@ -2465,18 +2356,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "serde"
version = "1.0.194"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.194"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
@ -2498,9 +2389,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.109"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
@ -2540,9 +2431,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.9.29"
version = "0.9.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129"
checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38"
dependencies = [
"indexmap 2.0.0",
"itoa",
@ -2721,15 +2612,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "synchronoise"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dbc01390fc626ce8d1cffe3376ded2b72a11bb70e1c75f404a210e4daa4def2"
dependencies = [
"crossbeam-queue",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
@ -3228,12 +3110,6 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
[[package]]
name = "unsigned-varint"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105"
[[package]]
name = "untrusted"
version = "0.7.1"
@ -3528,15 +3404,6 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
[[package]]
name = "zigzag"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70b40401a28d86ce16a330b863b86fd7dbee4d7c940587ab09ab8c019f9e3fdf"
dependencies = [
"num-traits",
]
[[package]]
name = "zstd"
version = "0.13.0"

View file

@ -6,14 +6,14 @@ authors = ["strawberry <strawberry@puppygock.gay>", "timokoesters <timo@koesters
homepage = "https://puppygock.gay/conduwuit"
repository = "https://gitlab.com/girlbossceo/conduwuit"
readme = "README.md"
version = "0.7.0-alpha+conduwuit-0.1.1"
version = "0.7.0-alpha+conduwuit-0.1.2"
edition = "2021"
# When changing this, make sure to update the `flake.lock` file by running
# `nix flake update`. If you don't have Nix installed or otherwise don't know
# how to do this, ping `@charles:computer.surgery` or `@dusk:gaze.systems` in
# the matrix room.
rust-version = "1.70.0"
rust-version = "1.74.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -37,10 +37,6 @@ hyperlocal = { git = "https://github.com/softprops/hyperlocal", rev = "2ee4d1496
hyper = { version = "0.14", features = ["server", "http1", "http2"] }
tokio = { version = "1.35.1", features = ["fs", "macros", "signal", "sync"] }
loole = "0.3"
# Used for storing data permanently
#sled = { version = "0.34.7", features = ["compression", "no_metrics"], optional = true }
#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] }
persy = { version = "1.4.7", optional = true, features = ["background_ops"] }
# Used for the http request / response body type for Ruma endpoints used with reqwest
bytes = "1.5.0"
@ -48,11 +44,11 @@ http = "0.2.11"
# Used to find data directory for default db path
directories = "5.0.1"
# Used for ruma wrapper
serde_json = { version = "1.0.109", features = ["raw_value"] }
serde_json = { version = "1.0.111", features = ["raw_value"] }
# Used for appservice registration files
serde_yaml = "0.9.29"
serde_yaml = "0.9.30"
# Used for pdu definition
serde = { version = "1.0.194", features = ["rc"] }
serde = { version = "1.0.195", features = ["rc"] }
# Used for secure identifiers
rand = "0.8.5"
# Used to hash passwords
@ -63,7 +59,7 @@ thiserror = "1.0.56"
# Used to generate thumbnails for images
image = { version = "0.24.7", default-features = false, features = ["jpeg", "png", "gif", "webp"] }
# Used to encode server public key
base64 = "0.21.5"
base64 = "0.21.7"
# Used when hashing the state
ring = "0.17.7"
# Used when querying the SRV record of other servers
@ -86,11 +82,10 @@ parking_lot = { version = "0.12.1", optional = true }
crossbeam = { version = "0.8.3", optional = true }
num_cpus = "1.16.0"
threadpool = "1.8.1"
heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true }
# Used for ruma wrapper
serde_html_form = "0.2.3"
rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a", default-features = false, features = ["multi-threaded-cf", "snappy", "lz4", "zstd"], optional = true }
rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "8fccdf5473e3e75a5ce0f42e5ff5e89c2012305b", default-features = false, features = ["multi-threaded-cf", "snappy", "lz4", "zstd"], optional = true }
thread_local = "1.1.7"
# used for TURN server authentication
@ -98,7 +93,7 @@ hmac = "0.12.1"
sha-1 = "0.10.1"
sha2 = { version = "0.10.8" }
# used for conduit's CLI and admin room command parsing
clap = { version = "4.4.13", default-features = false, features = ["std", "derive", "help", "usage", "error-context"] }
clap = { version = "4.4.16", default-features = false, features = ["std", "derive", "help", "usage", "error-context"] }
futures-util = { version = "0.3.30", default-features = false }
# Used for reading the configuration from conduit.toml & environment variables
figment = { version = "0.10.13", features = ["env", "toml"] }
@ -118,10 +113,7 @@ nix = { version = "0.27.1", features = ["resource"] }
[features]
default = ["conduit_bin", "backend_rocksdb", "systemd", "zstd_compression"]
#backend_sled = ["sled"]
backend_persy = ["persy", "parking_lot"]
backend_sqlite = ["sqlite"]
#backend_heed = ["heed", "crossbeam"]
backend_rocksdb = ["rocksdb"]
jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"]
sqlite = ["rusqlite", "parking_lot", "tokio/signal"]
@ -191,3 +183,52 @@ codegen-units=1
debug = 0
opt-level = 3
codegen-units=1
[lints]
workspace = true
[workspace.lints.rust]
# missing_abi = "warn"
# missing_docs = "warn"
# noop_method_call = "warn"
# pointer_structural_match = "warn"
# unreachable_pub = "warn"
# unused_extern_crates = "warn"
# unused_import_braces = "warn"
# unused_lifetimes = "warn"
unused_qualifications = "warn"
# unused_tuple_struct_fields = "warn"
[workspace.lints.clippy]
suspicious = "warn" # assume deny in practice
perf = "warn" # assume deny in practice
# redundant_clone = "warn"
# cloned_instead_of_copied = "warn"
expl_impl_clone_on_copy = "warn"
# pedantic = "warn"
# as_conversions = "warn"
dbg_macro = "warn"
# empty_structs_with_brackets = "warn"
# get_unwrap = "warn"
# if_then_some_else_none = "warn"
# let_underscore_must_use = "warn"
# map_err_ignore = "warn"
# missing_docs_in_private_items = "warn"
# negative_feature_names = "warn"
# pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
# redundant_feature_names = "warn"
# redundant_type_annotations = "warn"
# ref_patterns = "warn"
# rest_pat_in_fully_bound_structs = "warn"
# str_to_string = "warn"
# string_add = "warn"
# string_slice = "warn"
# string_to_string = "warn"
tests_outside_test_module = "warn"
undocumented_unsafe_blocks = "warn"
# unneeded_field_pattern = "warn"
# unseparated_literal_suffix = "warn"
# unwrap_used = "warn"
# wildcard_dependencies = "warn"

View file

@ -180,7 +180,7 @@ allow_check_for_updates = true
trusted_servers = ["matrix.org"]
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
#log = "warn,state_res=warn,rocket=off,_=off,sled=off"
#log = "warn,state_res=warn,rocket=off,_=off"
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.

View file

@ -40,3 +40,15 @@
- Add non-standard sliding sync proxy health check (?) endpoint at `/client/server.json` that some clients such as Element Web query using the `well_known_client` or `well_known_server` config options
- Send a User-Agent on all of our requests (`conduwuit/0.7.0-alpha+conduwuit-0.1.1`) which strangely was not done upstream since forever. Some providers consider no User-Agent suspicious and block said requests.
- Safer and cleaner shutdowns on both database side as we run cleanup on shutdown and exits database loop better (no potential hanging issues in database loop), overall cleaner shutdown logic
- Basic binary commands like `conduwuit --version` work (interested in expanding it more)
- Allow HEAD HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
- Bump MSRV to 1.74.1
- Purge unmaintained/irrelevant/broken database backends (heed, sled, persy)
- webp support for images
- Support for suggesting servers to join at `/_matrix/client/v3/directory/room/{roomAlias}`
- Prevent admin credential commands like reset password and deactivate user from modifying non-local users (https://gitlab.com/famedly/conduit/-/issues/377)
- Fixed spec compliance issue with room version 8 - 11 joins (https://github.com/matrix-org/synapse/issues/16717 / https://github.com/matrix-org/matrix-spec/issues/1708)
- Add basic cache eviction for true destinations when requests fail if we use a cached destination (e.g. a server has modified their well-known and we're still connecting to the old destination)
- Only follow 6 redirects total in our default reqwest ClientBuilder
- Generate passwords with 25 characters instead of 15
- Add missing `reason` field to user ban events (`/ban`)

View file

@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM docker.io/rust:1.70-bullseye AS base
FROM docker.io/rust:1.74.1-bullseye AS base
FROM base AS builder
WORKDIR /usr/src/conduit

View file

@ -33,7 +33,7 @@ RUN echo "allow_federation = true" >> conduit.toml
RUN echo "allow_check_for_updates = true" >> conduit.toml
RUN echo "allow_encryption = true" >> conduit.toml
RUN echo "allow_registration = true" >> conduit.toml
RUN echo "log = \"warn,_=off,sled=off\"" >> conduit.toml
RUN echo "log = \"warn,_=off\"" >> conduit.toml
RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml
COPY --from=builder /workdir/target/release/conduit /workdir/conduit

View file

@ -61,8 +61,8 @@ enable_lightning_bolt = true
# servers.)
trusted_servers = ["matrix.org"]
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
#log = "warn,state_res=warn,rocket=off,_=off,sled=off"
#max_concurrent_requests = 400 # How many requests Conduit sends to other servers at the same time
#log = "warn,state_res=warn"
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.

4
debian/postinst vendored
View file

@ -77,8 +77,8 @@ allow_check_for_updates = true
trusted_servers = ["matrix.org"]
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
#log = "warn,state_res=warn,rocket=off,_=off,sled=off"
#max_concurrent_requests = 400 # How many requests Conduit sends to other servers at the same time
#log = "warn,state_res=warn"
EOF
fi
;;

View file

@ -64,7 +64,7 @@ docker run -d -p 8448:6167 \
-e CONDUIT_MAX_REQUEST_SIZE="20_000_000" \
-e CONDUIT_TRUSTED_SERVERS="[\"matrix.org\"]" \
-e CONDUIT_MAX_CONCURRENT_REQUESTS="100" \
-e CONDUIT_LOG="warn,rocket=off,_=off,sled=off" \
-e CONDUIT_LOG="warn,state_res=warn" \
--name conduit <link>
```

View file

@ -32,7 +32,7 @@ services:
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
#CONDUIT_LOG: warn,rocket=off,_=off,sled=off
#CONDUIT_LOG: warn,state_res=warn
CONDUIT_ADDRESS: 0.0.0.0
CONDUIT_CONFIG: '' # Ignore this

View file

@ -33,7 +33,7 @@ services:
# CONDUIT_PORT: 6167
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if you want to configure purely by env vars, set this to an empty string ''
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
# CONDUIT_LOG: info # default is: "warn,_=off,sled=off"
# CONDUIT_LOG: info # default is: "warn,state_res=warn"
# CONDUIT_ALLOW_JAEGER: 'false'
# CONDUIT_ALLOW_ENCRYPTION: 'true'
# CONDUIT_ALLOW_FEDERATION: 'true'

View file

@ -31,8 +31,8 @@ services:
CONDUIT_ALLOW_FEDERATION: 'true'
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
#CONDUIT_LOG: warn,rocket=off,_=off,sled=off
#CONDUIT_MAX_CONCURRENT_REQUESTS: 400
#CONDUIT_LOG: warn,state_res=warn
CONDUIT_ADDRESS: 0.0.0.0
CONDUIT_CONFIG: '' # Ignore this
#

24
flake.lock generated
View file

@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1701622587,
"narHash": "sha256-o3XhxCCyrUHZ0tlta2W7/MuXzy+n0+BUt3rKFK3DIK4=",
"lastModified": 1704819371,
"narHash": "sha256-oFUfPWrWGQTZaCM3byxwYwrMLwshDxVGOrMH5cVP/X8=",
"owner": "ipetkov",
"repo": "crane",
"rev": "c09d2cbe84cc2adfe1943cb2a0b55a71c835ca9a",
"rev": "5c234301a1277e4cc759c23a2a7a00a06ddd7111",
"type": "github"
},
"original": {
@ -28,11 +28,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1701884128,
"narHash": "sha256-8PiBdXnFkbDlV6NMWgqNcnlHJr87g9mvyZVRUy9uDqs=",
"lastModified": 1705126891,
"narHash": "sha256-RnCWzRghSpyxKs3kXgYPkZv6TvzV3Pmve1je6RQHe1o=",
"owner": "nix-community",
"repo": "fenix",
"rev": "3c94b1be4688e3ddc8897a26c60dcd67a69a1845",
"rev": "89a02ff13d98d54f0b3b41f9b8326eb26d7cdc2e",
"type": "github"
},
"original": {
@ -61,11 +61,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1701436327,
"narHash": "sha256-tRHbnoNI8SIM5O5xuxOmtSLnswEByzmnQcGGyNRjxsE=",
"lastModified": 1704722960,
"narHash": "sha256-mKGJ3sPsT6//s+Knglai5YflJUF2DGj7Ai6Ynopz0kI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "91050ea1e57e50388fa87a3302ba12d188ef723a",
"rev": "317484b1ead87b9c1b8ac5261a8d2dd748a0492d",
"type": "github"
},
"original": {
@ -86,11 +86,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1701792945,
"narHash": "sha256-sDsGXchaAIqq/UsRcgQh/BtX6WzaIN8wa7FNhwhZ540=",
"lastModified": 1704974004,
"narHash": "sha256-H3RdtMxH8moTInVmracgtF8bgFpaEE3zYoSkuv7PBs0=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "05df6c52cac9f5ce37a543a0b81d3bd6a1ec9d6d",
"rev": "9d8889cdfcc3aa0302353fc988ed21ff9bc9925c",
"type": "github"
},
"original": {

View file

@ -30,7 +30,7 @@
(final: prev: {
rocksdb = prev.rocksdb.overrideAttrs (old:
let
version = "8.9.1";
version = "8.10.0";
in
{
inherit version;
@ -38,7 +38,7 @@
owner = "facebook";
repo = "rocksdb";
rev = "v${version}";
hash = "sha256-Pl7t4FVOvnORWFS+gjy2EEUQlPxjLukWW5I5gzCQwkI=";
hash = "sha256-KGsYDBc1fz/90YYNGwlZ0LUKXYsP1zyhP29TnRQwgjQ=";
};
});
})
@ -59,7 +59,7 @@
channel = cargoToml.package.rust-version;
# THE rust-version HASH
sha256 = "sha256-gdYqng0y9iHYzYPAdkC/ka3DRny3La/S5G8ASj0Ayyc=";
sha256 = "sha256-PjvuouwTsYfNKW5Vi5Ye7y+lL7SsWGBxCtBOOm2z14c=";
};
mkToolchain = fenix.packages.${system}.combine;

View file

@ -205,7 +205,8 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
services()
.users
.set_displayname(&user_id, Some(displayname.clone()))?;
.set_displayname(&user_id, Some(displayname.clone()))
.await?;
// Initial account data
services().account_data.update(

View file

@ -10,7 +10,7 @@ use ruma::{
},
federation,
},
OwnedRoomAliasId,
OwnedRoomAliasId, OwnedServerName,
};
/// # `PUT /_matrix/client/r0/directory/room/{roomAlias}`
@ -66,11 +66,9 @@ pub async fn delete_alias_route(
Ok(delete_alias::v3::Response::new())
}
/// # `GET /_matrix/client/r0/directory/room/{roomAlias}`
/// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
///
/// Resolve an alias locally or over federation.
///
/// - TODO: Suggest more servers to join via
pub async fn get_alias_route(
body: Ruma<get_alias::v3::Request>,
) -> Result<get_alias::v3::Response> {
@ -91,10 +89,37 @@ pub(crate) async fn get_alias_helper(
)
.await?;
let room_id = response.room_id;
let mut servers = response.servers;
// find active servers in room state cache to suggest
for extra_servers in services()
.rooms
.state_cache
.room_servers(&room_id)
.filter_map(|r| r.ok())
{
servers.push(extra_servers);
}
// insert our server as the very first choice if in list
if let Some(server_index) = servers
.clone()
.into_iter()
.position(|server| server == services().globals.server_name())
{
servers.remove(server_index);
servers.insert(0, services().globals.server_name().to_owned());
}
servers.sort_unstable();
servers.dedup();
// shuffle list of servers randomly after sort and dedupe
servers.shuffle(&mut rand::thread_rng());
return Ok(get_alias::v3::Response::new(response.room_id, servers));
return Ok(get_alias::v3::Response::new(room_id, servers));
}
let mut room_id = None;
@ -152,8 +177,33 @@ pub(crate) async fn get_alias_helper(
}
};
Ok(get_alias::v3::Response::new(
room_id,
vec![services().globals.server_name().to_owned()],
))
let mut servers: Vec<OwnedServerName> = Vec::new();
// find active servers in room state cache to suggest
for extra_servers in services()
.rooms
.state_cache
.room_servers(&room_id)
.filter_map(|r| r.ok())
{
servers.push(extra_servers);
}
// insert our server as the very first choice if in list
if let Some(server_index) = servers
.clone()
.into_iter()
.position(|server| server == services().globals.server_name())
{
servers.remove(server_index);
servers.insert(0, services().globals.server_name().to_owned());
}
servers.sort_unstable();
servers.dedup();
// shuffle list of servers randomly after sort and dedupe
servers.shuffle(&mut rand::thread_rng());
Ok(get_alias::v3::Response::new(room_id, servers))
}

View file

@ -259,6 +259,7 @@ pub async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_use
serde_json::from_str(event.content.get())
.map(|event: RoomMemberEventContent| RoomMemberEventContent {
membership: MembershipState::Ban,
reason: event.reason,
..event
})
.map_err(|_| Error::bad_database("Invalid member event in database."))
@ -559,7 +560,7 @@ async fn join_room_by_id_helper(
third_party_invite: None,
blurhash: services().users.blurhash(sender_user)?,
reason,
join_authorized_via_users_server,
join_authorized_via_users_server: join_authorized_via_users_server.clone(),
})
.expect("event is valid, we just created it"),
);
@ -594,7 +595,7 @@ async fn join_room_by_id_helper(
// It has enough fields to be called a proper event now
let mut join_event = join_event_stub;
info!("Asking {remote_server} for send_join");
info!("Asking {remote_server} for send_join in room {room_id}");
let send_join_response = services()
.sending
.send_federation_request(
@ -610,51 +611,77 @@ async fn join_room_by_id_helper(
info!("send_join finished");
if let Some(signed_raw) = &send_join_response.room_state.event {
info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event");
let (signed_event_id, signed_value) =
match gen_event_id_canonical_json(signed_raw, &room_version_id) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Could not convert event to canonical json.",
));
}
};
if signed_event_id != event_id {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent event with wrong event id",
));
}
match signed_value["signatures"]
.as_object()
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent invalid signatures type",
))
.and_then(|e| {
e.get(remote_server.as_str()).ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Server did not send its signature",
))
}) {
Ok(signature) => {
join_event
.get_mut("signatures")
.expect("we created a valid pdu")
.as_object_mut()
.expect("we created a valid pdu")
.insert(remote_server.to_string(), signature.clone());
if join_authorized_via_users_server.is_some() {
match &room_version_id {
RoomVersionId::V1
| RoomVersionId::V2
| RoomVersionId::V3
| RoomVersionId::V4
| RoomVersionId::V5
| RoomVersionId::V6
| RoomVersionId::V7 => {
warn!("Found `join_authorised_via_users_server` but room {} is version {}. Ignoring.", room_id, &room_version_id);
}
Err(e) => {
// only room versions 8 and above using `join_authorized_via_users_server` (restricted joins) need to validate and send signatures
RoomVersionId::V8 | RoomVersionId::V9 | RoomVersionId::V10 | RoomVersionId::V11 => {
if let Some(signed_raw) = &send_join_response.room_state.event {
info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event");
let (signed_event_id, signed_value) =
match gen_event_id_canonical_json(signed_raw, &room_version_id) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Could not convert event to canonical json.",
));
}
};
if signed_event_id != event_id {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent event with wrong event id",
));
}
match signed_value["signatures"]
.as_object()
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent invalid signatures type",
))
.and_then(|e| {
e.get(remote_server.as_str()).ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Server did not send its signature",
))
}) {
Ok(signature) => {
join_event
.get_mut("signatures")
.expect("we created a valid pdu")
.as_object_mut()
.expect("we created a valid pdu")
.insert(remote_server.to_string(), signature.clone());
}
Err(e) => {
warn!(
"Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}",
);
}
}
}
}
_ => {
warn!(
"Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}",
);
"Unexpected or unsupported room version {} for room {}",
&room_version_id, room_id
);
return Err(Error::BadRequest(
ErrorKind::BadJson,
"Unexpected or unsupported room version found",
));
}
}
}
@ -1426,7 +1453,7 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
.update_membership(
room_id,
user_id,
MembershipState::Leave,
RoomMemberEventContent::new(MembershipState::Leave),
user_id,
last_state,
true,
@ -1461,7 +1488,7 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
.update_membership(
room_id,
user_id,
MembershipState::Leave,
RoomMemberEventContent::new(MembershipState::Leave),
user_id,
None,
true,

View file

@ -71,4 +71,4 @@ pub use voip::*;
pub const DEVICE_ID_LENGTH: usize = 10;
pub const TOKEN_LENGTH: usize = 32;
pub const SESSION_ID_LENGTH: usize = 32;
pub const AUTO_GEN_PASSWORD_LENGTH: usize = 15;
pub const AUTO_GEN_PASSWORD_LENGTH: usize = 25;

View file

@ -27,7 +27,8 @@ pub async fn set_displayname_route(
services()
.users
.set_displayname(sender_user, body.displayname.clone())?;
.set_displayname(sender_user, body.displayname.clone())
.await?;
// Send a new membership event and presence update into all joined rooms
let all_rooms_joined: Vec<_> = services()
@ -103,15 +104,18 @@ pub async fn set_displayname_route(
Ok(set_display_name::v3::Response {})
}
/// # `GET /_matrix/client/r0/profile/{userId}/displayname`
/// # `GET /_matrix/client/v3/profile/{userId}/displayname`
///
/// Returns the displayname of the user.
///
/// - If user is on another server: Fetches displayname over federation
/// - If user is on another server and we do not have a local copy already
/// fetch displayname over federation
pub async fn get_displayname_route(
body: Ruma<get_display_name::v3::Request>,
) -> Result<get_display_name::v3::Response> {
if body.user_id.server_name() != services().globals.server_name() {
if (services().users.exists(&body.user_id)?)
&& (body.user_id.server_name() != services().globals.server_name())
{
let response = services()
.sending
.send_federation_request(
@ -123,6 +127,26 @@ pub async fn get_displayname_route(
)
.await?;
/*
TODO: ignore errors properly?
// Create and update our local copy of the user
// these are `let _` because it's fine if we can't find these for the user.
// also these requests are sent on room join so dead servers will make room joins annoying again
let _ = services().users.create(&body.user_id, None);
let _ = services()
.users
.set_displayname(&body.user_id, response.displayname.clone())
.await;
let _ = services()
.users
.set_avatar_url(&body.user_id, response.avatar_url)
.await;
let _ = services()
.users
.set_blurhash(&body.user_id, response.blurhash)
.await;
*/
return Ok(get_display_name::v3::Response {
displayname: response.displayname,
});
@ -145,11 +169,13 @@ pub async fn set_avatar_url_route(
services()
.users
.set_avatar_url(sender_user, body.avatar_url.clone())?;
.set_avatar_url(sender_user, body.avatar_url.clone())
.await?;
services()
.users
.set_blurhash(sender_user, body.blurhash.clone())?;
.set_blurhash(sender_user, body.blurhash.clone())
.await?;
// Send a new membership event and presence update into all joined rooms
let all_joined_rooms: Vec<_> = services()
@ -225,15 +251,18 @@ pub async fn set_avatar_url_route(
Ok(set_avatar_url::v3::Response {})
}
/// # `GET /_matrix/client/r0/profile/{userId}/avatar_url`
/// # `GET /_matrix/client/v3/profile/{userId}/avatar_url`
///
/// Returns the avatar_url and blurhash of the user.
///
/// - If user is on another server: Fetches avatar_url and blurhash over federation
/// - If user is on another server and we do not have a local copy already
/// fetch avatar_url and blurhash over federation
pub async fn get_avatar_url_route(
body: Ruma<get_avatar_url::v3::Request>,
) -> Result<get_avatar_url::v3::Response> {
if body.user_id.server_name() != services().globals.server_name() {
if (services().users.exists(&body.user_id)?)
&& (body.user_id.server_name() != services().globals.server_name())
{
let response = services()
.sending
.send_federation_request(
@ -245,6 +274,26 @@ pub async fn get_avatar_url_route(
)
.await?;
/*
TODO: ignore errors properly?
// Create and update our local copy of the user
// these are `let _` because it's fine if we can't find these for the user.
// also these requests are sent on room join so dead servers will make room joins annoying again
let _ = services().users.create(&body.user_id, None);
let _ = services()
.users
.set_displayname(&body.user_id, response.displayname)
.await;
let _ = services()
.users
.set_avatar_url(&body.user_id, response.avatar_url.clone())
.await;
let _ = services()
.users
.set_blurhash(&body.user_id, response.blurhash.clone())
.await;
*/
return Ok(get_avatar_url::v3::Response {
avatar_url: response.avatar_url,
blurhash: response.blurhash,
@ -257,15 +306,18 @@ pub async fn get_avatar_url_route(
})
}
/// # `GET /_matrix/client/r0/profile/{userId}`
/// # `GET /_matrix/client/v3/profile/{userId}`
///
/// Returns the displayname, avatar_url and blurhash of the user.
///
/// - If user is on another server: Fetches profile over federation
/// - If user is on another server and we do not have a local copy already,
/// fetch profile over federation.
pub async fn get_profile_route(
body: Ruma<get_profile::v3::Request>,
) -> Result<get_profile::v3::Response> {
if body.user_id.server_name() != services().globals.server_name() {
if (services().users.exists(&body.user_id)?)
&& (body.user_id.server_name() != services().globals.server_name())
{
let response = services()
.sending
.send_federation_request(
@ -277,6 +329,26 @@ pub async fn get_profile_route(
)
.await?;
/*
TODO: ignore errors properly?
// Create and update our local copy of the user
// these are `let _` because it's fine if we can't find these for the user.
// also these requests are sent on room join so dead servers will make room joins annoying again
let _ = services().users.create(&body.user_id, None);
let _ = services()
.users
.set_displayname(&body.user_id, response.displayname.clone())
.await;
let _ = services()
.users
.set_avatar_url(&body.user_id, response.avatar_url.clone())
.await;
let _ = services()
.users
.set_blurhash(&body.user_id, response.blurhash.clone())
.await;
*/
return Ok(get_profile::v3::Response {
displayname: response.displayname,
avatar_url: response.avatar_url,
@ -285,7 +357,7 @@ pub async fn get_profile_route(
}
if !services().users.exists(&body.user_id)? {
// Return 404 if this user doesn't exist
// Return 404 if this user doesn't exist and we couldn't fetch it over federation
return Err(Error::BadRequest(
ErrorKind::NotFound,
"Profile was not found.",

View file

@ -147,7 +147,13 @@ pub async fn create_room_route(
);
}
RoomVersionId::V11 => {} // V11 removed the "creator" key
_ => panic!("Unexpected room version {}", room_version),
_ => {
warn!("Unexpected or unsupported room version {}", room_version);
return Err(Error::BadRequest(
ErrorKind::BadJson,
"Unexpected or unsupported room version found",
));
}
}
content.insert(
@ -172,7 +178,13 @@ pub async fn create_room_route(
| RoomVersionId::V9
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
_ => panic!("Unexpected room version {}", room_version),
_ => {
warn!("Unexpected or unsupported room version {}", room_version);
return Err(Error::BadRequest(
ErrorKind::BadJson,
"Unexpected or unsupported room version found",
));
}
};
let mut content = serde_json::from_str::<CanonicalJsonObject>(
to_raw_value(&content)
@ -673,7 +685,16 @@ pub async fn upgrade_room_route(
// "creator" key no longer exists in V11 rooms
create_event_content.remove("creator");
}
_ => panic!("Unexpected room version {}", body.new_version),
_ => {
warn!(
"Unexpected or unsupported room version {}",
body.new_version
);
return Err(Error::BadRequest(
ErrorKind::BadJson,
"Unexpected or unsupported room version found",
));
}
}
create_event_content.insert(

View file

@ -185,6 +185,9 @@ pub async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logout::v3:
services().users.remove_device(sender_user, sender_device)?;
// send device list update for user after logout
services().users.mark_device_key_update(sender_user)?;
Ok(logout::v3::Response::new())
}
@ -208,5 +211,8 @@ pub async fn logout_all_route(
services().users.remove_device(sender_user, &device_id)?;
}
// send device list update for user after logout
services().users.mark_device_key_update(sender_user)?;
Ok(logout_all::v3::Response::new())
}

View file

@ -37,6 +37,7 @@ pub async fn get_supported_versions_route(
unstable_features: BTreeMap::from_iter([
("org.matrix.e2e_cross_signing".to_owned(), true),
("org.matrix.msc2836".to_owned(), true),
("org.matrix.msc3827".to_owned(), true),
("org.matrix.msc2946".to_owned(), true),
]),
};

View file

@ -160,7 +160,7 @@ where
.try_into_http_request::<Vec<u8>>(
&actual_destination_str,
SendAccessToken::IfRequired(""),
&[MatrixVersion::V1_4],
&[MatrixVersion::V1_5],
)
.map_err(|e| {
warn!(
@ -308,6 +308,18 @@ where
})
} else {
debug!("Returning error from {destination}");
// remove potentially dead destinations from our cache that may be from modified well-knowns
if !write_destination_to_cache {
info!("Evicting {destination} from our true destination cache due to failed request.");
services()
.globals
.actual_destination_cache
.write()
.unwrap()
.remove(destination);
}
Err(Error::FederationError(
destination.to_owned(),
RumaError::from_http_response(http_response),
@ -550,12 +562,14 @@ async fn request_well_known(destination: &str) -> Option<String> {
.send()
.await;
debug!("Got well known response");
debug!("Well known response: {:?}", response);
if let Err(e) = &response {
debug!("Well known error: {e:?}");
return None;
}
let text = response.ok()?.text().await;
debug!("Got well known response text");
debug!("Well known response text: {:?}", text);
let body: serde_json::Value = serde_json::from_str(&text.ok()?).ok()?;
Some(body.get("m.server")?.as_str()?.to_owned())
}
@ -1883,7 +1897,7 @@ pub async fn create_invite_route(
.update_membership(
&body.room_id,
&invited_user,
MembershipState::Invite,
RoomMemberEventContent::new(MembershipState::Invite),
&sender,
Some(invite_state),
true,

View file

@ -381,7 +381,7 @@ fn default_presence_idle_timeout_s() -> u64 {
}
fn default_presence_offline_timeout_s() -> u64 {
15 * 60
30 * 60
}
fn default_rocksdb_log_level() -> String {

View file

@ -3,27 +3,13 @@ use crate::Result;
use std::{future::Future, pin::Pin, sync::Arc};
#[cfg(feature = "sled")]
pub mod sled;
#[cfg(feature = "sqlite")]
pub mod sqlite;
#[cfg(feature = "heed")]
pub mod heed;
#[cfg(feature = "rocksdb")]
pub mod rocksdb;
#[cfg(feature = "persy")]
pub mod persy;
#[cfg(any(
feature = "sqlite",
feature = "rocksdb",
feature = "heed",
feature = "persy"
))]
#[cfg(any(feature = "sqlite", feature = "rocksdb"))]
pub mod watchers;
pub trait KeyValueDatabaseEngine: Send + Sync {

View file

@ -1,194 +0,0 @@
use super::{super::Config, watchers::Watchers};
use crossbeam::channel::{bounded, Sender as ChannelSender};
use threadpool::ThreadPool;
use crate::{Error, Result};
use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex},
};
use super::{DatabaseEngine, Tree};
type TupleOfBytes = (Vec<u8>, Vec<u8>);
pub struct Engine {
env: heed::Env,
iter_pool: Mutex<ThreadPool>,
}
pub struct EngineTree {
engine: Arc<Engine>,
tree: Arc<heed::UntypedDatabase>,
watchers: Watchers,
}
fn convert_error(error: heed::Error) -> Error {
Error::HeedError {
error: error.to_string(),
}
}
impl DatabaseEngine for Engine {
fn open(config: &Config) -> Result<Arc<Self>> {
let mut env_builder = heed::EnvOpenOptions::new();
env_builder.map_size(1024 * 1024 * 1024 * 1024); // 1 Terabyte
env_builder.max_readers(126);
env_builder.max_dbs(128);
unsafe {
env_builder.flag(heed::flags::Flags::MdbWriteMap);
env_builder.flag(heed::flags::Flags::MdbMapAsync);
}
Ok(Arc::new(Engine {
env: env_builder
.open(&config.database_path)
.map_err(convert_error)?,
iter_pool: Mutex::new(ThreadPool::new(10)),
}))
}
fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>> {
// Creates the db if it doesn't exist already
Ok(Arc::new(EngineTree {
engine: Arc::clone(self),
tree: Arc::new(
self.env
.create_database(Some(name))
.map_err(convert_error)?,
),
watchers: Default::default(),
}))
}
fn flush(self: &Arc<Self>) -> Result<()> {
self.env.force_sync().map_err(convert_error)?;
Ok(())
}
}
impl EngineTree {
fn iter_from_thread(
&self,
tree: Arc<heed::UntypedDatabase>,
from: Vec<u8>,
backwards: bool,
) -> Box<dyn Iterator<Item = TupleOfBytes> + Send + Sync> {
let (s, r) = bounded::<TupleOfBytes>(100);
let engine = Arc::clone(&self.engine);
let lock = self.engine.iter_pool.lock().await;
if lock.active_count() < lock.max_count() {
lock.execute(move || {
iter_from_thread_work(tree, &engine.env.read_txn().unwrap(), from, backwards, &s);
});
} else {
std::thread::spawn(move || {
iter_from_thread_work(tree, &engine.env.read_txn().unwrap(), from, backwards, &s);
});
}
Box::new(r.into_iter())
}
}
fn iter_from_thread_work(
tree: Arc<heed::UntypedDatabase>,
txn: &heed::RoTxn<'_>,
from: Vec<u8>,
backwards: bool,
s: &ChannelSender<(Vec<u8>, Vec<u8>)>,
) {
if backwards {
for (k, v) in tree.rev_range(txn, ..=&*from).unwrap().map(|r| r.unwrap()) {
if s.send((k.to_vec(), v.to_vec())).is_err() {
return;
}
}
} else {
if from.is_empty() {
for (k, v) in tree.iter(txn).unwrap().map(|r| r.unwrap()) {
if s.send((k.to_vec(), v.to_vec())).is_err() {
return;
}
}
} else {
for (k, v) in tree.range(txn, &*from..).unwrap().map(|r| r.unwrap()) {
if s.send((k.to_vec(), v.to_vec())).is_err() {
return;
}
}
}
}
}
impl Tree for EngineTree {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
let txn = self.engine.env.read_txn().map_err(convert_error)?;
Ok(self
.tree
.get(&txn, &key)
.map_err(convert_error)?
.map(|s| s.to_vec()))
}
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
let mut txn = self.engine.env.write_txn().map_err(convert_error)?;
self.tree
.put(&mut txn, &key, &value)
.map_err(convert_error)?;
txn.commit().map_err(convert_error)?;
self.watchers.wake(key);
Ok(())
}
fn remove(&self, key: &[u8]) -> Result<()> {
let mut txn = self.engine.env.write_txn().map_err(convert_error)?;
self.tree.delete(&mut txn, &key).map_err(convert_error)?;
txn.commit().map_err(convert_error)?;
Ok(())
}
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + Send + 'a> {
self.iter_from(&[], false)
}
fn iter_from(
&self,
from: &[u8],
backwards: bool,
) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + Send> {
self.iter_from_thread(Arc::clone(&self.tree), from.to_vec(), backwards)
}
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
let mut txn = self.engine.env.write_txn().map_err(convert_error)?;
let old = self.tree.get(&txn, &key).map_err(convert_error)?;
let new =
crate::utils::increment(old.as_deref()).expect("utils::increment always returns Some");
self.tree
.put(&mut txn, &key, &&*new)
.map_err(convert_error)?;
txn.commit().map_err(convert_error)?;
Ok(new)
}
fn scan_prefix<'a>(
&'a self,
prefix: Vec<u8>,
) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + Send + 'a> {
Box::new(
self.iter_from(&prefix, false)
.take_while(move |(key, _)| key.starts_with(&prefix)),
)
}
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
self.watchers.watch(prefix)
}
}

View file

@ -1,197 +0,0 @@
use crate::{
database::{
abstraction::{watchers::Watchers, KeyValueDatabaseEngine, KvTree},
Config,
},
Result,
};
use persy::{ByteVec, OpenOptions, Persy, Transaction, TransactionConfig, ValueMode};
use std::{future::Future, pin::Pin, sync::Arc};
use tracing::warn;
pub struct Engine {
persy: Persy,
}
impl KeyValueDatabaseEngine for Arc<Engine> {
fn open(config: &Config) -> Result<Self> {
let mut cfg = persy::Config::new();
cfg.change_cache_size((config.db_cache_capacity_mb * 1024.0 * 1024.0) as u64);
let persy = OpenOptions::new()
.create(true)
.config(cfg)
.open(&format!("{}/db.persy", config.database_path))?;
Ok(Arc::new(Engine { persy }))
}
fn open_tree(&self, name: &'static str) -> Result<Arc<dyn KvTree>> {
// Create if it doesn't exist
if !self.persy.exists_index(name)? {
let mut tx = self.persy.begin()?;
tx.create_index::<ByteVec, ByteVec>(name, ValueMode::Replace)?;
tx.prepare()?.commit()?;
}
Ok(Arc::new(PersyTree {
persy: self.persy.clone(),
name: name.to_owned(),
watchers: Watchers::default(),
}))
}
fn flush(&self) -> Result<()> {
Ok(())
}
}
pub struct PersyTree {
persy: Persy,
name: String,
watchers: Watchers,
}
impl PersyTree {
fn begin(&self) -> Result<Transaction> {
Ok(self
.persy
.begin_with(TransactionConfig::new().set_background_sync(true))?)
}
}
impl KvTree for PersyTree {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
let result = self
.persy
.get::<ByteVec, ByteVec>(&self.name, &ByteVec::from(key))?
.next()
.map(|v| (*v).to_owned());
Ok(result)
}
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
self.insert_batch(&mut Some((key.to_owned(), value.to_owned())).into_iter())?;
self.watchers.wake(key);
Ok(())
}
fn insert_batch<'a>(&self, iter: &mut dyn Iterator<Item = (Vec<u8>, Vec<u8>)>) -> Result<()> {
let mut tx = self.begin()?;
for (key, value) in iter {
tx.put::<ByteVec, ByteVec>(
&self.name,
ByteVec::from(key.clone()),
ByteVec::from(value),
)?;
}
tx.prepare()?.commit()?;
Ok(())
}
fn increment_batch<'a>(&self, iter: &mut dyn Iterator<Item = Vec<u8>>) -> Result<()> {
let mut tx = self.begin()?;
for key in iter {
let old = tx
.get::<ByteVec, ByteVec>(&self.name, &ByteVec::from(key.clone()))?
.next()
.map(|v| (*v).to_owned());
let new = crate::utils::increment(old.as_deref()).unwrap();
tx.put::<ByteVec, ByteVec>(&self.name, ByteVec::from(key), ByteVec::from(new))?;
}
tx.prepare()?.commit()?;
Ok(())
}
fn remove(&self, key: &[u8]) -> Result<()> {
let mut tx = self.begin()?;
tx.remove::<ByteVec, ByteVec>(&self.name, ByteVec::from(key), None)?;
tx.prepare()?.commit()?;
Ok(())
}
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
let iter = self.persy.range::<ByteVec, ByteVec, _>(&self.name, ..);
match iter {
Ok(iter) => Box::new(iter.filter_map(|(k, v)| {
v.into_iter()
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
.next()
})),
Err(e) => {
warn!("error iterating {:?}", e);
Box::new(std::iter::empty())
}
}
}
fn iter_from<'a>(
&'a self,
from: &[u8],
backwards: bool,
) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
let range = if backwards {
self.persy
.range::<ByteVec, ByteVec, _>(&self.name, ..=ByteVec::from(from))
} else {
self.persy
.range::<ByteVec, ByteVec, _>(&self.name, ByteVec::from(from)..)
};
match range {
Ok(iter) => {
let map = iter.filter_map(|(k, v)| {
v.into_iter()
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
.next()
});
if backwards {
Box::new(map.rev())
} else {
Box::new(map)
}
}
Err(e) => {
warn!("error iterating with prefix {:?}", e);
Box::new(std::iter::empty())
}
}
}
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
self.increment_batch(&mut Some(key.to_owned()).into_iter())?;
Ok(self.get(key)?.unwrap())
}
fn scan_prefix<'a>(
&'a self,
prefix: Vec<u8>,
) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
let range_prefix = ByteVec::from(prefix.clone());
let range = self
.persy
.range::<ByteVec, ByteVec, _>(&self.name, range_prefix..);
match range {
Ok(iter) => {
let owned_prefix = prefix.clone();
Box::new(
iter.take_while(move |(k, _)| (*k).starts_with(&owned_prefix))
.filter_map(|(k, v)| {
v.into_iter()
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
.next()
}),
)
}
Err(e) => {
warn!("error scanning prefix {:?}", e);
Box::new(std::iter::empty())
}
}
}
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
self.watchers.watch(prefix)
}
}

View file

@ -1,127 +0,0 @@
use super::super::Config;
use crate::{utils, Result};
use std::{future::Future, pin::Pin, sync::Arc};
use tracing::warn;
use super::{DatabaseEngine, Tree};
pub struct Engine(sled::Db);
pub struct SledEngineTree(sled::Tree);
impl DatabaseEngine for Engine {
fn open(config: &Config) -> Result<Arc<Self>> {
Ok(Arc::new(Engine(
sled::Config::default()
.path(&config.database_path)
.cache_capacity((config.db_cache_capacity_mb * 1024.0 * 1024.0) as u64)
.use_compression(true)
.open()?,
)))
}
fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>> {
Ok(Arc::new(SledEngineTree(self.0.open_tree(name)?)))
}
fn flush(self: &Arc<Self>) -> Result<()> {
Ok(()) // noop
}
}
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 insert_batch<'a>(&self, iter: &mut dyn Iterator<Item = (Vec<u8>, Vec<u8>)>) -> Result<()> {
for (key, value) in iter {
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 = (Vec<u8>, Vec<u8>)> + '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 = (Vec<u8>, Vec<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 = (Vec<u8>, Vec<u8>)> + '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;
})
}
}

View file

@ -192,16 +192,11 @@ impl KeyValueDatabase {
fn check_db_setup(config: &Config) -> Result<()> {
let path = Path::new(&config.database_path);
let sled_exists = path.join("db").exists();
let sqlite_exists = path.join("conduit.db").exists();
let rocksdb_exists = path.join("IDENTITY").exists();
let mut count = 0;
if sled_exists {
count += 1;
}
if sqlite_exists {
count += 1;
}
@ -215,12 +210,6 @@ impl KeyValueDatabase {
return Ok(());
}
if sled_exists && config.database_backend != "sled" {
return Err(Error::bad_config(
"Found sled at database_path, but is not specified in config.",
));
}
if sqlite_exists && config.database_backend != "sqlite" {
return Err(Error::bad_config(
"Found sqlite at database_path, but is not specified in config.",
@ -260,14 +249,8 @@ impl KeyValueDatabase {
#[cfg(feature = "rocksdb")]
Arc::new(Arc::<abstraction::rocksdb::Engine>::open(&config)?)
}
"persy" => {
#[cfg(not(feature = "persy"))]
return Err(Error::BadConfig("Database backend not found."));
#[cfg(feature = "persy")]
Arc::new(Arc::<abstraction::persy::Engine>::open(&config)?)
}
_ => {
return Err(Error::BadConfig("Database backend not found."));
return Err(Error::BadConfig("Database backend not found. sqlite (not recommended) and rocksdb are the only supported backends."));
}
};

View file

@ -1,11 +1,3 @@
#![warn(
rust_2018_idioms,
unused_qualifications,
clippy::cloned_instead_of_copied,
clippy::str_to_string
)]
#![deny(clippy::dbg_macro)]
pub mod api;
mod config;
mod database;

View file

@ -1,12 +1,3 @@
#![warn(
rust_2018_idioms,
unused_qualifications,
clippy::cloned_instead_of_copied,
clippy::str_to_string,
clippy::future_not_send
)]
#![deny(clippy::dbg_macro)]
use std::{
fs::Permissions, future::Future, io, net::SocketAddr, os::unix::fs::PermissionsExt,
sync::atomic, time::Duration,
@ -211,6 +202,7 @@ async fn run_server() -> io::Result<()> {
.allow_origin(cors::Any)
.allow_methods([
Method::GET,
Method::HEAD,
Method::POST,
Method::PUT,
Method::DELETE,

View file

@ -10,6 +10,7 @@ use std::fmt::Write;
use clap::{Parser, Subcommand};
use regex::Regex;
use ruma::{
api::client::error::ErrorKind,
events::{
relation::InReplyTo,
room::{
@ -30,6 +31,7 @@ use ruma::{
};
use serde_json::value::to_raw_value;
use tokio::sync::{mpsc, Mutex};
use tracing::warn;
use crate::{
api::client_server::{leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH},
@ -598,7 +600,8 @@ impl Service {
services()
.users
.set_displayname(&user_id, Some(displayname))?;
.set_displayname(&user_id, Some(displayname))
.await?;
// Initial account data
services().account_data.update(
@ -619,7 +622,7 @@ impl Service {
// Inhibit login does not work for guests
RoomMessageEventContent::text_plain(format!(
"Created user with user_id: {user_id} and password: {password}"
"Created user with user_id: {user_id} and password: `{password}`"
))
}
UserCommand::Deactivate {
@ -627,6 +630,14 @@ impl Service {
user_id,
} => {
let user_id = Arc::<UserId>::from(user_id);
// check if user belongs to our server
if user_id.server_name() != services().globals.server_name() {
return Ok(RoomMessageEventContent::text_plain(format!(
"User {user_id} does not belong to our server."
)));
}
if services().users.exists(&user_id)? {
RoomMessageEventContent::text_plain(format!(
"Making {user_id} leave all rooms before deactivation..."
@ -660,6 +671,13 @@ impl Service {
}
};
// check if user belongs to our server
if user_id.server_name() != services().globals.server_name() {
return Ok(RoomMessageEventContent::text_plain(format!(
"User {user_id} does not belong to our server."
)));
}
// Check if the specified user is valid
if !services().users.exists(&user_id)?
|| user_id
@ -681,7 +699,7 @@ impl Service {
.set_password(&user_id, Some(new_password.as_str()))
{
Ok(()) => RoomMessageEventContent::text_plain(format!(
"Successfully reset the password for user {user_id}: {new_password}"
"Successfully reset the password for user {user_id}: `{new_password}`"
)),
Err(e) => RoomMessageEventContent::text_plain(format!(
"Couldn't reset the password for user {user_id}: {e}"
@ -725,6 +743,11 @@ impl Service {
}
for &user_id in &user_ids {
// check if user belongs to our server and skips over non-local users
if user_id.server_name() != services().globals.server_name() {
continue;
}
if services().users.deactivate_account(user_id).is_ok() {
deactivation_count += 1
}
@ -1400,7 +1423,13 @@ impl Service {
| RoomVersionId::V9
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(conduit_user.clone()),
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
_ => panic!("Unexpected room version {}", room_version),
_ => {
warn!("Unexpected or unsupported room version {}", room_version);
return Err(Error::BadRequest(
ErrorKind::BadJson,
"Unexpected or unsupported room version found",
));
}
};
content.federate = true;

View file

@ -539,10 +539,19 @@ impl Service<'_> {
}
fn reqwest_client_builder(config: &Config) -> Result<reqwest::ClientBuilder> {
let redirect_policy = reqwest::redirect::Policy::custom(|attempt| {
if attempt.previous().len() > 6 {
attempt.error("Too many redirects (max is 6)")
} else {
attempt.follow()
}
});
let mut reqwest_client_builder = reqwest::Client::builder()
.pool_max_idle_per_host(0)
.connect_timeout(Duration::from_secs(60))
.timeout(Duration::from_secs(60 * 5))
.redirect(redirect_policy)
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",

View file

@ -8,14 +8,13 @@ pub use data::Data;
use ruma::{
api::client::error::ErrorKind,
events::{
room::{create::RoomCreateEventContent, member::MembershipState},
room::{create::RoomCreateEventContent, member::RoomMemberEventContent},
AnyStrippedStateEvent, StateEventType, TimelineEventType,
},
serde::Raw,
state_res::{self, StateMap},
EventId, OwnedEventId, RoomId, RoomVersionId, UserId,
};
use serde::Deserialize;
use tokio::sync::MutexGuard;
use tracing::warn;
@ -59,14 +58,9 @@ impl Service {
match pdu.kind {
TimelineEventType::RoomMember => {
#[derive(Deserialize)]
struct ExtractMembership {
membership: MembershipState,
}
let membership =
match serde_json::from_str::<ExtractMembership>(pdu.content.get()) {
Ok(e) => e.membership,
let membership_event =
match serde_json::from_str::<RoomMemberEventContent>(pdu.content.get()) {
Ok(e) => e,
Err(_) => continue,
};
@ -83,7 +77,14 @@ impl Service {
services()
.rooms
.state_cache
.update_membership(room_id, &user_id, membership, &pdu.sender, None, false)
.update_membership(
room_id,
&user_id,
membership_event,
&pdu.sender,
None,
false,
)
.await?;
}
TimelineEventType::SpaceChild => {

View file

@ -7,7 +7,10 @@ use ruma::{
events::{
direct::DirectEvent,
ignored_user_list::IgnoredUserListEvent,
room::{create::RoomCreateEventContent, member::MembershipState},
room::{
create::RoomCreateEventContent,
member::{MembershipState, RoomMemberEventContent},
},
AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType,
RoomAccountDataEventType, StateEventType,
},
@ -29,15 +32,47 @@ impl Service {
&self,
room_id: &RoomId,
user_id: &UserId,
membership: MembershipState,
membership_event: RoomMemberEventContent,
sender: &UserId,
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
update_joined_count: bool,
) -> Result<()> {
let membership = membership_event.membership;
// Keep track what remote users exist by adding them as "deactivated" users
if user_id.server_name() != services().globals.server_name() {
services().users.create(user_id, None)?;
// TODO: displayname, avatar url
/*
// Try to update our local copy of the user if ours does not match
// TODO: ignore errors properly?
if ((services().users.displayname(user_id)? != membership_event.displayname)
|| (services().users.avatar_url(user_id)? != membership_event.avatar_url)
|| (services().users.blurhash(user_id)? != membership_event.blurhash))
&& (membership != MembershipState::Leave)
{
let response = services()
.sending
.send_federation_request(
user_id.server_name(),
federation::query::get_profile_information::v1::Request {
user_id: user_id.into(),
field: Some(ProfileField::AvatarUrl),
},
)
.await?;
let _ = services()
.users
.set_displayname(user_id, response.displayname.clone())
.await;
let _ = services()
.users
.set_avatar_url(user_id, response.avatar_url)
.await;
let _ = services()
.users
.set_blurhash(user_id, response.blurhash)
.await;
};
*/
}
match &membership {

View file

@ -18,7 +18,9 @@ use ruma::{
events::{
push_rules::PushRulesEvent,
room::{
create::RoomCreateEventContent, encrypted::Relation, member::MembershipState,
create::RoomCreateEventContent,
encrypted::Relation,
member::{MembershipState, RoomMemberEventContent},
power_levels::RoomPowerLevelsEventContent,
},
GlobalAccountDataEventType, StateEventType, TimelineEventType,
@ -437,7 +439,13 @@ impl Service {
self.redact_pdu(redact_id, pdu)?;
}
}
_ => panic!("Unexpected room version {}", room_version_id),
_ => {
warn!("Unexpected or unsupported room version {}", room_version_id);
return Err(Error::BadRequest(
ErrorKind::BadJson,
"Unexpected or unsupported room version found",
));
}
};
}
TimelineEventType::SpaceChild => {
@ -453,17 +461,15 @@ impl Service {
}
TimelineEventType::RoomMember => {
if let Some(state_key) = &pdu.state_key {
#[derive(Deserialize)]
struct ExtractMembership {
membership: MembershipState,
}
// if the state_key fails
let target_user_id = UserId::parse(state_key.clone())
.expect("This state_key was previously validated");
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
let content = serde_json::from_str::<RoomMemberEventContent>(pdu.content.get())
.map_err(|e| {
error!("Invalid room member event content in pdu: {e}");
Error::bad_database("Invalid room member event content in pdu.")
})?;
let invite_state = match content.membership {
MembershipState::Invite => {
@ -481,7 +487,7 @@ impl Service {
.update_membership(
&pdu.room_id,
&target_user_id,
content.membership,
content,
&pdu.sender,
invite_state,
true,

View file

@ -314,7 +314,11 @@ impl Service {
}
/// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change.
pub fn set_displayname(&self, user_id: &UserId, displayname: Option<String>) -> Result<()> {
pub async fn set_displayname(
&self,
user_id: &UserId,
displayname: Option<String>,
) -> Result<()> {
self.db.set_displayname(user_id, displayname)
}
@ -324,7 +328,11 @@ impl Service {
}
/// Sets a new avatar_url or removes it if avatar_url is None.
pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()> {
pub async fn set_avatar_url(
&self,
user_id: &UserId,
avatar_url: Option<OwnedMxcUri>,
) -> Result<()> {
self.db.set_avatar_url(user_id, avatar_url)
}
@ -334,7 +342,7 @@ impl Service {
}
/// Sets a new avatar_url or removes it if avatar_url is None.
pub fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()> {
pub async fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()> {
self.db.set_blurhash(user_id, blurhash)
}

View file

@ -11,33 +11,18 @@ use ruma::{
use thiserror::Error;
use tracing::{error, info};
#[cfg(feature = "persy")]
use persy::PersyError;
use crate::RumaResponse;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Error, Debug)]
pub enum Error {
#[cfg(feature = "sled")]
#[error("There was a problem with the connection to the sled database.")]
SledError {
#[from]
source: sled::Error,
},
#[cfg(feature = "sqlite")]
#[error("There was a problem with the connection to the sqlite database: {source}")]
SqliteError {
#[from]
source: rusqlite::Error,
},
#[cfg(feature = "persy")]
#[error("There was a problem with the connection to the persy database.")]
PersyError { source: PersyError },
#[cfg(feature = "heed")]
#[error("There was a problem with the connection to the heed database: {error}")]
HeedError { error: String },
#[cfg(feature = "rocksdb")]
#[error("There was a problem with the connection to the rocksdb database: {source}")]
RocksDbError {
@ -150,14 +135,8 @@ impl Error {
let db_error = String::from("Database or I/O error occurred.");
match self {
#[cfg(feature = "sled")]
Self::SledError { .. } => db_error,
#[cfg(feature = "sqlite")]
Self::SqliteError { .. } => db_error,
#[cfg(feature = "persy")]
Self::PersyError { .. } => db_error,
#[cfg(feature = "heed")]
Self::HeedError => db_error,
#[cfg(feature = "rocksdb")]
Self::RocksDbError { .. } => db_error,
Self::IoError { .. } => db_error,
@ -168,15 +147,6 @@ impl Error {
}
}
#[cfg(feature = "persy")]
impl<T: Into<PersyError>> From<persy::PE<T>> for Error {
fn from(err: persy::PE<T>) -> Self {
Error::PersyError {
source: err.error().into(),
}
}
}
impl From<Infallible> for Error {
fn from(i: Infallible) -> Self {
match i {}