mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-07-04 23:21:37 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e713c7c9e1 | |||
| 5901efef71 | |||
| a5cb5d812b | |||
| fc93f79a0c | |||
| 8d1e6a167e | |||
| 3746f3c80b | |||
| d495ad2541 | |||
| be1c344787 | |||
| d1c7ee8769 | |||
| d6a2760903 |
Generated
+106
-122
@@ -974,6 +974,7 @@ dependencies = [
|
||||
name = "conduwuit_admin"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"clap",
|
||||
"conduwuit_api",
|
||||
"conduwuit_core",
|
||||
@@ -996,6 +997,7 @@ dependencies = [
|
||||
name = "conduwuit_api"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"axum-client-ip",
|
||||
@@ -1017,6 +1019,7 @@ dependencies = [
|
||||
"rand 0.10.0",
|
||||
"reqwest",
|
||||
"ruma",
|
||||
"ruminuwuity",
|
||||
"serde",
|
||||
"serde_html_form",
|
||||
"serde_json",
|
||||
@@ -1038,6 +1041,7 @@ version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"arrayvec",
|
||||
"assign",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"bytes",
|
||||
@@ -1089,7 +1093,7 @@ dependencies = [
|
||||
"tikv-jemallocator",
|
||||
"tokio",
|
||||
"tokio-metrics",
|
||||
"toml 0.9.12+spec-1.1.0",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
@@ -1128,6 +1132,7 @@ dependencies = [
|
||||
name = "conduwuit_router"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"axum",
|
||||
"axum-client-ip",
|
||||
"axum-server",
|
||||
@@ -1163,6 +1168,7 @@ name = "conduwuit_service"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"assign",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"blurhash",
|
||||
@@ -1189,6 +1195,7 @@ dependencies = [
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ruma",
|
||||
"ruminuwuity",
|
||||
"rustyline-async",
|
||||
"sd-notify",
|
||||
"serde",
|
||||
@@ -1208,6 +1215,7 @@ name = "conduwuit_web"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"assign",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
@@ -1288,16 +1296,6 @@ dependencies = [
|
||||
"typewit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "continuwuity-admin-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
@@ -1723,16 +1721,6 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "draupnir-antispam"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtor"
|
||||
version = "0.1.1"
|
||||
@@ -2889,30 +2877,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js_option"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68421373957a1593a767013698dbf206e2b221eefe97a44d98d18672ff38423c"
|
||||
checksum = "c7dd3e281add16813cf673bf74a32249b0aa0d1c8117519a17b3ada5e8552b3c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "konst"
|
||||
version = "0.3.16"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9"
|
||||
checksum = "f660d5f887e3562f9ab6f4a14988795b694099d66b4f5dedc02d197ba9becb1d"
|
||||
dependencies = [
|
||||
"const_panic",
|
||||
"konst_kernel",
|
||||
"typewit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "konst_kernel"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c"
|
||||
dependencies = [
|
||||
"typewit",
|
||||
]
|
||||
|
||||
@@ -3238,16 +3216,6 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "meowlnir-antispam"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@@ -4291,6 +4259,8 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
@@ -4300,7 +4270,7 @@ version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
@@ -4315,6 +4285,16 @@ dependencies = [
|
||||
"rand_core 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
@@ -4377,7 +4357,7 @@ dependencies = [
|
||||
"paste",
|
||||
"profiling",
|
||||
"rand 0.9.2",
|
||||
"rand_chacha",
|
||||
"rand_chacha 0.9.0",
|
||||
"simd_helpers",
|
||||
"thiserror 2.0.18",
|
||||
"v_frame",
|
||||
@@ -4544,31 +4524,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.10.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.14.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"continuwuity-admin-api",
|
||||
"draupnir-antispam",
|
||||
"js_int",
|
||||
"js_option",
|
||||
"meowlnir-antispam",
|
||||
"ruma-appservice-api",
|
||||
"ruma-client-api",
|
||||
"ruma-common",
|
||||
"ruma-events",
|
||||
"ruma-federation-api",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-identity-service-api",
|
||||
"ruma-push-gateway-api",
|
||||
"ruma-signatures",
|
||||
"ruma-state-res",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.10.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.14.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4579,13 +4555,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.18.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.22.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
"bytes",
|
||||
"date_header",
|
||||
"http",
|
||||
"js_int",
|
||||
"js_option",
|
||||
@@ -4602,12 +4577,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.17.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"date_header",
|
||||
"form_urlencoded",
|
||||
"getrandom 0.2.17",
|
||||
"http",
|
||||
@@ -4615,14 +4591,13 @@ dependencies = [
|
||||
"js_int",
|
||||
"konst",
|
||||
"percent-encoding",
|
||||
"rand 0.10.0",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_html_form",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"tracing",
|
||||
@@ -4630,37 +4605,34 @@ dependencies = [
|
||||
"uuid",
|
||||
"web-time",
|
||||
"wildmatch",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.28.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.32.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap",
|
||||
"js_int",
|
||||
"js_option",
|
||||
"percent-encoding",
|
||||
"pulldown-cmark",
|
||||
"regex",
|
||||
"ruma-common",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"thiserror 2.0.18",
|
||||
"tracing",
|
||||
"url",
|
||||
"web-time",
|
||||
"wildmatch",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.13.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
@@ -4670,9 +4642,10 @@ dependencies = [
|
||||
"js_int",
|
||||
"memchr",
|
||||
"mime",
|
||||
"rand 0.10.0",
|
||||
"rand 0.8.5",
|
||||
"ruma-common",
|
||||
"ruma-events",
|
||||
"ruma-signatures",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
@@ -4681,28 +4654,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.17.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"cfg-if",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -4710,13 +4674,13 @@ dependencies = [
|
||||
"ruma-identifiers-validation",
|
||||
"serde",
|
||||
"syn",
|
||||
"toml 0.8.23",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4727,21 +4691,46 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.15.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
version = "0.19.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
"memchr",
|
||||
"pkcs8",
|
||||
"rand 0.10.0",
|
||||
"rand_core 0.6.4",
|
||||
"rand 0.8.5",
|
||||
"ruma-common",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"subslice",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=e2e5fece57800a2559ab028fd775a7978f3725e9#e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
"ruma-events",
|
||||
"ruma-signatures",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruminuwuity"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wildmatch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-librocksdb-sys"
|
||||
version = "0.42.0+10.10.1"
|
||||
@@ -5226,9 +5215,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.4"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -5476,15 +5465,6 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subslice"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a8e4809a3bb02de01f1f7faf1ba01a83af9e8eabcd4d31dd6e413d14d56aae"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -5806,13 +5786,26 @@ checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned 1.0.4",
|
||||
"serde_spanned 1.1.1",
|
||||
"toml_datetime 0.7.5+spec-1.1.0",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow 0.7.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_spanned 1.1.1",
|
||||
"toml_datetime 1.1.1+spec-1.1.0",
|
||||
"toml_parser",
|
||||
"winnow 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
@@ -5833,9 +5826,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.0.1+spec-1.1.0"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -5861,16 +5854,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime 1.0.1+spec-1.1.0",
|
||||
"toml_datetime 1.1.1+spec-1.1.0",
|
||||
"toml_parser",
|
||||
"winnow 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.10+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow 1.0.0",
|
||||
]
|
||||
@@ -6111,15 +6104,6 @@ name = "typewit"
|
||||
version = "1.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71"
|
||||
dependencies = [
|
||||
"typewit_proc_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typewit_proc_macros"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"
|
||||
|
||||
[[package]]
|
||||
name = "uname"
|
||||
|
||||
+18
-20
@@ -47,9 +47,9 @@ default-features = false
|
||||
features = ["features"]
|
||||
|
||||
[workspace.dependencies.toml]
|
||||
version = "0.9.5"
|
||||
version = "1.0.0"
|
||||
default-features = false
|
||||
features = ["parse"]
|
||||
features = ["parse", "serde"]
|
||||
|
||||
[workspace.dependencies.sanitize-filename]
|
||||
version = "0.6.0"
|
||||
@@ -340,51 +340,45 @@ version = "0.1.88"
|
||||
[workspace.dependencies.lru-cache]
|
||||
version = "0.1.2"
|
||||
|
||||
[workspace.dependencies.assign]
|
||||
version = "1.1.1"
|
||||
|
||||
# Used for matrix spec type definitions and helpers
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
# version = "0.14.1"
|
||||
git = "https://github.com/ruma/ruma.git"
|
||||
rev = "e2e5fece57800a2559ab028fd775a7978f3725e9"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
"appservice-api-c",
|
||||
"client-api",
|
||||
"federation-api",
|
||||
"markdown",
|
||||
"push-gateway-api-c",
|
||||
"unstable-exhaustive-types",
|
||||
"state-res",
|
||||
"rand",
|
||||
"markdown",
|
||||
"ring-compat",
|
||||
"compat-upload-signatures",
|
||||
"identifiers-validation",
|
||||
"unstable-unspecified",
|
||||
"unstable-msc2448",
|
||||
"unstable-msc2666",
|
||||
"unstable-msc2867",
|
||||
"unstable-msc2870",
|
||||
"unstable-msc3026",
|
||||
"unstable-msc3061",
|
||||
"unstable-msc3814",
|
||||
"unstable-msc3245",
|
||||
"unstable-msc3266",
|
||||
"unstable-msc3381", # polls
|
||||
"unstable-msc3489", # beacon / live location
|
||||
"unstable-msc3575",
|
||||
"unstable-msc3930", # polls push rules
|
||||
"unstable-msc4075",
|
||||
"unstable-msc4095",
|
||||
"unstable-msc4121",
|
||||
"unstable-msc4125",
|
||||
"unstable-msc4155",
|
||||
"unstable-msc4186",
|
||||
"unstable-msc4203", # sending to-device events to appservices
|
||||
"unstable-msc4210", # remove legacy mentions
|
||||
"unstable-extensible-events",
|
||||
"unstable-pdu",
|
||||
"unstable-msc4155",
|
||||
"unstable-msc4310",
|
||||
"unstable-msc4380",
|
||||
"unstable-msc4143", # livekit well_known response
|
||||
"unstable-msc4284",
|
||||
"unstable-msc4439", # pgp_key in .well_known/matrix/support
|
||||
"unstable-extensible-events",
|
||||
]
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
@@ -657,6 +651,10 @@ default-features = false
|
||||
package = "conduwuit"
|
||||
path = "src/main"
|
||||
|
||||
[workspace.dependencies.ruminuwuity]
|
||||
package = "ruminuwuity"
|
||||
path = "src/ruminuwuity"
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Release profiles
|
||||
|
||||
@@ -82,6 +82,7 @@ const-str.workspace = true
|
||||
futures.workspace = true
|
||||
lettre.workspace = true
|
||||
log.workspace = true
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde-saphyr.workspace = true
|
||||
|
||||
+15
-22
@@ -9,7 +9,7 @@
|
||||
};
|
||||
use conduwuit::{
|
||||
Err, Result, debug_warn, error, info,
|
||||
matrix::{Event, pdu::PduBuilder},
|
||||
matrix::{Event, pdu::PartialPdu},
|
||||
utils::{self, ReadyExt},
|
||||
warn,
|
||||
};
|
||||
@@ -732,40 +732,33 @@ pub(super) async fn force_demote(&self, user_id: String, room_id: OwnedRoomOrAli
|
||||
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||
|
||||
let room_power_levels: Option<RoomPowerLevelsEventContent> = self
|
||||
let mut room_power_levels = self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.await
|
||||
.ok();
|
||||
.get_room_power_levels(&room_id)
|
||||
.await;
|
||||
|
||||
let user_can_demote_self = room_power_levels
|
||||
.as_ref()
|
||||
.is_some_and(|power_levels_content| {
|
||||
RoomPowerLevels::from(power_levels_content.clone())
|
||||
.user_can_change_user_power_level(&user_id, &user_id)
|
||||
}) || self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
let user_can_demote_self =
|
||||
room_power_levels.user_can_change_user_power_level(&user_id, &user_id);
|
||||
|
||||
if !user_can_demote_self {
|
||||
return Err!("User is not allowed to modify their own power levels in the room.",);
|
||||
}
|
||||
|
||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||
power_levels_content.users.remove(&user_id);
|
||||
room_power_levels.users.remove(&user_id);
|
||||
|
||||
let event_id = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &power_levels_content),
|
||||
PartialPdu::state(
|
||||
String::new(),
|
||||
room_power_levels
|
||||
.try_into()
|
||||
.expect("PLs should be valid for room version"),
|
||||
),
|
||||
&user_id,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -928,9 +921,9 @@ pub(super) async fn redact_event(&self, event_id: OwnedEventId) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
PartialPdu {
|
||||
redacts: Some(event.event_id().to_owned()),
|
||||
..PduBuilder::timeline(&RoomRedactionEventContent {
|
||||
..PartialPdu::timeline(&RoomRedactionEventContent {
|
||||
redacts: Some(event.event_id().to_owned()),
|
||||
reason: Some(reason),
|
||||
})
|
||||
|
||||
@@ -89,7 +89,9 @@ lettre.workspace = true
|
||||
log.workspace = true
|
||||
rand.workspace = true
|
||||
reqwest.workspace = true
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
ruminuwuity.workspace = true
|
||||
serde_html_form.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, info, utils::ReadyExt, warn};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedRoomAliasId, continuwuity_admin_api::rooms,
|
||||
events::room::message::RoomMessageEventContent,
|
||||
};
|
||||
use ruma::{OwnedRoomAliasId, events::room::message::RoomMessageEventContent};
|
||||
use ruminuwuity::admin::continuwuity::rooms;
|
||||
|
||||
use crate::{Ruma, client::leave_room};
|
||||
|
||||
@@ -36,7 +34,6 @@ pub(crate) async fn ban_room(
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.ready_filter(|user| services.globals.user_is_local(user))
|
||||
.boxed();
|
||||
let mut evicted = Vec::new();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::{OwnedRoomId, continuwuity_admin_api::rooms};
|
||||
use ruma::OwnedRoomId;
|
||||
use ruminuwuity::admin::continuwuity::rooms;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -22,7 +23,7 @@ pub(crate) async fn list_rooms(
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(|room_id| async move {
|
||||
if !services.rooms.metadata.is_banned(room_id).await {
|
||||
if !services.rooms.metadata.is_banned(&room_id).await {
|
||||
Some(room_id.to_owned())
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Event, Result, err, info,
|
||||
pdu::PduBuilder,
|
||||
Err, Result, err, info,
|
||||
pdu::PartialPdu,
|
||||
utils::{ReadyExt, stream::BroadbandExt},
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use lettre::{Address, message::Mailbox};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId, UserId,
|
||||
OwnedRoomId, UserId,
|
||||
api::client::{
|
||||
account::{
|
||||
ThirdPartyIdRemovalStatus, change_password, check_registration_token_validity,
|
||||
@@ -18,12 +18,10 @@
|
||||
},
|
||||
uiaa::{AuthFlow, AuthType},
|
||||
},
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
assign,
|
||||
events::room::{
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
},
|
||||
};
|
||||
use service::{mailer::messages, uiaa::Identity};
|
||||
@@ -87,7 +85,7 @@ pub(crate) async fn get_register_available_route(
|
||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
||||
}
|
||||
|
||||
Ok(get_username_availability::v3::Response { available: true })
|
||||
Ok(get_username_availability::v3::Response::new(true))
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/account/password`
|
||||
@@ -143,7 +141,7 @@ pub(crate) async fn change_password_route(
|
||||
.await?
|
||||
};
|
||||
|
||||
let sender_user = OwnedUserId::parse(format!(
|
||||
let sender_user = UserId::parse(format!(
|
||||
"@{}:{}",
|
||||
identity.localpart.expect("localpart should be known"),
|
||||
services.globals.server_name()
|
||||
@@ -161,7 +159,7 @@ pub(crate) async fn change_password_route(
|
||||
.users
|
||||
.all_device_ids(&sender_user)
|
||||
.ready_filter(|id| *id != body.sender_device())
|
||||
.for_each(|id| services.users.remove_device(&sender_user, id))
|
||||
.for_each(async |id| services.users.remove_device(&sender_user, &id).await)
|
||||
.await;
|
||||
|
||||
// Remove all pushers except the ones associated with this session
|
||||
@@ -194,7 +192,7 @@ pub(crate) async fn change_password_route(
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(change_password::v3::Response {})
|
||||
Ok(change_password::v3::Response::new())
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v3/account/password/email/requestToken`
|
||||
@@ -215,7 +213,7 @@ pub(crate) async fn request_password_change_token_via_email_route(
|
||||
};
|
||||
|
||||
let user_id =
|
||||
OwnedUserId::parse(format!("@{localpart}:{}", services.globals.server_name())).unwrap();
|
||||
UserId::parse(format!("@{localpart}:{}", services.globals.server_name())).unwrap();
|
||||
let display_name = services.users.displayname(&user_id).await.ok();
|
||||
|
||||
let session = services
|
||||
@@ -251,11 +249,10 @@ pub(crate) async fn whoami_route(
|
||||
.map_err(|_| {
|
||||
err!(Request(Forbidden("Application service has not registered this user.")))
|
||||
})? && body.appservice_info.is_none();
|
||||
Ok(whoami::v3::Response {
|
||||
user_id: body.sender_user().to_owned(),
|
||||
|
||||
Ok(assign!(whoami::v3::Response::new(body.sender_user().to_owned(), is_guest), {
|
||||
device_id: body.sender_device.clone(),
|
||||
is_guest,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/account/deactivate`
|
||||
@@ -310,9 +307,7 @@ pub(crate) async fn deactivate_route(
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(deactivate::v3::Response {
|
||||
id_server_unbind_result: ThirdPartyIdRemovalStatus::Success,
|
||||
})
|
||||
Ok(deactivate::v3::Response::new(ThirdPartyIdRemovalStatus::Success))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v1/register/m.login.registration_token/validity`
|
||||
@@ -330,7 +325,7 @@ pub(crate) async fn check_registration_token_validity(
|
||||
.await
|
||||
.is_some();
|
||||
|
||||
Ok(check_registration_token_validity::v1::Response { valid })
|
||||
Ok(check_registration_token_validity::v1::Response::new(valid))
|
||||
}
|
||||
|
||||
/// Runs through all the deactivation steps:
|
||||
@@ -372,53 +367,33 @@ pub async fn full_user_deactivate(
|
||||
|
||||
// TODO: Rescind all user invites
|
||||
|
||||
let mut pdu_queue: Vec<(PduBuilder, &OwnedRoomId)> = Vec::new();
|
||||
let mut pdu_queue: Vec<(PartialPdu, &OwnedRoomId)> = Vec::new();
|
||||
|
||||
for room_id in all_joined_rooms {
|
||||
let room_power_levels = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomPowerLevelsEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomPowerLevels,
|
||||
"",
|
||||
)
|
||||
.await
|
||||
.ok();
|
||||
.get_room_power_levels(&room_id)
|
||||
.await;
|
||||
|
||||
let user_can_demote_self =
|
||||
room_power_levels
|
||||
.as_ref()
|
||||
.is_some_and(|power_levels_content| {
|
||||
RoomPowerLevels::from(power_levels_content.clone())
|
||||
.user_can_change_user_power_level(user_id, user_id)
|
||||
}) || services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
room_power_levels.user_can_change_user_power_level(user_id, user_id);
|
||||
|
||||
if user_can_demote_self {
|
||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||
if user_can_demote_self
|
||||
&& let Ok(mut power_levels_content) =
|
||||
RoomPowerLevelsEventContent::try_from(room_power_levels)
|
||||
{
|
||||
power_levels_content.users.remove(user_id);
|
||||
let pl_evt = PduBuilder::state(String::new(), &power_levels_content);
|
||||
let pl_evt = PartialPdu::state(String::new(), &power_levels_content);
|
||||
pdu_queue.push((pl_evt, room_id));
|
||||
}
|
||||
|
||||
// Leave the room
|
||||
pdu_queue.push((
|
||||
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
avatar_url: None,
|
||||
blurhash: None,
|
||||
membership: MembershipState::Leave,
|
||||
displayname: None,
|
||||
join_authorized_via_users_server: None,
|
||||
reason: None,
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
redact_events: None,
|
||||
}),
|
||||
PartialPdu::state(
|
||||
user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Leave),
|
||||
),
|
||||
room_id,
|
||||
));
|
||||
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
},
|
||||
uiaa::{AuthFlow, AuthType},
|
||||
},
|
||||
events::{GlobalAccountDataEventType, room::message::RoomMessageEventContent},
|
||||
assign,
|
||||
events::{
|
||||
GlobalAccountDataEventType, push_rules::PushRulesEvent,
|
||||
room::message::RoomMessageEventContent,
|
||||
},
|
||||
push,
|
||||
};
|
||||
use serde_json::value::RawValue;
|
||||
@@ -209,23 +213,15 @@ pub(crate) async fn register_route(
|
||||
None,
|
||||
&user_id,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(ruma::events::push_rules::PushRulesEvent {
|
||||
content: ruma::events::push_rules::PushRulesEventContent {
|
||||
global: push::Ruleset::server_default(&user_id),
|
||||
},
|
||||
})?,
|
||||
&serde_json::to_value(PushRulesEvent::new(
|
||||
push::Ruleset::server_default(&user_id).into(),
|
||||
))
|
||||
.expect("should be able to serialize push rules"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Generate new device id if the user didn't specify one
|
||||
let no_device = body.inhibit_login
|
||||
|| body
|
||||
.appservice_info
|
||||
.as_ref()
|
||||
.is_some_and(|aps| aps.registration.device_management);
|
||||
|
||||
let (token, device) = if !no_device {
|
||||
// Don't create a device for inhibited logins
|
||||
let (token, device) = if !body.inhibit_login {
|
||||
let device_id = if is_guest { None } else { body.device_id.clone() }
|
||||
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
||||
|
||||
@@ -243,12 +239,14 @@ pub(crate) async fn register_route(
|
||||
Some(client.to_string()),
|
||||
)
|
||||
.await?;
|
||||
debug_info!(%user_id, %device_id, "User account was created");
|
||||
(Some(new_token), Some(device_id))
|
||||
} else {
|
||||
// Don't create a device for inhibited logins
|
||||
(None, None)
|
||||
};
|
||||
|
||||
debug_info!(%user_id, ?device, "User account was created");
|
||||
|
||||
// If the user registered with an email, associate it with their account.
|
||||
if let Some(identity) = identity
|
||||
&& let Some(email) = identity.email
|
||||
@@ -393,13 +391,12 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(register::v3::Response {
|
||||
Ok(assign!(register::v3::Response::new(user_id), {
|
||||
access_token: token,
|
||||
user_id,
|
||||
device_id: device,
|
||||
refresh_token: None,
|
||||
expires_in: None,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Determine which flows and parameters should be presented when
|
||||
|
||||
@@ -40,7 +40,7 @@ pub(crate) async fn set_global_account_data_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(set_global_account_data::v3::Response {})
|
||||
Ok(set_global_account_data::v3::Response::new())
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
|
||||
@@ -65,7 +65,7 @@ pub(crate) async fn set_room_account_data_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(set_room_account_data::v3::Response {})
|
||||
Ok(set_room_account_data::v3::Response::new())
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/user/{userId}/account_data/{type}`
|
||||
@@ -119,7 +119,7 @@ async fn set_account_data(
|
||||
event_type_s: &str,
|
||||
data: &RawJsonValue,
|
||||
) -> Result {
|
||||
if event_type_s == RoomAccountDataEventType::FullyRead.to_cow_str() {
|
||||
if event_type_s == "m.fully_read" {
|
||||
return Err!(Request(BadJson(
|
||||
"This endpoint cannot be used for marking a room as fully read (setting \
|
||||
m.fully_read)"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::future::{join, join3};
|
||||
use ruma::api::client::admin::{get_suspended, set_suspended};
|
||||
use ruminuwuity::admin::{get_suspended, set_suspended};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
|
||||
@@ -47,5 +47,5 @@ pub(crate) async fn appservice_ping(
|
||||
.await?
|
||||
.expect("We already validated if an appservice URL exists above");
|
||||
|
||||
Ok(request_ping::v1::Response { duration: timer.elapsed() })
|
||||
Ok(request_ping::v1::Response::new(timer.elapsed()))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(crate) async fn create_backup_version_route(
|
||||
.key_backups
|
||||
.create_backup(body.sender_user(), &body.algorithm)?;
|
||||
|
||||
Ok(create_backup_version::v3::Response { version })
|
||||
Ok(create_backup_version::v3::Response::new(version))
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/r0/room_keys/version/{version}`
|
||||
@@ -44,7 +44,7 @@ pub(crate) async fn update_backup_version_route(
|
||||
.update_backup(body.sender_user(), &body.version, &body.algorithm)
|
||||
.await?;
|
||||
|
||||
Ok(update_backup_version::v3::Response {})
|
||||
Ok(update_backup_version::v3::Response::new())
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/room_keys/version`
|
||||
@@ -105,7 +105,7 @@ pub(crate) async fn delete_backup_version_route(
|
||||
.delete_backup(body.sender_user(), &body.version)
|
||||
.await;
|
||||
|
||||
Ok(delete_backup_version::v3::Response {})
|
||||
Ok(delete_backup_version::v3::Response::new())
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/r0/room_keys/keys`
|
||||
@@ -292,7 +292,7 @@ pub(crate) async fn get_backup_keys_route(
|
||||
.get_all(body.sender_user(), &body.version)
|
||||
.await;
|
||||
|
||||
Ok(get_backup_keys::v3::Response { rooms })
|
||||
Ok(get_backup_keys::v3::Response::new(rooms))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||
@@ -307,7 +307,7 @@ pub(crate) async fn get_backup_keys_for_room_route(
|
||||
.get_room(body.sender_user(), &body.version, &body.room_id)
|
||||
.await;
|
||||
|
||||
Ok(get_backup_keys_for_room::v3::Response { sessions })
|
||||
Ok(get_backup_keys_for_room::v3::Response::new(sessions))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||
@@ -325,7 +325,7 @@ pub(crate) async fn get_backup_keys_for_session_route(
|
||||
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
|
||||
})?;
|
||||
|
||||
Ok(get_backup_keys_for_session::v3::Response { key_data })
|
||||
Ok(get_backup_keys_for_session::v3::Response::new(key_data))
|
||||
}
|
||||
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys`
|
||||
@@ -342,7 +342,7 @@ pub(crate) async fn delete_backup_keys_route(
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(delete_backup_keys::v3::Response { count, etag })
|
||||
Ok(delete_backup_keys::v3::Response::new(etag, count))
|
||||
}
|
||||
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||
@@ -359,7 +359,7 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(delete_backup_keys_for_room::v3::Response { count, etag })
|
||||
Ok(delete_backup_keys_for_room::v3::Response::new(etag, count))
|
||||
}
|
||||
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||
@@ -376,7 +376,7 @@ pub(crate) async fn delete_backup_keys_for_session_route(
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(delete_backup_keys_for_session::v3::Response { count, etag })
|
||||
Ok(delete_backup_keys_for_session::v3::Response::new(etag, count))
|
||||
}
|
||||
|
||||
async fn get_count_etag(
|
||||
|
||||
@@ -56,5 +56,5 @@ pub(crate) async fn get_capabilities_route(
|
||||
capabilities.set("uk.timedout.msc4323", json!({"suspend": true, "lock": false}))?;
|
||||
}
|
||||
|
||||
Ok(get_capabilities::v3::Response { capabilities })
|
||||
Ok(get_capabilities::v3::Response::new(capabilities))
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ pub(crate) async fn put_dehydrated_device_route(
|
||||
.set_dehydrated_device(sender_user, body.body)
|
||||
.await?;
|
||||
|
||||
Ok(put_dehydrated_device::Response { device_id })
|
||||
Ok(put_dehydrated_device::Response::new(device_id))
|
||||
}
|
||||
|
||||
/// # `DELETE /_matrix/client/../dehydrated_device`
|
||||
@@ -51,7 +51,7 @@ pub(crate) async fn delete_dehydrated_device_route(
|
||||
|
||||
services.users.remove_device(sender_user, &device_id).await;
|
||||
|
||||
Ok(delete_dehydrated_device::Response { device_id })
|
||||
Ok(delete_dehydrated_device::Response::new(device_id))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/../dehydrated_device`
|
||||
@@ -67,10 +67,7 @@ pub(crate) async fn get_dehydrated_device_route(
|
||||
|
||||
let device = services.users.get_dehydrated_device(sender_user).await?;
|
||||
|
||||
Ok(get_dehydrated_device::Response {
|
||||
device_id: device.device_id,
|
||||
device_data: device.device_data,
|
||||
})
|
||||
Ok(get_dehydrated_device::Response::new(device.device_id, device.device_data))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/../dehydrated_device/{device_id}/events`
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(crate) async fn get_devices_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(get_devices::v3::Response { devices })
|
||||
Ok(get_devices::v3::Response::new(devices))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
||||
@@ -41,7 +41,7 @@ pub(crate) async fn get_device_route(
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
||||
|
||||
Ok(get_device::v3::Response { device })
|
||||
Ok(get_device::v3::Response::new(device))
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
||||
@@ -73,19 +73,16 @@ pub(crate) async fn update_device_route(
|
||||
.update_device_metadata(sender_user, &body.device_id, &device)
|
||||
.await?;
|
||||
|
||||
Ok(update_device::v3::Response {})
|
||||
Ok(update_device::v3::Response::new())
|
||||
},
|
||||
| Err(_) => {
|
||||
let Some(appservice) = appservice else {
|
||||
return Err!(Request(NotFound("Device not found.")));
|
||||
};
|
||||
if !appservice.registration.device_management {
|
||||
return Err!(Request(NotFound("Device not found.")));
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Creating new device for {sender_user} from appservice {} as MSC4190 is enabled \
|
||||
and device ID does not exist",
|
||||
"Creating new device for {sender_user} from appservice {} as device ID does not \
|
||||
exist",
|
||||
appservice.registration.id
|
||||
);
|
||||
|
||||
@@ -102,7 +99,7 @@ pub(crate) async fn update_device_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
return Ok(update_device::v3::Response {});
|
||||
return Ok(update_device::v3::Response::new());
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -124,17 +121,14 @@ pub(crate) async fn delete_device_route(
|
||||
let sender_user = body.sender_user();
|
||||
let appservice = body.appservice_info.as_ref();
|
||||
|
||||
if appservice.is_some_and(|appservice| appservice.registration.device_management) {
|
||||
debug!(
|
||||
"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
|
||||
enabled"
|
||||
);
|
||||
if appservice.is_some() {
|
||||
debug!("Skipping UIAA for {sender_user} as this is from an appservice");
|
||||
services
|
||||
.users
|
||||
.remove_device(sender_user, &body.device_id)
|
||||
.await;
|
||||
|
||||
return Ok(delete_device::v3::Response {});
|
||||
return Ok(delete_device::v3::Response::new());
|
||||
}
|
||||
|
||||
// Prompt the user to confirm with their password using UIAA
|
||||
|
||||
+10
-36
@@ -26,7 +26,7 @@
|
||||
},
|
||||
federation,
|
||||
},
|
||||
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork, RoomTypeFilter},
|
||||
directory::{Filter, PublicRoomsChunk, RoomNetwork, RoomTypeFilter},
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
@@ -34,6 +34,7 @@
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
},
|
||||
room::JoinRuleKind,
|
||||
uint,
|
||||
};
|
||||
|
||||
@@ -142,7 +143,13 @@ pub(crate) async fn set_room_visibility_route(
|
||||
return Err!(Request(Forbidden("Guests cannot publish to room directories")));
|
||||
}
|
||||
|
||||
if !user_can_publish_room(&services, sender_user, &body.room_id).await? {
|
||||
let room_power_levels = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_power_levels(&body.room_id)
|
||||
.await;
|
||||
|
||||
if !room_power_levels.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility) {
|
||||
return Err!(Request(Forbidden("User is not allowed to publish this room")));
|
||||
}
|
||||
|
||||
@@ -339,39 +346,6 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
})
|
||||
}
|
||||
|
||||
/// Check whether the user can publish to the room directory via power levels of
|
||||
/// room history visibility event or room creator
|
||||
async fn user_can_publish_room(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
) -> Result<bool> {
|
||||
match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.await
|
||||
{
|
||||
| Ok(event) => serde_json::from_str(event.content().get())
|
||||
.map_err(|_| err!(Database("Invalid event content for m.room.power_levels")))
|
||||
.map(|content: RoomPowerLevelsEventContent| {
|
||||
RoomPowerLevels::from(content)
|
||||
.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||
}),
|
||||
| _ => {
|
||||
match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
{
|
||||
| Ok(event) => Ok(event.sender() == user_id),
|
||||
| _ => Err!(Request(Forbidden("User is not allowed to publish this room"))),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> PublicRoomsChunk {
|
||||
let name = services.rooms.state_accessor.get_name(&room_id).ok();
|
||||
|
||||
@@ -394,7 +368,7 @@ async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> Public
|
||||
.state_accessor
|
||||
.room_state_get_content(&room_id, &StateEventType::RoomJoinRules, "")
|
||||
.map_ok(|c: RoomJoinRulesEventContent| match c.join_rule {
|
||||
| JoinRule::Public => PublicRoomJoinRule::Public,
|
||||
| JoinRule::Public => JoinRuleKind::Public,
|
||||
| JoinRule::Knock => "knock".into(),
|
||||
| JoinRule::KnockRestricted(_) => "knock_restricted".into(),
|
||||
| _ => "invite".into(),
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
};
|
||||
use reqwest::Url;
|
||||
use ruma::{
|
||||
Mxc, UserId,
|
||||
UserId,
|
||||
api::client::{
|
||||
authenticated_media::{
|
||||
get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use conduwuit::{Err, Result, matrix::pdu::PartialPdu};
|
||||
use ruma::{
|
||||
api::client::membership::ban_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
@@ -24,30 +24,29 @@ pub(crate) async fn ban_user_route(
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
let state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
||||
|
||||
let current_member_content = services
|
||||
let mut content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
.unwrap_or_else(|_| RoomMemberEventContent::new(MembershipState::Ban));
|
||||
|
||||
content.membership = MembershipState::Ban;
|
||||
content.reason = body.reason.clone();
|
||||
content.displayname = None;
|
||||
content.avatar_url = None;
|
||||
content.is_direct = None;
|
||||
content.join_authorized_via_users_server = None;
|
||||
content.third_party_invite = None;
|
||||
// TODO(upstream): MSC4293
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Ban,
|
||||
reason: body.reason.clone(),
|
||||
displayname: None, // display name may be offensive
|
||||
avatar_url: None, // avatar may be offensive
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
redact_events: body.redact_events,
|
||||
..current_member_content
|
||||
}),
|
||||
PartialPdu::state(body.user_id.to_string(), &content),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&state_lock,
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_error, err, info,
|
||||
matrix::{event::gen_event_id_canonical_json, pdu::PduBuilder},
|
||||
matrix::{event::gen_event_id_canonical_json, pdu::PartialPdu},
|
||||
warn,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
RoomId, UserId,
|
||||
api::{client::membership::invite_user, federation::membership::create_invite},
|
||||
events::{
|
||||
invite_permission_config::FilterLevel,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
api::{
|
||||
client::membership::invite_user::{self, v3::InviteUserId},
|
||||
federation::membership::{RawStrippedState, create_invite},
|
||||
},
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||
use service::Services;
|
||||
|
||||
use super::banned_room_check;
|
||||
@@ -51,7 +52,11 @@ pub(crate) async fn invite_user_route(
|
||||
.await?;
|
||||
|
||||
match &body.recipient {
|
||||
| invite_user::v3::InvitationRecipient::UserId { user_id: recipient_user } => {
|
||||
| invite_user::v3::InvitationRecipient::UserId(InviteUserId {
|
||||
user_id: recipient_user,
|
||||
reason,
|
||||
..
|
||||
}) => {
|
||||
let sender_filter_level = services
|
||||
.users
|
||||
.invite_filter_level(recipient_user, sender_user)
|
||||
@@ -59,7 +64,7 @@ pub(crate) async fn invite_user_route(
|
||||
|
||||
if !matches!(sender_filter_level, FilterLevel::Allow) {
|
||||
// drop invites if the sender has the recipient filtered
|
||||
return Ok(invite_user::v3::Response {});
|
||||
return Ok(invite_user::v3::Response::new());
|
||||
}
|
||||
|
||||
if let Ok(target_user_membership) = services
|
||||
@@ -95,13 +100,13 @@ pub(crate) async fn invite_user_route(
|
||||
sender_user,
|
||||
recipient_user,
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
reason.clone(),
|
||||
false,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(invite_user::v3::Response {})
|
||||
Ok(invite_user::v3::Response::new())
|
||||
},
|
||||
| _ => {
|
||||
Err!(Request(NotFound("User not found.")))
|
||||
@@ -141,25 +146,32 @@ pub(crate) async fn invite_helper(
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
avatar_url: services.users.avatar_url(recipient_user).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Invite);
|
||||
content.displayname = services.users.displayname(recipient_user).await.ok();
|
||||
content.avatar_url = services.users.avatar_url(recipient_user).await.ok();
|
||||
content.is_direct = Some(is_direct);
|
||||
content.reason = reason;
|
||||
|
||||
let (pdu, pdu_json) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(recipient_user.to_string(), &content),
|
||||
PartialPdu::state(recipient_user.to_string(), &content),
|
||||
sender_user,
|
||||
Some(room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let invite_room_state = services.rooms.state.summary_stripped(&pdu, room_id).await;
|
||||
#[allow(deprecated)]
|
||||
let invite_room_state = services
|
||||
.rooms
|
||||
.state
|
||||
.summary_stripped(&pdu, room_id)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|event| RawStrippedState::Stripped(event))
|
||||
.collect();
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
@@ -168,32 +180,39 @@ pub(crate) async fn invite_helper(
|
||||
|
||||
let room_version_id = services.rooms.state.get_room_version(room_id).await?;
|
||||
|
||||
let mut request = create_invite::v2::Request::new(
|
||||
room_id.to_owned(),
|
||||
(*pdu.event_id).to_owned(),
|
||||
room_version_id.clone(),
|
||||
services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(pdu_json.clone())
|
||||
.await,
|
||||
invite_room_state,
|
||||
);
|
||||
request.via = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_route_via(room_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let response = services
|
||||
.sending
|
||||
.send_federation_request(recipient_user.server_name(), create_invite::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: (*pdu.event_id).to_owned(),
|
||||
room_version: room_version_id.clone(),
|
||||
event: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(pdu_json.clone())
|
||||
.await,
|
||||
invite_room_state,
|
||||
via: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_route_via(room_id)
|
||||
.await
|
||||
.ok(),
|
||||
})
|
||||
.send_federation_request(recipient_user.server_name(), request)
|
||||
.await?;
|
||||
|
||||
// We do not add the event_id field to the pdu here because of signature and
|
||||
// hashes checks
|
||||
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
let (event_id, value) = gen_event_id_canonical_json(
|
||||
&response.event,
|
||||
&room_version_id
|
||||
.rules()
|
||||
.expect("room version should have defined rules"),
|
||||
)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
if pdu.event_id != event_id {
|
||||
return Err!(Request(BadJson(warn!(
|
||||
@@ -229,19 +248,17 @@ pub(crate) async fn invite_helper(
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(recipient_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(recipient_user).await.ok(),
|
||||
blurhash: services.users.blurhash(recipient_user).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Invite);
|
||||
content.displayname = services.users.displayname(recipient_user).await.ok();
|
||||
content.avatar_url = services.users.avatar_url(recipient_user).await.ok();
|
||||
content.blurhash = services.users.blurhash(recipient_user).await.ok();
|
||||
content.is_direct = Some(is_direct);
|
||||
content.reason = reason;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder::state(recipient_user.to_string(), &content),
|
||||
sender_user,
|
||||
Some(room_id),
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
matrix::{
|
||||
StateKey,
|
||||
event::{gen_event_id, gen_event_id_canonical_json},
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
pdu::{PartialPdu, PduEvent},
|
||||
state_res,
|
||||
},
|
||||
result::FlatOk,
|
||||
@@ -24,10 +24,8 @@
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
|
||||
RoomVersionId, UserId,
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind,
|
||||
membership::{join_room_by_id, join_room_by_id_or_alias},
|
||||
},
|
||||
client::membership::{join_room_by_id, join_room_by_id_or_alias},
|
||||
error::{ErrorKind, IncompatibleRoomVersionErrorData},
|
||||
federation::{self},
|
||||
},
|
||||
canonical_json::to_canonical_value,
|
||||
@@ -89,7 +87,6 @@ pub(crate) async fn join_room_by_id_route(
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
@@ -168,7 +165,6 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
@@ -209,11 +205,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let addl_via_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned);
|
||||
let addl_via_servers = services.rooms.state_cache.servers_invite_via(&room_id);
|
||||
|
||||
let addl_state_servers = services
|
||||
.rooms
|
||||
@@ -226,7 +218,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
.iter()
|
||||
.map(|event| event.get_field("sender"))
|
||||
.filter_map(FlatOk::flat_ok)
|
||||
.map(|user: &UserId| user.server_name().to_owned())
|
||||
.map(|user: OwnedUserId| user.server_name().to_owned())
|
||||
.stream()
|
||||
.chain(addl_via_servers)
|
||||
.collect()
|
||||
@@ -252,7 +244,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id })
|
||||
Ok(join_room_by_id_or_alias::v3::Response::new(join_room_response.room_id))
|
||||
}
|
||||
|
||||
pub async fn join_room_by_id_helper(
|
||||
@@ -283,7 +275,7 @@ pub async fn join_room_by_id_helper(
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already joined in {room_id}");
|
||||
return Ok(join_room_by_id::v3::Response { room_id: room_id.into() });
|
||||
return Ok(join_room_by_id::v3::Response::new(room_id.to_owned()));
|
||||
}
|
||||
|
||||
if let Err(e) = services
|
||||
@@ -383,12 +375,15 @@ async fn join_room_by_id_helper_remote(
|
||||
|
||||
info!("make_join finished");
|
||||
|
||||
let room_version_id = make_join_response.room_version.unwrap_or(RoomVersionId::V1);
|
||||
let room_version = make_join_response.room_version.unwrap_or(RoomVersionId::V1);
|
||||
let room_version_rules = room_version
|
||||
.rules()
|
||||
.expect("room version should have defined rules");
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
if !services.server.supported_room_version(&room_version) {
|
||||
// How did we get here?
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
"Remote room version {room_version} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -401,7 +396,7 @@ async fn join_room_by_id_helper_remote(
|
||||
|
||||
let join_authorized_via_users_server = {
|
||||
use RoomVersionId::*;
|
||||
if !matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
||||
if !matches!(room_version, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
||||
join_event_stub
|
||||
.get("content")
|
||||
.map(|s| {
|
||||
@@ -423,36 +418,30 @@ async fn join_room_by_id_helper_remote(
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
|
||||
let mut join_content = RoomMemberEventContent::new(MembershipState::Join);
|
||||
join_content.displayname = services.users.displayname(sender_user).await.ok();
|
||||
join_content.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
join_content.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
join_content.reason = reason;
|
||||
join_content.join_authorized_via_users_server = join_authorized_via_users_server.clone();
|
||||
|
||||
join_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
join_authorized_via_users_server: join_authorized_via_users_server.clone(),
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
to_canonical_value(join_content).expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// We keep the "event_id" in the pdu only in v1 or
|
||||
// v2 rooms
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
join_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
// Remove event id if it exists
|
||||
join_event_stub.remove("event_id");
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_id)?;
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_rules)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_rules)?;
|
||||
|
||||
// Add event_id back
|
||||
join_event_stub
|
||||
@@ -462,15 +451,14 @@ async fn join_room_by_id_helper_remote(
|
||||
let mut join_event = join_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_join in room {room_id}");
|
||||
let send_join_request = federation::membership::create_join_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
omit_members: false,
|
||||
pdu: services
|
||||
let send_join_request = federation::membership::create_join_event::v2::Request::new(
|
||||
room_id.to_owned(),
|
||||
event_id.clone(),
|
||||
services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(join_event.clone())
|
||||
.await,
|
||||
};
|
||||
);
|
||||
|
||||
let send_join_response = match services
|
||||
.sending
|
||||
@@ -494,7 +482,7 @@ async fn join_room_by_id_helper_remote(
|
||||
);
|
||||
|
||||
let (signed_event_id, signed_value) =
|
||||
gen_event_id_canonical_json(signed_raw, &room_version_id).map_err(|e| {
|
||||
gen_event_id_canonical_json(signed_raw, &room_version_rules).map_err(|e| {
|
||||
err!(Request(BadJson(warn!(
|
||||
"Could not convert event to canonical JSON: {e}"
|
||||
))))
|
||||
@@ -569,7 +557,7 @@ async fn join_room_by_id_helper_remote(
|
||||
.then(|pdu| {
|
||||
services
|
||||
.server_keys
|
||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
|
||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_rules)
|
||||
.inspect_err(|e| {
|
||||
debug_warn!("Could not validate send_join response room_state event: {e:?}");
|
||||
})
|
||||
@@ -617,7 +605,7 @@ async fn join_room_by_id_helper_remote(
|
||||
.then(|pdu| {
|
||||
services
|
||||
.server_keys
|
||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
|
||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_rules)
|
||||
})
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_for_each(|(event_id, value)| {
|
||||
@@ -638,7 +626,7 @@ async fn join_room_by_id_helper_remote(
|
||||
};
|
||||
|
||||
let auth_check = state_res::event_auth::auth_check(
|
||||
&state_res::RoomVersion::new(&room_version_id)?,
|
||||
&room_version.rules().unwrap(),
|
||||
&parsed_join_pdu,
|
||||
None, // TODO: third party invite
|
||||
|k, s| state_fetch(k.clone(), s.into()),
|
||||
@@ -759,21 +747,19 @@ async fn join_room_by_id_helper_local(
|
||||
}
|
||||
}
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: reason.clone(),
|
||||
join_authorized_via_users_server: auth_user,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
};
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Join);
|
||||
content.displayname = services.users.displayname(sender_user).await.ok();
|
||||
content.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
content.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
content.reason = reason.clone();
|
||||
content.join_authorized_via_users_server = auth_user;
|
||||
|
||||
// Try normal join first
|
||||
let Err(error) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &content),
|
||||
PartialPdu::state(sender_user.to_string(), &content),
|
||||
sender_user,
|
||||
Some(room_id),
|
||||
&state_lock,
|
||||
@@ -822,16 +808,16 @@ async fn make_join_request(
|
||||
"Asking {remote_server} for make_join (attempt {make_join_counter}/{})",
|
||||
servers.len()
|
||||
);
|
||||
|
||||
let mut request = federation::membership::prepare_join_event::v1::Request::new(
|
||||
room_id.to_owned(),
|
||||
sender_user.to_owned(),
|
||||
);
|
||||
request.ver = services.server.supported_room_versions().collect();
|
||||
|
||||
let make_join_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server,
|
||||
federation::membership::prepare_join_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: sender_user.to_owned(),
|
||||
ver: services.server.supported_room_versions().collect(),
|
||||
},
|
||||
)
|
||||
.send_federation_request(remote_server, request)
|
||||
.await;
|
||||
|
||||
trace!("make_join response: {:?}", make_join_response);
|
||||
@@ -864,7 +850,10 @@ async fn make_join_request(
|
||||
rules, but is unable to authorise a join for us. Will continue trying."
|
||||
);
|
||||
},
|
||||
| ErrorKind::IncompatibleRoomVersion { room_version } => {
|
||||
| ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData {
|
||||
room_version,
|
||||
..
|
||||
}) => {
|
||||
warn!(
|
||||
"{remote_server} reports the room we are trying to join is \
|
||||
v{room_version}, which we do not support."
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use ruma::{
|
||||
api::client::membership::kick_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use conduwuit::{Err, Result, matrix::pdu::PartialPdu};
|
||||
use ruma::{api::client::membership::kick_user, events::room::member::MembershipState};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -18,9 +15,9 @@ pub(crate) async fn kick_user_route(
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
let state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
||||
|
||||
let Ok(event) = services
|
||||
let Ok(mut event) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
@@ -41,18 +38,17 @@ pub(crate) async fn kick_user_route(
|
||||
)));
|
||||
}
|
||||
|
||||
event.membership = MembershipState::Leave;
|
||||
event.reason = body.reason.clone();
|
||||
event.is_direct = None;
|
||||
event.join_authorized_via_users_server = None;
|
||||
event.third_party_invite = None;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: body.reason.clone(),
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
..event
|
||||
}),
|
||||
PartialPdu::state(body.user_id.to_string(), &event),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&state_lock,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Err, Result, debug, debug_info, debug_warn, err, info,
|
||||
matrix::{
|
||||
event::gen_event_id,
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
pdu::{PartialPdu, PduEvent},
|
||||
},
|
||||
result::FlatOk,
|
||||
trace,
|
||||
@@ -15,8 +15,8 @@
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, RoomId,
|
||||
RoomVersionId, UserId,
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName,
|
||||
OwnedUserId, RoomId, UserId,
|
||||
api::{
|
||||
client::knock::knock_room,
|
||||
federation::{self},
|
||||
@@ -73,7 +73,6 @@ pub(crate) async fn knock_room_route(
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
@@ -113,11 +112,7 @@ pub(crate) async fn knock_room_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let addl_via_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned);
|
||||
let addl_via_servers = services.rooms.state_cache.servers_invite_via(&room_id);
|
||||
|
||||
let addl_state_servers = services
|
||||
.rooms
|
||||
@@ -130,7 +125,7 @@ pub(crate) async fn knock_room_route(
|
||||
.iter()
|
||||
.map(|event| event.get_field("sender"))
|
||||
.filter_map(FlatOk::flat_ok)
|
||||
.map(|user: &UserId| user.server_name().to_owned())
|
||||
.map(|user: OwnedUserId| user.server_name().to_owned())
|
||||
.stream()
|
||||
.chain(addl_via_servers)
|
||||
.collect()
|
||||
@@ -188,7 +183,7 @@ async fn knock_room_by_id_helper(
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already knocked in {room_id}");
|
||||
return Ok(knock_room::v3::Response { room_id: room_id.into() });
|
||||
return Ok(knock_room::v3::Response::new(room_id.into()));
|
||||
}
|
||||
|
||||
if let Ok(membership) = services
|
||||
@@ -339,34 +334,27 @@ async fn knock_room_helper_local(
|
||||
) -> Result {
|
||||
debug_info!("We can knock locally");
|
||||
|
||||
let room_version_id = services.rooms.state.get_room_version(room_id).await?;
|
||||
let room_version = services.rooms.state.get_room_version(room_id).await?;
|
||||
let room_version_rules = room_version
|
||||
.rules()
|
||||
.expect("room version should have defined rules");
|
||||
|
||||
if matches!(
|
||||
room_version_id,
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
) {
|
||||
if !room_version_rules.authorization.knocking {
|
||||
return Err!(Request(Forbidden("This room does not support knocking.")));
|
||||
}
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: reason.clone(),
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
};
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Knock);
|
||||
content.displayname = services.users.displayname(sender_user).await.ok();
|
||||
content.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
content.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
content.reason = reason.clone();
|
||||
|
||||
// Try normal knock first
|
||||
let Err(error) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &content),
|
||||
PartialPdu::state(sender_user.to_string(), &content),
|
||||
sender_user,
|
||||
Some(room_id),
|
||||
&state_lock,
|
||||
@@ -381,19 +369,18 @@ async fn knock_room_helper_local(
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
warn!("We couldn't do the knock locally, maybe federation can help to satisfy the knock");
|
||||
|
||||
let (make_knock_response, remote_server) =
|
||||
make_knock_request(services, sender_user, room_id, servers).await?;
|
||||
|
||||
info!("make_knock finished");
|
||||
|
||||
let room_version_id = make_knock_response.room_version;
|
||||
let room_version = make_knock_response.room_version;
|
||||
let room_version_rules = room_version
|
||||
.rules()
|
||||
.expect("room version should have defined rules");
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
if !services.server.supported_room_version(&room_version) {
|
||||
return Err!(BadServerResponse("Remote room version {room_version} is not supported"));
|
||||
}
|
||||
|
||||
let mut knock_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||
@@ -424,24 +411,17 @@ async fn knock_room_helper_local(
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
to_canonical_value(content).expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_id)?;
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_rules)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_id)?;
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_rules)?;
|
||||
|
||||
// Add event_id
|
||||
knock_event_stub
|
||||
@@ -451,14 +431,14 @@ async fn knock_room_helper_local(
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
let send_knock_request = federation::membership::create_knock_event::v1::Request::new(
|
||||
room_id.to_owned(),
|
||||
event_id.clone(),
|
||||
services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.await,
|
||||
};
|
||||
);
|
||||
|
||||
services
|
||||
.sending
|
||||
@@ -520,11 +500,14 @@ async fn knock_room_helper_remote(
|
||||
|
||||
info!("make_knock finished");
|
||||
|
||||
let room_version_id = make_knock_response.room_version;
|
||||
let room_version = make_knock_response.room_version;
|
||||
let room_version_rules = room_version
|
||||
.rules()
|
||||
.expect("room version should have defined rules");
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
if !services.server.supported_room_version(&room_version) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
"Remote room version {room_version} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -545,26 +528,26 @@ async fn knock_room_helper_remote(
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
|
||||
let mut knock_content = RoomMemberEventContent::new(MembershipState::Knock);
|
||||
knock_content.displayname = services.users.displayname(sender_user).await.ok();
|
||||
knock_content.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
knock_content.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
knock_content.reason = reason;
|
||||
|
||||
knock_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
to_canonical_value(knock_content).expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_id)?;
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_rules)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_id)?;
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_rules)?;
|
||||
|
||||
// Add event_id
|
||||
knock_event_stub
|
||||
@@ -574,18 +557,18 @@ async fn knock_room_helper_remote(
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
let request = federation::membership::create_knock_event::v1::Request::new(
|
||||
room_id.to_owned(),
|
||||
event_id.clone(),
|
||||
services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.await,
|
||||
};
|
||||
);
|
||||
|
||||
let send_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(&remote_server, send_knock_request)
|
||||
.send_federation_request(&remote_server, request)
|
||||
.await?;
|
||||
|
||||
info!("send_knock finished");
|
||||
@@ -604,7 +587,17 @@ async fn knock_room_helper_remote(
|
||||
let state = send_knock_response
|
||||
.knock_room_state
|
||||
.iter()
|
||||
.map(|event| serde_json::from_str::<CanonicalJsonObject>(event.clone().into_json().get()))
|
||||
.map(|event| {
|
||||
#[allow(deprecated)]
|
||||
let raw_value = match event {
|
||||
| federation::membership::RawStrippedState::Stripped(raw_state) =>
|
||||
&raw_state.clone().into_json(),
|
||||
| federation::membership::RawStrippedState::Pdu(raw_value) => raw_value,
|
||||
| _ => panic!("unknown raw stripped state type"),
|
||||
};
|
||||
|
||||
serde_json::from_str::<CanonicalJsonObject>(raw_value.get())
|
||||
})
|
||||
.filter_map(Result::ok);
|
||||
|
||||
let mut state_map: HashMap<u64, OwnedEventId> = HashMap::new();
|
||||
@@ -629,7 +622,7 @@ async fn knock_room_helper_remote(
|
||||
continue;
|
||||
};
|
||||
|
||||
let event_id = gen_event_id(&event, &room_version_id)?;
|
||||
let event_id = gen_event_id(&event, &room_version_rules)?;
|
||||
let shortstatekey = services
|
||||
.rooms
|
||||
.short
|
||||
@@ -709,7 +702,7 @@ async fn make_knock_request(
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<(federation::knock::create_knock_event_template::v1::Response, OwnedServerName)> {
|
||||
) -> Result<(federation::membership::prepare_knock_event::v1::Response, OwnedServerName)> {
|
||||
let mut make_knock_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in knocking."));
|
||||
|
||||
@@ -722,16 +715,15 @@ async fn make_knock_request(
|
||||
|
||||
info!("Asking {remote_server} for make_knock ({make_knock_counter})");
|
||||
|
||||
let mut request = federation::membership::prepare_knock_event::v1::Request::new(
|
||||
room_id.to_owned(),
|
||||
sender_user.to_owned(),
|
||||
);
|
||||
request.ver = services.server.supported_room_versions().collect();
|
||||
|
||||
let make_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server,
|
||||
federation::knock::create_knock_event_template::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: sender_user.to_owned(),
|
||||
ver: services.server.supported_room_versions().collect(),
|
||||
},
|
||||
)
|
||||
.send_federation_request(remote_server, request)
|
||||
.await;
|
||||
|
||||
trace!("make_knock response: {make_knock_response:?}");
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Pdu, Result, debug_info, debug_warn, err,
|
||||
matrix::{event::gen_event_id, pdu::PduBuilder},
|
||||
matrix::{event::gen_event_id, pdu::PartialPdu},
|
||||
utils::{self, FutureBoolExt, future::ReadyEqExt},
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, pin_mut};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, RoomVersionId, UserId,
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, UserId,
|
||||
api::{
|
||||
client::membership::leave_room,
|
||||
federation::{self},
|
||||
@@ -42,11 +42,7 @@ pub(crate) async fn leave_room_route(
|
||||
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
|
||||
// and ignores errors
|
||||
pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
||||
let rooms_joined = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(ToOwned::to_owned);
|
||||
let rooms_joined = services.rooms.state_cache.rooms_joined(user_id);
|
||||
|
||||
let rooms_invited = services
|
||||
.rooms
|
||||
@@ -142,18 +138,17 @@ pub async fn leave_room(
|
||||
.await;
|
||||
|
||||
match user_member_event_content {
|
||||
| Ok(content) => {
|
||||
| Ok(mut content) => {
|
||||
content.membership = MembershipState::Leave;
|
||||
content.reason = reason;
|
||||
content.join_authorized_via_users_server = None;
|
||||
content.is_direct = None;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason,
|
||||
join_authorized_via_users_server: None,
|
||||
is_direct: None,
|
||||
..content
|
||||
}),
|
||||
PartialPdu::state(user_id.to_string(), &content),
|
||||
user_id,
|
||||
Some(room_id),
|
||||
&state_lock,
|
||||
@@ -226,7 +221,6 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<HashSet<OwnedServerName>>()
|
||||
.await,
|
||||
);
|
||||
@@ -260,7 +254,7 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.filter_map(|sender| {
|
||||
if !services.globals.user_is_local(sender) {
|
||||
if !services.globals.user_is_local(&sender) {
|
||||
Some(sender.server_name().to_owned())
|
||||
} else {
|
||||
None
|
||||
@@ -289,10 +283,10 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server.as_ref(),
|
||||
federation::membership::prepare_leave_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: user_id.to_owned(),
|
||||
},
|
||||
federation::membership::prepare_leave_event::v1::Request::new(
|
||||
room_id.to_owned(),
|
||||
user_id.to_owned(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -329,6 +323,10 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
)));
|
||||
}
|
||||
|
||||
let room_version_rules = room_version_id
|
||||
.rules()
|
||||
.expect("room version should have defined rules");
|
||||
|
||||
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||
make_leave_response.event.get(),
|
||||
)
|
||||
@@ -366,21 +364,16 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
}
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
leave_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
leave_event_stub.remove("event_id");
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut leave_event_stub, &room_version_id)?;
|
||||
.hash_and_sign_event(&mut leave_event_stub, &room_version_rules)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&leave_event_stub, &room_version_id)?;
|
||||
let event_id = gen_event_id(&leave_event_stub, &room_version_rules)?;
|
||||
|
||||
// Add event_id back
|
||||
leave_event_stub
|
||||
@@ -393,14 +386,14 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&remote_server,
|
||||
federation::membership::create_leave_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
federation::membership::create_leave_event::v2::Request::new(
|
||||
room_id.to_owned(),
|
||||
event_id.clone(),
|
||||
services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(leave_event.clone())
|
||||
.await,
|
||||
},
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
use futures::{FutureExt, StreamExt, future::join};
|
||||
use ruma::{
|
||||
api::client::membership::{
|
||||
get_member_events::{self, v3::MembershipEventFilter},
|
||||
get_member_events::{self},
|
||||
joined_members::{self, v3::RoomMember},
|
||||
},
|
||||
events::{
|
||||
@@ -43,20 +43,20 @@ pub(crate) async fn get_member_events_route(
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
}
|
||||
|
||||
Ok(get_member_events::v3::Response {
|
||||
chunk: services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&body.room_id)
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
||||
.map(at!(1))
|
||||
.ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership))
|
||||
.map(Event::into_format)
|
||||
.collect()
|
||||
.boxed()
|
||||
.await,
|
||||
})
|
||||
let chunk = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&body.room_id)
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
||||
.map(at!(1))
|
||||
.ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership))
|
||||
.map(Event::into_format)
|
||||
.collect()
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
Ok(get_member_events::v3::Response::new(chunk))
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/joined_members`
|
||||
@@ -78,70 +78,46 @@ pub(crate) async fn joined_members_route(
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
}
|
||||
|
||||
Ok(joined_members::v3::Response {
|
||||
joined: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_then(|user_id| async move {
|
||||
let (display_name, avatar_url) = join(
|
||||
services.users.displayname(&user_id).ok(),
|
||||
services.users.avatar_url(&user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
let joined = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.broad_then(|user_id| async move {
|
||||
let mut member = RoomMember::new();
|
||||
let (display_name, avatar_url) = join(
|
||||
services.users.displayname(&user_id).ok(),
|
||||
services.users.avatar_url(&user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
member.display_name = display_name;
|
||||
member.avatar_url = avatar_url;
|
||||
|
||||
(user_id, RoomMember { display_name, avatar_url })
|
||||
})
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
(user_id, member)
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(joined_members::v3::Response::new(joined))
|
||||
}
|
||||
|
||||
fn membership_filter<Pdu: Event>(
|
||||
pdu: Pdu,
|
||||
for_membership: Option<&MembershipEventFilter>,
|
||||
not_membership: Option<&MembershipEventFilter>,
|
||||
membership_state_filter: Option<&MembershipState>,
|
||||
not_membership_state_filter: Option<&MembershipState>,
|
||||
) -> Option<impl Event> {
|
||||
let membership_state_filter = match for_membership {
|
||||
| Some(MembershipEventFilter::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipEventFilter::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipEventFilter::Knock) => MembershipState::Knock,
|
||||
| Some(MembershipEventFilter::Leave) => MembershipState::Leave,
|
||||
| Some(_) | None => MembershipState::Join,
|
||||
};
|
||||
|
||||
let not_membership_state_filter = match not_membership {
|
||||
| Some(MembershipEventFilter::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipEventFilter::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipEventFilter::Join) => MembershipState::Join,
|
||||
| Some(MembershipEventFilter::Knock) => MembershipState::Knock,
|
||||
| Some(_) | None => MembershipState::Leave,
|
||||
};
|
||||
|
||||
let evt_membership = pdu.get_content::<RoomMemberEventContent>().ok()?.membership;
|
||||
|
||||
if for_membership.is_some() && not_membership.is_some() {
|
||||
if membership_state_filter != evt_membership
|
||||
|| not_membership_state_filter == evt_membership
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
} else if for_membership.is_some() && not_membership.is_none() {
|
||||
if membership_state_filter != evt_membership {
|
||||
None
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
} else if not_membership.is_some() && for_membership.is_none() {
|
||||
if not_membership_state_filter == evt_membership {
|
||||
None
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
} else {
|
||||
Some(pdu)
|
||||
if let Some(membership_state_filter) = membership_state_filter
|
||||
&& *membership_state_filter != evt_membership
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(not_membership_state_filter) = not_membership_state_filter
|
||||
&& *not_membership_state_filter == evt_membership
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(pdu);
|
||||
}
|
||||
|
||||
@@ -47,15 +47,14 @@ pub(crate) async fn joined_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<joined_rooms::v3::Request>,
|
||||
) -> Result<joined_rooms::v3::Response> {
|
||||
Ok(joined_rooms::v3::Response {
|
||||
joined_rooms: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(body.sender_user())
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
let joined_rooms = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(body.sender_user())
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(joined_rooms::v3::Response::new(joined_rooms))
|
||||
}
|
||||
|
||||
/// Checks if the room is banned in any way possible and the sender user is not
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use conduwuit::{Err, Result, matrix::pdu::PartialPdu};
|
||||
use ruma::{
|
||||
api::client::membership::unban_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
@@ -18,9 +18,9 @@ pub(crate) async fn unban_user_route(
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
let state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
||||
|
||||
let current_member_content = services
|
||||
let mut current_member_content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
@@ -34,18 +34,17 @@ pub(crate) async fn unban_user_route(
|
||||
)));
|
||||
}
|
||||
|
||||
current_member_content.membership = MembershipState::Leave;
|
||||
current_member_content.reason = body.reason.clone();
|
||||
current_member_content.join_authorized_via_users_server = None;
|
||||
current_member_content.third_party_invite = None;
|
||||
current_member_content.is_direct = None;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: body.reason.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
is_direct: None,
|
||||
..current_member_content
|
||||
}),
|
||||
PartialPdu::state(body.user_id.to_string(), ¤t_member_content),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&state_lock,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result,
|
||||
matrix::pdu::PduBuilder,
|
||||
matrix::pdu::PartialPdu,
|
||||
utils::{IterStream, future::TryExtExt, stream::TryIgnore},
|
||||
warn,
|
||||
};
|
||||
@@ -326,7 +326,7 @@ pub async fn update_displayname(
|
||||
.iter()
|
||||
.try_stream()
|
||||
.and_then(|room_id: &OwnedRoomId| async move {
|
||||
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
let pdu = PartialPdu::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
displayname: displayname.clone(),
|
||||
membership: MembershipState::Join,
|
||||
avatar_url: avatar_url.clone(),
|
||||
@@ -376,7 +376,7 @@ pub async fn update_avatar_url(
|
||||
.iter()
|
||||
.try_stream()
|
||||
.and_then(|room_id: &OwnedRoomId| async move {
|
||||
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
let pdu = PartialPdu::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
avatar_url: avatar_url.clone(),
|
||||
blurhash: blurhash.clone(),
|
||||
membership: MembershipState::Join,
|
||||
@@ -399,15 +399,15 @@ pub async fn update_avatar_url(
|
||||
|
||||
pub async fn update_all_rooms(
|
||||
services: &Services,
|
||||
all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>,
|
||||
all_joined_rooms: Vec<(PartialPdu, &OwnedRoomId)>,
|
||||
user_id: &UserId,
|
||||
) {
|
||||
for (pdu_builder, room_id) in all_joined_rooms {
|
||||
for (partial_pdu, room_id) in all_joined_rooms {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
if let Err(e) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(pdu_builder, user_id, Some(room_id), &state_lock)
|
||||
.build_and_append_pdu(partial_pdu, user_id, Some(room_id), &state_lock)
|
||||
.await
|
||||
{
|
||||
warn!(%user_id, %room_id, "Failed to update/send new profile join membership update in room: {e}");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use conduwuit::{Err, Result, matrix::pdu::PartialPdu};
|
||||
use ruma::{
|
||||
api::client::redact::redact_event, events::room::redaction::RoomRedactionEventContent,
|
||||
};
|
||||
@@ -34,9 +34,9 @@ pub(crate) async fn redact_event_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
PartialPdu {
|
||||
redacts: Some(body.event_id.clone()),
|
||||
..PduBuilder::timeline(&RoomRedactionEventContent {
|
||||
..PartialPdu::timeline(&RoomRedactionEventContent {
|
||||
redacts: Some(body.event_id.clone()),
|
||||
reason: body.reason.clone(),
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use ruma::{
|
||||
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||
api::client::{
|
||||
report_user,
|
||||
reporting::report_user,
|
||||
room::{report_content, report_room},
|
||||
},
|
||||
events::{Mentions, room::message::RoomMessageEventContent},
|
||||
|
||||
@@ -26,13 +26,12 @@ pub(crate) async fn get_room_aliases_route(
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.",)));
|
||||
}
|
||||
|
||||
Ok(aliases::v3::Response {
|
||||
aliases: services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
let aliases = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(aliases::v3::Response::new(aliases))
|
||||
}
|
||||
|
||||
+34
-102
@@ -2,8 +2,8 @@
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, RoomVersion, debug, debug_info, debug_warn, err, info,
|
||||
matrix::{StateKey, pdu::PduBuilder},
|
||||
Err, Result, debug, debug_info, debug_warn, err, info,
|
||||
matrix::{StateKey, pdu::PartialPdu},
|
||||
trace, warn,
|
||||
};
|
||||
use conduwuit_service::{Services, appservice::RegistrationInfo};
|
||||
@@ -13,7 +13,6 @@
|
||||
api::client::room::{self, create_room},
|
||||
events::{
|
||||
TimelineEventType,
|
||||
invite_permission_config::FilterLevel,
|
||||
room::{
|
||||
canonical_alias::RoomCanonicalAliasEventContent,
|
||||
create::RoomCreateEventContent,
|
||||
@@ -27,8 +26,10 @@
|
||||
},
|
||||
},
|
||||
int,
|
||||
room_version_rules::RoomIdFormatVersion,
|
||||
serde::{JsonObject, Raw},
|
||||
};
|
||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
|
||||
use crate::{Ruma, client::invite_helper};
|
||||
@@ -81,15 +82,11 @@ pub(crate) async fn create_room_route(
|
||||
},
|
||||
| None => services.server.config.default_room_version.clone(),
|
||||
};
|
||||
let room_features = RoomVersion::new(&room_version)?;
|
||||
let room_version_rules = room_version.rules().unwrap();
|
||||
|
||||
let room_id: Option<OwnedRoomId> = if !room_features.room_ids_as_hashes {
|
||||
match &body.room_id {
|
||||
| Some(custom_room_id) => Some(custom_room_id_check(&services, custom_room_id)?),
|
||||
| None => Some(RoomId::new(services.globals.server_name())),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
let room_id: Option<OwnedRoomId> = match room_version_rules.room_id_format {
|
||||
| RoomIdFormatVersion::V1 => Some(RoomId::new_v1(services.globals.server_name())),
|
||||
| _ => None,
|
||||
};
|
||||
|
||||
// check if room ID doesn't already exist instead of erroring on auth check
|
||||
@@ -167,7 +164,7 @@ pub(crate) async fn create_room_route(
|
||||
use RoomVersionId::*;
|
||||
|
||||
let mut content = content
|
||||
.deserialize_as::<CanonicalJsonObject>()
|
||||
.deserialize_as_unchecked::<CanonicalJsonObject>()
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(error!(
|
||||
"Failed to deserialise content as canonical JSON: {e}"
|
||||
@@ -201,8 +198,7 @@ pub(crate) async fn create_room_route(
|
||||
let content = match room_version {
|
||||
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
|
||||
RoomCreateEventContent::new_v1(sender_user.to_owned()),
|
||||
| V11 => RoomCreateEventContent::new_v11(),
|
||||
| _ => RoomCreateEventContent::new_v12(),
|
||||
| _ => RoomCreateEventContent::new_v11(),
|
||||
};
|
||||
let mut content =
|
||||
serde_json::from_str::<CanonicalJsonObject>(to_raw_value(&content)?.get())?;
|
||||
@@ -234,7 +230,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
PartialPdu {
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_content)?,
|
||||
state_key: Some(StateKey::new()),
|
||||
@@ -257,30 +253,23 @@ pub(crate) async fn create_room_route(
|
||||
},
|
||||
};
|
||||
drop(state_lock);
|
||||
if let Some(expected_room_id) = body.room_id.as_ref() {
|
||||
if expected_room_id.as_str() != room_id.as_str() {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID {expected_room_id} does not match the generated room ID \
|
||||
{room_id}.",
|
||||
)));
|
||||
}
|
||||
}
|
||||
debug!("Room created with ID {room_id}");
|
||||
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
|
||||
|
||||
// 2. Let the room creator join
|
||||
|
||||
let mut join_event = RoomMemberEventContent::new(MembershipState::Join);
|
||||
join_event.displayname = services.users.displayname(sender_user).await.ok();
|
||||
join_event.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
join_event.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
join_event.is_direct = Some(body.is_direct);
|
||||
|
||||
debug_info!("Joining {sender_user} to room {room_id}");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
is_direct: Some(body.is_direct),
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
}),
|
||||
PartialPdu::state(sender_user.to_string(), &join_event),
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -306,7 +295,7 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
let mut creators: Vec<OwnedUserId> = vec![sender_user.to_owned()];
|
||||
// Do we care about additional_creators?
|
||||
if room_features.explicitly_privilege_room_creators {
|
||||
if room_version_rules.explicitly_privilege_room_creators {
|
||||
// Have they been specified?
|
||||
if let Some(additional_creators) = create_content.get("additional_creators") {
|
||||
// Are they a real array?
|
||||
@@ -341,7 +330,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
PartialPdu {
|
||||
event_type: TimelineEventType::RoomPowerLevels,
|
||||
content: to_raw_value(&power_levels_content)?,
|
||||
state_key: Some(StateKey::new()),
|
||||
@@ -360,7 +349,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
|
||||
PartialPdu::state(String::new(), &RoomCanonicalAliasEventContent {
|
||||
alias: Some(room_alias_id.to_owned()),
|
||||
alt_aliases: vec![],
|
||||
}),
|
||||
@@ -379,7 +368,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomJoinRulesEventContent::new(match preset {
|
||||
| RoomPreset::PublicChat => JoinRule::Public,
|
||||
@@ -399,7 +388,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared),
|
||||
),
|
||||
@@ -415,7 +404,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomGuestAccessEventContent::new(match preset {
|
||||
| RoomPreset::PublicChat => GuestAccess::Forbidden,
|
||||
@@ -431,7 +420,7 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
// 6. Events listed in initial_state
|
||||
for event in &body.initial_state {
|
||||
let mut pdu_builder = event.deserialize_as::<PduBuilder>().map_err(|e| {
|
||||
let mut partial_pdu = event.deserialize_as::<PartialPdu>().map_err(|e| {
|
||||
err!(Request(InvalidParam(warn!("Invalid initial state event: {e:?}"))))
|
||||
})?;
|
||||
|
||||
@@ -440,17 +429,17 @@ pub(crate) async fn create_room_route(
|
||||
// client/appservice workaround: if a user sends an initial_state event with a
|
||||
// state event in there with the content of literally `{}` (not null or empty
|
||||
// string), let's just skip it over and warn.
|
||||
if pdu_builder.content.get().eq("{}") {
|
||||
if partial_pdu.content.get().eq("{}") {
|
||||
debug_warn!("skipping empty initial state event with content of `{{}}`: {event:?}");
|
||||
debug_warn!("content: {}", pdu_builder.content.get());
|
||||
debug_warn!("content: {}", partial_pdu.content.get());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Implicit state key defaults to ""
|
||||
pdu_builder.state_key.get_or_insert_with(StateKey::new);
|
||||
partial_pdu.state_key.get_or_insert_with(StateKey::new);
|
||||
|
||||
// Silently skip encryption events if they are not allowed
|
||||
if pdu_builder.event_type == TimelineEventType::RoomEncryption
|
||||
if partial_pdu.event_type == TimelineEventType::RoomEncryption
|
||||
&& !services.config.allow_encryption
|
||||
{
|
||||
continue;
|
||||
@@ -459,7 +448,7 @@ pub(crate) async fn create_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(pdu_builder, sender_user, Some(&room_id), &state_lock)
|
||||
.build_and_append_pdu(partial_pdu, sender_user, Some(&room_id), &state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
@@ -470,7 +459,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomNameEventContent::new(name.clone())),
|
||||
PartialPdu::state(String::new(), &RoomNameEventContent::new(name.clone())),
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -484,7 +473,7 @@ pub(crate) async fn create_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomTopicEventContent { topic: topic.clone() }),
|
||||
PartialPdu::state(String::new(), &RoomTopicEventContent { topic: topic.clone() }),
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -667,60 +656,3 @@ async fn room_alias_check(
|
||||
|
||||
Ok(full_room_alias)
|
||||
}
|
||||
|
||||
/// if a room is being created with a custom room ID, run our checks against it
|
||||
fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<OwnedRoomId> {
|
||||
// apply forbidden room alias checks to custom room IDs too
|
||||
if services
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.is_match(custom_room_id)
|
||||
{
|
||||
return Err!(Request(Unknown("Custom room ID is forbidden.")));
|
||||
}
|
||||
|
||||
if custom_room_id.contains(':') {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID contained `:` which is not allowed. Please note that this expects a \
|
||||
localpart, not the full room ID.",
|
||||
)));
|
||||
} else if custom_room_id.contains(char::is_whitespace) {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID contained spaces which is not valid."
|
||||
)));
|
||||
}
|
||||
|
||||
let server_name = services.globals.server_name();
|
||||
let mut room_id = custom_room_id.to_owned();
|
||||
if custom_room_id.contains(':') {
|
||||
if !custom_room_id.starts_with('!') {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID contains an unexpected `:` which is not allowed.",
|
||||
)));
|
||||
}
|
||||
} else if custom_room_id.starts_with('!') {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Room ID is prefixed with !, but is not fully qualified. You likely did not want \
|
||||
this.",
|
||||
)));
|
||||
} else {
|
||||
room_id = format!("!{custom_room_id}:{server_name}");
|
||||
}
|
||||
OwnedRoomId::parse(room_id)
|
||||
.map_err(Into::into)
|
||||
.and_then(|full_room_id| {
|
||||
if full_room_id
|
||||
.server_name()
|
||||
.expect("failed to extract server name from room ID")
|
||||
!= server_name
|
||||
{
|
||||
Err!(Request(InvalidParam("Custom room ID must be on this server.",)))
|
||||
} else {
|
||||
Ok(full_room_id)
|
||||
}
|
||||
})
|
||||
.inspect(|full_room_id| {
|
||||
debug_info!(%full_room_id, "Full custom room ID");
|
||||
})
|
||||
.inspect_err(|e| warn!(?e, %custom_room_id, "Failed to create room with custom room ID",))
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
mod upgrade;
|
||||
|
||||
pub(crate) use self::{
|
||||
aliases::get_room_aliases_route,
|
||||
create::create_room_route,
|
||||
event::get_room_event_route,
|
||||
initial_sync::room_initial_sync_route,
|
||||
summary::{get_room_summary, get_room_summary_legacy},
|
||||
aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
|
||||
initial_sync::room_initial_sync_route, summary::get_room_summary,
|
||||
upgrade::upgrade_room_route,
|
||||
};
|
||||
|
||||
+15
-318
@@ -1,48 +1,11 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_warn, info, trace,
|
||||
utils::{IterStream, future::TryExtExt},
|
||||
};
|
||||
use futures::{
|
||||
FutureExt, StreamExt, TryFutureExt,
|
||||
future::{OptionFuture, join3},
|
||||
stream::FuturesUnordered,
|
||||
};
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
api::{
|
||||
client::room::get_summary,
|
||||
federation::space::{SpaceHierarchyParentSummary, get_hierarchy},
|
||||
},
|
||||
events::room::member::MembershipState,
|
||||
space::SpaceRoomJoinRule::{self, *},
|
||||
};
|
||||
use service::Services;
|
||||
use conduwuit::{Err, Result};
|
||||
use ruma::api::client::room::get_summary;
|
||||
use service::rooms::summary::Accessibility;
|
||||
|
||||
use crate::{Ruma, RumaResponse};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary`
|
||||
///
|
||||
/// Returns a short description of the state of a room.
|
||||
///
|
||||
/// This is the "wrong" endpoint that some implementations/clients may use
|
||||
/// according to the MSC. Request and response bodies are the same as
|
||||
/// `get_room_summary`.
|
||||
///
|
||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||
pub(crate) async fn get_room_summary_legacy(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_summary::msc3266::Request>,
|
||||
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
||||
get_room_summary(State(services), InsecureClientIp(client), body)
|
||||
.boxed()
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`
|
||||
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
|
||||
///
|
||||
/// Returns a short description of the state of a room.
|
||||
@@ -50,8 +13,8 @@ pub(crate) async fn get_room_summary_legacy(
|
||||
pub(crate) async fn get_room_summary(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_summary::msc3266::Request>,
|
||||
) -> Result<get_summary::msc3266::Response> {
|
||||
body: Ruma<get_summary::v1::Request>,
|
||||
) -> Result<get_summary::v1::Response> {
|
||||
let (room_id, servers) = services
|
||||
.rooms
|
||||
.alias
|
||||
@@ -62,285 +25,19 @@ pub(crate) async fn get_room_summary(
|
||||
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||
}
|
||||
|
||||
room_summary_response(&services, &room_id, &servers, body.sender_user.as_deref())
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
async fn room_summary_response(
|
||||
services: &Services,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
sender_user: Option<&UserId>,
|
||||
) -> Result<get_summary::msc3266::Response> {
|
||||
if services
|
||||
let summary = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await
|
||||
{
|
||||
match local_room_summary_response(services, room_id, sender_user)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
| Ok(response) => return Ok(response),
|
||||
| Err(e) => {
|
||||
debug_warn!("Failed to get local room summary: {e:?}, falling back to remote");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let room =
|
||||
remote_room_summary_hierarchy_response(services, room_id, servers, sender_user).await?;
|
||||
|
||||
Ok(get_summary::msc3266::Response {
|
||||
room_id: room_id.to_owned(),
|
||||
canonical_alias: room.canonical_alias,
|
||||
avatar_url: room.avatar_url,
|
||||
guest_can_join: room.guest_can_join,
|
||||
name: room.name,
|
||||
num_joined_members: room.num_joined_members,
|
||||
topic: room.topic,
|
||||
world_readable: room.world_readable,
|
||||
join_rule: room.join_rule,
|
||||
room_type: room.room_type,
|
||||
room_version: room.room_version,
|
||||
encryption: room.encryption,
|
||||
allowed_room_ids: room.allowed_room_ids,
|
||||
membership: sender_user.is_some().then_some(MembershipState::Leave),
|
||||
})
|
||||
}
|
||||
|
||||
async fn local_room_summary_response(
|
||||
services: &Services,
|
||||
room_id: &RoomId,
|
||||
sender_user: Option<&UserId>,
|
||||
) -> Result<get_summary::msc3266::Response> {
|
||||
trace!(
|
||||
sender_user = sender_user.map(tracing::field::display),
|
||||
"Sending local room summary response for {room_id:?}"
|
||||
);
|
||||
let (join_rule, world_readable, guest_can_join) = join3(
|
||||
services.rooms.state_accessor.get_join_rules(room_id),
|
||||
services.rooms.state_accessor.is_world_readable(room_id),
|
||||
services.rooms.state_accessor.guest_can_join(room_id),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Synapse allows server admins to bypass visibility checks.
|
||||
// That seems neat so we'll copy that behaviour.
|
||||
if sender_user.is_none() || !services.users.is_admin(sender_user.unwrap()).await {
|
||||
user_can_see_summary(
|
||||
services,
|
||||
room_id,
|
||||
&join_rule.clone().into(),
|
||||
guest_can_join,
|
||||
world_readable,
|
||||
join_rule.allowed_rooms(),
|
||||
sender_user,
|
||||
)
|
||||
.summary
|
||||
.get_room_summary_for_user(body.sender_user.as_deref(), &room_id, &servers)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let canonical_alias = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_canonical_alias(room_id)
|
||||
.ok();
|
||||
|
||||
let name = services.rooms.state_accessor.get_name(room_id).ok();
|
||||
|
||||
let topic = services.rooms.state_accessor.get_room_topic(room_id).ok();
|
||||
|
||||
let room_type = services.rooms.state_accessor.get_room_type(room_id).ok();
|
||||
|
||||
let avatar_url = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_avatar(room_id)
|
||||
.map(|res| res.into_option().unwrap_or_default().url);
|
||||
|
||||
let room_version = services.rooms.state.get_room_version(room_id).ok();
|
||||
|
||||
let encryption = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_encryption(room_id)
|
||||
.ok();
|
||||
|
||||
let num_joined_members = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(room_id)
|
||||
.unwrap_or(0);
|
||||
|
||||
let membership: OptionFuture<_> = sender_user
|
||||
.map(|sender_user| {
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, sender_user)
|
||||
.map_ok_or(MembershipState::Leave, |content| content.membership)
|
||||
})
|
||||
.into();
|
||||
|
||||
let (
|
||||
canonical_alias,
|
||||
name,
|
||||
num_joined_members,
|
||||
topic,
|
||||
avatar_url,
|
||||
room_type,
|
||||
room_version,
|
||||
encryption,
|
||||
membership,
|
||||
) = futures::join!(
|
||||
canonical_alias,
|
||||
name,
|
||||
num_joined_members,
|
||||
topic,
|
||||
avatar_url,
|
||||
room_type,
|
||||
room_version,
|
||||
encryption,
|
||||
membership,
|
||||
);
|
||||
|
||||
Ok(get_summary::msc3266::Response {
|
||||
room_id: room_id.to_owned(),
|
||||
canonical_alias,
|
||||
avatar_url,
|
||||
guest_can_join,
|
||||
name,
|
||||
num_joined_members: num_joined_members.try_into().unwrap_or_default(),
|
||||
topic,
|
||||
world_readable,
|
||||
room_type,
|
||||
room_version,
|
||||
encryption,
|
||||
membership,
|
||||
allowed_room_ids: join_rule.allowed_rooms().map(Into::into).collect(),
|
||||
join_rule: join_rule.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// used by MSC3266 to fetch a room's info if we do not know about it
|
||||
async fn remote_room_summary_hierarchy_response(
|
||||
services: &Services,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
sender_user: Option<&UserId>,
|
||||
) -> Result<SpaceHierarchyParentSummary> {
|
||||
trace!(sender_user = ?sender_user.map(tracing::field::display), ?servers, "Sending remote room summary response for {room_id:?}");
|
||||
if !services.config.allow_federation {
|
||||
return Err!(Request(Forbidden("Federation is disabled.")));
|
||||
}
|
||||
|
||||
if services.rooms.metadata.is_disabled(room_id).await {
|
||||
return Err!(Request(Forbidden(
|
||||
"Federaton of room {room_id} is currently disabled on this server."
|
||||
)));
|
||||
}
|
||||
if servers.is_empty() {
|
||||
return Err!(Request(MissingParam(
|
||||
"No servers were provided to fetch the room over federation"
|
||||
)));
|
||||
}
|
||||
|
||||
let request = get_hierarchy::v1::Request::new(room_id.to_owned());
|
||||
|
||||
let mut requests: FuturesUnordered<_> = servers
|
||||
.iter()
|
||||
.map(|server| {
|
||||
info!("Fetching room summary for {room_id} from server {server}");
|
||||
services
|
||||
.sending
|
||||
.send_federation_request(server, request.clone())
|
||||
.inspect_ok(move |v| {
|
||||
debug!("Fetched room summary for {room_id} from server {server}: {v:?}");
|
||||
})
|
||||
.inspect_err(move |e| {
|
||||
info!("Failed to fetch room summary for {room_id} from server {server}: {e}");
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
while let Some(Ok(response)) = requests.next().await {
|
||||
trace!("{response:?}");
|
||||
let room = response.room.clone();
|
||||
if room.room_id != room_id {
|
||||
debug_warn!(
|
||||
"Room ID {} returned does not belong to the requested room ID {}",
|
||||
room.room_id,
|
||||
room_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if sender_user.is_none() || !services.users.is_admin(sender_user.unwrap()).await {
|
||||
return user_can_see_summary(
|
||||
services,
|
||||
room_id,
|
||||
&room.join_rule,
|
||||
room.guest_can_join,
|
||||
room.world_readable,
|
||||
room.allowed_room_ids.iter().map(AsRef::as_ref),
|
||||
sender_user,
|
||||
)
|
||||
.await
|
||||
.map(|()| room);
|
||||
}
|
||||
return Ok(room);
|
||||
}
|
||||
|
||||
Err!(Request(NotFound("Room not found or is not accessible")))
|
||||
}
|
||||
|
||||
async fn user_can_see_summary<'a, I>(
|
||||
services: &Services,
|
||||
room_id: &RoomId,
|
||||
join_rule: &SpaceRoomJoinRule,
|
||||
guest_can_join: bool,
|
||||
world_readable: bool,
|
||||
allowed_room_ids: I,
|
||||
sender_user: Option<&UserId>,
|
||||
) -> Result
|
||||
where
|
||||
I: Iterator<Item = &'a RoomId> + Send,
|
||||
{
|
||||
let is_public_room = matches!(join_rule, Public | Knock | KnockRestricted);
|
||||
match sender_user {
|
||||
| Some(sender_user) => {
|
||||
let user_can_see_state_events = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, room_id);
|
||||
let is_guest = services.users.is_deactivated(sender_user).unwrap_or(false);
|
||||
let user_in_allowed_restricted_room = allowed_room_ids
|
||||
.stream()
|
||||
.any(|room| services.rooms.state_cache.is_joined(sender_user, room));
|
||||
|
||||
let (user_can_see_state_events, is_guest, user_in_allowed_restricted_room) =
|
||||
join3(user_can_see_state_events, is_guest, user_in_allowed_restricted_room)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
if user_can_see_state_events
|
||||
|| (is_guest && guest_can_join)
|
||||
|| is_public_room
|
||||
|| user_in_allowed_restricted_room
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err!(Request(Forbidden("Room is not accessible")))
|
||||
match summary {
|
||||
| Accessibility::Accessible(summary) => Ok(get_summary::v1::Response::new(summary)),
|
||||
| Accessibility::Inaccessible => {
|
||||
Err!(Request(Forbidden("You may not preview this room."), FORBIDDEN))
|
||||
},
|
||||
| None => {
|
||||
if is_public_room || world_readable {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err!(Request(Forbidden("Room is not accessible")))
|
||||
| Accessibility::NotFound => {
|
||||
Err!(Request(Forbidden("This room does not exist."), FORBIDDEN))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Error, Event, Result, RoomVersion, debug, err, info,
|
||||
matrix::{StateKey, pdu::PduBuilder},
|
||||
matrix::{StateKey, pdu::PartialPdu},
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
@@ -85,7 +85,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(StateKey::new(), &RoomTombstoneEventContent {
|
||||
PartialPdu::state(StateKey::new(), &RoomTombstoneEventContent {
|
||||
body: "This room has been replaced".to_owned(),
|
||||
replacement_room: RoomId::new(services.globals.server_name()),
|
||||
}),
|
||||
@@ -128,7 +128,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
let tombstone_event_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder::state(StateKey::new(), &RoomTombstoneEventContent {
|
||||
body: "This room has been replaced".to_owned(),
|
||||
replacement_room: replacement_room.unwrap().to_owned(),
|
||||
@@ -209,7 +209,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
let create_event_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_event_content)
|
||||
@@ -237,7 +237,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomMember,
|
||||
content: to_raw_value(&RoomMemberEventContent {
|
||||
@@ -304,7 +304,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder {
|
||||
event_type: event_type.to_string().into(),
|
||||
content: event_content,
|
||||
@@ -364,7 +364,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder::state(StateKey::new(), &RoomPowerLevelsEventContent {
|
||||
events_default: new_level,
|
||||
invite: new_level,
|
||||
@@ -387,7 +387,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder::state(StateKey::new(), &RoomTombstoneEventContent {
|
||||
body: "This room has been replaced".to_owned(),
|
||||
replacement_room: replacement_room.unwrap().to_owned(),
|
||||
@@ -434,7 +434,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder {
|
||||
event_type: StateEventType::SpaceChild.into(),
|
||||
content: to_raw_value(&RedactedSpaceChildEventContent {})
|
||||
@@ -457,7 +457,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
.PartialPduappend_pdu(
|
||||
PduBuilder {
|
||||
event_type: StateEventType::SpaceChild.into(),
|
||||
content: to_raw_value(&SpaceChildEventContent {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{Err, Result, err, matrix::pdu::PduBuilder, utils};
|
||||
use conduwuit::{Err, Result, err, matrix::pdu::PartialPdu, utils};
|
||||
use ruma::{api::client::message::send_message_event, events::MessageLikeEventType};
|
||||
use serde_json::from_str;
|
||||
|
||||
@@ -79,7 +79,7 @@ pub(crate) async fn send_message_event_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
PartialPdu {
|
||||
event_type: body.event_type.clone().into(),
|
||||
content,
|
||||
unsigned: Some(unsigned),
|
||||
|
||||
+28
-176
@@ -1,26 +1,12 @@
|
||||
use std::{
|
||||
collections::{BTreeSet, VecDeque},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result,
|
||||
utils::{future::TryExtExt, stream::IterStream},
|
||||
};
|
||||
use conduwuit_service::{
|
||||
Services,
|
||||
rooms::spaces::{
|
||||
PaginationToken, SummaryAccessibility, get_parent_children_via, summary_to_chunk,
|
||||
},
|
||||
};
|
||||
use futures::{StreamExt, TryFutureExt, future::OptionFuture};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedServerName, RoomId, UInt, UserId, api::client::space::get_hierarchy,
|
||||
};
|
||||
use conduwuit::{Err, Result};
|
||||
use ruma::{UInt, api::client::space::get_hierarchy, assign};
|
||||
use service::rooms::summary::Accessibility;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
const MAX_MAX_DEPTH: u32 = 10;
|
||||
|
||||
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
|
||||
///
|
||||
/// Paginates over the space tree in a depth-first manner to locate child rooms
|
||||
@@ -29,167 +15,33 @@ pub(crate) async fn get_hierarchy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_hierarchy::v1::Request>,
|
||||
) -> Result<get_hierarchy::v1::Response> {
|
||||
let limit = body
|
||||
.limit
|
||||
.unwrap_or_else(|| UInt::from(10_u32))
|
||||
.min(UInt::from(100_u32));
|
||||
// We don't do pagination for this route (and therefore ignore `limit`), since
|
||||
// there's no reasonable way to handle a space hierarchy changing during
|
||||
// pagination.
|
||||
|
||||
let max_depth = body
|
||||
.max_depth
|
||||
.unwrap_or_else(|| UInt::from(3_u32))
|
||||
.min(UInt::from(10_u32));
|
||||
.map(|max_depth| max_depth.min(UInt::from(MAX_MAX_DEPTH)));
|
||||
|
||||
let key = body
|
||||
.from
|
||||
.as_ref()
|
||||
.and_then(|s| PaginationToken::from_str(s).ok());
|
||||
let hierarchy = services
|
||||
.rooms
|
||||
.summary
|
||||
.get_room_hierarchy_for_user(
|
||||
body.sender_user(),
|
||||
body.room_id.clone(),
|
||||
max_depth,
|
||||
body.suggested_only,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Should prevent unexpected behaviour in (bad) clients
|
||||
if let Some(ref token) = key {
|
||||
if token.suggested_only != body.suggested_only || token.max_depth != max_depth {
|
||||
return Err!(Request(InvalidParam(
|
||||
"suggested_only and max_depth cannot change on paginated requests"
|
||||
)));
|
||||
}
|
||||
match hierarchy {
|
||||
| Accessibility::Accessible(rooms) =>
|
||||
Ok(assign!(get_hierarchy::v1::Response::new(), { rooms: rooms })),
|
||||
| Accessibility::Inaccessible => {
|
||||
Err!(Request(Forbidden("You may not preview this room."), FORBIDDEN))
|
||||
},
|
||||
| Accessibility::NotFound => {
|
||||
Err!(Request(Forbidden("This room does not exist."), FORBIDDEN))
|
||||
},
|
||||
}
|
||||
|
||||
get_client_hierarchy(
|
||||
&services,
|
||||
body.sender_user(),
|
||||
&body.room_id,
|
||||
limit.try_into().unwrap_or(10),
|
||||
max_depth.try_into().unwrap_or(usize::MAX),
|
||||
body.suggested_only,
|
||||
key.as_ref()
|
||||
.into_iter()
|
||||
.flat_map(|t| t.short_room_ids.iter()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_client_hierarchy<'a, ShortRoomIds>(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
limit: usize,
|
||||
max_depth: usize,
|
||||
suggested_only: bool,
|
||||
short_room_ids: ShortRoomIds,
|
||||
) -> Result<get_hierarchy::v1::Response>
|
||||
where
|
||||
ShortRoomIds: Iterator<Item = &'a u64> + Clone + Send + Sync + 'a,
|
||||
{
|
||||
type Via = Vec<OwnedServerName>;
|
||||
type Entry = (OwnedRoomId, Via);
|
||||
type Rooms = VecDeque<Entry>;
|
||||
|
||||
let mut queue: Rooms = [(
|
||||
room_id.to_owned(),
|
||||
room_id
|
||||
.server_name()
|
||||
.map(ToOwned::to_owned)
|
||||
.into_iter()
|
||||
.collect(),
|
||||
)]
|
||||
.into();
|
||||
|
||||
let mut rooms = Vec::with_capacity(limit);
|
||||
let mut parents = BTreeSet::new();
|
||||
while let Some((current_room, via)) = queue.pop_front() {
|
||||
let summary = services
|
||||
.rooms
|
||||
.spaces
|
||||
.get_summary_and_children_client(¤t_room, suggested_only, sender_user, &via)
|
||||
.await?;
|
||||
|
||||
match (summary, current_room == room_id) {
|
||||
| (None | Some(SummaryAccessibility::Inaccessible), false) => {
|
||||
// Just ignore other unavailable rooms
|
||||
},
|
||||
| (None, true) => {
|
||||
return Err!(Request(Forbidden("The requested room was not found")));
|
||||
},
|
||||
| (Some(SummaryAccessibility::Inaccessible), true) => {
|
||||
return Err!(Request(Forbidden("The requested room is inaccessible")));
|
||||
},
|
||||
| (Some(SummaryAccessibility::Accessible(summary)), _) => {
|
||||
let populate = parents.len() >= short_room_ids.clone().count();
|
||||
|
||||
let mut children: Vec<Entry> = get_parent_children_via(&summary, suggested_only)
|
||||
.filter(|(room, _)| !parents.contains(room))
|
||||
.rev()
|
||||
.map(|(key, val)| (key, val.collect()))
|
||||
.collect();
|
||||
|
||||
if populate {
|
||||
rooms.push(summary_to_chunk(summary.clone()));
|
||||
} else {
|
||||
children = children
|
||||
.iter()
|
||||
.rev()
|
||||
.stream()
|
||||
.skip_while(|(room, _)| {
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_shortroomid(room)
|
||||
.map_ok(|short| {
|
||||
Some(&short) != short_room_ids.clone().nth(parents.len())
|
||||
})
|
||||
.unwrap_or_else(|_| false)
|
||||
})
|
||||
.map(Clone::clone)
|
||||
.collect::<Vec<Entry>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.rev()
|
||||
.collect();
|
||||
}
|
||||
|
||||
if !populate && queue.is_empty() && children.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
parents.insert(current_room.clone());
|
||||
if rooms.len() >= limit {
|
||||
break;
|
||||
}
|
||||
|
||||
if parents.len() > max_depth {
|
||||
continue;
|
||||
}
|
||||
|
||||
queue.extend(children);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let next_batch: OptionFuture<_> = queue
|
||||
.pop_front()
|
||||
.map(|(room, _)| async move {
|
||||
parents.insert(room);
|
||||
|
||||
let next_short_room_ids: Vec<_> = parents
|
||||
.iter()
|
||||
.stream()
|
||||
.filter_map(|room_id| services.rooms.short.get_shortroomid(room_id).ok())
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
(next_short_room_ids.iter().ne(short_room_ids) && !next_short_room_ids.is_empty())
|
||||
.then_some(PaginationToken {
|
||||
short_room_ids: next_short_room_ids,
|
||||
limit: limit.try_into().ok()?,
|
||||
max_depth: max_depth.try_into().ok()?,
|
||||
suggested_only,
|
||||
})
|
||||
.as_ref()
|
||||
.map(PaginationToken::to_string)
|
||||
})
|
||||
.into();
|
||||
|
||||
Ok(get_hierarchy::v1::Response {
|
||||
next_batch: next_batch.await.flatten(),
|
||||
rooms,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err,
|
||||
matrix::{Event, pdu::PduBuilder},
|
||||
matrix::{Event, pdu::PartialPdu},
|
||||
utils::BoolExt,
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
@@ -203,7 +203,7 @@ async fn send_state_event_for_key_helper(
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
PartialPdu {
|
||||
event_type: event_type.to_string().into(),
|
||||
content: serde_json::from_str(json.json().get())?,
|
||||
state_key: Some(state_key.into()),
|
||||
|
||||
+1
-5
@@ -184,18 +184,14 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.ruma_route(&client::get_hierarchy_route)
|
||||
.ruma_route(&client::get_mutual_rooms_route)
|
||||
.ruma_route(&client::get_room_summary)
|
||||
.route(
|
||||
"/_matrix/client/unstable/im.nheko.summary/rooms/{room_id_or_alias}/summary",
|
||||
get(client::get_room_summary_legacy)
|
||||
)
|
||||
.ruma_route(&client::get_suspended_status)
|
||||
.ruma_route(&client::put_suspended_status)
|
||||
.ruma_route(&client::well_known_support)
|
||||
.ruma_route(&client::well_known_client)
|
||||
.ruma_route(&client::get_rtc_transports)
|
||||
.ruma_route(&client::room_initial_sync_route)
|
||||
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
||||
.route("/_continuwuity/server_version", get(client::conduwuit_server_version))
|
||||
.ruma_route(&client::room_initial_sync_route)
|
||||
.route("/client/server.json", get(client::syncv3_client_server_json))
|
||||
.ruma_route(&admin::rooms::ban::ban_room)
|
||||
.ruma_route(&admin::rooms::list::list_rooms);
|
||||
|
||||
@@ -44,15 +44,16 @@ impl<Err, Req, Fut, Fun, $($tx,)*> RumaHandler<($($tx,)* Ruma<Req>,)> for Fun
|
||||
$( $tx: FromRequestParts<State> + Send + Sync + 'static, )*
|
||||
{
|
||||
fn add_routes(&'static self, router: Router<State>) -> Router<State> {
|
||||
Req::METADATA
|
||||
.history
|
||||
use ruma::api::path_builder::PathBuilder;
|
||||
|
||||
Req::PATH_BUILDER
|
||||
.all_paths()
|
||||
.fold(router, |router, path| self.add_route(router, path))
|
||||
}
|
||||
|
||||
fn add_route(&'static self, router: Router<State>, path: &str) -> Router<State> {
|
||||
let action = |$($tx,)* req| self($($tx,)* req).map_ok(RumaResponse);
|
||||
let method = method_to_filter(&Req::METADATA.method);
|
||||
let method = method_to_filter(&Req::METHOD);
|
||||
router.route(path, on(method, action))
|
||||
}
|
||||
}
|
||||
|
||||
+10
-57
@@ -1,13 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, info,
|
||||
utils::stream::{BroadbandExt, IterStream},
|
||||
};
|
||||
use conduwuit_service::rooms::spaces::{
|
||||
Identifier, SummaryAccessibility, get_parent_children_via,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use conduwuit::{Err, Result, info};
|
||||
use ruma::api::federation::space::get_hierarchy;
|
||||
use service::rooms::summary::Accessibility;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -19,10 +13,6 @@ pub(crate) async fn get_hierarchy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_hierarchy::v1::Request>,
|
||||
) -> Result<get_hierarchy::v1::Response> {
|
||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||
return Err!(Request(NotFound("Room does not exist.")));
|
||||
}
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -36,52 +26,15 @@ pub(crate) async fn get_hierarchy_route(
|
||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||
}
|
||||
|
||||
let room_id = &body.room_id;
|
||||
let suggested_only = body.suggested_only;
|
||||
let ref identifier = Identifier::ServerName(body.origin());
|
||||
match services
|
||||
let response = services
|
||||
.rooms
|
||||
.spaces
|
||||
.get_summary_and_children_local(room_id, identifier)
|
||||
.await?
|
||||
{
|
||||
| None => Err!(Request(NotFound("The requested room was not found"))),
|
||||
.summary
|
||||
.get_local_room_summary_for_server(body.origin(), &body.room_id, body.suggested_only)
|
||||
.await;
|
||||
|
||||
| Some(SummaryAccessibility::Inaccessible) => {
|
||||
Err!(Request(NotFound("The requested room is inaccessible")))
|
||||
},
|
||||
|
||||
| Some(SummaryAccessibility::Accessible(room)) => {
|
||||
let (children, inaccessible_children) =
|
||||
get_parent_children_via(&room, suggested_only)
|
||||
.stream()
|
||||
.broad_filter_map(|(child, _via)| async move {
|
||||
match services
|
||||
.rooms
|
||||
.spaces
|
||||
.get_summary_and_children_local(&child, identifier)
|
||||
.await
|
||||
.ok()?
|
||||
{
|
||||
| None => None,
|
||||
|
||||
| Some(SummaryAccessibility::Inaccessible) =>
|
||||
Some((None, Some(child))),
|
||||
|
||||
| Some(SummaryAccessibility::Accessible(summary)) =>
|
||||
Some((Some(summary), None)),
|
||||
}
|
||||
})
|
||||
.unzip()
|
||||
.map(|(children, inaccessible_children): (Vec<_>, Vec<_>)| {
|
||||
(
|
||||
children.into_iter().flatten().map(Into::into).collect(),
|
||||
inaccessible_children.into_iter().flatten().collect(),
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(get_hierarchy::v1::Response { room, children, inaccessible_children })
|
||||
},
|
||||
if let Accessibility::Accessible(response) = response {
|
||||
Ok(response)
|
||||
} else {
|
||||
Err!(Request(NotFound("This room is not accessible.")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
|
||||
use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PartialPdu, warn};
|
||||
use conduwuit_service::Services;
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
@@ -137,7 +137,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
PartialPdu::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
join_authorized_via_users_server,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
}),
|
||||
@@ -172,7 +172,7 @@ pub(crate) async fn select_authorising_user(
|
||||
.state_accessor
|
||||
.user_can_invite(room_id, user, user_id, state_lock)
|
||||
})
|
||||
.boxed()
|
||||
.boPartialPdu
|
||||
.next()
|
||||
.await
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use RoomVersionId::*;
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PduBuilder, warn};
|
||||
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PartialPdu, warn};
|
||||
use ruma::{
|
||||
RoomVersionId,
|
||||
api::{client::error::ErrorKind, federation::knock::create_knock_event_template},
|
||||
@@ -102,7 +102,7 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
body.user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Knock),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, info, matrix::pdu::PduBuilder};
|
||||
use conduwuit::{Err, Result, info, matrix::pdu::PartialPdu};
|
||||
use ruma::{
|
||||
api::federation::membership::prepare_leave_event,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
@@ -53,7 +53,7 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
body.user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Leave),
|
||||
),
|
||||
|
||||
@@ -91,6 +91,7 @@ rand_core = { version = "0.6.4", features = ["getrandom"] }
|
||||
regex.workspace = true
|
||||
reqwest.workspace = true
|
||||
ring.workspace = true
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
sanitize-filename.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -46,7 +46,7 @@ macro_rules! err {
|
||||
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
|
||||
let mut buf = String::new();
|
||||
$crate::error::Error::Request(
|
||||
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
||||
$crate::ruma::api::error::ErrorKind::Forbidden,
|
||||
$crate::err_log!(buf, $level, $($args)+),
|
||||
$crate::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
@@ -54,7 +54,7 @@ macro_rules! err {
|
||||
|
||||
(Request(Forbidden($($args:tt)+))) => {
|
||||
$crate::error::Error::Request(
|
||||
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
||||
$crate::ruma::api::error::ErrorKind::Forbidden,
|
||||
$crate::format_maybe!($($args)+),
|
||||
$crate::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
@@ -63,7 +63,7 @@ macro_rules! err {
|
||||
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
|
||||
let mut buf = String::new();
|
||||
$crate::error::Error::Request(
|
||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::ruma::api::error::ErrorKind::$variant,
|
||||
$crate::err_log!(buf, $level, $($args)+),
|
||||
$crate::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
@@ -71,7 +71,7 @@ macro_rules! err {
|
||||
|
||||
(Request($variant:ident($($args:tt)+))) => {
|
||||
$crate::error::Error::Request(
|
||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::ruma::api::error::ErrorKind::$variant,
|
||||
$crate::format_maybe!($($args)+),
|
||||
$crate::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
@@ -79,7 +79,7 @@ macro_rules! err {
|
||||
|
||||
(Request($variant:ident($($args:tt)+), $status_code:ident)) => {
|
||||
$crate::error::Error::Request(
|
||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::ruma::api::error::ErrorKind::$variant,
|
||||
$crate::format_maybe!($($args)+),
|
||||
$crate::http::StatusCode::$status_code,
|
||||
)
|
||||
|
||||
+12
-14
@@ -57,10 +57,6 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
Json(#[from] serde_json::Error),
|
||||
#[error(transparent)]
|
||||
JsParseInt(#[from] ruma::JsParseIntError), // js_int re-export
|
||||
#[error(transparent)]
|
||||
JsTryFromInt(#[from] ruma::JsTryFromIntError), // js_int re-export
|
||||
#[error(transparent)]
|
||||
Path(#[from] axum::extract::rejection::PathRejection),
|
||||
#[error("Mutex poisoned: {0}")]
|
||||
Poison(Cow<'static, str>),
|
||||
@@ -90,8 +86,8 @@ pub enum Error {
|
||||
// ruma/conduwuit
|
||||
#[error("Arithmetic operation failed: {0}")]
|
||||
Arithmetic(Cow<'static, str>),
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
|
||||
#[error("{0:?}: {1}")]
|
||||
BadRequest(ruma::api::error::ErrorKind, &'static str), //TODO: remove
|
||||
#[error("{0}")]
|
||||
BadServerResponse(Cow<'static, str>),
|
||||
#[error(transparent)]
|
||||
@@ -107,7 +103,7 @@ pub enum Error {
|
||||
#[error("Feature '{0}' is not available on this server.")]
|
||||
FeatureDisabled(Cow<'static, str>),
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
Federation(ruma::OwnedServerName, ruma::api::client::error::Error),
|
||||
Federation(ruma::OwnedServerName, ruma::api::error::Error),
|
||||
#[error("{0} in {1}")]
|
||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
#[error(transparent)]
|
||||
@@ -120,12 +116,14 @@ pub enum Error {
|
||||
Mxid(#[from] ruma::IdParseError),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("{0}: {1}")]
|
||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
||||
#[error("{0:?}: {1}")]
|
||||
Request(ruma::api::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
||||
#[error(transparent)]
|
||||
Ruma(#[from] ruma::api::client::error::Error),
|
||||
Ruma(#[from] ruma::api::error::Error),
|
||||
#[error(transparent)]
|
||||
Signatures(#[from] ruma::signatures::Error),
|
||||
SignatureJson(#[from] ruma::signatures::JsonError),
|
||||
#[error(transparent)]
|
||||
SignatureVerification(#[from] ruma::signatures::VerificationError),
|
||||
#[error(transparent)]
|
||||
StateRes(#[from] crate::state_res::Error),
|
||||
#[error("uiaa")]
|
||||
@@ -166,14 +164,14 @@ pub fn message(&self) -> String {
|
||||
|
||||
/// Returns the Matrix error code / error kind
|
||||
#[inline]
|
||||
pub fn kind(&self) -> ruma::api::client::error::ErrorKind {
|
||||
use ruma::api::client::error::ErrorKind::{FeatureDisabled, Unknown};
|
||||
pub fn kind(&self) -> ruma::api::error::ErrorKind {
|
||||
use ruma::api::error::ErrorKind::{Unknown, Unrecognized};
|
||||
|
||||
match self {
|
||||
| Self::Federation(_, error) | Self::Ruma(error) =>
|
||||
response::ruma_error_kind(error).clone(),
|
||||
| Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
|
||||
| Self::FeatureDisabled(..) => FeatureDisabled,
|
||||
| Self::FeatureDisabled(..) => Unrecognized,
|
||||
| _ => Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
+12
-23
@@ -3,10 +3,8 @@
|
||||
use http_body_util::Full;
|
||||
use ruma::api::{
|
||||
OutgoingResponse,
|
||||
client::{
|
||||
error::{ErrorBody, ErrorKind},
|
||||
uiaa::UiaaResponse,
|
||||
},
|
||||
client::uiaa::UiaaResponse,
|
||||
error::{ErrorBody, ErrorKind, StandardErrorBody},
|
||||
};
|
||||
|
||||
use super::Error;
|
||||
@@ -51,15 +49,9 @@ fn from(error: Error) -> Self {
|
||||
return Self::AuthResponse(uiaainfo);
|
||||
}
|
||||
|
||||
let body = ErrorBody::Standard {
|
||||
kind: error.kind(),
|
||||
message: error.message(),
|
||||
};
|
||||
let body = ErrorBody::Standard(StandardErrorBody::new(error.kind(), error.message()));
|
||||
|
||||
Self::MatrixError(ruma::api::client::error::Error {
|
||||
status_code: error.status_code(),
|
||||
body,
|
||||
})
|
||||
Self::MatrixError(ruma::api::error::Error::new(error.status_code(), body))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +68,7 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
|
||||
match kind {
|
||||
// 429
|
||||
| LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
|
||||
| LimitExceeded(_) => StatusCode::TOO_MANY_REQUESTS,
|
||||
|
||||
// 413
|
||||
| TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
@@ -85,37 +77,34 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
| Unrecognized => StatusCode::METHOD_NOT_ALLOWED,
|
||||
|
||||
// 404
|
||||
| NotFound | NotImplemented | FeatureDisabled | SenderIgnored { .. } =>
|
||||
StatusCode::NOT_FOUND,
|
||||
| NotFound => StatusCode::NOT_FOUND,
|
||||
|
||||
// 403
|
||||
| GuestAccessForbidden
|
||||
| ThreepidAuthFailed
|
||||
| UserDeactivated
|
||||
| ThreepidDenied
|
||||
| InviteBlocked
|
||||
| WrongRoomKeysVersion { .. }
|
||||
| WrongRoomKeysVersion(_)
|
||||
| UserSuspended
|
||||
| Forbidden { .. } => StatusCode::FORBIDDEN,
|
||||
| Forbidden => StatusCode::FORBIDDEN,
|
||||
|
||||
// 401
|
||||
| UnknownToken { .. } | MissingToken | Unauthorized | UserLocked =>
|
||||
StatusCode::UNAUTHORIZED,
|
||||
| UnknownToken(_) | MissingToken | Unauthorized | UserLocked => StatusCode::UNAUTHORIZED,
|
||||
|
||||
// 400
|
||||
| _ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> String {
|
||||
if let ErrorBody::Standard { message, .. } = &error.body {
|
||||
pub(super) fn ruma_error_message(error: &ruma::api::error::Error) -> String {
|
||||
if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
|
||||
return message.clone();
|
||||
}
|
||||
|
||||
format!("{error}")
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_kind(e: &ruma::api::client::error::Error) -> &ErrorKind {
|
||||
pub(super) fn ruma_error_kind(e: &ruma::api::error::Error) -> &ErrorKind {
|
||||
e.error_kind().unwrap_or(&ErrorKind::Unknown)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::iter::once;
|
||||
|
||||
use ruma::{RoomVersionId, api::client::discovery::get_capabilities::RoomVersionStability};
|
||||
use ruma::{RoomVersionId, api::client::discovery::get_capabilities::v3::RoomVersionStability};
|
||||
|
||||
use crate::{at, is_equal_to};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
use ruma::{
|
||||
CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, RoomId,
|
||||
RoomVersionId, UserId, events::TimelineEventType,
|
||||
RoomVersionId, UserId, events::TimelineEventType, room_version_rules::RoomVersionRules,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Value as JsonValue, value::RawValue as RawJsonValue};
|
||||
@@ -95,11 +95,11 @@ fn get_content<T>(&self) -> Result<T>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn redacts_id(&self, room_version: &RoomVersionId) -> Option<OwnedEventId>
|
||||
fn redacts_id(&self, room_version_rules: &RoomVersionRules) -> Option<OwnedEventId>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
redact::redacts_id(self, room_version)
|
||||
redact::redacts_id(self, room_version_rules)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -67,7 +67,7 @@ fn matches_sender<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
|
||||
}
|
||||
|
||||
fn matches_type<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
|
||||
let kind = event.kind().to_cow_str();
|
||||
let kind = event.kind().to_string();
|
||||
|
||||
if filter.not_types.iter().any(is_equal_to!(&kind)) {
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruma::{CanonicalJsonObject, OwnedEventId, RoomVersionId};
|
||||
use ruma::{CanonicalJsonObject, OwnedEventId, room_version_rules::RoomVersionRules};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{Result, err};
|
||||
use crate::{Err, Result, err};
|
||||
|
||||
/// Generates a correct eventId for the incoming pdu.
|
||||
///
|
||||
@@ -9,12 +9,12 @@
|
||||
/// CanonicalJsonValue>`.
|
||||
pub fn gen_event_id_canonical_json(
|
||||
pdu: &RawJsonValue,
|
||||
room_version_id: &RoomVersionId,
|
||||
room_version_rules: &RoomVersionRules,
|
||||
) -> Result<(OwnedEventId, CanonicalJsonObject)> {
|
||||
let value: CanonicalJsonObject = serde_json::from_str(pdu.get())
|
||||
.map_err(|e| err!(BadServerResponse(warn!("Error parsing incoming event: {e:?}"))))?;
|
||||
|
||||
let event_id = gen_event_id(&value, room_version_id)?;
|
||||
let event_id = gen_event_id(&value, room_version_rules)?;
|
||||
|
||||
Ok((event_id, value))
|
||||
}
|
||||
@@ -22,9 +22,9 @@ pub fn gen_event_id_canonical_json(
|
||||
/// Generates a correct eventId for the incoming pdu.
|
||||
pub fn gen_event_id(
|
||||
value: &CanonicalJsonObject,
|
||||
room_version_id: &RoomVersionId,
|
||||
room_version_rules: &RoomVersionRules,
|
||||
) -> Result<OwnedEventId> {
|
||||
let reference_hash = ruma::signatures::reference_hash(value, room_version_id)?;
|
||||
let reference_hash = ruma::signatures::reference_hash(value, room_version_rules)?;
|
||||
let event_id: OwnedEventId = format!("${reference_hash}").try_into()?;
|
||||
|
||||
Ok(event_id)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use ruma::{
|
||||
OwnedEventId, RoomVersionId,
|
||||
events::{TimelineEventType, room::redaction::RoomRedactionEventContent},
|
||||
room_version_rules::RoomVersionRules,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
|
||||
@@ -61,7 +62,7 @@ pub(super) fn is_redacted<E: Event>(event: &E) -> bool {
|
||||
#[must_use]
|
||||
pub(super) fn redacts_id<E: Event>(
|
||||
event: &E,
|
||||
room_version: &RoomVersionId,
|
||||
room_version_rules: &RoomVersionRules,
|
||||
) -> Option<OwnedEventId> {
|
||||
use RoomVersionId::*;
|
||||
|
||||
@@ -69,14 +70,13 @@ pub(super) fn redacts_id<E: Event>(
|
||||
return None;
|
||||
}
|
||||
|
||||
match *room_version {
|
||||
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
|
||||
event.redacts().map(ToOwned::to_owned),
|
||||
| _ =>
|
||||
event
|
||||
.get_content::<RoomRedactionEventContent>()
|
||||
.ok()?
|
||||
.redacts,
|
||||
if room_version_rules.redaction.content_field_redacts {
|
||||
event.redacts().map(ToOwned::to_owned)
|
||||
} else {
|
||||
event
|
||||
.get_content::<RoomRedactionEventContent>()
|
||||
.ok()?
|
||||
.redacts
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, Stat
|
||||
|
||||
impl TypeExt for TimelineEventType {
|
||||
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
|
||||
(self.into(), state_key.into())
|
||||
(self.to_string().into(), state_key.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExt for &TimelineEventType {
|
||||
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
|
||||
(self.clone().into(), state_key.into())
|
||||
(self.clone().to_string().into(), state_key.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
pub mod pdu;
|
||||
pub mod state_key;
|
||||
pub mod state_res;
|
||||
pub mod versions;
|
||||
|
||||
pub use event::{Event, TypeExt as EventTypeExt};
|
||||
pub use pdu::{Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId};
|
||||
pub use pdu::{PartialPdu, Pdu, PduCount, PduEvent, PduId, RawPduId, ShortId};
|
||||
pub use state_key::StateKey;
|
||||
pub use state_res::{RoomVersion, StateMap, TypeStateKey};
|
||||
pub use state_res::{StateMap, TypeStateKey};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod builder;
|
||||
mod count;
|
||||
mod id;
|
||||
mod partial;
|
||||
mod raw_id;
|
||||
mod redact;
|
||||
#[cfg(test)]
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
pub use self::{
|
||||
Count as PduCount, Id as PduId, Pdu as PduEvent, RawId as RawPduId,
|
||||
builder::{Builder, Builder as PduBuilder},
|
||||
count::Count,
|
||||
id::{ShortId, *},
|
||||
partial::PartialPdu,
|
||||
raw_id::*,
|
||||
};
|
||||
use super::{Event, StateKey};
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedEventId,
|
||||
events::{EventContent, MessageLikeEventType, StateEventType, TimelineEventType},
|
||||
events::{MessageLikeEventContent, StateEventContent, TimelineEventType},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
|
||||
|
||||
use super::StateKey;
|
||||
|
||||
/// Build the start of a PDU in order to add it to the Database.
|
||||
/// An event and its associated metadata, without an ID, signatures, or hashes.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Builder {
|
||||
pub struct PartialPdu {
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: TimelineEventType,
|
||||
|
||||
@@ -30,10 +30,10 @@ pub struct Builder {
|
||||
|
||||
type Unsigned = BTreeMap<String, serde_json::Value>;
|
||||
|
||||
impl Builder {
|
||||
impl PartialPdu {
|
||||
pub fn state<S, T>(state_key: S, content: &T) -> Self
|
||||
where
|
||||
T: EventContent<EventType = StateEventType>,
|
||||
T: StateEventContent,
|
||||
S: Into<StateKey>,
|
||||
{
|
||||
Self {
|
||||
@@ -47,7 +47,7 @@ pub fn state<S, T>(state_key: S, content: &T) -> Self
|
||||
|
||||
pub fn timeline<T>(content: &T) -> Self
|
||||
where
|
||||
T: EventContent<EventType = MessageLikeEventType>,
|
||||
T: MessageLikeEventContent,
|
||||
{
|
||||
Self {
|
||||
event_type: content.event_type().into(),
|
||||
@@ -58,7 +58,7 @@ pub fn timeline<T>(content: &T) -> Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
impl Default for PartialPdu {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event_type: "m.room.message".into(),
|
||||
@@ -1,17 +1,20 @@
|
||||
use ruma::{RoomVersionId, canonical_json::redact_content_in_place};
|
||||
use serde_json::{Value as JsonValue, json, value::to_raw_value};
|
||||
|
||||
use crate::{Error, Result, err, implement};
|
||||
use crate::{Err, Error, Result, err, implement};
|
||||
|
||||
#[implement(super::Pdu)]
|
||||
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
|
||||
let Some(rules) = room_version_id.rules() else {
|
||||
return Err!("Cannot redact event for unknown room version {room_version_id}");
|
||||
};
|
||||
|
||||
self.unsigned = None;
|
||||
|
||||
let mut content = serde_json::from_str(self.content.get())
|
||||
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
|
||||
|
||||
redact_content_in_place(&mut content, room_version_id, self.kind.to_string())
|
||||
.map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?;
|
||||
redact_content_in_place(&mut content, &rules.redaction, self.kind.to_string());
|
||||
|
||||
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ fn set_up(
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
|
||||
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
@@ -328,7 +328,7 @@ fn set_up(
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
|
||||
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
@@ -338,7 +338,7 @@ fn set_up(
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
|
||||
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
member::{MembershipState, ThirdPartyInvite},
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
third_party_invite::RoomThirdPartyInviteEventContent,
|
||||
},
|
||||
int,
|
||||
serde::{Base64, Raw},
|
||||
room_version_rules::{RoomIdFormatVersion, RoomVersionRules},
|
||||
serde::Raw,
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
@@ -28,7 +28,6 @@
|
||||
deserialize_power_levels, deserialize_power_levels_content_fields,
|
||||
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
|
||||
},
|
||||
room_version::RoomVersion,
|
||||
};
|
||||
use crate::{debug, error, trace, warn};
|
||||
|
||||
@@ -65,13 +64,13 @@ pub fn auth_types_for_event(
|
||||
sender: &UserId,
|
||||
state_key: Option<&str>,
|
||||
content: &RawJsonValue,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> serde_json::Result<Vec<(StateEventType, StateKey)>> {
|
||||
if kind == &TimelineEventType::RoomCreate {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let mut auth_types = if room_version.room_ids_as_hashes {
|
||||
let mut auth_types = if room_version.room_id_format == RoomIdFormatVersion::V2 {
|
||||
vec![
|
||||
(StateEventType::RoomPowerLevels, StateKey::new()),
|
||||
(StateEventType::RoomMember, sender.as_str().into()),
|
||||
@@ -120,15 +119,16 @@ struct RoomMemberContentFields {
|
||||
auth_types.push(key);
|
||||
}
|
||||
|
||||
if membership == MembershipState::Invite {
|
||||
if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
|
||||
let key =
|
||||
(StateEventType::RoomThirdPartyInvite, t_id.signed.token.into());
|
||||
if !auth_types.contains(&key) {
|
||||
auth_types.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: restore this once 3pid support isn't broken
|
||||
// if membership == MembershipState::Invite {
|
||||
// if let Some(Ok(t_id)) = content.third_party_invite.map(|t|
|
||||
// t.deserialize()) { let key =
|
||||
// (StateEventType::RoomThirdPartyInvite,
|
||||
// t_id.signed.token.into()); if !auth_types.contains(&
|
||||
// key) { auth_types.push(key);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ struct RoomMemberContentFields {
|
||||
)]
|
||||
#[allow(clippy::suspicious_operation_groupings)]
|
||||
pub async fn auth_check<E, F, Fut>(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
incoming_event: &E,
|
||||
current_third_party_invite: Option<&E>,
|
||||
fetch_state: F,
|
||||
@@ -218,13 +218,17 @@ pub async fn auth_check<E, F, Fut>(
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if room_version.room_ids_as_hashes && incoming_event.room_id().is_some() {
|
||||
if room_version.room_id_format == RoomIdFormatVersion::V2
|
||||
&& incoming_event.room_id().is_some()
|
||||
{
|
||||
warn!("room create event incorrectly claims to have a room ID when it should not");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !room_version.use_room_create_sender
|
||||
&& !room_version.explicitly_privilege_room_creators
|
||||
if !room_version.authorization.use_room_create_sender
|
||||
&& !room_version
|
||||
.authorization
|
||||
.explicitly_privilege_room_creators
|
||||
{
|
||||
// If content has no creator field, reject
|
||||
if content.creator.is_none() {
|
||||
@@ -305,10 +309,10 @@ pub async fn auth_check<E, F, Fut>(
|
||||
let claims_create_event = incoming_event
|
||||
.auth_events()
|
||||
.any(|id| id == room_create_event.event_id());
|
||||
if room_version.room_ids_as_hashes && claims_create_event {
|
||||
if room_version.room_id_format == RoomIdFormatVersion::V2 && claims_create_event {
|
||||
warn!("event incorrectly references m.room.create event in auth events");
|
||||
return Ok(false);
|
||||
} else if !room_version.room_ids_as_hashes && !claims_create_event {
|
||||
} else if !(room_version.room_id_format == RoomIdFormatVersion::V2) && !claims_create_event {
|
||||
// If the create event is not referenced in the event's auth events, and this is
|
||||
// a v11 room, reject
|
||||
warn!(
|
||||
@@ -332,7 +336,7 @@ pub async fn auth_check<E, F, Fut>(
|
||||
// If the create event content has the field m.federate set to false and the
|
||||
// sender domain of the event does not match the sender domain of the create
|
||||
// event, reject.
|
||||
if !room_version.room_ids_as_hashes
|
||||
if !(room_version.room_id_format == RoomIdFormatVersion::V2)
|
||||
&& !room_create_content.federate
|
||||
&& room_create_event.sender().server_name() != incoming_event.sender().server_name()
|
||||
{
|
||||
@@ -345,9 +349,9 @@ pub async fn auth_check<E, F, Fut>(
|
||||
}
|
||||
|
||||
// Only in some room versions 6 and below
|
||||
if room_version.special_case_aliases_auth {
|
||||
if room_version.authorization.special_case_room_aliases {
|
||||
// 4. If type is m.room.aliases
|
||||
if *incoming_event.event_type() == TimelineEventType::RoomAliases {
|
||||
if *incoming_event.event_type() == TimelineEventType::from("m.room.aliases") {
|
||||
debug!("starting m.room.aliases check");
|
||||
|
||||
// If sender's domain doesn't matches state_key, reject
|
||||
@@ -486,7 +490,7 @@ pub async fn auth_check<E, F, Fut>(
|
||||
},
|
||||
| _ => {
|
||||
// If no power level event found the creator gets 100 everyone else gets 0
|
||||
let is_creator = if room_version.use_room_create_sender {
|
||||
let is_creator = if room_version.authorization.use_room_create_sender {
|
||||
room_create_event.sender() == sender
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
@@ -497,7 +501,10 @@ pub async fn auth_check<E, F, Fut>(
|
||||
if is_creator { int!(100) } else { int!(0) }
|
||||
},
|
||||
};
|
||||
if room_version.explicitly_privilege_room_creators {
|
||||
if room_version
|
||||
.authorization
|
||||
.explicitly_privilege_room_creators
|
||||
{
|
||||
// If the user sent the create event, or is listed in additional_creators, just
|
||||
// give them Int::MAX
|
||||
if sender == room_create_event.sender()
|
||||
@@ -559,7 +566,10 @@ pub async fn auth_check<E, F, Fut>(
|
||||
if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
|
||||
debug!("starting m.room.power_levels check");
|
||||
let mut creators = BTreeSet::new();
|
||||
if room_version.explicitly_privilege_room_creators {
|
||||
if room_version
|
||||
.authorization
|
||||
.explicitly_privilege_room_creators
|
||||
{
|
||||
creators.insert(create_event.sender().to_owned());
|
||||
for creator in room_create_content.additional_creators.iter().flatten() {
|
||||
creators.insert(creator.deserialize()?);
|
||||
@@ -593,7 +603,7 @@ pub async fn auth_check<E, F, Fut>(
|
||||
// the sender of the redaction has the appropriate permissions per the
|
||||
// power levels.
|
||||
|
||||
if room_version.extra_redaction_checks
|
||||
if room_version.authorization.special_case_room_redaction
|
||||
&& *incoming_event.event_type() == TimelineEventType::RoomRedaction
|
||||
{
|
||||
let redact_level = match power_levels_event {
|
||||
@@ -618,7 +628,7 @@ pub async fn auth_check<E, F, Fut>(
|
||||
}
|
||||
|
||||
fn is_creator<EV>(
|
||||
v: &RoomVersion,
|
||||
v: &RoomVersionRules,
|
||||
c: &BTreeSet<OwnedUserId>,
|
||||
ce: &EV,
|
||||
user_id: &UserId,
|
||||
@@ -627,9 +637,9 @@ fn is_creator<EV>(
|
||||
where
|
||||
EV: Event + Send + Sync,
|
||||
{
|
||||
if v.explicitly_privilege_room_creators {
|
||||
if v.authorization.explicitly_privilege_room_creators {
|
||||
c.contains(user_id)
|
||||
} else if v.use_room_create_sender && !have_pls {
|
||||
} else if v.authorization.use_room_create_sender && !have_pls {
|
||||
ce.sender() == user_id
|
||||
} else if !have_pls {
|
||||
#[allow(deprecated)]
|
||||
@@ -659,7 +669,7 @@ fn is_creator<EV>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn valid_membership_change<E>(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
target_user: &UserId,
|
||||
target_user_membership_event: Option<&E>,
|
||||
sender: &UserId,
|
||||
@@ -700,7 +710,7 @@ struct GetThirdPartyInvite {
|
||||
|
||||
let power_levels: RoomPowerLevelsEventContent = match &power_levels_event {
|
||||
| Some(ev) => from_json_str(ev.content().get())?,
|
||||
| None => RoomPowerLevelsEventContent::default(),
|
||||
| None => RoomPowerLevelsEventContent::new(&room_version.authorization),
|
||||
};
|
||||
|
||||
let mut sender_power = power_levels
|
||||
@@ -714,7 +724,10 @@ struct GetThirdPartyInvite {
|
||||
|
||||
let mut creators = BTreeSet::new();
|
||||
creators.insert(create_room.sender().to_owned());
|
||||
if room_version.explicitly_privilege_room_creators {
|
||||
if room_version
|
||||
.authorization
|
||||
.explicitly_privilege_room_creators
|
||||
{
|
||||
// Explicitly privilege room creators
|
||||
// If the sender sent the create event, or in additional_creators, give them
|
||||
// Int::MAX. Same case for target.
|
||||
@@ -865,7 +878,7 @@ struct GetThirdPartyInvite {
|
||||
trace!(sender=%sender, "sender is invited to room, allowing join");
|
||||
true
|
||||
},
|
||||
| JoinRule::Knock if !room_version.allow_knocking => {
|
||||
| JoinRule::Knock if !room_version.authorization.knocking => {
|
||||
warn!("Join rule is knock but room version does not allow knocking");
|
||||
false
|
||||
},
|
||||
@@ -882,7 +895,8 @@ struct GetThirdPartyInvite {
|
||||
trace!(sender=%sender, "sender is invited or already joined to room, allowing join");
|
||||
true
|
||||
},
|
||||
| JoinRule::KnockRestricted(_) if !room_version.knock_restricted_join_rule =>
|
||||
| JoinRule::KnockRestricted(_)
|
||||
if !room_version.authorization.knock_restricted_join_rule =>
|
||||
{
|
||||
warn!(
|
||||
"Join rule is knock_restricted but room version does not support it"
|
||||
@@ -1127,7 +1141,7 @@ struct GetThirdPartyInvite {
|
||||
}
|
||||
allow
|
||||
},
|
||||
| MembershipState::Knock if room_version.allow_knocking => {
|
||||
| MembershipState::Knock if room_version.authorization.knocking => {
|
||||
// 1. If the `join_rule` is anything other than `knock` or `knock_restricted`,
|
||||
// reject.
|
||||
if !matches!(join_rules, JoinRule::KnockRestricted(_) | JoinRule::Knock) {
|
||||
@@ -1136,7 +1150,7 @@ struct GetThirdPartyInvite {
|
||||
);
|
||||
false
|
||||
} else if matches!(join_rules, JoinRule::KnockRestricted(_))
|
||||
&& !room_version.knock_restricted_join_rule
|
||||
&& !room_version.authorization.knock_restricted_join_rule
|
||||
{
|
||||
// 2. If the `join_rule` is `knock_restricted`, but the room does not support
|
||||
// `knock_restricted`, reject.
|
||||
@@ -1226,7 +1240,7 @@ fn can_send_event(event: &impl Event, ple: Option<&impl Event>, user_level: Int)
|
||||
/// Confirm that the event sender has the required power levels.
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn check_power_levels(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
power_event: &impl Event,
|
||||
previous_power_event: Option<&impl Event>,
|
||||
user_level: Int,
|
||||
@@ -1382,7 +1396,7 @@ fn check_power_levels(
|
||||
}
|
||||
|
||||
// Notifications, currently there is only @room
|
||||
if room_version.limit_notifications_power_levels {
|
||||
if room_version.authorization.limit_notifications_power_levels {
|
||||
let old_level = old_state.notifications.room;
|
||||
let new_level = new_state.notifications.room;
|
||||
if old_level != new_level {
|
||||
@@ -1451,7 +1465,7 @@ fn get_deserialize_levels(
|
||||
/// Does the event redacting come from a user with enough power to redact the
|
||||
/// given event.
|
||||
fn check_redaction(
|
||||
_room_version: &RoomVersion,
|
||||
_room_version: &RoomVersionRules,
|
||||
redaction_event: &impl Event,
|
||||
user_level: Int,
|
||||
redact_level: Int,
|
||||
@@ -1501,79 +1515,34 @@ fn get_send_level(
|
||||
}
|
||||
|
||||
fn verify_third_party_invite(
|
||||
target_user: Option<&UserId>,
|
||||
sender: &UserId,
|
||||
tp_id: &ThirdPartyInvite,
|
||||
current_third_party_invite: Option<&impl Event>,
|
||||
_target_user: Option<&UserId>,
|
||||
_sender: &UserId,
|
||||
_tp_id: &ThirdPartyInvite,
|
||||
_current_third_party_invite: Option<&impl Event>,
|
||||
) -> bool {
|
||||
// 1. Check for user being banned happens before this is called
|
||||
// checking for mxid and token keys is done by ruma when deserializing
|
||||
|
||||
// The state key must match the invitee
|
||||
if target_user != Some(&tp_id.signed.mxid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no m.room.third_party_invite event in the current room state with
|
||||
// state_key matching token, reject
|
||||
#[allow(clippy::manual_let_else)]
|
||||
let current_tpid = match current_third_party_invite {
|
||||
| Some(id) => id,
|
||||
| None => return false,
|
||||
};
|
||||
|
||||
if current_tpid.state_key() != Some(&tp_id.signed.token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if sender != current_tpid.sender() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If any signature in signed matches any public key in the
|
||||
// m.room.third_party_invite event, allow
|
||||
#[allow(clippy::manual_let_else)]
|
||||
let tpid_ev =
|
||||
match from_json_str::<RoomThirdPartyInviteEventContent>(current_tpid.content().get()) {
|
||||
| Ok(ev) => ev,
|
||||
| Err(_) => return false,
|
||||
};
|
||||
|
||||
#[allow(clippy::manual_let_else)]
|
||||
let decoded_invite_token = match Base64::parse(&tp_id.signed.token) {
|
||||
| Ok(tok) => tok,
|
||||
// FIXME: Log a warning?
|
||||
| Err(_) => return false,
|
||||
};
|
||||
|
||||
// A list of public keys in the public_keys field
|
||||
for key in tpid_ev.public_keys.unwrap_or_default() {
|
||||
if key.public_key == decoded_invite_token {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// A single public key in the public_key field
|
||||
tpid_ev.public_key == decoded_invite_token
|
||||
// TODO: implement proper verification here
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruma::events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::{
|
||||
join_rules::{
|
||||
AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent, RoomMembership,
|
||||
use ruma::{
|
||||
events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::{
|
||||
join_rules::{AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
room::RoomMembership,
|
||||
room_version_rules::RoomVersionRules,
|
||||
};
|
||||
use serde_json::value::to_raw_value as to_raw_json_value;
|
||||
|
||||
use crate::{
|
||||
matrix::{Event, EventTypeExt, Pdu as PduEvent},
|
||||
state_res::{
|
||||
RoomVersion, StateMap,
|
||||
StateMap,
|
||||
event_auth::valid_membership_change,
|
||||
test_utils::{
|
||||
INITIAL_EVENTS, INITIAL_EVENTS_CREATE_ROOM, alice, charlie, ella, event_id,
|
||||
@@ -1610,7 +1579,7 @@ fn test_ban_pass() {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1655,7 +1624,7 @@ fn test_join_non_creator() {
|
||||
|
||||
assert!(
|
||||
!valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1700,7 +1669,7 @@ fn test_join_creator() {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1745,7 +1714,7 @@ fn test_ban_fail() {
|
||||
|
||||
assert!(
|
||||
!valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1807,7 +1776,7 @@ fn test_restricted_join_rule() {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V9,
|
||||
&RoomVersionRules::V9,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1825,7 +1794,7 @@ fn test_restricted_join_rule() {
|
||||
|
||||
assert!(
|
||||
!valid_membership_change(
|
||||
&RoomVersion::V9,
|
||||
&RoomVersionRules::V9,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1879,7 +1848,7 @@ fn test_knock() {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V7,
|
||||
&RoomVersionRules::V7,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
pub(crate) mod error;
|
||||
pub mod event_auth;
|
||||
mod power_levels;
|
||||
mod room_version;
|
||||
mod serde_backports;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
@@ -20,25 +20,22 @@
|
||||
|
||||
use futures::{Future, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future};
|
||||
use ruma::{
|
||||
EventId, Int, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId,
|
||||
EventId, Int, MilliSecondsSinceUnixEpoch, OwnedEventId,
|
||||
events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
int,
|
||||
room_version_rules::{RoomIdFormatVersion, RoomVersionRules, StateResolutionVersion},
|
||||
};
|
||||
use serde_json::from_str as from_json_str;
|
||||
|
||||
pub(crate) use self::error::Error;
|
||||
pub use self::event_auth::{auth_check, auth_types_for_event};
|
||||
use self::power_levels::PowerLevelsContentFields;
|
||||
pub use self::{
|
||||
event_auth::{auth_check, auth_types_for_event},
|
||||
room_version::RoomVersion,
|
||||
};
|
||||
use crate::{
|
||||
debug, debug_error, err,
|
||||
matrix::{Event, StateKey},
|
||||
state_res::room_version::StateResolutionVersion,
|
||||
trace,
|
||||
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, WidebandExt},
|
||||
warn,
|
||||
@@ -77,7 +74,7 @@
|
||||
//#[tracing::instrument(level event_fetch))]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub async fn resolve<'a, Pdu, Sets, SetIter, Hasher, Fetch, FetchFut, Exists, ExistsFut>(
|
||||
room_version: &RoomVersionId,
|
||||
room_version: &RoomVersionRules,
|
||||
state_sets: Sets,
|
||||
auth_chain_sets: &'a [HashSet<OwnedEventId, Hasher>],
|
||||
event_fetch: &Fetch,
|
||||
@@ -94,11 +91,7 @@ pub async fn resolve<'a, Pdu, Sets, SetIter, Hasher, Fetch, FetchFut, Exists, Ex
|
||||
Pdu: Event + Clone + Send + Sync,
|
||||
for<'b> &'b Pdu: Event + Send,
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
let stateres_version = match room_version {
|
||||
| V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | V11 => StateResolutionVersion::V2,
|
||||
| _ => StateResolutionVersion::V2_1,
|
||||
};
|
||||
let stateres_version = room_version.state_res;
|
||||
debug!(version = ?stateres_version, "State resolution starting");
|
||||
|
||||
// Split non-conflicting and conflicting state
|
||||
@@ -114,19 +107,21 @@ pub async fn resolve<'a, Pdu, Sets, SetIter, Hasher, Fetch, FetchFut, Exists, Ex
|
||||
|
||||
debug!(count = conflicting.len(), "conflicting events");
|
||||
trace!(map = ?conflicting, "conflicting events");
|
||||
let (conflicted_state_subgraph, initial_state) =
|
||||
if stateres_version == StateResolutionVersion::V2_1 {
|
||||
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
Error::InvalidPdu("Failed to calculate conflicted subgraph".to_owned())
|
||||
})?;
|
||||
debug!(count = csg.len(), "conflicted subgraph");
|
||||
trace!(set = ?csg, "conflicted subgraph");
|
||||
(csg, HashMap::new())
|
||||
} else {
|
||||
(HashSet::new(), unconflicted.clone())
|
||||
};
|
||||
let (conflicted_state_subgraph, initial_state) = if let StateResolutionVersion::V2(v2_rules) =
|
||||
stateres_version
|
||||
&& v2_rules.consider_conflicted_state_subgraph
|
||||
{
|
||||
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
Error::InvalidPdu("Failed to calculate conflicted subgraph".to_owned())
|
||||
})?;
|
||||
debug!(count = csg.len(), "conflicted subgraph");
|
||||
trace!(set = ?csg, "conflicted subgraph");
|
||||
(csg, HashMap::new())
|
||||
} else {
|
||||
(HashSet::new(), unconflicted.clone())
|
||||
};
|
||||
|
||||
// `all_conflicted` contains unique items
|
||||
// synapse says `full_set = {eid for eid in full_conflicted_set if eid in
|
||||
@@ -166,7 +161,6 @@ pub async fn resolve<'a, Pdu, Sets, SetIter, Hasher, Fetch, FetchFut, Exists, Ex
|
||||
debug!(count = sorted_control_levels.len(), "power events");
|
||||
trace!(list = ?sorted_control_levels, "sorted power events");
|
||||
|
||||
let room_version = RoomVersion::new(room_version)?;
|
||||
// Sequentially auth check each control event.
|
||||
let resolved_control = iterative_auth_check(
|
||||
&room_version,
|
||||
@@ -596,7 +590,7 @@ async fn get_power_level_for_sender<E, F, Fut>(
|
||||
/// `event_auth::auth_check` function.
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn iterative_auth_check<'a, E, F, Fut, S>(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
events_to_check: S,
|
||||
unconflicted_state: StateMap<OwnedEventId>,
|
||||
fetch_event: &F,
|
||||
@@ -666,7 +660,7 @@ async fn iterative_auth_check<'a, E, F, Fut, S>(
|
||||
trace!(list = ?auth_types, event_id = event.event_id().as_str(), "auth types for event");
|
||||
|
||||
let mut auth_state = StateMap::new();
|
||||
if room_version.room_ids_as_hashes {
|
||||
if room_version.room_id_format == RoomIdFormatVersion::V2 {
|
||||
trace!("room version uses hashed IDs, manually fetching create event");
|
||||
let create_event_id_raw = event.room_id_or_hash().as_str().replace('!', "$");
|
||||
let create_event_id = EventId::parse(&create_event_id_raw).map_err(|e| {
|
||||
@@ -963,7 +957,7 @@ fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, Stat
|
||||
|
||||
impl EventTypeExt for TimelineEventType {
|
||||
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
|
||||
(self.into(), state_key.into())
|
||||
(self.to_string().into(), state_key.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,13 +982,14 @@ mod tests {
|
||||
StateEventType, TimelineEventType,
|
||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
},
|
||||
int, uint,
|
||||
int,
|
||||
room_version_rules::RoomVersionRules,
|
||||
uint,
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value as to_raw_json_value};
|
||||
|
||||
use super::{
|
||||
StateMap, is_power_event,
|
||||
room_version::RoomVersion,
|
||||
test_utils::{
|
||||
INITIAL_EVENTS, TestStore, alice, bob, charlie, do_check, ella, event_id,
|
||||
member_content_ban, member_content_join, room_id, to_init_pdu_event, to_pdu_event,
|
||||
@@ -1004,7 +999,6 @@ mod tests {
|
||||
use crate::{
|
||||
debug,
|
||||
matrix::{Event, EventTypeExt, Pdu as PduEvent},
|
||||
state_res::room_version::StateResolutionVersion,
|
||||
utils::stream::IterStream,
|
||||
};
|
||||
|
||||
@@ -1036,7 +1030,7 @@ async fn test_event_sort() {
|
||||
.unwrap();
|
||||
|
||||
let resolved_power = super::iterative_auth_check(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
sorted_power_events.iter().map(AsRef::as_ref).stream(),
|
||||
HashMap::new(), // unconflicted events
|
||||
&fetcher,
|
||||
@@ -1436,13 +1430,18 @@ async fn test_event_map_none() {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let resolved =
|
||||
match super::resolve(&RoomVersionId::V2, &state_sets, &auth_chain, &fetcher, &exists)
|
||||
.await
|
||||
{
|
||||
| Ok(state) => state,
|
||||
| Err(e) => panic!("{e}"),
|
||||
};
|
||||
let resolved = match super::resolve(
|
||||
&RoomVersionRules::V2,
|
||||
&state_sets,
|
||||
&auth_chain,
|
||||
&fetcher,
|
||||
&exists,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(state) => state,
|
||||
| Err(e) => panic!("{e}"),
|
||||
};
|
||||
|
||||
assert_eq!(expected, resolved);
|
||||
}
|
||||
@@ -1549,13 +1548,18 @@ async fn ban_with_auth_chains2() {
|
||||
|
||||
let fetcher = |id: OwnedEventId| ready(ev_map.get(&id).cloned());
|
||||
let exists = |id: OwnedEventId| ready(ev_map.get(&id).is_some());
|
||||
let resolved =
|
||||
match super::resolve(&RoomVersionId::V6, &state_sets, &auth_chain, &fetcher, &exists)
|
||||
.await
|
||||
{
|
||||
| Ok(state) => state,
|
||||
| Err(e) => panic!("{e}"),
|
||||
};
|
||||
let resolved = match super::resolve(
|
||||
&RoomVersionRules::V6,
|
||||
&state_sets,
|
||||
&auth_chain,
|
||||
&fetcher,
|
||||
&exists,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(state) => state,
|
||||
| Err(e) => panic!("{e}"),
|
||||
};
|
||||
|
||||
debug!(
|
||||
resolved = ?resolved
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
Int, OwnedUserId, UserId,
|
||||
events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent},
|
||||
power_levels::{NotificationPowerLevels, default_power_level},
|
||||
serde::{
|
||||
deserialize_v1_powerlevel, vec_deserialize_int_powerlevel_values,
|
||||
vec_deserialize_v1_powerlevel_values,
|
||||
},
|
||||
room_version_rules::{AuthorizationRules, RoomVersionRules},
|
||||
serde::deserialize_v1_powerlevel,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Error, from_str as from_json_str};
|
||||
|
||||
use super::{Result, RoomVersion};
|
||||
use super::{Result, serde_backports::*};
|
||||
use crate::error;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -48,8 +46,11 @@ struct IntRoomPowerLevelsEventContent {
|
||||
notifications: IntNotificationPowerLevels,
|
||||
}
|
||||
|
||||
impl From<IntRoomPowerLevelsEventContent> for RoomPowerLevelsEventContent {
|
||||
fn from(int_pl: IntRoomPowerLevelsEventContent) -> Self {
|
||||
impl IntRoomPowerLevelsEventContent {
|
||||
fn to_room_power_levels_content(
|
||||
self,
|
||||
auth_rules: &AuthorizationRules,
|
||||
) -> RoomPowerLevelsEventContent {
|
||||
let IntRoomPowerLevelsEventContent {
|
||||
ban,
|
||||
events,
|
||||
@@ -61,9 +62,9 @@ fn from(int_pl: IntRoomPowerLevelsEventContent) -> Self {
|
||||
users,
|
||||
users_default,
|
||||
notifications,
|
||||
} = int_pl;
|
||||
} = self;
|
||||
|
||||
let mut pl = Self::new();
|
||||
let mut pl = RoomPowerLevelsEventContent::new(auth_rules);
|
||||
pl.ban = ban;
|
||||
pl.events = events;
|
||||
pl.events_default = events_default;
|
||||
@@ -101,18 +102,21 @@ fn from(int_notif: IntNotificationPowerLevels) -> Self {
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_power_levels(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Option<RoomPowerLevelsEventContent> {
|
||||
if room_version.integer_power_levels {
|
||||
deserialize_integer_power_levels(content)
|
||||
if room_version.authorization.integer_power_levels {
|
||||
deserialize_integer_power_levels(content, &room_version.authorization)
|
||||
} else {
|
||||
deserialize_legacy_power_levels(content)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_integer_power_levels(content: &str) -> Option<RoomPowerLevelsEventContent> {
|
||||
fn deserialize_integer_power_levels(
|
||||
content: &str,
|
||||
auth_rules: &AuthorizationRules,
|
||||
) -> Option<RoomPowerLevelsEventContent> {
|
||||
match from_json_str::<IntRoomPowerLevelsEventContent>(content) {
|
||||
| Ok(content) => Some(content.into()),
|
||||
| Ok(content) => Some(content.to_room_power_levels_content(auth_rules)),
|
||||
| Err(_) => {
|
||||
error!("m.room.power_levels event is not valid with integer values");
|
||||
None
|
||||
@@ -174,9 +178,9 @@ fn from(pl: IntPowerLevelsContentFields) -> Self {
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_power_levels_content_fields(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Result<PowerLevelsContentFields, Error> {
|
||||
if room_version.integer_power_levels {
|
||||
if room_version.authorization.integer_power_levels {
|
||||
deserialize_integer_power_levels_content_fields(content)
|
||||
} else {
|
||||
deserialize_legacy_power_levels_content_fields(content)
|
||||
@@ -216,9 +220,9 @@ fn from(pl: IntPowerLevelsContentInvite) -> Self {
|
||||
|
||||
pub(crate) fn deserialize_power_levels_content_invite(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Result<PowerLevelsContentInvite, Error> {
|
||||
if room_version.integer_power_levels {
|
||||
if room_version.authorization.integer_power_levels {
|
||||
from_json_str::<IntPowerLevelsContentInvite>(content).map(Into::into)
|
||||
} else {
|
||||
from_json_str(content)
|
||||
@@ -246,9 +250,9 @@ fn from(pl: IntPowerLevelsContentRedact) -> Self {
|
||||
|
||||
pub(crate) fn deserialize_power_levels_content_redact(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Result<PowerLevelsContentRedact, Error> {
|
||||
if room_version.integer_power_levels {
|
||||
if room_version.authorization.integer_power_levels {
|
||||
from_json_str::<IntPowerLevelsContentRedact>(content).map(Into::into)
|
||||
} else {
|
||||
from_json_str(content)
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
use ruma::RoomVersionId;
|
||||
|
||||
use super::{Error, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::exhaustive_enums)]
|
||||
pub enum RoomDisposition {
|
||||
/// A room version that has a stable specification.
|
||||
Stable,
|
||||
/// A room version that is not yet fully specified.
|
||||
Unstable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum EventFormatVersion {
|
||||
/// $id:server event id format
|
||||
V1,
|
||||
/// MSC1659-style $hash event id format: introduced for room v3
|
||||
V2,
|
||||
/// MSC1884-style $hash format: introduced for room v4
|
||||
V3,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum StateResolutionVersion {
|
||||
/// State resolution for rooms at version 1.
|
||||
V1,
|
||||
/// State resolution for room at version 2 or later.
|
||||
V2,
|
||||
/// State resolution for room at version 12 or later.
|
||||
V2_1,
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct RoomVersion {
|
||||
/// The stability of this room.
|
||||
pub disposition: RoomDisposition,
|
||||
/// The format of the EventId.
|
||||
pub event_format: EventFormatVersion,
|
||||
/// Which state resolution algorithm is used.
|
||||
pub state_res: StateResolutionVersion,
|
||||
// FIXME: not sure what this one means?
|
||||
pub enforce_key_validity: bool,
|
||||
|
||||
/// `m.room.aliases` had special auth rules and redaction rules
|
||||
/// before room version 6.
|
||||
///
|
||||
/// before MSC2261/MSC2432,
|
||||
pub special_case_aliases_auth: bool,
|
||||
/// Strictly enforce canonical json, do not allow:
|
||||
/// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
|
||||
/// * Floats
|
||||
/// * NaN, Infinity, -Infinity
|
||||
pub strict_canonicaljson: bool,
|
||||
/// Verify notifications key while checking m.room.power_levels.
|
||||
///
|
||||
/// bool: MSC2209: Check 'notifications'
|
||||
pub limit_notifications_power_levels: bool,
|
||||
/// Extra rules when verifying redaction events.
|
||||
pub extra_redaction_checks: bool,
|
||||
/// Allow knocking in event authentication.
|
||||
///
|
||||
/// See [room v7 specification](https://spec.matrix.org/latest/rooms/v7/)
|
||||
pub allow_knocking: bool,
|
||||
/// Adds support for the restricted join rule.
|
||||
///
|
||||
/// See: [MSC3289](https://github.com/matrix-org/matrix-spec-proposals/pull/3289)
|
||||
pub restricted_join_rules: bool,
|
||||
/// Adds support for the knock_restricted join rule.
|
||||
///
|
||||
/// See: [MSC3787](https://github.com/matrix-org/matrix-spec-proposals/pull/3787)
|
||||
pub knock_restricted_join_rule: bool,
|
||||
/// Enforces integer power levels.
|
||||
///
|
||||
/// See: [MSC3667](https://github.com/matrix-org/matrix-spec-proposals/pull/3667)
|
||||
pub integer_power_levels: bool,
|
||||
/// Determine the room creator using the `m.room.create` event's `sender`,
|
||||
/// instead of the event content's `creator` field.
|
||||
///
|
||||
/// See: [MSC2175](https://github.com/matrix-org/matrix-spec-proposals/pull/2175)
|
||||
pub use_room_create_sender: bool,
|
||||
/// Whether the room creators are considered superusers.
|
||||
/// A superuser will always have infinite power levels in the room.
|
||||
///
|
||||
/// See: [MSC4289](https://github.com/matrix-org/matrix-spec-proposals/pull/4289)
|
||||
pub explicitly_privilege_room_creators: bool,
|
||||
/// Whether the room's m.room.create event ID is itself the room ID.
|
||||
///
|
||||
/// See: [MSC4291](https://github.com/matrix-org/matrix-spec-proposals/pull/4291)
|
||||
pub room_ids_as_hashes: bool,
|
||||
}
|
||||
|
||||
impl RoomVersion {
|
||||
pub const V1: Self = Self {
|
||||
disposition: RoomDisposition::Stable,
|
||||
event_format: EventFormatVersion::V1,
|
||||
state_res: StateResolutionVersion::V1,
|
||||
enforce_key_validity: false,
|
||||
special_case_aliases_auth: true,
|
||||
strict_canonicaljson: false,
|
||||
limit_notifications_power_levels: false,
|
||||
extra_redaction_checks: true,
|
||||
allow_knocking: false,
|
||||
restricted_join_rules: false,
|
||||
knock_restricted_join_rule: false,
|
||||
integer_power_levels: false,
|
||||
use_room_create_sender: false,
|
||||
explicitly_privilege_room_creators: false,
|
||||
room_ids_as_hashes: false,
|
||||
};
|
||||
pub const V10: Self = Self {
|
||||
knock_restricted_join_rule: true,
|
||||
integer_power_levels: true,
|
||||
..Self::V9
|
||||
};
|
||||
pub const V11: Self = Self {
|
||||
use_room_create_sender: true,
|
||||
..Self::V10
|
||||
};
|
||||
pub const V12: Self = Self {
|
||||
explicitly_privilege_room_creators: true,
|
||||
room_ids_as_hashes: true,
|
||||
..Self::V11
|
||||
};
|
||||
pub const V2: Self = Self {
|
||||
state_res: StateResolutionVersion::V2,
|
||||
..Self::V1
|
||||
};
|
||||
pub const V3: Self = Self {
|
||||
event_format: EventFormatVersion::V2,
|
||||
extra_redaction_checks: false,
|
||||
..Self::V2
|
||||
};
|
||||
pub const V4: Self = Self {
|
||||
event_format: EventFormatVersion::V3,
|
||||
..Self::V3
|
||||
};
|
||||
pub const V5: Self = Self { enforce_key_validity: true, ..Self::V4 };
|
||||
pub const V6: Self = Self {
|
||||
special_case_aliases_auth: false,
|
||||
strict_canonicaljson: true,
|
||||
limit_notifications_power_levels: true,
|
||||
..Self::V5
|
||||
};
|
||||
pub const V7: Self = Self { allow_knocking: true, ..Self::V6 };
|
||||
pub const V8: Self = Self { restricted_join_rules: true, ..Self::V7 };
|
||||
pub const V9: Self = Self::V8;
|
||||
|
||||
pub fn new(version: &RoomVersionId) -> Result<Self> {
|
||||
Ok(match version {
|
||||
| RoomVersionId::V1 => Self::V1,
|
||||
| RoomVersionId::V2 => Self::V2,
|
||||
| RoomVersionId::V3 => Self::V3,
|
||||
| RoomVersionId::V4 => Self::V4,
|
||||
| RoomVersionId::V5 => Self::V5,
|
||||
| RoomVersionId::V6 => Self::V6,
|
||||
| RoomVersionId::V7 => Self::V7,
|
||||
| RoomVersionId::V8 => Self::V8,
|
||||
| RoomVersionId::V9 => Self::V9,
|
||||
| RoomVersionId::V10 => Self::V10,
|
||||
| RoomVersionId::V11 => Self::V11,
|
||||
| RoomVersionId::V12 => Self::V12,
|
||||
| ver => return Err(Error::Unsupported(format!("found version `{ver}`"))),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//! These functions are copied from an old version of Ruma. power_levels.rs uses
|
||||
//! them to lazily deserialize power level events. Upstream Ruma uses a much
|
||||
//! more elegant approach in its state resolution code, which we may want
|
||||
//! to look into at some point.
|
||||
|
||||
use std::{fmt, marker::PhantomData};
|
||||
|
||||
use ruma::{Int, serde::deserialize_v1_powerlevel};
|
||||
use serde::{
|
||||
Deserialize, Deserializer,
|
||||
de::{MapAccess, Visitor},
|
||||
};
|
||||
|
||||
/// Take a Map with values of either an integer number or a string and
|
||||
/// deserialize those to integer numbers in a Vec of sorted pairs.
|
||||
///
|
||||
/// To be used like this:
|
||||
/// `#[serde(deserialize_with = "vec_deserialize_v1_powerlevel_values")]`
|
||||
pub fn vec_deserialize_v1_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
#[repr(transparent)]
|
||||
struct IntWrap(Int);
|
||||
|
||||
impl<'de> Deserialize<'de> for IntWrap {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_v1_powerlevel(deserializer).map(IntWrap)
|
||||
}
|
||||
}
|
||||
|
||||
struct IntMapVisitor<T> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> IntMapVisitor<T> {
|
||||
fn new() -> Self { Self { _phantom: PhantomData } }
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
||||
where
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
type Value = Vec<(T, Int)>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a map with integers or strings as values")
|
||||
}
|
||||
|
||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||
let mut res = Vec::new();
|
||||
if let Some(hint) = map.size_hint() {
|
||||
res.reserve(hint);
|
||||
}
|
||||
|
||||
while let Some((k, IntWrap(v))) = map.next_entry()? {
|
||||
res.push((k, v));
|
||||
}
|
||||
|
||||
res.sort_unstable();
|
||||
res.dedup_by(|a, b| a.0 == b.0);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
de.deserialize_map(IntMapVisitor::new())
|
||||
}
|
||||
|
||||
/// Take a Map with integer values and deserialize those to a Vec of sorted
|
||||
/// pairs
|
||||
///
|
||||
/// To be used like this:
|
||||
/// `#[serde(deserialize_with = "vec_deserialize_int_powerlevel_values")]`
|
||||
pub fn vec_deserialize_int_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
struct IntMapVisitor<T> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> IntMapVisitor<T> {
|
||||
fn new() -> Self { Self { _phantom: PhantomData } }
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
||||
where
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
type Value = Vec<(T, Int)>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a map with integers as values")
|
||||
}
|
||||
|
||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||
let mut res = Vec::new();
|
||||
if let Some(hint) = map.size_hint() {
|
||||
res.reserve(hint);
|
||||
}
|
||||
|
||||
while let Some(item) = map.next_entry()? {
|
||||
res.push(item);
|
||||
}
|
||||
|
||||
res.sort_unstable();
|
||||
res.dedup_by(|a, b| a.0 == b.0);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
de.deserialize_map(IntMapVisitor::new())
|
||||
}
|
||||
@@ -15,7 +15,9 @@
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
int, room_id, uint, user_id,
|
||||
int, room_id,
|
||||
room_version_rules::RoomVersionRules,
|
||||
uint, user_id,
|
||||
};
|
||||
use serde_json::{
|
||||
json,
|
||||
@@ -24,7 +26,7 @@
|
||||
|
||||
use super::auth_types_for_event;
|
||||
use crate::{
|
||||
Result, RoomVersion, info,
|
||||
Result, info,
|
||||
matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash},
|
||||
};
|
||||
|
||||
@@ -133,9 +135,14 @@ pub(crate) async fn do_check(
|
||||
let event_map = &event_map;
|
||||
let fetch = |id: OwnedEventId| ready(event_map.get(&id).cloned());
|
||||
let exists = |id: OwnedEventId| ready(event_map.get(&id).is_some());
|
||||
let resolved =
|
||||
super::resolve(&RoomVersionId::V6, state_sets, &auth_chain_sets, &fetch, &exists)
|
||||
.await;
|
||||
let resolved = super::resolve(
|
||||
&RoomVersionRules::V6,
|
||||
state_sets,
|
||||
&auth_chain_sets,
|
||||
&fetch,
|
||||
&exists,
|
||||
)
|
||||
.await;
|
||||
|
||||
match resolved {
|
||||
| Ok(state) => state,
|
||||
@@ -154,7 +161,7 @@ pub(crate) async fn do_check(
|
||||
fake_event.sender(),
|
||||
fake_event.state_key(),
|
||||
fake_event.content(),
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn versions() -> Vec<String> {
|
||||
vec![
|
||||
"r0.0.1".to_owned(),
|
||||
"r0.1.0".to_owned(),
|
||||
"r0.2.0".to_owned(),
|
||||
"r0.3.0".to_owned(),
|
||||
"r0.4.0".to_owned(),
|
||||
"r0.5.0".to_owned(),
|
||||
"r0.6.0".to_owned(),
|
||||
"r0.6.1".to_owned(),
|
||||
"v1.1".to_owned(),
|
||||
"v1.2".to_owned(),
|
||||
"v1.3".to_owned(),
|
||||
"v1.4".to_owned(),
|
||||
"v1.5".to_owned(),
|
||||
"v1.8".to_owned(),
|
||||
"v1.11".to_owned(),
|
||||
"v1.12".to_owned(),
|
||||
"v1.13".to_owned(),
|
||||
"v1.14".to_owned(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn unstable_features() -> BTreeMap<String, bool> {
|
||||
BTreeMap::from_iter([
|
||||
("org.matrix.e2e_cross_signing".to_owned(), true),
|
||||
("org.matrix.msc2285.stable".to_owned(), true), /* private read receipts (https://github.com/matrix-org/matrix-spec-proposals/pull/2285) */
|
||||
("uk.half-shot.msc2666.query_mutual_rooms".to_owned(), true), /* query mutual rooms (https://github.com/matrix-org/matrix-spec-proposals/pull/2666) */
|
||||
("org.matrix.msc2836".to_owned(), true), /* threading/threads (https://github.com/matrix-org/matrix-spec-proposals/pull/2836) */
|
||||
("org.matrix.msc2946".to_owned(), true), /* spaces/hierarchy summaries (https://github.com/matrix-org/matrix-spec-proposals/pull/2946) */
|
||||
("org.matrix.msc3026.busy_presence".to_owned(), true), /* busy presence status (https://github.com/matrix-org/matrix-spec-proposals/pull/3026) */
|
||||
("org.matrix.msc3827".to_owned(), true), /* filtering of /publicRooms by room type (https://github.com/matrix-org/matrix-spec-proposals/pull/3827) */
|
||||
("org.matrix.msc3952_intentional_mentions".to_owned(), true), /* intentional mentions (https://github.com/matrix-org/matrix-spec-proposals/pull/3952) */
|
||||
("org.matrix.msc3916.stable".to_owned(), true), /* authenticated media (https://github.com/matrix-org/matrix-spec-proposals/pull/3916) */
|
||||
("org.matrix.msc4180".to_owned(), true), /* stable flag for 3916 (https://github.com/matrix-org/matrix-spec-proposals/pull/4180) */
|
||||
("uk.tcpip.msc4133".to_owned(), true), /* Extending User Profile API with Key:Value Pairs (https://github.com/matrix-org/matrix-spec-proposals/pull/4133) */
|
||||
("us.cloke.msc4175".to_owned(), true), /* Profile field for user time zone (https://github.com/matrix-org/matrix-spec-proposals/pull/4175) */
|
||||
("org.matrix.simplified_msc3575".to_owned(), true), /* Simplified Sliding sync (https://github.com/matrix-org/matrix-spec-proposals/pull/4186) */
|
||||
("uk.timedout.msc4323".to_owned(), true), /* agnostic suspend (https://github.com/matrix-org/matrix-spec-proposals/pull/4323) */
|
||||
("org.matrix.msc4155".to_owned(), true), /* invite filtering (https://github.com/matrix-org/matrix-spec-proposals/pull/4155) */
|
||||
])
|
||||
}
|
||||
+1
-3
@@ -25,9 +25,7 @@
|
||||
version,
|
||||
version::{name, version},
|
||||
};
|
||||
pub use matrix::{
|
||||
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res,
|
||||
};
|
||||
pub use matrix::{Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, pdu, state_res};
|
||||
pub use parking_lot::{Mutex as SyncMutex, RwLock as SyncRwLock};
|
||||
pub use server::Server;
|
||||
pub use utils::{implement, result, result::Result};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{fmt, marker::PhantomData, str::FromStr};
|
||||
|
||||
use ruma::{CanonicalJsonError, CanonicalJsonObject, canonical_json::try_from_json_map};
|
||||
use ruma::{CanonicalJsonError, CanonicalJsonObject, canonical_json::to_canonical_value};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
pub fn to_canonical_object<T: serde::Serialize>(
|
||||
value: T,
|
||||
) -> Result<CanonicalJsonObject, CanonicalJsonError> {
|
||||
use CanonicalJsonError::SerDe;
|
||||
use serde::ser::Error;
|
||||
use ruma::CanonicalJsonValue;
|
||||
|
||||
match serde_json::to_value(value).map_err(SerDe)? {
|
||||
| serde_json::Value::Object(map) => try_from_json_map(map),
|
||||
| _ => Err(SerDe(serde_json::Error::custom("Value must be an object"))),
|
||||
match to_canonical_value(value)? {
|
||||
| CanonicalJsonValue::Object(map) => Ok(map),
|
||||
| _ => Err(to_canonical_value(1.0_f32).unwrap_err()), /* Hack to return a
|
||||
* CanonicalJsonError */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+73
-111
@@ -1,10 +1,13 @@
|
||||
#![allow(clippy::needless_borrows_for_generic_args)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
|
||||
use conduwuit::{
|
||||
arrayvec::ArrayVec,
|
||||
ruma::{EventId, RoomId, UserId, serde::Raw},
|
||||
ruma::{
|
||||
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, room_id, serde::Raw,
|
||||
user_id,
|
||||
},
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -16,15 +19,15 @@
|
||||
#[test]
|
||||
#[cfg_attr(debug_assertions, should_panic(expected = "serializing string at the top-level"))]
|
||||
fn ser_str() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let s = serialize_to_vec(&user_id).expect("failed to serialize user_id");
|
||||
assert_eq!(&s, user_id.as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_tuple() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
let mut a = user_id.as_bytes().to_vec();
|
||||
a.push(0xFF);
|
||||
@@ -38,8 +41,8 @@ fn ser_tuple() {
|
||||
|
||||
#[test]
|
||||
fn ser_tuple_option() {
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id = room_id!("!room:example.com");
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let mut a = Vec::<u8>::new();
|
||||
a.push(0xFF);
|
||||
@@ -64,8 +67,8 @@ fn ser_tuple_option() {
|
||||
fn ser_overflow() {
|
||||
const BUFSIZE: usize = 10;
|
||||
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
assert!(BUFSIZE < user_id.as_str().len() + room_id.as_str().len());
|
||||
let mut buf = ArrayVec::<u8, BUFSIZE>::new();
|
||||
@@ -74,48 +77,12 @@ fn ser_overflow() {
|
||||
_ = ser::serialize(&mut buf, val).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_complex() {
|
||||
use conduwuit::ruma::Mxc;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Dim {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
let mxc = Mxc {
|
||||
server_name: "example.com".try_into().unwrap(),
|
||||
media_id: "AbCdEfGhIjK",
|
||||
};
|
||||
|
||||
let dim = Dim { width: 123, height: 456 };
|
||||
|
||||
let mut a = Vec::new();
|
||||
a.extend_from_slice(b"mxc://");
|
||||
a.extend_from_slice(mxc.server_name.as_bytes());
|
||||
a.extend_from_slice(b"/");
|
||||
a.extend_from_slice(mxc.media_id.as_bytes());
|
||||
a.push(0xFF);
|
||||
a.extend_from_slice(&dim.width.to_be_bytes());
|
||||
a.extend_from_slice(&dim.height.to_be_bytes());
|
||||
a.push(0xFF);
|
||||
|
||||
let d: &[u32] = &[dim.width, dim.height];
|
||||
let b = (mxc, d, Interfix);
|
||||
let b = serialize_to_vec(b).expect("failed to serialize complex");
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_json() {
|
||||
use conduwuit::ruma::api::client::filter::FilterDefinition;
|
||||
|
||||
let filter = FilterDefinition {
|
||||
event_fields: Some(vec!["content.body".to_owned()]),
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = FilterDefinition::default();
|
||||
filter.event_fields = Some(vec!["content.body".to_owned()]);
|
||||
|
||||
let serialized = serialize_to_vec(Json(&filter)).expect("failed to serialize value");
|
||||
|
||||
@@ -127,10 +94,8 @@ fn ser_json() {
|
||||
fn ser_json_value() {
|
||||
use conduwuit::ruma::api::client::filter::FilterDefinition;
|
||||
|
||||
let filter = FilterDefinition {
|
||||
event_fields: Some(vec!["content.body".to_owned()]),
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = FilterDefinition::default();
|
||||
filter.event_fields = Some(vec!["content.body".to_owned()]);
|
||||
|
||||
let value = serde_json::to_value(filter).expect("failed to serialize to serde_json::value");
|
||||
let serialized = serialize_to_vec(Json(value)).expect("failed to serialize value");
|
||||
@@ -166,10 +131,8 @@ struct Foo {
|
||||
fn ser_json_raw() {
|
||||
use conduwuit::ruma::api::client::filter::FilterDefinition;
|
||||
|
||||
let filter = FilterDefinition {
|
||||
event_fields: Some(vec!["content.body".to_owned()]),
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = FilterDefinition::default();
|
||||
filter.event_fields = Some(vec!["content.body".to_owned()]);
|
||||
|
||||
let value =
|
||||
serde_json::value::to_raw_value(&filter).expect("failed to serialize to raw value");
|
||||
@@ -183,10 +146,8 @@ fn ser_json_raw() {
|
||||
fn ser_json_raw_json() {
|
||||
use conduwuit::ruma::api::client::filter::FilterDefinition;
|
||||
|
||||
let filter = FilterDefinition {
|
||||
event_fields: Some(vec!["content.body".to_owned()]),
|
||||
..Default::default()
|
||||
};
|
||||
let mut filter = FilterDefinition::default();
|
||||
filter.event_fields = Some(vec!["content.body".to_owned()]);
|
||||
|
||||
let value =
|
||||
serde_json::value::to_raw_value(&filter).expect("failed to serialize to raw value");
|
||||
@@ -197,11 +158,11 @@ fn ser_json_raw_json() {
|
||||
|
||||
#[test]
|
||||
fn de_tuple() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
let raw: &[u8] = b"@user:example.com\xFF!room:example.com";
|
||||
let (a, b): (&UserId, &RoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
let (a, b): (OwnedUserId, OwnedRoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(a, user_id, "deserialized user_id does not match");
|
||||
assert_eq!(b, room_id, "deserialized room_id does not match");
|
||||
@@ -210,11 +171,11 @@ fn de_tuple() {
|
||||
#[test]
|
||||
#[should_panic(expected = "failed to deserialize")]
|
||||
fn de_tuple_invalid() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
let raw: &[u8] = b"@user:example.com\xFF@user:example.com";
|
||||
let (a, b): (&UserId, &RoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
let (a, b): (OwnedUserId, OwnedRoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(a, user_id, "deserialized user_id does not match");
|
||||
assert_eq!(b, room_id, "deserialized room_id does not match");
|
||||
@@ -223,10 +184,10 @@ fn de_tuple_invalid() {
|
||||
#[test]
|
||||
#[should_panic(expected = "failed to deserialize")]
|
||||
fn de_tuple_incomplete() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let raw: &[u8] = b"@user:example.com";
|
||||
let (a, _): (&UserId, &RoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
let (a, _): (OwnedUserId, OwnedRoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(a, user_id, "deserialized user_id does not match");
|
||||
}
|
||||
@@ -234,10 +195,10 @@ fn de_tuple_incomplete() {
|
||||
#[test]
|
||||
#[should_panic(expected = "failed to deserialize")]
|
||||
fn de_tuple_incomplete_with_sep() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let raw: &[u8] = b"@user:example.com\xFF";
|
||||
let (a, _): (&UserId, &RoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
let (a, _): (OwnedUserId, OwnedRoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(a, user_id, "deserialized user_id does not match");
|
||||
}
|
||||
@@ -248,11 +209,11 @@ fn de_tuple_incomplete_with_sep() {
|
||||
should_panic(expected = "deserialization failed to consume trailing bytes")
|
||||
)]
|
||||
fn de_tuple_unfinished() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
let raw: &[u8] = b"@user:example.com\xFF!room:example.com\xFF@user:example.com";
|
||||
let (a, b): (&UserId, &RoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
let (a, b): (OwnedUserId, OwnedRoomId) = de::from_slice(raw).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(a, user_id, "deserialized user_id does not match");
|
||||
assert_eq!(b, room_id, "deserialized room_id does not match");
|
||||
@@ -260,11 +221,11 @@ fn de_tuple_unfinished() {
|
||||
|
||||
#[test]
|
||||
fn de_tuple_ignore() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
let raw: &[u8] = b"@user:example.com\xFF@user2:example.net\xFF!room:example.com";
|
||||
let (a, _, c): (&UserId, Ignore, &RoomId) =
|
||||
let (a, _, c): (OwnedUserId, Ignore, OwnedRoomId) =
|
||||
de::from_slice(raw).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(a, user_id, "deserialized user_id does not match");
|
||||
@@ -360,10 +321,10 @@ fn de_array() {
|
||||
#[test]
|
||||
#[ignore = "Nested sequences are not supported"]
|
||||
fn de_complex() {
|
||||
type Key<'a> = (&'a UserId, ArrayVec<u64, 2>, &'a RoomId);
|
||||
type Key = (OwnedUserId, ArrayVec<u64, 2>, OwnedRoomId);
|
||||
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
let room_id = room_id!("!room:example.com");
|
||||
let a: u64 = 123_456;
|
||||
let b: u64 = 987_654;
|
||||
|
||||
@@ -376,36 +337,36 @@ fn de_complex() {
|
||||
v.extend_from_slice(room_id.as_bytes());
|
||||
|
||||
let arr: &[u64] = &[a, b];
|
||||
let key = (user_id, arr, room_id);
|
||||
let key = (user_id.to_owned(), arr, room_id.to_owned());
|
||||
let s = serialize_to_vec(&key).expect("failed to serialize");
|
||||
|
||||
assert_eq!(&s, &v, "serialization does not match");
|
||||
|
||||
let key = (user_id, [a, b].into(), room_id);
|
||||
let arr: Key<'_> = de::from_slice(&v).expect("failed to deserialize");
|
||||
let key = (user_id.to_owned(), [a, b].into(), room_id.to_owned());
|
||||
let arr: Key = de::from_slice(&v).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(arr, key, "deserialization does not match");
|
||||
|
||||
let arr: Key<'_> = de::from_slice(&s).expect("failed to deserialize");
|
||||
let arr: Key = de::from_slice(&s).expect("failed to deserialize");
|
||||
|
||||
assert_eq!(arr, key, "deserialization of serialization does not match");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_value_some() {
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id = room_id!("!room:example.com");
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let mut aa = Vec::<u8>::new();
|
||||
aa.extend_from_slice(room_id.as_bytes());
|
||||
aa.push(0xFF);
|
||||
aa.extend_from_slice(user_id.as_bytes());
|
||||
|
||||
let bb: (&RoomId, Option<&UserId>) = (room_id, Some(user_id));
|
||||
let bb: (OwnedRoomId, Option<OwnedUserId>) = (room_id.to_owned(), Some(user_id.to_owned()));
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (&RoomId, Option<&UserId>) =
|
||||
let cc: (OwnedRoomId, Option<OwnedUserId>) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(bb.1, cc.1);
|
||||
@@ -414,17 +375,17 @@ fn serde_tuple_option_value_some() {
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_value_none() {
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let room_id = room_id!("!room:example.com");
|
||||
|
||||
let mut aa = Vec::<u8>::new();
|
||||
aa.extend_from_slice(room_id.as_bytes());
|
||||
aa.push(0xFF);
|
||||
|
||||
let bb: (&RoomId, Option<&UserId>) = (room_id, None);
|
||||
let bb: (OwnedRoomId, Option<OwnedUserId>) = (room_id.to_owned(), None);
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (&RoomId, Option<&UserId>) =
|
||||
let cc: (OwnedRoomId, Option<OwnedUserId>) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(None, cc.1);
|
||||
@@ -433,17 +394,17 @@ fn serde_tuple_option_value_none() {
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_none_value() {
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let mut aa = Vec::<u8>::new();
|
||||
aa.push(0xFF);
|
||||
aa.extend_from_slice(user_id.as_bytes());
|
||||
|
||||
let bb: (Option<&RoomId>, &UserId) = (None, user_id);
|
||||
let bb: (Option<OwnedRoomId>, OwnedUserId) = (None, user_id.to_owned());
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (Option<&RoomId>, &UserId) =
|
||||
let cc: (Option<OwnedRoomId>, OwnedUserId) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(None, cc.0);
|
||||
@@ -452,19 +413,19 @@ fn serde_tuple_option_none_value() {
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_some_value() {
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id = room_id!("!room:example.com");
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let mut aa = Vec::<u8>::new();
|
||||
aa.extend_from_slice(room_id.as_bytes());
|
||||
aa.push(0xFF);
|
||||
aa.extend_from_slice(user_id.as_bytes());
|
||||
|
||||
let bb: (Option<&RoomId>, &UserId) = (Some(room_id), user_id);
|
||||
let bb: (Option<OwnedRoomId>, OwnedUserId) = (Some(room_id.to_owned()), user_id.to_owned());
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (Option<&RoomId>, &UserId) =
|
||||
let cc: (Option<OwnedRoomId>, OwnedUserId) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(bb.0, cc.0);
|
||||
@@ -473,19 +434,20 @@ fn serde_tuple_option_some_value() {
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_some_some() {
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id = room_id!("!room:example.com");
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let mut aa = Vec::<u8>::new();
|
||||
aa.extend_from_slice(room_id.as_bytes());
|
||||
aa.push(0xFF);
|
||||
aa.extend_from_slice(user_id.as_bytes());
|
||||
|
||||
let bb: (Option<&RoomId>, Option<&UserId>) = (Some(room_id), Some(user_id));
|
||||
let bb: (Option<OwnedRoomId>, Option<OwnedUserId>) =
|
||||
(Some(room_id.to_owned()), Some(user_id.to_owned()));
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (Option<&RoomId>, Option<&UserId>) =
|
||||
let cc: (Option<OwnedRoomId>, Option<OwnedUserId>) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(cc.0, bb.0);
|
||||
@@ -496,11 +458,11 @@ fn serde_tuple_option_some_some() {
|
||||
fn serde_tuple_option_none_none() {
|
||||
let aa = vec![0xFF];
|
||||
|
||||
let bb: (Option<&RoomId>, Option<&UserId>) = (None, None);
|
||||
let bb: (Option<OwnedRoomId>, Option<OwnedUserId>) = (None, None);
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (Option<&RoomId>, Option<&UserId>) =
|
||||
let cc: (Option<OwnedRoomId>, Option<OwnedUserId>) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(cc.0, bb.0);
|
||||
@@ -509,8 +471,8 @@ fn serde_tuple_option_none_none() {
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_some_none_some() {
|
||||
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
|
||||
let user_id: &UserId = "@user:example.com".try_into().unwrap();
|
||||
let room_id = room_id!("!room:example.com");
|
||||
let user_id = user_id!("@user:example.com");
|
||||
|
||||
let mut aa = Vec::<u8>::new();
|
||||
aa.extend_from_slice(room_id.as_bytes());
|
||||
@@ -524,24 +486,24 @@ fn serde_tuple_option_some_none_some() {
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (Option<&RoomId>, Option<&EventId>, Option<&UserId>) =
|
||||
let cc: (Option<Cow<'_, RoomId>>, Option<Cow<'_, EventId>>, Option<Cow<'_, UserId>>) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(bb.0, cc.0);
|
||||
assert_eq!(None, cc.1);
|
||||
assert_eq!(bb.1, cc.1);
|
||||
assert_eq!(bb.2, cc.2);
|
||||
assert_eq!(bb.0, cc.0.as_deref());
|
||||
assert_eq!(None, cc.1.as_deref());
|
||||
assert_eq!(bb.1, cc.1.as_deref());
|
||||
assert_eq!(bb.2, cc.2.as_deref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_tuple_option_none_none_none() {
|
||||
let aa = vec![0xFF, 0xFF];
|
||||
|
||||
let bb: (Option<&RoomId>, Option<&EventId>, Option<&UserId>) = (None, None, None);
|
||||
let bb: (Option<OwnedRoomId>, Option<OwnedEventId>, Option<OwnedUserId>) = (None, None, None);
|
||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||
assert_eq!(aa, bbs);
|
||||
|
||||
let cc: (Option<&RoomId>, Option<&EventId>, Option<&UserId>) =
|
||||
let cc: (Option<OwnedRoomId>, Option<OwnedEventId>, Option<OwnedUserId>) =
|
||||
de::from_slice(&bbs).expect("failed to deserialize tuple");
|
||||
|
||||
assert_eq!(None, cc.0);
|
||||
|
||||
@@ -108,6 +108,7 @@ http-body-util.workspace = true
|
||||
hyper.workspace = true
|
||||
hyper-util.workspace = true
|
||||
log.workspace = true
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
rustls.workspace = true
|
||||
rustls.optional = true
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use conduwuit::Error;
|
||||
use conduwuit_service::{Services, state, state::Guard};
|
||||
use http::{StatusCode, Uri};
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
use ruma::api::error::ErrorKind;
|
||||
|
||||
pub(crate) fn build(services: &Arc<Services>) -> (Router, Guard) {
|
||||
let router = Router::<state::State>::new();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "ruminuwuity"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "mod.rs"
|
||||
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
client = []
|
||||
server = []
|
||||
|
||||
unstable-exhaustive-types = []
|
||||
unstable-msc3202 = []
|
||||
unstable-msc4203 = []
|
||||
|
||||
[dependencies]
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
wildmatch = "2.6.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
pub mod rooms;
|
||||
@@ -0,0 +1,56 @@
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomAliasId, OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: PUT,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
1.0 => "/_continuwuity/admin/rooms/{room_id}/ban",
|
||||
}
|
||||
}
|
||||
|
||||
#[request]
|
||||
pub struct Request {
|
||||
#[ruma_api(path)]
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
/// Whether to ban (true) or unban (false) the room.
|
||||
/// If true, and the room is not banned, all local users will be
|
||||
/// evacuated and prevented from re-joining.
|
||||
/// If false, and the room is unbanned, local users will be allowed to
|
||||
/// re-join. No-ops are no-ops.
|
||||
pub banned: bool,
|
||||
}
|
||||
|
||||
#[response]
|
||||
pub struct Response {
|
||||
pub kicked_users: Vec<OwnedUserId>,
|
||||
pub failed_kicked_users: Vec<OwnedUserId>,
|
||||
pub local_aliases: Vec<OwnedRoomAliasId>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, banned: bool) -> Self { Self { room_id, banned } }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
kicked_users: Vec<OwnedUserId>,
|
||||
failed_kicked_users: Vec<OwnedUserId>,
|
||||
local_aliases: Vec<OwnedRoomAliasId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
kicked_users,
|
||||
failed_kicked_users,
|
||||
local_aliases,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: GET,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
1.0 => "/_continuwuity/admin/rooms/list",
|
||||
}
|
||||
}
|
||||
|
||||
#[request]
|
||||
#[derive(Default)]
|
||||
pub struct Request;
|
||||
|
||||
#[response]
|
||||
pub struct Response {
|
||||
pub rooms: Vec<OwnedRoomId>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
#[must_use]
|
||||
pub fn new(rooms: Vec<OwnedRoomId>) -> Self { Self { rooms } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod ban;
|
||||
pub mod list;
|
||||
@@ -0,0 +1,52 @@
|
||||
//! `GET /_matrix/client/v1/admin/suspend/{userId}`
|
||||
//!
|
||||
//! Check the suspension status of a target user
|
||||
|
||||
pub mod v1 {
|
||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}`
|
||||
//! ([msc])
|
||||
//!
|
||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||
|
||||
use ruma::{
|
||||
OwnedUserId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: GET,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the get & set user suspension status endpoint.
|
||||
#[request(error = ruma::api::error::Error)]
|
||||
pub struct Request {
|
||||
/// The user to look up.
|
||||
#[ruma_api(path)]
|
||||
pub user_id: OwnedUserId,
|
||||
}
|
||||
|
||||
/// Response type for the suspension endpoints
|
||||
#[response(error = ruma::api::error::Error)]
|
||||
pub struct Response {
|
||||
/// Whether the user is currently suspended.
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given user id.
|
||||
#[must_use]
|
||||
pub fn new(user_id: OwnedUserId) -> Self { Self { user_id } }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given suspension status.
|
||||
#[must_use]
|
||||
pub fn new(suspended: bool) -> Self { Self { suspended } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod continuwuity;
|
||||
pub mod get_suspended;
|
||||
pub mod set_suspended;
|
||||
@@ -0,0 +1,54 @@
|
||||
//! `PUT /_matrix/client/v1/admin/suspend/{userId}`
|
||||
//!
|
||||
//! Set the suspension status of a target user
|
||||
|
||||
pub mod v1 {
|
||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}`
|
||||
//! ([msc])
|
||||
//!
|
||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||
|
||||
use ruma::{
|
||||
OwnedUserId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: PUT,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the set user suspension status endpoint.
|
||||
#[request(error = ruma::api::error::Error)]
|
||||
pub struct Request {
|
||||
/// The user to look up.
|
||||
#[ruma_api(path)]
|
||||
pub user_id: OwnedUserId,
|
||||
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
/// Response type for the suspension endpoints
|
||||
#[response(error = ruma::api::error::Error)]
|
||||
pub struct Response {
|
||||
/// Whether the user is currently suspended.
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given user id.
|
||||
#[must_use]
|
||||
pub fn new(user_id: OwnedUserId, suspended: bool) -> Self { Self { user_id, suspended } }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given suspension status.
|
||||
#[must_use]
|
||||
pub fn new(suspended: bool) -> Self { Self { suspended } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod user_may_invite;
|
||||
pub mod user_may_join_room;
|
||||
@@ -0,0 +1,51 @@
|
||||
//! `POST /api/1/spam_check/user_may_invite`
|
||||
//!
|
||||
//! Checks that a user may invite the given user to the given room via Draupnir
|
||||
//! anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/api/1/spam_check/user_may_invite",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_invite` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The room the invitee is being invited to
|
||||
pub room_id: OwnedRoomId,
|
||||
/// The user sending the invite
|
||||
pub inviter: OwnedUserId,
|
||||
/// The user being invited
|
||||
pub invitee: OwnedUserId,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_invite` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, inviter: OwnedUserId, invitee: OwnedUserId) -> Self {
|
||||
Self { room_id, inviter, invitee }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//! `POST /api/1/spam_check/user_may_join_room`
|
||||
//!
|
||||
//! Endpoint that checks whether a user may join a given room via Draupnir
|
||||
//! anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/api/1/spam_check/user_may_join_room",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_join_room` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
/// Whether the user was invited to this room
|
||||
pub is_invited: bool,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_join_room` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(user: OwnedUserId, room: OwnedRoomId, is_invited: bool) -> Self {
|
||||
Self { user, room, is_invited }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
//! Types for invite filtering ([MSC4155]).
|
||||
//!
|
||||
//! MSC4155: https://github.com/matrix-org/matrix-spec-proposals/pull/4155
|
||||
|
||||
use ruma::{ServerName, UserId, exports::ruma_macros::EventContent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wildmatch::WildMatch;
|
||||
|
||||
/// Represents a user's level of filtering on actions from another user or
|
||||
/// server. "Ignore" and "block" are defined in [MSC4283].
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FilterLevel {
|
||||
Allow,
|
||||
Ignore,
|
||||
Block,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.invite_permission_config", kind = GlobalAccountData)]
|
||||
pub struct InvitePermissionConfigEventContent {
|
||||
/// A global on/off toggle for all rules
|
||||
#[serde(default = "ruma::serde::default_true")]
|
||||
pub enabled: bool,
|
||||
|
||||
/// A list of globs matching users which are allowed to send an invite.
|
||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_users: Vec<String>,
|
||||
/// A list of globs matching users whose invites should be ignored (as
|
||||
/// defined in [MSC4283]).
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub ignored_users: Vec<String>,
|
||||
/// A list of globs matching users whose invites should be blocked (as
|
||||
/// defined in [MSC4283]). Invites from blocked users should be refused
|
||||
/// with the M_INVITE_BLOCKED status code.
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_users: Vec<String>,
|
||||
|
||||
/// A list of globs matching servers which are allowed to send an invite.
|
||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_servers: Vec<String>,
|
||||
/// A list of globs matching servers whose invites should be ignored (as
|
||||
/// defined in [MSC4283]).
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub ignored_servers: Vec<String>,
|
||||
/// A list of globs matching servers whose invites should be blocked (as
|
||||
/// defined in [MSC4283]). Invites from blocked servers should be refused
|
||||
/// with the M_INVITE_BLOCKED status code.
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_servers: Vec<String>,
|
||||
}
|
||||
|
||||
impl InvitePermissionConfigEventContent {
|
||||
/// Creates a new `InvitePermissionConfigEventContent` from six lists of
|
||||
/// globs.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
enabled: bool,
|
||||
allowed_users: Vec<String>,
|
||||
ignored_users: Vec<String>,
|
||||
blocked_users: Vec<String>,
|
||||
allowed_servers: Vec<String>,
|
||||
ignored_servers: Vec<String>,
|
||||
blocked_servers: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
enabled,
|
||||
allowed_users,
|
||||
ignored_users,
|
||||
blocked_users,
|
||||
allowed_servers,
|
||||
ignored_servers,
|
||||
blocked_servers,
|
||||
}
|
||||
}
|
||||
|
||||
/// Test the filters against a user id. This function will check both the
|
||||
/// user rules _and_ the server rules.
|
||||
#[must_use]
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
pub fn user_filter_level(&self, user: &UserId) -> FilterLevel {
|
||||
if !self.enabled {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.allowed_users, user.as_str()) {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.ignored_users, user.as_str()) {
|
||||
FilterLevel::Ignore
|
||||
} else if Self::matches(&self.blocked_users, user.as_str()) {
|
||||
FilterLevel::Block
|
||||
} else {
|
||||
self.server_filter_level(user.server_name())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test the filters against a server name. Port numbers are ignored.
|
||||
#[must_use]
|
||||
pub fn server_filter_level(&self, server: &ServerName) -> FilterLevel {
|
||||
if !self.enabled {
|
||||
FilterLevel::Allow
|
||||
} else {
|
||||
let server = server.host();
|
||||
if Self::matches(&self.allowed_servers, server) {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.ignored_servers, server) {
|
||||
FilterLevel::Ignore
|
||||
} else if Self::matches(&self.blocked_servers, server) {
|
||||
FilterLevel::Block
|
||||
} else {
|
||||
FilterLevel::Allow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn matches(a: &[String], s: &str) -> bool {
|
||||
a.iter()
|
||||
.map(String::as_str)
|
||||
.any(|a| WildMatch::new(a).matches(s))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruma::{ServerName, UserId, events::GlobalAccountDataEvent};
|
||||
use serde_json::{from_value as from_json_value, json};
|
||||
|
||||
use crate::invite_permission_config::{FilterLevel, InvitePermissionConfigEventContent};
|
||||
|
||||
fn user_id(id: &str) -> &UserId { <&UserId>::try_from(id).unwrap() }
|
||||
|
||||
fn server_name(name: &str) -> &ServerName { <&ServerName>::try_from(name).unwrap() }
|
||||
|
||||
#[test]
|
||||
fn default_values() {
|
||||
let data = json!({
|
||||
"content": {},
|
||||
"type": "org.matrix.msc4155.invite_permission_config"
|
||||
});
|
||||
|
||||
let event: GlobalAccountDataEvent<InvitePermissionConfigEventContent> =
|
||||
from_json_value(data).unwrap();
|
||||
assert!(event.content.enabled);
|
||||
assert!(event.content.allowed_users.is_empty());
|
||||
assert!(event.content.ignored_users.is_empty());
|
||||
assert!(event.content.blocked_users.is_empty());
|
||||
assert!(event.content.allowed_servers.is_empty());
|
||||
assert!(event.content.ignored_servers.is_empty());
|
||||
assert!(event.content.blocked_servers.is_empty());
|
||||
assert_eq!(
|
||||
event
|
||||
.content
|
||||
.user_filter_level(user_id("@alice:example.com")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
assert_eq!(
|
||||
event
|
||||
.content
|
||||
.server_filter_level(server_name("example.com")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_the_world() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:foo.com:8080")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_goodguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@alice:goodguys.org:8080")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude_badguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_servers: vec!["badguys.org".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@kevin:badguys.org:8080")),
|
||||
FilterLevel::Block
|
||||
);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_goodguys_except_for_kevin() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_users: vec!["@kevin:goodguys.org".to_owned()],
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:goodguys.org")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_badguys_except_for_alice() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_users: vec!["@alice:badguys.org".to_owned()],
|
||||
blocked_servers: vec!["badguys.org".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:badguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_goodguys_and_ignore_reallybadguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
ignored_servers: vec!["reallybadguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@alice:goodguys.org:8080")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@kevin:reallybadguys.org")),
|
||||
FilterLevel::Ignore
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//! `POST /_meowlnir/antispam/*/accept_make_join`
|
||||
//!
|
||||
//! Endpoint to accept or decline incoming make_join federation requests.
|
||||
//! Used by the `fi.mau.spam_check` restricted join rule.
|
||||
//!
|
||||
//! References:
|
||||
//! - https://mau.dev/maunium/synapse/-/blob/52741d3/synapse/handlers/event_auth.py#L280-292
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/accept_make_join",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `accept_make_join` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
}
|
||||
|
||||
/// Response type for the `accept_make_join` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
user: OwnedUserId,
|
||||
room: OwnedRoomId,
|
||||
) -> Self {
|
||||
Self { management_room_id, user, room }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod user_may_invite;
|
||||
pub mod user_may_join_room;
|
||||
|
||||
pub mod accept_make_join;
|
||||
@@ -0,0 +1,64 @@
|
||||
//! `POST /_meowlnir/antispam/*/user_may_invite`
|
||||
//!
|
||||
//! Checks that a user may invite the given user to the given room via Meowlnir
|
||||
//! anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_invite",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_invite` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user sending the invite
|
||||
pub inviter: OwnedUserId,
|
||||
/// The user being invited
|
||||
pub invitee: OwnedUserId,
|
||||
/// The room the invitee is being invited to
|
||||
pub room_id: OwnedRoomId,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_invite` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
inviter: OwnedUserId,
|
||||
invitee: OwnedUserId,
|
||||
room_id: OwnedRoomId,
|
||||
) -> Self {
|
||||
Self {
|
||||
management_room_id,
|
||||
inviter,
|
||||
invitee,
|
||||
room_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//! `POST /_meowlnir/antispam/*/user_may_join_room`
|
||||
//!
|
||||
//! Endpoint to track invite joins via Meowlnir anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_join_room",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_join_room` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
/// Whether the user was invited to this room
|
||||
pub is_invited: bool,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_join_room` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
user: OwnedUserId,
|
||||
room: OwnedRoomId,
|
||||
is_invited: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
management_room_id,
|
||||
user,
|
||||
room,
|
||||
is_invited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//! Ruminuwuity: Continuwuity-specific APIs and structs that depend only on Ruma
|
||||
|
||||
pub mod admin;
|
||||
pub mod draupnir_antispam;
|
||||
pub mod invite_permission_config;
|
||||
pub mod meowlnir_antispam;
|
||||
pub mod policy;
|
||||
@@ -0,0 +1,91 @@
|
||||
//! Types for the [`org.matrix.msc4284.policy`] event.
|
||||
//!
|
||||
//! [`org.matrix.msc4284.policy`]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
|
||||
use ruma::{events::EmptyStateKey, exports::ruma_macros::EventContent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Default)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "org.matrix.msc4284.policy", kind = State, state_key_type = EmptyStateKey)]
|
||||
pub struct RoomPolicyEventContent {
|
||||
/// The server name of the room's policy server.
|
||||
///
|
||||
/// If the value is empty or unreachable, the policy server should be
|
||||
/// ignored.
|
||||
pub via: Option<String>,
|
||||
/// The public key this policy server will sign with.
|
||||
pub public_key: Option<String>,
|
||||
}
|
||||
|
||||
impl RoomPolicyEventContent {
|
||||
/// Create an empty `RoomPolicyEventContent`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct PolicyServerResponseContent {
|
||||
/// The policy server's verdict. Either `ok` or `spam`.
|
||||
pub recommendation: String,
|
||||
}
|
||||
|
||||
impl PolicyServerResponseContent {
|
||||
/// Create a new `PolicyServerResponseContent` with the given
|
||||
/// recommendation.
|
||||
#[must_use]
|
||||
pub fn new(recommendation: String) -> Self { Self { recommendation } }
|
||||
}
|
||||
|
||||
impl From<String> for PolicyServerResponseContent {
|
||||
fn from(recommendation: String) -> Self { Self::new(recommendation) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruma::events::OriginalStateEvent;
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::RoomPolicyEventContent;
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let content = RoomPolicyEventContent {
|
||||
via: Some("example.com".to_owned()),
|
||||
public_key: Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned()),
|
||||
};
|
||||
|
||||
let actual = to_json_value(content).unwrap();
|
||||
let expected = json!({
|
||||
"via": "example.com",
|
||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||
});
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"via": "example.com",
|
||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||
},
|
||||
"event_id": "123:example.com",
|
||||
"origin_server_ts": 1,
|
||||
"room_id": "!123456:example.com",
|
||||
"sender": "@carl:example.com",
|
||||
"state_key": "",
|
||||
"type": "org.matrix.msc4284.policy"
|
||||
});
|
||||
|
||||
let content = from_json_value::<OriginalStateEvent<RoomPolicyEventContent>>(json_data)
|
||||
.unwrap()
|
||||
.content;
|
||||
assert_eq!(content.via, Some("example.com".to_owned()));
|
||||
assert_eq!(
|
||||
content.public_key,
|
||||
Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod event;
|
||||
pub mod policy_check;
|
||||
pub mod policy_sign;
|
||||
pub mod report_content;
|
||||
@@ -0,0 +1,59 @@
|
||||
//! `POST /_matrix/policy/unstable/org.matrix.msc4284/event/{eventId}/check`
|
||||
//!
|
||||
//! Checks if an event is allowed by the room's policy server.
|
||||
//! This is now a fallback behaviour that will be removed later.
|
||||
|
||||
pub mod unstable {
|
||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
|
||||
use ruma::{
|
||||
OwnedEventId,
|
||||
api::{federation::authentication::ServerSignatures, request, response},
|
||||
metadata,
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
history: {
|
||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/event/{event_id}/check",
|
||||
}
|
||||
}
|
||||
|
||||
/// Response type for the `check` endpoint.
|
||||
#[response]
|
||||
pub struct Response {
|
||||
/// Either `ok` or `spam`, indicating the policy server's
|
||||
/// recommendation.
|
||||
pub recommendation: String,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(recommendation: String) -> Self { Self { recommendation } }
|
||||
}
|
||||
|
||||
/// Request type for the `check` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The event ID to check.
|
||||
#[ruma_api(path)]
|
||||
pub event_id: OwnedEventId,
|
||||
|
||||
/// The PDU body (optional)
|
||||
#[ruma_api(body)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pdu: Option<Box<RawJsonValue>>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given event ID.
|
||||
#[must_use]
|
||||
pub fn new(event_id: OwnedEventId) -> Self { Self { event_id, pdu: None } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//! `POST /_matrix/policy/unstable/org.matrix.msc4284/sign`
|
||||
//!
|
||||
//! Asks a policy server to sign our event
|
||||
|
||||
pub mod unstable {
|
||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
use ruma::{
|
||||
ServerSignatures,
|
||||
api::{
|
||||
federation::authentication::ServerSignatures as ServerSignaturesAuth, request,
|
||||
response,
|
||||
},
|
||||
metadata,
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignaturesAuth,
|
||||
history: {
|
||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/sign",
|
||||
}
|
||||
}
|
||||
|
||||
/// Response type for the `sign` endpoint.
|
||||
#[response]
|
||||
pub struct Response {
|
||||
/// The signatures returned from the policy server (if provided)
|
||||
#[ruma_api(body)]
|
||||
pub signatures: Option<ServerSignatures>,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(signatures: Option<ServerSignatures>) -> Self { Self { signatures } }
|
||||
}
|
||||
|
||||
/// Request type for the `sign` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The PDU body (in canonical JSON)
|
||||
#[ruma_api(body)]
|
||||
pub pdu: Box<RawJsonValue>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given event JSON
|
||||
#[must_use]
|
||||
pub fn new(pdu: Box<RawJsonValue>) -> Self { Self { pdu } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//! `GET /_matrix/federation/*/rooms/{roomId}/report/{eventId}`
|
||||
//!
|
||||
//! Send a request to report an event originating from another server.
|
||||
|
||||
pub mod msc3843 {
|
||||
//! `MSC3843` ([MSC])
|
||||
//!
|
||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
|
||||
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId,
|
||||
api::{federation::authentication::ServerSignatures, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
history: {
|
||||
unstable => "/_matrix/federation/unstable/org.matrix.msc3843/rooms/{room_id}/report/{event_id}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `report_content` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The room ID that the reported event was sent in.
|
||||
#[ruma_api(path)]
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
/// The event being reported.
|
||||
#[ruma_api(path)]
|
||||
pub event_id: OwnedEventId,
|
||||
|
||||
/// The reason that the event is being reported.
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
/// Response type for the `report_content` endpoint.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a `Request` with the given room ID, event ID and reason.
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, event_id: OwnedEventId, reason: String) -> Self {
|
||||
Self { room_id, event_id, reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,9 @@ nonzero_ext.workspace = true
|
||||
rand.workspace = true
|
||||
regex.workspace = true
|
||||
reqwest.workspace = true
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
ruminuwuity.workspace = true
|
||||
rustyline-async.workspace = true
|
||||
rustyline-async.optional = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
use database::{Deserialized, Handle, Ignore, Json, Map};
|
||||
use futures::{Stream, StreamExt, TryFutureExt};
|
||||
use ruma::{
|
||||
RoomId, UserId,
|
||||
OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||
events::{
|
||||
AnyGlobalAccountDataEvent, AnyRawAccountDataEvent, AnyRoomAccountDataEvent,
|
||||
GlobalAccountDataEventType, RoomAccountDataEventType,
|
||||
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, GlobalAccountDataEventType,
|
||||
RoomAccountDataEventType,
|
||||
},
|
||||
serde::Raw,
|
||||
};
|
||||
@@ -18,6 +18,12 @@
|
||||
|
||||
use crate::{Dep, globals};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AnyRawAccountDataEvent {
|
||||
Room(Raw<AnyRoomAccountDataEvent>),
|
||||
Global(Raw<AnyGlobalAccountDataEvent>),
|
||||
}
|
||||
|
||||
pub struct Service {
|
||||
services: Services,
|
||||
db: Data,
|
||||
@@ -132,7 +138,7 @@ pub fn changes_since<'a>(
|
||||
since: Option<u64>,
|
||||
to: Option<u64>,
|
||||
) -> impl Stream<Item = AnyRawAccountDataEvent> + Send + 'a {
|
||||
type Key<'a> = (Option<&'a RoomId>, &'a UserId, u64, Ignore);
|
||||
type Key = (Option<OwnedRoomId>, OwnedUserId, u64, Ignore);
|
||||
|
||||
// Skip the data that's exactly at since, because we sent that last time
|
||||
// ...unless this is an initial sync, in which case send everything
|
||||
@@ -142,8 +148,10 @@ pub fn changes_since<'a>(
|
||||
.roomuserdataid_accountdata
|
||||
.stream_from(&first_possible)
|
||||
.ignore_err()
|
||||
.ready_take_while(move |((room_id_, user_id_, count, _), _): &(Key<'_>, _)| {
|
||||
room_id == *room_id_ && user_id == *user_id_ && to.is_none_or(|to| *count <= to)
|
||||
.ready_take_while(move |((room_id_, user_id_, count, _), _): &(Key, _)| {
|
||||
room_id == room_id_.as_deref()
|
||||
&& user_id == user_id_
|
||||
&& to.is_none_or(|to| *count <= to)
|
||||
})
|
||||
.map(move |(_, v)| {
|
||||
match room_id {
|
||||
|
||||
+25
-44
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use conduwuit::{Result, info, pdu::PduBuilder};
|
||||
use conduwuit::{Result, info, pdu::PartialPdu};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
RoomId, RoomVersionId,
|
||||
@@ -13,7 +13,6 @@
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
name::RoomNameEventContent,
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
preview_url::RoomPreviewUrlsEventContent,
|
||||
topic::RoomTopicEventContent,
|
||||
},
|
||||
};
|
||||
@@ -25,7 +24,7 @@
|
||||
/// Users in this room are considered admins by conduwuit, and the room can be
|
||||
/// used to issue admin commands by talking to the server user inside it.
|
||||
pub async fn create_admin_room(services: &Services) -> Result {
|
||||
let room_id = RoomId::new(services.globals.server_name());
|
||||
let room_id = RoomId::new_v1(services.globals.server_name());
|
||||
let room_version = &RoomVersionId::V11;
|
||||
|
||||
let _short_id = services
|
||||
@@ -34,22 +33,24 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.get_or_create_shortroomid(&room_id)
|
||||
.await;
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
|
||||
|
||||
// Create a user for the server
|
||||
let server_user = services.globals.server_user.as_ref();
|
||||
services.users.create(server_user, None, None).await?;
|
||||
|
||||
let create_content = {
|
||||
let mut create_content = {
|
||||
use RoomVersionId::*;
|
||||
match room_version {
|
||||
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
|
||||
RoomCreateEventContent::new_v1(server_user.into()),
|
||||
| V11 => RoomCreateEventContent::new_v11(),
|
||||
| _ => RoomCreateEventContent::new_v12(),
|
||||
| _ => RoomCreateEventContent::new_v11(),
|
||||
}
|
||||
};
|
||||
|
||||
create_content.federate = true;
|
||||
create_content.room_version = room_version.clone();
|
||||
|
||||
info!("Creating admin room {} with version {}", room_id, room_version);
|
||||
|
||||
// 1. The room create event
|
||||
@@ -57,12 +58,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomCreateEventContent {
|
||||
federate: true,
|
||||
predecessor: None,
|
||||
room_version: room_version.clone(),
|
||||
..create_content
|
||||
}),
|
||||
PartialPdu::state(String::new(), &create_content),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -75,7 +71,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::from(server_user),
|
||||
&RoomMemberEventContent::new(MembershipState::Join),
|
||||
),
|
||||
@@ -89,14 +85,15 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
// 3. Power levels
|
||||
let users = BTreeMap::from_iter([(server_user.into(), 69420.into())]);
|
||||
|
||||
let mut power_levels_content =
|
||||
RoomPowerLevelsEventContent::new(&room_version.rules().unwrap().authorization);
|
||||
power_levels_content.users = users;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomPowerLevelsEventContent {
|
||||
users,
|
||||
..Default::default()
|
||||
}),
|
||||
PartialPdu::state(String::new(), &power_levels_content),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -109,7 +106,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomJoinRulesEventContent::new(JoinRule::Invite)),
|
||||
PartialPdu::state(String::new(), &RoomJoinRulesEventContent::new(JoinRule::Invite)),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -122,7 +119,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared),
|
||||
),
|
||||
@@ -138,7 +135,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomGuestAccessEventContent::new(GuestAccess::Forbidden),
|
||||
),
|
||||
@@ -155,7 +152,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomNameEventContent::new(room_name)),
|
||||
PartialPdu::state(String::new(), &RoomNameEventContent::new(room_name)),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -163,13 +160,12 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let room_topic = format!("Manage {} | Run commands prefixed with `!admin` | Run `!admin -h` for help | Documentation: https://continuwuity.org/", services.config.server_name);
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomTopicEventContent {
|
||||
topic: format!("Manage {} | Run commands prefixed with `!admin` | Run `!admin -h` for help | Documentation: https://continuwuity.org/", services.config.server_name),
|
||||
}),
|
||||
PartialPdu::state(String::new(), &RoomTopicEventContent::markdown(room_topic)),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -178,16 +174,14 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
.await?;
|
||||
|
||||
// 6. Room alias
|
||||
let alias = &services.globals.admin_alias;
|
||||
let mut alias_content = RoomCanonicalAliasEventContent::new();
|
||||
alias_content.alias = Some(services.globals.admin_alias.clone());
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
|
||||
alias: Some(alias.clone()),
|
||||
alt_aliases: Vec::new(),
|
||||
}),
|
||||
PartialPdu::state(String::new(), &alias_content),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -198,20 +192,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(alias, &room_id, server_user)?;
|
||||
|
||||
// 7. (ad-hoc) Disable room URL previews for everyone by default
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &RoomPreviewUrlsEventContent { disabled: true }),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
.set_alias(&services.globals.admin_alias, &room_id, server_user)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+14
-20
@@ -1,7 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use conduwuit::{
|
||||
Err, Result, debug_info, debug_warn, error, implement, matrix::pdu::PduBuilder, warn,
|
||||
Err, Result, debug_info, debug_warn, error, implement, matrix::pdu::PartialPdu, warn,
|
||||
};
|
||||
use ruma::{
|
||||
RoomId, UserId,
|
||||
@@ -27,7 +27,7 @@ pub async fn make_user_admin(&self, user_id: &UserId) -> Result {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
let state_lock = self.services.state.mutex.lock(room_id.as_str()).await;
|
||||
|
||||
if self.services.state_cache.is_joined(user_id, &room_id).await {
|
||||
return Err!(debug_warn!("User is already joined in the admin room"));
|
||||
@@ -51,7 +51,7 @@ pub async fn make_user_admin(&self, user_id: &UserId) -> Result {
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::from(user_id),
|
||||
&RoomMemberEventContent::new(MembershipState::Invite),
|
||||
),
|
||||
@@ -65,7 +65,7 @@ pub async fn make_user_admin(&self, user_id: &UserId) -> Result {
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
String::from(user_id),
|
||||
&RoomMemberEventContent::new(MembershipState::Join),
|
||||
),
|
||||
@@ -79,7 +79,7 @@ pub async fn make_user_admin(&self, user_id: &UserId) -> Result {
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
PartialPdu::state(
|
||||
user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Invite),
|
||||
),
|
||||
@@ -100,7 +100,7 @@ pub async fn make_user_admin(&self, user_id: &UserId) -> Result {
|
||||
"",
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
.expect("admin room should have power levels");
|
||||
|
||||
room_power_levels
|
||||
.users
|
||||
@@ -110,7 +110,7 @@ pub async fn make_user_admin(&self, user_id: &UserId) -> Result {
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &room_power_levels),
|
||||
PartialPdu::state(String::new(), &room_power_levels),
|
||||
server_user,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
@@ -135,9 +135,7 @@ async fn set_room_tag(&self, room_id: &RoomId, user_id: &UserId, tag: &str) -> R
|
||||
.account_data
|
||||
.get_room(room_id, user_id, RoomAccountDataEventType::Tag)
|
||||
.await
|
||||
.unwrap_or_else(|_| TagEvent {
|
||||
content: TagEventContent { tags: BTreeMap::new() },
|
||||
});
|
||||
.unwrap_or_else(|_| TagEvent::new(TagEventContent::new(BTreeMap::new())));
|
||||
|
||||
event
|
||||
.content
|
||||
@@ -177,9 +175,9 @@ pub async fn revoke_admin(&self, user_id: &UserId) -> Result {
|
||||
return Err!(error!("No admin room available or created."));
|
||||
};
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
let state_lock = self.services.state.mutex.lock(room_id.as_str()).await;
|
||||
|
||||
let event = match self
|
||||
let mut member_content = match self
|
||||
.services
|
||||
.state_accessor
|
||||
.get_member(&room_id, user_id)
|
||||
@@ -203,17 +201,13 @@ pub async fn revoke_admin(&self, user_id: &UserId) -> Result {
|
||||
},
|
||||
};
|
||||
|
||||
member_content.membership = Leave;
|
||||
member_content.reason = Some("Admin Revoked".to_owned());
|
||||
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: Leave,
|
||||
reason: Some("Admin Revoked".into()),
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
..event
|
||||
}),
|
||||
PartialPdu::state(user_id.to_string(), &member_content),
|
||||
self.services.globals.server_user.as_ref(),
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
|
||||
+33
-28
@@ -11,26 +11,27 @@
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Err, SyncRwLock, utils};
|
||||
use conduwuit_core::{
|
||||
Error, Event, Result, Server, debug, err, error, error::default_log, pdu::PduBuilder,
|
||||
Error, Event, Result, Server, debug, err, error, error::default_log, pdu::PartialPdu,
|
||||
};
|
||||
pub use create::create_admin_room;
|
||||
use futures::{Future, FutureExt, StreamExt, TryFutureExt};
|
||||
use loole::{Receiver, Sender};
|
||||
use ruma::{
|
||||
Mxc, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UInt, UserId,
|
||||
OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UInt, UserId,
|
||||
events::{
|
||||
Mentions,
|
||||
room::{
|
||||
MediaSource,
|
||||
message::{
|
||||
FileInfo, FileMessageEventContent, MessageType, Relation, RoomMessageEventContent,
|
||||
},
|
||||
room::message::{
|
||||
FileInfo, FileMessageEventContent, MessageType, Relation, RoomMessageEventContent,
|
||||
},
|
||||
},
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{Dep, account_data, globals, media::MXC_LENGTH, rooms, rooms::state::RoomMutexGuard};
|
||||
use crate::{
|
||||
Dep, account_data, globals,
|
||||
media::{MXC_LENGTH, mxc::Mxc},
|
||||
rooms::{self, state::RoomMutexGuard},
|
||||
};
|
||||
|
||||
pub struct Service {
|
||||
services: Services,
|
||||
@@ -61,7 +62,7 @@ pub struct CommandInput {
|
||||
pub command: String,
|
||||
pub reply_id: Option<OwnedEventId>,
|
||||
pub source: InvocationSource,
|
||||
pub sender: Option<Box<UserId>>,
|
||||
pub sender: Option<OwnedUserId>,
|
||||
}
|
||||
|
||||
/// Where a command is being invoked from.
|
||||
@@ -200,19 +201,18 @@ pub async fn text_or_file(
|
||||
.await
|
||||
.expect("failed to create text file");
|
||||
let size_u64: u64 = message_content.body().len().try_into().map_or(0, |n| n);
|
||||
let metadata = FileInfo {
|
||||
mimetype: Some("text/markdown".to_owned()),
|
||||
size: Some(UInt::new_saturating(size_u64)),
|
||||
thumbnail_info: None,
|
||||
thumbnail_source: None,
|
||||
};
|
||||
let content = FileMessageEventContent {
|
||||
body: "Output was too large to send as text.".to_owned(),
|
||||
formatted: None,
|
||||
filename: Some("output.md".to_owned()),
|
||||
source: MediaSource::Plain(file),
|
||||
info: Some(Box::new(metadata)),
|
||||
};
|
||||
|
||||
let mut metadata = FileInfo::new();
|
||||
metadata.mimetype = Some("text/markdown".to_owned());
|
||||
metadata.size = Some(UInt::new_saturating(size_u64));
|
||||
|
||||
let mut content = FileMessageEventContent::plain(
|
||||
"Output was too large to send as text.".to_owned(),
|
||||
file,
|
||||
);
|
||||
content.filename = Some("output.md".to_owned());
|
||||
content.info = Some(Box::new(metadata));
|
||||
|
||||
RoomMessageEventContent::new(MessageType::File(content))
|
||||
} else {
|
||||
message_content
|
||||
@@ -317,7 +317,7 @@ pub fn command_with_sender(
|
||||
command: String,
|
||||
reply_id: Option<OwnedEventId>,
|
||||
source: InvocationSource,
|
||||
sender: Box<UserId>,
|
||||
sender: OwnedUserId,
|
||||
) -> Result<()> {
|
||||
self.channel
|
||||
.0
|
||||
@@ -452,13 +452,18 @@ pub async fn get_admin_room(&self) -> Result<OwnedRoomId> {
|
||||
}
|
||||
|
||||
async fn handle_response(&self, content: RoomMessageEventContent) -> Result<()> {
|
||||
let Some(Relation::Reply { in_reply_to }) = content.relates_to.as_ref() else {
|
||||
let Some(Relation::Reply(reply)) = content.relates_to.as_ref() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Ok(pdu) = self.services.timeline.get_pdu(&in_reply_to.event_id).await else {
|
||||
let Ok(pdu) = self
|
||||
.services
|
||||
.timeline
|
||||
.get_pdu(&reply.in_reply_to.event_id)
|
||||
.await
|
||||
else {
|
||||
error!(
|
||||
event_id = ?in_reply_to.event_id,
|
||||
event_id = ?reply.in_reply_to.event_id,
|
||||
"Missing admin command in_reply_to event"
|
||||
);
|
||||
return Ok(());
|
||||
@@ -488,7 +493,7 @@ async fn respond_to_room(
|
||||
.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::timeline(&self.text_or_file(content).await),
|
||||
PartialPdu::timeline(&self.text_or_file(content).await),
|
||||
user_id,
|
||||
Some(room_id),
|
||||
&state_lock,
|
||||
@@ -520,7 +525,7 @@ async fn handle_response_error(
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::timeline(&content),
|
||||
PartialPdu::timeline(&content),
|
||||
user_id,
|
||||
Some(room_id),
|
||||
state_lock,
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Result, config::Antispam, debug};
|
||||
use ruma::{OwnedRoomId, OwnedUserId, draupnir_antispam, meowlnir_antispam};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, path_builder::VersionHistory},
|
||||
};
|
||||
use ruminuwuity::{draupnir_antispam, meowlnir_antispam};
|
||||
|
||||
use crate::{client, config, sending, service::Dep};
|
||||
|
||||
@@ -37,7 +41,11 @@ async fn send_antispam_request<T>(
|
||||
request: T,
|
||||
) -> Result<T::IncomingResponse>
|
||||
where
|
||||
T: ruma::api::OutgoingRequest + Debug + Send,
|
||||
T: ruma::api::OutgoingRequest<
|
||||
Authentication = AppserviceToken,
|
||||
PathBuilder = VersionHistory,
|
||||
> + Debug
|
||||
+ Send,
|
||||
{
|
||||
sending::antispam::send_antispam_request(
|
||||
&self.services.client.appservice,
|
||||
|
||||
@@ -72,9 +72,9 @@ async fn set_emergency_access(&self) -> Result {
|
||||
None,
|
||||
server_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(&GlobalAccountDataEvent {
|
||||
content: PushRulesEventContent { global: ruleset },
|
||||
})
|
||||
&serde_json::to_value(&GlobalAccountDataEvent::new(PushRulesEventContent::new(
|
||||
ruleset,
|
||||
)))
|
||||
.expect("to json value always works"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user