Compare commits

..

1 Commits

Author SHA1 Message Date
Renovate Bot b5e3a2d238 chore(deps): update ruma digest to aa266b1 2026-05-16 17:12:14 +00:00
137 changed files with 1873 additions and 2651 deletions
@@ -44,7 +44,7 @@ runs:
- name: Login to builtin registry
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ${{ env.BUILTIN_REGISTRY }}
username: ${{ inputs.registry_user }}
@@ -52,7 +52,7 @@ runs:
- name: Set up Docker Buildx
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
with:
# Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125)
driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }}
@@ -61,7 +61,7 @@ runs:
- name: Extract metadata (tags) for Docker
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with:
flavor: |
latest=auto
@@ -67,7 +67,7 @@ runs:
uses: ./.forgejo/actions/rust-toolchain
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
with:
# Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125)
driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }}
@@ -79,7 +79,7 @@ runs:
- name: Login to builtin registry
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ${{ env.BUILTIN_REGISTRY }}
username: ${{ inputs.registry_user }}
@@ -87,7 +87,7 @@ runs:
- name: Extract metadata (labels, annotations) for Docker
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with:
images: ${{ inputs.images }}
# default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509
@@ -17,7 +17,7 @@ inputs:
llvm-version:
description: 'LLVM version to install'
required: false
default: '21'
default: '20'
outputs:
llvm-version:
+1 -1
View File
@@ -71,7 +71,7 @@ runs:
- name: Install timelord-cli and git-warp-time
if: steps.check-binaries.outputs.need-install == 'true'
uses: https://github.com/taiki-e/install-action@920ab1831fbf4fb3ef75c8ead83556c918bb7290 # v2
uses: https://github.com/taiki-e/install-action@184183c2401be73c3bf42c2e61268aa5855379c1 # v2
with:
tool: git-warp-time,timelord-cli@3.0.1
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
const labelsToAdd = new Set();
for (const file of fileNames) {
if (file.startsWith('docs/') || file.startsWith('theme/') || (file.endsWith('.md') && !file.startsWith('changelog.d/')) || file == 'rspress.config.ts') {
if (file.startsWith('docs/') || file.startsWith('theme/') || file.endsWith('.md') || file == 'rspress.config.ts') {
labelsToAdd.add('Documentation');
}
if (file.startsWith('.forgejo/')) {
+2 -2
View File
@@ -62,7 +62,7 @@ jobs:
registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
- name: Build and push Docker image by digest
id: build
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: .
file: "docker/Dockerfile"
@@ -149,7 +149,7 @@ jobs:
registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
- name: Build and push max-perf Docker image by digest
id: build
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: .
file: "docker/Dockerfile"
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
name: Renovate
runs-on: ubuntu-latest
container:
image: ghcr.io/renovatebot/renovate:43.195.3@sha256:868dffc3d6a46f42dfefe48b6978cc063d8df9c1d58a93a694c8989afa503e34
image: ghcr.io/renovatebot/renovate:43.140.0@sha256:61303c28b10a491c559529fb6f41745850e4755a43a54c04c3ae6848d6eaf5cc
options: --tmpfs /tmp:exec
steps:
- name: Checkout
+1 -1
View File
@@ -24,7 +24,7 @@ repos:
- id: check-added-large-files
- repo: https://github.com/crate-ci/typos
rev: v1.46.2
rev: v1.46.1
hooks:
- id: typos
- id: typos
Generated
+140 -138
View File
@@ -71,9 +71,9 @@ dependencies = [
[[package]]
name = "annotate-snippets"
version = "0.12.16"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f211a51805bc641f3ad5b7664c77d2547af685cc33b4cd8d31964027a46f13f1"
checksum = "92570a3f9c98e7e84df84b71d0965ac99b1871fcd75a3773a3bd1bad13f64cf7"
dependencies = [
"anstyle",
"memchr",
@@ -193,7 +193,7 @@ dependencies = [
"serde",
"serde_derive",
"unicode-ident",
"winnow 1.0.3",
"winnow 1.0.2",
]
[[package]]
@@ -253,15 +253,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-lc-rs"
version = "1.17.0"
version = "1.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -269,9 +269,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.41.0"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
dependencies = [
"cc",
"cmake",
@@ -530,15 +530,15 @@ dependencies = [
[[package]]
name = "built"
version = "0.8.1"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9"
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]]
name = "bumpalo"
version = "3.20.3"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytemuck"
@@ -625,9 +625,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.62"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -949,7 +949,7 @@ dependencies = [
"lock_api",
"log",
"maplit",
"nix",
"nix 0.31.3",
"num-traits",
"parking_lot",
"rand 0.10.1",
@@ -1393,18 +1393,18 @@ dependencies = [
[[package]]
name = "crypto-common"
version = "0.2.2"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
dependencies = [
"hybrid-array",
]
[[package]]
name = "ctor"
version = "1.0.6"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d765eb1c0bda10d31e0ea185f5ee15da532d60b0912d2bd1441783439e749c5"
checksum = "3429e8f8e3ce0ffe475c411850f70468c70d7a87d2ac3d15dd44703fb885aede"
dependencies = [
"link-section",
"linktime-proc-macro",
@@ -1563,12 +1563,12 @@ dependencies = [
[[package]]
name = "digest"
version = "0.11.3"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c"
dependencies = [
"block-buffer 0.12.0",
"crypto-common 0.2.2",
"crypto-common 0.2.1",
"ctutils",
]
@@ -1604,9 +1604,9 @@ dependencies = [
[[package]]
name = "dtor"
version = "1.0.3"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2137ce22f50d4c43ce098daf41c904cc700de1ce8bc2daf53ed4e702180a464"
checksum = "66261f2a6a4976b45407e398e63bc50a25fccf464d417f7d20f2e2b0974b9888"
dependencies = [
"linktime-proc-macro",
]
@@ -1643,9 +1643,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.16.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
dependencies = [
"serde",
]
@@ -1709,7 +1709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -1926,9 +1926,9 @@ checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-timer"
version = "3.0.4"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
@@ -2051,9 +2051,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.4.14"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733"
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
dependencies = [
"atomic-waker",
"bytes",
@@ -2124,9 +2124,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.1"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "hdrhistogram"
@@ -2237,7 +2237,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
dependencies = [
"digest 0.11.3",
"digest 0.11.2",
]
[[package]]
@@ -2327,9 +2327,9 @@ checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
[[package]]
name = "hybrid-array"
version = "0.4.12"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5"
dependencies = [
"typenum",
]
@@ -2557,7 +2557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
"hashbrown 0.17.0",
"serde",
"serde_core",
]
@@ -2686,9 +2686,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.99"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
dependencies = [
"cfg-if",
"futures-util",
@@ -2828,9 +2828,9 @@ dependencies = [
[[package]]
name = "link-section"
version = "0.17.2"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d1e908a416d6e9f725743b84a36feea40c4c131e805fbc26d61f9f451f36080"
checksum = "ea2c24837c4fd5ab6a31d64133eae954f5199247523cf29586117e85245c0dd3"
[[package]]
name = "linked-hash-map"
@@ -3005,15 +3005,15 @@ dependencies = [
[[package]]
name = "minicbor"
version = "2.2.2"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b7a5041e12946f8b7d3f5a9d96383a19d694b9335457c522be7815b9abafb02"
checksum = "e70eae6d4f18f7d76877fe7b13f0bc21f7c2b7239d2041c338335f7b388d0dd7"
[[package]]
name = "minicbor-serde"
version = "0.7.0"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "293c7245401f035e2dcc4b12ebdb5c9d8847247fc79fe1b5b0a0d58d7275324c"
checksum = "80047f75e28e3b38f6ab2ec3c2c7669f6b411fa6f8424e1a90a3fd784b19a3f4"
dependencies = [
"minicbor",
"serde",
@@ -3089,6 +3089,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nix"
version = "0.31.3"
@@ -3138,7 +3150,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -3176,9 +3188,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.2.2"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-integer"
@@ -3416,9 +3428,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "opentelemetry"
version = "0.32.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682"
checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0"
dependencies = [
"futures-core",
"futures-sink",
@@ -3430,22 +3442,22 @@ dependencies = [
[[package]]
name = "opentelemetry-http"
version = "0.32.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331"
checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d"
dependencies = [
"async-trait",
"bytes",
"http",
"opentelemetry",
"reqwest 0.13.3",
"reqwest 0.12.28",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.32.0"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35"
checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f"
dependencies = [
"http",
"opentelemetry",
@@ -3453,18 +3465,18 @@ dependencies = [
"opentelemetry-proto",
"opentelemetry_sdk",
"prost",
"reqwest 0.13.3",
"reqwest 0.12.28",
"thiserror",
"tokio",
"tonic",
"tonic-types",
"tracing",
]
[[package]]
name = "opentelemetry-proto"
version = "0.32.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638"
checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f"
dependencies = [
"opentelemetry",
"opentelemetry_sdk",
@@ -3475,16 +3487,15 @@ dependencies = [
[[package]]
name = "opentelemetry_sdk"
version = "0.32.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368afaed344110f40b179bb8fbe54bc52d98f9bd2b281799ef32487c2650c956"
checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd"
dependencies = [
"futures-channel",
"futures-executor",
"futures-util",
"opentelemetry",
"percent-encoding",
"portable-atomic",
"rand 0.9.4",
"thiserror",
"tokio",
@@ -3493,13 +3504,13 @@ dependencies = [
[[package]]
name = "os_info"
version = "3.15.0"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b"
checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224"
dependencies = [
"android_system_properties",
"log",
"nix",
"nix 0.30.1",
"objc2",
"objc2-foundation",
"objc2-ui-kit",
@@ -3634,18 +3645,18 @@ dependencies = [
[[package]]
name = "pin-project"
version = "1.1.13"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.13"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6"
dependencies = [
"proc-macro2",
"quote",
@@ -3820,9 +3831,9 @@ dependencies = [
[[package]]
name = "pulldown-cmark"
version = "0.13.4"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e"
checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad"
dependencies = [
"bitflags",
"memchr",
@@ -4053,7 +4064,9 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
@@ -4151,7 +4164,7 @@ dependencies = [
[[package]]
name = "ruma"
version = "0.15.1"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"assign",
"js_int",
@@ -4170,7 +4183,7 @@ dependencies = [
[[package]]
name = "ruma-appservice-api"
version = "0.15.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"js_int",
"ruma-common",
@@ -4182,7 +4195,7 @@ dependencies = [
[[package]]
name = "ruma-client-api"
version = "0.23.1"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"as_variant",
"assign",
@@ -4204,7 +4217,7 @@ dependencies = [
[[package]]
name = "ruma-common"
version = "0.18.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"as_variant",
"base64 0.22.1",
@@ -4237,7 +4250,7 @@ dependencies = [
[[package]]
name = "ruma-events"
version = "0.33.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"as_variant",
"indexmap",
@@ -4258,7 +4271,7 @@ dependencies = [
[[package]]
name = "ruma-federation-api"
version = "0.14.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"bytes",
"headers",
@@ -4281,7 +4294,7 @@ dependencies = [
[[package]]
name = "ruma-identifiers-validation"
version = "0.12.1"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"js_int",
"thiserror",
@@ -4290,7 +4303,7 @@ dependencies = [
[[package]]
name = "ruma-macros"
version = "0.18.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"as_variant",
"cfg-if",
@@ -4306,7 +4319,7 @@ dependencies = [
[[package]]
name = "ruma-push-gateway-api"
version = "0.14.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"js_int",
"ruma-common",
@@ -4318,7 +4331,7 @@ dependencies = [
[[package]]
name = "ruma-signatures"
version = "0.20.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"base64 0.22.1",
"ed25519-dalek",
@@ -4334,7 +4347,7 @@ dependencies = [
[[package]]
name = "ruma-state-res"
version = "0.16.0"
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
source = "git+https://github.com/ruma/ruma.git?rev=aa266b1e15c782322ffcbd2574264ef74ad2ebe6#aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
dependencies = [
"js_int",
"ruma-common",
@@ -4359,8 +4372,8 @@ dependencies = [
[[package]]
name = "rust-librocksdb-sys"
version = "0.45.1+11.1.1"
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=0a25ff92f7c09b55eec496b9c192c7d5136ab2b8#0a25ff92f7c09b55eec496b9c192c7d5136ab2b8"
version = "0.42.0+10.10.1"
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=31fb8f772c7afcdc0061ab6a40cfa3a1be2fccd9#31fb8f772c7afcdc0061ab6a40cfa3a1be2fccd9"
dependencies = [
"bindgen",
"bzip2-sys",
@@ -4376,8 +4389,8 @@ dependencies = [
[[package]]
name = "rust-rocksdb"
version = "0.49.1"
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=0a25ff92f7c09b55eec496b9c192c7d5136ab2b8#0a25ff92f7c09b55eec496b9c192c7d5136ab2b8"
version = "0.46.0"
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=31fb8f772c7afcdc0061ab6a40cfa3a1be2fccd9#31fb8f772c7afcdc0061ab6a40cfa3a1be2fccd9"
dependencies = [
"libc",
"parking_lot",
@@ -4415,7 +4428,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4474,7 +4487,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4747,7 +4760,7 @@ checksum = "dcc7fe48e34d02a97bc8e6253b8b91e5a47fe2c47eaacb5149cefbb69922eaf0"
dependencies = [
"ahash",
"annotate-snippets",
"base64 0.22.1",
"base64 0.21.7",
"encoding_rs_io",
"getrandom 0.3.4",
"granit-parser",
@@ -4793,9 +4806,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
@@ -4874,7 +4887,7 @@ checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.3",
"digest 0.11.2",
]
[[package]]
@@ -4896,7 +4909,7 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.3",
"digest 0.11.2",
]
[[package]]
@@ -4991,9 +5004,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "siphasher"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
@@ -5027,7 +5040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -5287,9 +5300,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.52.3"
version = "1.52.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
dependencies = [
"bytes",
"libc",
@@ -5396,7 +5409,7 @@ dependencies = [
"serde_spanned 1.1.1",
"toml_datetime 1.1.1+spec-1.1.0",
"toml_parser",
"winnow 1.0.3",
"winnow 1.0.2",
]
[[package]]
@@ -5449,7 +5462,7 @@ dependencies = [
"indexmap",
"toml_datetime 1.1.1+spec-1.1.0",
"toml_parser",
"winnow 1.0.3",
"winnow 1.0.2",
]
[[package]]
@@ -5458,7 +5471,7 @@ version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow 1.0.3",
"winnow 1.0.2",
]
[[package]]
@@ -5475,9 +5488,9 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "tonic"
version = "0.14.6"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec"
dependencies = [
"async-trait",
"axum",
@@ -5504,26 +5517,15 @@ dependencies = [
[[package]]
name = "tonic-prost"
version = "0.14.6"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0"
checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309"
dependencies = [
"bytes",
"prost",
"tonic",
]
[[package]]
name = "tonic-types"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab1b02061f83d519bba3caa167f88f261ef05720ab8ebc954ade70de3348e8"
dependencies = [
"prost",
"prost-types",
"tonic",
]
[[package]]
name = "tower"
version = "0.5.3"
@@ -5545,9 +5547,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.11"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51"
dependencies = [
"async-compression",
"bitflags",
@@ -5659,9 +5661,9 @@ dependencies = [
[[package]]
name = "tracing-opentelemetry"
version = "0.33.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26"
checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc"
dependencies = [
"js-sys",
"opentelemetry",
@@ -5905,9 +5907,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.122"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
@@ -5918,9 +5920,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.72"
version = "0.4.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -5928,9 +5930,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.122"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -5938,9 +5940,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.122"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -5951,9 +5953,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.122"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
@@ -6007,9 +6009,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.99"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436"
checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -6086,7 +6088,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -6297,9 +6299,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
dependencies = [
"memchr",
]
@@ -6477,9 +6479,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
+9 -9
View File
@@ -39,10 +39,10 @@ features = ["ffi", "std", "union"]
version = "1.1.0"
[workspace.dependencies.ctor]
version = "1.0.6"
version = "0.13.0"
[workspace.dependencies.dtor]
version = "1.0.0"
version = "0.13.0"
[workspace.dependencies.cargo_toml]
version = "0.22"
@@ -344,7 +344,7 @@ version = "1.1.1"
[workspace.dependencies.ruma]
# version = "0.14.1"
git = "https://github.com/ruma/ruma.git"
rev = "9c9dccc93f054bbd28f23f630223fffa6289ecbc"
rev = "aa266b1e15c782322ffcbd2574264ef74ad2ebe6"
features = [
"appservice-api-c",
"client-api",
@@ -384,7 +384,7 @@ features = [
[workspace.dependencies.rust-rocksdb]
git = "https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1"
rev = "0a25ff92f7c09b55eec496b9c192c7d5136ab2b8"
rev = "31fb8f772c7afcdc0061ab6a40cfa3a1be2fccd9"
default-features = false
features = [
"multi-threaded-cf",
@@ -404,20 +404,20 @@ default-features = false
# optional opentelemetry, performance measurements, flamegraphs, etc for performance measurements and monitoring
[workspace.dependencies.opentelemetry]
version = "0.32.0"
version = "0.31.0"
[workspace.dependencies.tracing-flame]
version = "0.2.0"
[workspace.dependencies.tracing-opentelemetry]
version = "0.33.0"
version = "0.32.0"
[workspace.dependencies.opentelemetry_sdk]
version = "0.32.0"
version = "0.31.0"
features = ["rt-tokio"]
[workspace.dependencies.opentelemetry-otlp]
version = "0.32.0"
version = "0.31.0"
features = ["http", "grpc-tonic", "trace", "logs", "metrics"]
@@ -534,7 +534,7 @@ version = "2.1.1"
features = ["std"]
[workspace.dependencies.minicbor-serde]
version = "0.7.0"
version = "0.6.0"
features = ["std"]
[workspace.dependencies.maplit]
-1
View File
@@ -1 +0,0 @@
Added support for Matrix 1.16's `state_after` feature, allowing clients which understand it to sync room state changes more reliably. Contributed by @ginger.
-1
View File
@@ -1 +0,0 @@
The version of Debian that the Docker-based build process uses has been upgraded from Bookworm to Trixie, meaning that standalone binaries now have a minimum glibc of 2.41, and can no longer be used on distro versions from before 2025-01-30
-1
View File
@@ -1 +0,0 @@
Updated [MSC4284: Policy Servers](https://github.com/matrix-org/matrix-spec-proposals/pull/4284) implementation to support the newly stabilised proposal. Contributed by @nex.
-1
View File
@@ -1 +0,0 @@
Added config option for default room ACLs. Contributed by @eve.
-9
View File
@@ -1,9 +0,0 @@
Implemented event rejection, which should resolve and prevent future netsplits of the kinds observed
within some Continuwuity rooms.
Also resolved several bugs related to both soft-failing events, and event backfilling, which should
improve state resolution stability.
The `!admin debug get-pdu` command was updated to disambiguate event acceptance status, and
`!admin debug show-auth-chain` was added to visually display event auth chains, which may assist
developers in debugging strangely complex events.
Contributed by @nex.
-1
View File
@@ -1 +0,0 @@
Fixed several bugs in the `POST /_matrix/client/v3/rooms/{roomId}/upgrade` endpoint. Contributed by @nex.
-1
View File
@@ -1 +0,0 @@
Added full support for [MSC4168: Update `m.space.*` state on room upgrade](https://github.com/matrix-org/matrix-spec-proposals/pull/4168). Contributed by @nex.
-1
View File
@@ -1 +0,0 @@
Adjusted legacy sync logic to no longer use the `roomsynctoken_shortstatehash` database column. Once this change has been confirmed to be stable and reliable, a future update will remove it entirely, significantly decreasing database sizes. Contributed by @ginger.
+24 -40
View File
@@ -372,18 +372,21 @@
#
#federation_timeout = 60
# Policy server request timeout (seconds). Generally policy
# MSC4284 Policy server request timeout (seconds). Generally policy
# servers should respond near instantly, however may slow down under
# load. If a policy server doesn't respond in a short amount of time, the
# room it is configured in may become unusable if this limit is set too
# high. 30 seconds is a good default, however lower values may be
# acceptable if temporary send failures are an okay trade-off.
# high. 10 seconds is a good default, however dropping this to 3-5 seconds
# can be acceptable.
#
# Please be aware that policy requests are *NOT* currently re-tried, so if
# a spam check request fails, the event will be assumed to be not spam,
# which in some cases may result in spam being sent to or received from
# the room that would typically be prevented.
#
# About policy servers: https://matrix.org/blog/2025/04/introducing-policy-servers/
# (Stabilized in Matrix v1.18)
#
#policy_server_request_timeout = 30
#policy_server_request_timeout = 10
# Federation client idle connection pool timeout (seconds).
#
@@ -621,30 +624,6 @@
#
#default_room_version = "12"
# A default allow value for the Access Control List when creating a room.
#
# If a list is provided, new rooms will be created with
# a m.room.server_acl event. Only servers which match one of the patterns
# in the list will be permitted to participate in the room.
#
# ACLs in existing rooms will not be updated automatically. This is not
# a substitute for moderation bots.
#
#default_room_acl_allow =
# A default deny value for the Access Control List when creating a room.
#
# If a list is provided, new rooms will be created with
# a m.room.server_acl event. Servers which match one of the patterns
# in the list will be NOT permitted to participate in the room.
#
# This config cannot be used if the default_room_acl_allow config is used.
#
# ACLs in existing rooms will not be updated automatically. This is not
# a substitute for moderation bots.
#
#default_room_acl_deny =
# Enable OpenTelemetry OTLP tracing export. This replaces the deprecated
# Jaeger exporter. Traces will be sent via OTLP to a collector (such as
# Jaeger) that supports the OpenTelemetry Protocol.
@@ -1591,6 +1570,19 @@
#
#block_non_admin_invites = false
# Enable or disable making requests to MSC4284 Policy Servers.
# It is recommended you keep this enabled unless you experience frequent
# connectivity issues, such as in a restricted networking environment.
#
#enable_msc4284_policy_servers = true
# Enable running locally generated events through configured MSC4284
# policy servers. You may wish to disable this if your server is
# single-user for a slight speed benefit in some rooms, but otherwise
# should leave it enabled.
#
#policy_server_check_own_events = true
# Allow admins to enter commands in rooms other than "#admins" (admin
# room) by prefixing your message with "\!admin" or "\\!admin" followed up
# a normal continuwuity admin command. The reply will be publicly visible
@@ -1857,11 +1849,6 @@
#
#support_page =
# The ed25519 public key for the policy server available at this server's
# name. Must be unpadded base64.
#
#policy_server_public_key =
# Role string for server support contacts, to be served as part of the
# MSC1929 server support endpoint at /.well-known/matrix/support.
#
@@ -1972,10 +1959,8 @@
#
#sender =
# Whether to allow public registration with an email address.
#
# Note that, if this option is enabled, anyone will be able to register an
# account with just an email address.
# Whether to require that users provide an email address when they
# register.
#
# If either this option or `require_email_for_token_registration` are set,
# users will not be allowed to remove their email address.
@@ -1983,7 +1968,6 @@
#require_email_for_registration = false
# Whether to require that users who register with a registration token
# provide an email address. This option is independent of
# `require_email_for_registration`.
# provide an email address.
#
#require_email_for_token_registration = false
+3 -3
View File
@@ -1,5 +1,5 @@
ARG RUST_VERSION=1
ARG DEBIAN_VERSION=trixie
ARG DEBIAN_VERSION=bookworm
FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} AS base
@@ -10,7 +10,7 @@ RUN rm -f /etc/apt/apt.conf.d/docker-clean
# Match Rustc version as close as possible
# rustc -vV
ARG LLVM_VERSION=22
ARG LLVM_VERSION=21
# ENV RUSTUP_TOOLCHAIN=${RUST_VERSION}
# Install repo tools
@@ -22,7 +22,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y \
pkg-config make jq \
wget curl git lsb-release gpg \
wget curl git software-properties-common \
file
# golang cmake
Generated
+21 -21
View File
@@ -3,11 +3,11 @@
"advisory-db": {
"flake": false,
"locked": {
"lastModified": 1779575509,
"narHash": "sha256-wXKYURZz76ZC5lbuDA1oVQA/MxSB3pSJ1raF1HG0oIc=",
"lastModified": 1777645914,
"narHash": "sha256-P1T7QVQS13OvkXEuEhI91CLaQfyv6iqV9vW8IBLLDYg=",
"owner": "rustsec",
"repo": "advisory-db",
"rev": "831c50f4a4304068f125e603add6a8839f08b3eb",
"rev": "d6ba1f7070ba91f45efe372d68eb648be67d0417",
"type": "github"
},
"original": {
@@ -18,11 +18,11 @@
},
"crane": {
"locked": {
"lastModified": 1779130139,
"narHash": "sha256-BLrtr42azquO7MdGFU5a7KiMl3YpFlTeIXqy1fT5GlQ=",
"lastModified": 1777335812,
"narHash": "sha256-bEg5xoAxAwsyfnGhkEX7RJViTIBIYPd8ISg4O1c0HFc=",
"owner": "ipetkov",
"repo": "crane",
"rev": "edb38893982a3338972bb4a2ec7ce7c29ba10fd9",
"rev": "5e0fb2f64edff2822249f21293b8304dedaaf676",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1779612045,
"narHash": "sha256-+7lfNVnmXJDkiRYHd5NoNwYoyUcc0LcXPaIJqjO7VWM=",
"lastModified": 1777624102,
"narHash": "sha256-thSyElkje577x/kAbP72nHlfiFc1a+tCudskLPHXe9s=",
"owner": "nix-community",
"repo": "fenix",
"rev": "d7be747f0a65af378de515fc3cee131bf99a008f",
"rev": "4d81601e0b73f20d81d066754ad0e7d1e7f75a06",
"type": "github"
},
"original": {
@@ -74,11 +74,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"lastModified": 1775087534,
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
"type": "github"
},
"original": {
@@ -89,11 +89,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1779508470,
"narHash": "sha256-Ap9KJX+5xHIn3bPIpfNgT6MEXdAECECwo4/rmlQD74M=",
"lastModified": 1777268161,
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "29916453413845e54a65b8a1cf996842300cd299",
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
"type": "github"
},
"original": {
@@ -105,11 +105,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1777168982,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
"lastModified": 1774748309,
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
"type": "github"
},
"original": {
@@ -132,11 +132,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1779569060,
"narHash": "sha256-NSnk5D+3KEfRdbgPijs33N2RAKSG6A74SwfnynLcouo=",
"lastModified": 1777583169,
"narHash": "sha256-dVJ4+wrRKc8oIgp3rLOFSq1obt/sCKlXy3h47qof/w0=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "987ea33645ab1c709b1df6823038abcb2fe8973e",
"rev": "aa64e4828a2bbba44463c1229a81c748d3cce583",
"type": "github"
},
"original": {
+4 -7
View File
@@ -5,11 +5,11 @@
liburing,
craneLib,
pkg-config,
callPackage,
rustPlatform,
cargoExtraArgs ? "",
rustflags ? "",
target_cpu ? null,
rocksdb,
rocksdb ? callPackage ./rocksdb.nix { },
profile ? "release",
}:
let
@@ -39,10 +39,7 @@ let
ROCKSDB_LIB_DIR = "${rocksdb}/lib";
CARGO_PROFILE = profile;
RUSTFLAGS = rustflags;
}
// (lib.optionalAttrs (target_cpu != null) {
TARGET_CPU = target_cpu;
});
};
};
in
craneLib.buildPackage (
@@ -59,7 +56,7 @@ craneLib.buildPackage (
]
}"
patchelf --set-rpath "$old_rpath:$extra_rpath" $out/bin/conduwuit
patchelf --set-rpath "$old_rpath:$extra_rpath" $out/bin/conduwuit
'';
meta = {
+5 -8
View File
@@ -15,7 +15,6 @@
rocksdb = pkgs.callPackage ./rocksdb.nix { };
default = pkgs.callPackage ./continuwuity.nix {
inherit self craneLib;
inherit (self'.packages) rocksdb;
# extra features via `cargoExtraArgs`
cargoExtraArgs = "-F http3";
# extra RUSTFLAGS via `rustflags`
@@ -23,13 +22,11 @@
rustflags = "--cfg reqwest_unstable";
};
# users may also override this with other cargo profiles to build for other feature sets
# for features configuration see `default` package which enables http3 by default
# example: different compilation profile and different target_cpu
max-perf-haswell = self'.packages.default.override {
# compiles explicitly for haswell arch cpus
target_cpu = "haswell";
# compiles slower but with more thorough optimizations
#
# other examples include:
#
# - release-high-perf
max-perf = self'.packages.default.override {
profile = "release-max-perf";
};
};
+5 -7
View File
@@ -1,7 +1,5 @@
{
# stdenv,
# enableJemalloc ? stdenv.hostPlatform.isLinux,
enableJemalloc ? false,
stdenv,
rocksdb,
fetchFromGitea,
rust-jemalloc-sys-unprefixed,
@@ -15,16 +13,16 @@
#
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
jemalloc = rust-jemalloc-sys-unprefixed;
inherit enableJemalloc;
enableJemalloc = stdenv.hostPlatform.isLinux;
}).overrideAttrs
({
version = "continuwuity-v0.5.0-unstable-2026-05-19";
version = "continuwuity-v0.5.0-unstable-2026-03-27";
src = fetchFromGitea {
domain = "forgejo.ellis.link";
owner = "continuwuation";
repo = "rocksdb";
rev = "3756b2b905e13216d8b56bcc783d814e7b073aff";
hash = "sha256-rSv4fr2bf9JJwdodgeuPCuceeh7k97KVxrAOC0wyPQY=";
rev = "463f47afceebfe088f6922420265546bd237f249";
hash = "sha256-1ef75IDMs5Hba4VWEyXPJb02JyShy5k4gJfzGDhopRk=";
};
# We have this already at https://forgejo.ellis.link/continuwuation/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
+1 -1
View File
@@ -16,7 +16,7 @@
file = inputs.self + "/rust-toolchain.toml";
# See also `rust-toolchain.toml`
sha256 = "sha256-gh/xTkxKHL4eiRXzWv8KP7vfjSk61Iq48x47BEDFgfk=";
sha256 = "sha256-sqSWJDUxc+zaz1nBWMAJKTAGBuGWP25GCftIOlCEAtA=";
};
in
{
+133 -133
View File
@@ -125,13 +125,13 @@
}
},
"node_modules/@rsbuild/core": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-2.0.7.tgz",
"integrity": "sha512-LsBONEzsjzOAqO72ot39eI7g53zSfF9QuDXTu4ks8IUX+EZsxRSniQfc+1zVA6a6y3b9KUUtG96avoMLKbWklQ==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-2.0.5.tgz",
"integrity": "sha512-KajO50hbXb32S8MsyDh2f+xKcVeRy9Gfzdcy0JjpMLj22djHugly6jrGo7jH7ls9X6/TDcyCTncSuNK4+D2lTw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rspack/core": "~2.0.4",
"@rspack/core": "~2.0.2",
"@swc/helpers": "^0.5.21"
},
"bin": {
@@ -169,28 +169,28 @@
}
},
"node_modules/@rspack/binding": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.0.4.tgz",
"integrity": "sha512-/QeJDPUw/lWkBJESG264KA9u6/rAjvoJhKncU4rkTi5Ap45kue5HTgOzr0ufxKdd2Xl72fjFBuqlKmtFDD5LiQ==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.0.2.tgz",
"integrity": "sha512-0kZPplW9GWx8mfC6DfsaRY3QBIYPuUs42JfmSM6aSb8tMHZAXQeLeMB8M+h8i4SeI+aFtCgO6UuYGtyWf7+L+A==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "2.0.4",
"@rspack/binding-darwin-x64": "2.0.4",
"@rspack/binding-linux-arm64-gnu": "2.0.4",
"@rspack/binding-linux-arm64-musl": "2.0.4",
"@rspack/binding-linux-x64-gnu": "2.0.4",
"@rspack/binding-linux-x64-musl": "2.0.4",
"@rspack/binding-wasm32-wasi": "2.0.4",
"@rspack/binding-win32-arm64-msvc": "2.0.4",
"@rspack/binding-win32-ia32-msvc": "2.0.4",
"@rspack/binding-win32-x64-msvc": "2.0.4"
"@rspack/binding-darwin-arm64": "2.0.2",
"@rspack/binding-darwin-x64": "2.0.2",
"@rspack/binding-linux-arm64-gnu": "2.0.2",
"@rspack/binding-linux-arm64-musl": "2.0.2",
"@rspack/binding-linux-x64-gnu": "2.0.2",
"@rspack/binding-linux-x64-musl": "2.0.2",
"@rspack/binding-wasm32-wasi": "2.0.2",
"@rspack/binding-win32-arm64-msvc": "2.0.2",
"@rspack/binding-win32-ia32-msvc": "2.0.2",
"@rspack/binding-win32-x64-msvc": "2.0.2"
}
},
"node_modules/@rspack/binding-darwin-arm64": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.0.4.tgz",
"integrity": "sha512-0Q1QXFEsZfDc4opiDnb8q50KlBbC2VovViDaYlMJZBzvjAo325mh3itXPfz7YZ31M+TxRE7TUiJXH3ltiV1Hdg==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.0.2.tgz",
"integrity": "sha512-0o7lbgBBsDlICWdjIH0q3e0BsSco4GRiImHWVfZSVEG+q2+ykZJvSvYCVhPM1Co375Z0S3VMPa/8SjcY1FHwlw==",
"cpu": [
"arm64"
],
@@ -202,9 +202,9 @@
]
},
"node_modules/@rspack/binding-darwin-x64": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.0.4.tgz",
"integrity": "sha512-oO5J2QYf7+H+aYRj85EiGrDOoDEE9EDDl7NgXv46iWvIF0wXowEHXqnjMFxHxRq2Vf6scT+0yYQX9blWcvMWAA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.0.2.tgz",
"integrity": "sha512-tOwxZpoPlTlRs/w6UyUinXJ4TYRVHMlR7+eQxO1R3muKpixvhXQjtvoaY16HuFyTVky5F0IfOoWr3x9FEsgdLg==",
"cpu": [
"x64"
],
@@ -216,9 +216,9 @@
]
},
"node_modules/@rspack/binding-linux-arm64-gnu": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.0.4.tgz",
"integrity": "sha512-BEk6mIYBK4BihW9qXXITJORrVXecTlkRjrqhgefili4xjXtLdcUnxAm9sN/2oJ8m378n2h33qDh4gr2orPBFWQ==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.0.2.tgz",
"integrity": "sha512-1ZD4YFhG1rmgqj+W8hfwHyKV8xDxGsc/3KgU0FwmiVEX7JfzhCkgBO/xlCG79kRKSrzuVzt4icO/G3cCKn0pag==",
"cpu": [
"arm64"
],
@@ -233,9 +233,9 @@
]
},
"node_modules/@rspack/binding-linux-arm64-musl": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.0.4.tgz",
"integrity": "sha512-Hyt3z1RwNcSMIoaoWLN4Hb/696/O5JPukf8rXQASvf2UkC+X3ij7tr+8lMSYi3Zysi1QL375CnT4fNoABEW0JA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.0.2.tgz",
"integrity": "sha512-/PtTkM/DsDLjeuXTmeJeRfbjCDbcL9jvoVgZrgxYFZ28y2cdLvbChbW9uigOzs5dQEs1CIBQXMTTj7KhdBTuQg==",
"cpu": [
"arm64"
],
@@ -250,9 +250,9 @@
]
},
"node_modules/@rspack/binding-linux-x64-gnu": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.0.4.tgz",
"integrity": "sha512-xHorBPBZAg0Pn9Q0k9dWZ9euowieDxcSOzQ9JhTCmhDY6wZH5M/kCBFlCs/OQeW5/NUArW3x3MwEdO/0QJHMxg==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.0.2.tgz",
"integrity": "sha512-bBjsZxMHRaPo6X9SokApm6ucs+UhXtAJFyJJyuk2BH4XJsLeCU9Dz1vMwioeohFbJUUeTASVPm6/BL+RhSaunw==",
"cpu": [
"x64"
],
@@ -267,9 +267,9 @@
]
},
"node_modules/@rspack/binding-linux-x64-musl": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.0.4.tgz",
"integrity": "sha512-QLxEGUXofF0kVNU12Y2NT3Qi9lGs+WbnYpapVeb+2IXtrAXJfU7Rhy7lAp5GLMzYMQNrKKL9SVnTWKbODbNW9Q==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.0.2.tgz",
"integrity": "sha512-HjlpInqzabDNkhVsUJpsHPqa9QYVWBViJoyWNjzXCAW0vKMDvwaphyUvokSinX8FGTlZi/sr5UEaHJo6XtQ35g==",
"cpu": [
"x64"
],
@@ -284,9 +284,9 @@
]
},
"node_modules/@rspack/binding-wasm32-wasi": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.0.4.tgz",
"integrity": "sha512-YhN8HkiH46ONU9tm5dyoXDImDWGpU7E4SPqGI4OyAVF0445uIChurIUmTIOYcD6cg81GGeIjozWJOcb635Dcqw==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.0.2.tgz",
"integrity": "sha512-YaRYNFLJRpkGfYjSWR7n9f+nQKtrlmrrffpAn/blc2geHcRvXoBc5SCs1idPtsLhj7H9qWWhs7ucjyHy4csWFg==",
"cpu": [
"wasm32"
],
@@ -300,9 +300,9 @@
}
},
"node_modules/@rspack/binding-win32-arm64-msvc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.0.4.tgz",
"integrity": "sha512-MUlYIz82xQRN0aoiXXyEBrNVUwiOSSFRi7nuCgUKduaSdlbPWzCY31IdtOygZ06LVp5JIGUEtyqSrjQq4FrMRw==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.0.2.tgz",
"integrity": "sha512-d/3kTEKq+asLjRFPO96t+wfWiM7DLN76VQEPDD9bc1kdsZXlVJBuvyXfsgK8bbEvKplWXYcSsokhmEnuXrLOpg==",
"cpu": [
"arm64"
],
@@ -314,9 +314,9 @@
]
},
"node_modules/@rspack/binding-win32-ia32-msvc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.0.4.tgz",
"integrity": "sha512-D7UcIFMzlY2yhhyuW4Ej15gBWmTwUM5DxuObl3Kv31qRv/pmV3MsqUeG5m2dqLbUxzqPH87qnp0cArbkJQ1b+w==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.0.2.tgz",
"integrity": "sha512-161cWineq3RW+Jdm1FAfSpXeUtYWvhB3kAbm46vNT9h/YYz+spwsFMvveAZ1nsVSVL0IC5lDBGUte7yUAY8K2g==",
"cpu": [
"ia32"
],
@@ -328,9 +328,9 @@
]
},
"node_modules/@rspack/binding-win32-x64-msvc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.0.4.tgz",
"integrity": "sha512-MnYKPfdrAEbtpKg/1SZ6cNtzreIRyQJK4APbxLLPXENdTH5QXQkaTjLMKEeJcJ51FRhI/+yNpOUm2oTHdCQ1Og==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.0.2.tgz",
"integrity": "sha512-y7Q0S1FE+OlkL5GMqLG0PwxrPw6E1r892KhGrGKE1Vdufe5YTEx6xTPxzZ+b7N2KPD7s9G1/iJmWHQxb1+Bjkg==",
"cpu": [
"x64"
],
@@ -342,13 +342,13 @@
]
},
"node_modules/@rspack/core": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.0.4.tgz",
"integrity": "sha512-OuxdQeeKWQpNvFBRDOcnoSaQvp6E4APM/6JJMM/k0p6oL1TEFQVGdNu3VGY4mRAsebnNBXapMVMhj+v66Bn2pg==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.0.2.tgz",
"integrity": "sha512-VM3UHOo26uC+4QSqY5tU1ybI7KuXY5rTof8nhFOaBY9SYau0Smvr+hMSAPmrmHwknB6dXT8yaNVxrj7I+qxE1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rspack/binding": "2.0.4"
"@rspack/binding": "2.0.2"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
@@ -383,17 +383,17 @@
}
},
"node_modules/@rspress/core": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.13.tgz",
"integrity": "sha512-lbaBA5eqh7wKdH98TUQEI+SfS3Z6YgaNCup3X+ttrYVLOrxN8PJvbedo6fFAcl+wP3XLy6D0pcnnzAgu8y3tdg==",
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.11.tgz",
"integrity": "sha512-4YBOFmSMFv5GWrCa80qSIW8VxqZQQS/PknVq2r7Hb7kgfB38Fzciopn3hjb3hNwI4TTRbsi/Jev2HyRWD4bYAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@mdx-js/mdx": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@rsbuild/core": "^2.0.7",
"@rsbuild/core": "^2.0.5",
"@rsbuild/plugin-react": "~2.0.0",
"@rspress/shared": "2.0.13",
"@rspress/shared": "2.0.11",
"@shikijs/rehype": "^4.0.2",
"@types/unist": "^3.0.3",
"@unhead/react": "^2.1.15",
@@ -411,8 +411,8 @@
"react-dom": "^19.2.6",
"react-lazy-with-preload": "^2.2.1",
"react-reconciler": "0.33.0",
"react-render-to-markdown": "19.1.0",
"react-router-dom": "^7.15.1",
"react-render-to-markdown": "19.0.1",
"react-router-dom": "^7.15.0",
"rehype-external-links": "^3.0.0",
"rehype-raw": "^7.0.0",
"remark-cjk-friendly": "^2.0.1",
@@ -436,9 +436,9 @@
}
},
"node_modules/@rspress/plugin-client-redirects": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.13.tgz",
"integrity": "sha512-dP753ASTvH6eDtSEulcqq2lE/kTSdOWSCw0nzvXG+7atTWTHDp6z47uH3CGD8E78cBuKyEi4OH+U7V0EtCTc0Q==",
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.11.tgz",
"integrity": "sha512-DI9vod5mGccg57c19CuFpN3mGP1FEEueOUnEUz1UHXSyXg9YTj+ox7Xla4jUUzAzoPVGiWSSsfbtCTwdoxAsbg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -449,9 +449,9 @@
}
},
"node_modules/@rspress/plugin-sitemap": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.13.tgz",
"integrity": "sha512-JtkNlxNuA7BzknKIrLvLQkSk0XVi7OXzrE76ma/cLvleccNWr8LGrHtrac4IrDr+HauK4WKTM2JaHGGHUdOUKw==",
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.11.tgz",
"integrity": "sha512-046LCHgbJXdaPipWB2SWMjZcAtIrOjXGZOD92xlTjhZ74D7Mk1Nod1MQdtOEoISWedcHdgpUVXMDbB1doKBpPQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -462,26 +462,26 @@
}
},
"node_modules/@rspress/shared": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.13.tgz",
"integrity": "sha512-LmDfr7+MDNWRBbxcNoWkW68A35oRonpDJq2Jlx3L8GCzG4sAsyd6Yw0DebTWAxx7hVOXGMf37nEf1J4aOLEZfg==",
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.11.tgz",
"integrity": "sha512-7l5Pso4s597utJyisVEnd7n/40h053nfE8DwGQMeS8RLGtSwVgxFwNHsSrvQEGtFlLrg2aWWSITqnAVO1wfTew==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rsbuild/core": "^2.0.7",
"@rsbuild/core": "^2.0.5",
"@shikijs/rehype": "^4.0.2",
"unified": "^11.0.5"
}
},
"node_modules/@shikijs/core": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.1.0.tgz",
"integrity": "sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz",
"integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/primitive": "4.1.0",
"@shikijs/types": "4.1.0",
"@shikijs/primitive": "4.0.2",
"@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5"
@@ -491,28 +491,28 @@
}
},
"node_modules/@shikijs/engine-javascript": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.1.0.tgz",
"integrity": "sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz",
"integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.1.0",
"@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.6"
"oniguruma-to-es": "^4.3.4"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/engine-oniguruma": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.1.0.tgz",
"integrity": "sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz",
"integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.1.0",
"@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2"
},
"engines": {
@@ -520,26 +520,26 @@
}
},
"node_modules/@shikijs/langs": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.1.0.tgz",
"integrity": "sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz",
"integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.1.0"
"@shikijs/types": "4.0.2"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/primitive": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.1.0.tgz",
"integrity": "sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz",
"integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.1.0",
"@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
},
@@ -548,16 +548,16 @@
}
},
"node_modules/@shikijs/rehype": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.1.0.tgz",
"integrity": "sha512-HQwltCcO2/UiFz44/8whyji4rP1VghLu++MgvQn+lQA8/gvuycGkay8DH8o8VAOvLBDKGOkBEw7cC1Cm33GObQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.0.2.tgz",
"integrity": "sha512-cmPlKLD8JeojasNFoY64162ScpEdEdQUMuVodPCrv1nx1z3bjmGwoKWDruQWa/ejSznImlaeB0Ty6Q3zPaVQAA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.1.0",
"@shikijs/types": "4.0.2",
"@types/hast": "^3.0.4",
"hast-util-to-string": "^3.0.1",
"shiki": "4.1.0",
"shiki": "4.0.2",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0"
},
@@ -566,22 +566,22 @@
}
},
"node_modules/@shikijs/themes": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.1.0.tgz",
"integrity": "sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz",
"integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.1.0"
"@shikijs/types": "4.0.2"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/types": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.1.0.tgz",
"integrity": "sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz",
"integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -631,9 +631,9 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
@@ -682,9 +682,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.2.15",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
"integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -700,9 +700,9 @@
"license": "MIT"
},
"node_modules/@ungap/structured-clone": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
"integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"dev": true,
"license": "ISC"
},
@@ -1150,9 +1150,9 @@
"license": "Apache-2.0"
},
"node_modules/get-east-asian-width": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
"integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2809,9 +2809,9 @@
}
},
"node_modules/react-render-to-markdown": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-render-to-markdown/-/react-render-to-markdown-19.1.0.tgz",
"integrity": "sha512-dF9b3tO41ezqdmHP8X92kbHbMexJ6iC7iHw4ykC8fwiO7DgpFc9PhMoKlI+BcPzRxGcWgQSdrixVB9RykhjJpQ==",
"version": "19.0.1",
"resolved": "https://registry.npmjs.org/react-render-to-markdown/-/react-render-to-markdown-19.0.1.tgz",
"integrity": "sha512-BPv48o+ubcu2JyUDIktvJXFqLIZqR7hA4mvGu1eFIofz9fogT2me9UvXwRvqvGs9jEtNaJkxZIUKUX0oiK4hDA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2822,9 +2822,9 @@
}
},
"node_modules/react-router": {
"version": "7.15.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz",
"integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==",
"version": "7.15.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.0.tgz",
"integrity": "sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2845,13 +2845,13 @@
}
},
"node_modules/react-router-dom": {
"version": "7.15.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.1.tgz",
"integrity": "sha512-AzF62gjY6U9rkMq4RfP/r2EVtQ7DMfNMjyOp/flLTCrtRylLiK4wT4pSq6O8rOXZ2eXdZYJPEYe+ifomiv+Igg==",
"version": "7.15.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.0.tgz",
"integrity": "sha512-VcrVg64Fo8nwBvDscajG8gRTLIuTC6N50nb22l2HOOV4PTOHgoGp8mUjy9wLiHYoYTSYI36tUnXZgasSRFZorQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"react-router": "7.15.1"
"react-router": "7.15.0"
},
"engines": {
"node": ">=20.0.0"
@@ -3164,18 +3164,18 @@
"license": "MIT"
},
"node_modules/shiki": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.1.0.tgz",
"integrity": "sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz",
"integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/core": "4.1.0",
"@shikijs/engine-javascript": "4.1.0",
"@shikijs/engine-oniguruma": "4.1.0",
"@shikijs/langs": "4.1.0",
"@shikijs/themes": "4.1.0",
"@shikijs/types": "4.1.0",
"@shikijs/core": "4.0.2",
"@shikijs/engine-javascript": "4.0.2",
"@shikijs/engine-oniguruma": "4.0.2",
"@shikijs/langs": "4.0.2",
"@shikijs/themes": "4.0.2",
"@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
},
+3 -19
View File
@@ -5,7 +5,7 @@
"osvVulnerabilityAlerts": true,
"lockFileMaintenance": {
"enabled": true,
"schedule": ["* * * * 0,6"]
"schedule": ["at any time"]
},
"platformAutomerge": true,
"nix": {
@@ -66,17 +66,6 @@
"matchUpdateTypes": ["minor", "patch"],
"groupName": "github-actions-non-major"
},
{
"description": "Batch GitHub Actions digest updates",
"matchManagers": ["github-actions"],
"matchUpdateTypes": ["digest"],
"groupName": "github-actions-digest",
"automerge": true,
"automergeStrategy": "fast-forward",
"schedule": [
"* 0-7 * * 2"
]
},
{
"description": "Batch patch-level Node.js dependency updates",
"matchManagers": ["npm"],
@@ -94,10 +83,7 @@
"matchPackageNames": ["crate-ci/typos"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeStrategy": "fast-forward",
"schedule": [
"* 0-7 * * 3"
]
"automergeStrategy": "fast-forward"
},
{
"description": "Auto-merge renovatebot docker image updates",
@@ -105,9 +91,7 @@
"matchPackageNames": ["ghcr.io/renovatebot/renovate"],
"automerge": true,
"automergeStrategy": "fast-forward",
"schedule": [
"* 0-7 * * 1"
]
"extends": ["schedule:earlyMondays"]
}
],
"customManagers": [
+1 -1
View File
@@ -10,7 +10,7 @@
[toolchain]
profile = "minimal"
channel = "1.95.0"
channel = "1.92.0"
components = [
# For rust-analyzer
"rust-src",
+9 -229
View File
@@ -1,5 +1,5 @@
use std::{
collections::{HashMap, HashSet},
collections::HashMap,
fmt::Write,
iter::once,
time::{Instant, SystemTime},
@@ -22,7 +22,7 @@ use futures::{FutureExt, StreamExt, TryStreamExt};
use lettre::message::Mailbox;
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId, UInt,
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId,
api::federation::event::get_room_state, events::AnyStateEvent, serde::Raw,
};
use service::rooms::{
@@ -69,205 +69,6 @@ pub(super) async fn get_auth_chain(&self, event_id: OwnedEventId) -> Result {
self.write_str(&out).await
}
#[derive(Clone, Copy, Eq, PartialEq)]
enum NodeStatus {
Normal(bool),
SoftFailed(bool),
Rejected(bool),
}
struct AuthChild {
node_id: String,
event_id: OwnedEventId,
depth: UInt,
ts: UInt,
first_seen: bool,
pdu: Option<PduEvent>,
}
fn render_node(
graph: &mut String,
node_id: &str,
event_id: &EventId,
name: &str,
status: NodeStatus,
) -> Result {
let evt_str = event_id.to_string();
let status_label = match status {
| NodeStatus::Normal(false) => format!("{evt_str}: {name}"),
| NodeStatus::Normal(true) => format!("{evt_str}: {name} (missing locally)"),
| NodeStatus::SoftFailed(false) => format!("{evt_str}: {name} (soft-failed)"),
| NodeStatus::SoftFailed(true) =>
format!("{evt_str}: {name} (soft-failed & missing locally)"),
| NodeStatus::Rejected(false) => format!("{evt_str}: {name} (rejected)"),
| NodeStatus::Rejected(true) => format!("{evt_str}: {name} (rejected & missing locally)"),
};
writeln!(graph, "{node_id}[\"{}\"]", status_label.as_str())?;
match status {
| NodeStatus::Rejected(_) => writeln!(graph, "class {node_id} rejected;")?,
| NodeStatus::SoftFailed(_) => writeln!(graph, "class {node_id} soft_failed;")?,
| NodeStatus::Normal(_) => {},
}
Ok(())
}
#[admin_command]
pub(super) async fn show_auth_chain(&self, event_id: OwnedEventId) -> Result {
let node_status = async |event_id: &EventId, missing: bool| -> NodeStatus {
if self
.services
.rooms
.pdu_metadata
.is_event_rejected(event_id)
.await
{
NodeStatus::Rejected(missing)
} else if self
.services
.rooms
.pdu_metadata
.is_event_soft_failed(event_id)
.await
{
NodeStatus::SoftFailed(missing)
} else {
NodeStatus::Normal(missing)
}
};
let Ok(root) = self.services.rooms.timeline.get_pdu(&event_id).await else {
return Err!("Event not found.");
};
let mut graph = String::from(
"```mermaid\n%% This is a mermaid graph. You can plug this output into\n\
%% https://mermaid.live/edit to visualise it on-the-fly.\nflowchart TD\n\
classDef rejected fill:#ffe5e5,stroke:#cc0000,stroke-width:2px,color:#000;\n\
classDef soft_failed fill:#fff6cc,stroke:#c9a400,stroke-width:2px,color:#000;\n"
);
let mut node_ids: HashMap<OwnedEventId, String> = HashMap::new();
let mut cached_events: HashMap<OwnedEventId, PduEvent> =
HashMap::from([(event_id.clone(), root.clone())]);
let mut scheduled: HashSet<OwnedEventId> = HashSet::from([event_id.clone()]);
let mut visited: HashSet<OwnedEventId> = HashSet::new();
let mut stack = vec![root];
let mut next_node_id = 0_usize;
let node_id_for = |event_id: &OwnedEventId,
node_ids: &mut HashMap<OwnedEventId, String>,
next_node_id: &mut usize| {
node_ids
.entry(event_id.clone())
.or_insert_with(|| {
let id = format!("n{}", *next_node_id);
*next_node_id = next_node_id.saturating_add(1);
id
})
.clone()
};
let node_name = |e: &PduEvent| {
if let Some(state_key) = e.state_key() {
format!("{},'{}'", e.event_type(), state_key)
} else {
format!("{}", e.event_type())
}
};
while let Some(event) = stack.pop() {
let current_event_id = event.event_id().to_owned();
if !visited.insert(current_event_id.clone()) {
continue;
}
let current_node_id = node_id_for(&current_event_id, &mut node_ids, &mut next_node_id);
let current_status = node_status(&current_event_id, false).await;
render_node(
&mut graph,
&current_node_id,
&current_event_id,
&node_name(&event),
current_status,
)?;
let mut children = Vec::with_capacity(event.auth_events.len());
for auth_event_id in event.auth_events().rev() {
let auth_event_id = auth_event_id.to_owned();
let auth_node_id = node_id_for(&auth_event_id, &mut node_ids, &mut next_node_id);
writeln!(graph, "{current_node_id} --> {auth_node_id}")?;
let first_seen = scheduled.insert(auth_event_id.clone());
let auth_pdu = if let Some(auth_pdu) = cached_events.get(&auth_event_id) {
// NOTE: events might be referenced multiple times (like the create event)
// so this saves some cheeky db lookup time
Some(auth_pdu.clone())
} else if first_seen {
match self.services.rooms.timeline.get_pdu(&auth_event_id).await {
| Ok(auth_event) => {
cached_events.insert(auth_event_id.clone(), auth_event.clone());
Some(auth_event)
},
| Err(_) => None,
}
} else {
None
};
// NOTE: Depth is used as the primary sorting key here, even though it has no
// bearing on state resolution or anything. Timestamp is used as a
// tiebreaker, failing back to lexicographical comparison.
let (depth, ts) = auth_pdu
.as_ref()
.map_or((UInt::MAX, UInt::MAX), |pdu| (pdu.depth, pdu.origin_server_ts));
children.push(AuthChild {
node_id: auth_node_id,
event_id: auth_event_id,
depth,
ts,
first_seen,
pdu: auth_pdu,
});
}
children.sort_by(|a, b| {
a.depth
.cmp(&b.depth)
.then(a.ts.cmp(&b.ts))
.then(a.event_id.as_str().cmp(b.event_id.as_str()))
});
for child in children.into_iter().rev() {
if !child.first_seen {
continue;
}
if let Some(child_pdu) = child.pdu {
// We have this PDU so will want to traverse it.
stack.push(child_pdu);
} else {
// We don't have this PDU locally so we can't traverse its auth events,
// but we can still render it as a node.
render_node(
&mut graph,
&child.node_id,
&child.event_id,
"",
node_status(&child.event_id, true).await,
)?;
}
}
}
graph.push_str("```\n");
self.write_str(&graph).await
}
#[admin_command]
pub(super) async fn parse_pdu(&self) -> Result {
if self.body.len() < 2
@@ -310,31 +111,15 @@ pub(super) async fn get_pdu(&self, event_id: OwnedEventId) -> Result {
outlier = true;
pdu_json = self.services.rooms.timeline.get_pdu_json(&event_id).await;
}
let rejected = self
.services
.rooms
.pdu_metadata
.is_event_rejected(&event_id)
.await;
let soft_failed = self
.services
.rooms
.pdu_metadata
.is_event_soft_failed(&event_id)
.await;
match pdu_json {
| Err(_) => return Err!("PDU not found locally."),
| Ok(json) => {
let text = serde_json::to_string_pretty(&json)?;
let msg = if rejected {
"Rejected PDU:"
} else if soft_failed {
"Soft-failed PDU:"
} else if outlier {
"Outlier PDU:"
let msg = if outlier {
"Outlier (Rejected / Soft Failed) PDU found in our database"
} else {
"PDU:"
"PDU found in our database"
};
write!(self, "{msg}\n```json\n{text}\n```")
},
@@ -829,10 +614,6 @@ pub(super) async fn force_set_room_state_from_server(
.await;
state.insert(shortstatekey, pdu.event_id.clone());
self.services
.rooms
.pdu_metadata
.clear_pdu_markers(pdu.event_id());
}
}
@@ -850,10 +631,6 @@ pub(super) async fn force_set_room_state_from_server(
.rooms
.outlier
.add_pdu_outlier(&event_id, &value);
self.services
.rooms
.pdu_metadata
.clear_pdu_markers(&event_id);
}
info!("Resolving new room state");
@@ -885,7 +662,10 @@ pub(super) async fn force_set_room_state_from_server(
.force_state(room_id.clone().as_ref(), short_state_hash, added, removed, &state_lock)
.await?;
info!("Updating joined counts for room");
info!(
"Updating joined counts for room just in case (e.g. we may have found a difference in \
the room's m.room.member state"
);
self.services
.rooms
.state_cache
+1 -10
View File
@@ -17,21 +17,12 @@ pub enum DebugCommand {
message: Vec<String>,
},
/// Loads the auth_chain of a PDU, reporting how long it took.
/// Get the auth_chain of a PDU
GetAuthChain {
/// An event ID (the $ character followed by the base64 reference hash)
event_id: OwnedEventId,
},
/// Walks & displays the auth_chain of a PDU in a mermaid graph format.
///
/// This is useless to basically anyone but developers, and is also probably
/// slow and memory hungry.
ShowAuthChain {
/// The root event ID to start walking back from.
event_id: OwnedEventId,
},
/// Parse and print a PDU from a JSON
///
/// The PDU event is only checked for validity and is not added to the
+1 -6
View File
@@ -740,19 +740,14 @@ pub(super) async fn force_join_room(
&self,
user_id: String,
room_id: OwnedRoomOrAliasId,
via: Option<String>,
) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?;
let (room_id, mut servers) = self
let (room_id, servers) = self
.services
.rooms
.alias
.resolve_with_servers(&room_id, None)
.await?;
if let Some(via) = via.map(ServerName::parse).transpose()? {
servers.retain(|n| *n != via);
servers.insert(0, via);
}
assert!(
self.services.globals.user_is_local(&user_id),
-7
View File
@@ -179,15 +179,8 @@ pub enum UserCommand {
/// Manually join a local user to a room.
ForceJoinRoom {
/// The user to join
user_id: String,
/// The room to join
room_id: OwnedRoomOrAliasId,
/// The server name to join via.
///
/// This server will always be tried first, however if more are
/// available, they may be tried after.
via: Option<String>,
},
/// Manually leave a local user from a room.
+1 -1
View File
@@ -13,7 +13,7 @@ pub(crate) async fn ban_room(
State(services): State<crate::State>,
body: Ruma<rooms::ban::v1::Request>,
) -> Result<rooms::ban::v1::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services.users.is_admin(sender_user).await {
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
}
+1 -1
View File
@@ -13,7 +13,7 @@ pub(crate) async fn list_rooms(
State(services): State<crate::State>,
body: Ruma<rooms::list::v1::Request>,
) -> Result<rooms::list::v1::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services.users.is_admin(sender_user).await {
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
}
+16 -28
View File
@@ -27,7 +27,7 @@ use ruma::{
use service::{mailer::messages, uiaa::Identity, users::HashedPassword};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use crate::{Ruma, router::ClientIdentity};
use crate::Ruma;
pub(crate) mod register;
pub(crate) mod threepid;
@@ -75,11 +75,13 @@ pub(crate) async fn get_register_available_route(
return Err!(Request(UserInUse("User ID is not available.")));
}
if let Some(ClientIdentity::Appservice { appservice_info, .. }) = &body.identity
&& !appservice_info.is_user_match(&user_id)
{
return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
} else if services.appservice.is_exclusive_user_id(&user_id).await {
if let Some(ref info) = body.appservice_info {
if !info.is_user_match(&user_id) {
return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
}
}
if services.appservice.is_exclusive_user_id(&user_id).await {
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
}
@@ -109,8 +111,7 @@ pub(crate) async fn change_password_route(
ClientIp(client): ClientIp,
body: Ruma<change_password::v3::Request>,
) -> Result<change_password::v3::Response> {
let identity = if let Some(user_id) = body.identity.as_ref().map(ClientIdentity::sender_user)
{
let identity = if let Some(ref user_id) = body.sender_user {
// A signed-in user is trying to change their password, prompt them for their
// existing one
@@ -156,12 +157,7 @@ pub(crate) async fn change_password_route(
services
.users
.all_device_ids(&sender_user)
.ready_filter(|id| {
body.identity
.as_ref()
.and_then(|identity| identity.sender_device())
.is_none_or(|sender_device| sender_device != *id)
})
.ready_filter(|id| *id != body.sender_device())
.for_each(async |id| services.users.remove_device(&sender_user, &id).await)
.await;
@@ -177,12 +173,7 @@ pub(crate) async fn change_password_route(
.await
.ok()
.as_ref()
.is_some_and(|pusher_device| {
body.identity
.as_ref()
.and_then(|identity| identity.sender_device())
.is_none_or(|sender_device| sender_device != *pusher_device)
})
.is_some_and(|pusher_device| pusher_device != body.sender_device())
.then_some(pushkey)
})
.for_each(async |pushkey| {
@@ -196,7 +187,7 @@ pub(crate) async fn change_password_route(
if services.server.config.admin_room_notices {
services
.admin
.notice(&format!("User {sender_user} changed their password."))
.notice(&format!("User {} changed their password.", &sender_user))
.await;
}
@@ -250,11 +241,9 @@ pub(crate) async fn whoami_route(
State(_): State<crate::State>,
body: Ruma<whoami::v3::Request>,
) -> Result<whoami::v3::Response> {
Ok(
assign!(whoami::v3::Response::new(body.identity.sender_user().to_owned(), false), {
device_id: body.identity.sender_device().map(ToOwned::to_owned),
}),
)
Ok(assign!(whoami::v3::Response::new(body.sender_user().to_owned(), false), {
device_id: body.sender_device,
}))
}
/// # `POST /_matrix/client/r0/account/deactivate`
@@ -277,9 +266,8 @@ pub(crate) async fn deactivate_route(
// Authentication for this endpoint is technically optional,
// but we require the user to be logged in
let sender_user = body
.identity
.sender_user
.as_ref()
.map(ClientIdentity::sender_user)
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
// Prompt the user to confirm with their password using UIAA
+9 -7
View File
@@ -59,7 +59,7 @@ pub(crate) async fn register_route(
let allow_registration =
services.config.allow_registration || services.firstrun.is_first_run();
if !allow_registration && body.identity.is_none() {
if !allow_registration && body.appservice_info.is_none() {
info!(
?body.username,
?body.initial_device_display_name,
@@ -71,7 +71,7 @@ pub(crate) async fn register_route(
)));
}
let identity = if body.identity.is_some() {
let identity = if body.appservice_info.is_some() {
// Appservices can skip auth
None
} else {
@@ -107,7 +107,7 @@ pub(crate) async fn register_route(
// For appservice logins, make sure that the user ID is in the appservice's
// namespace
match body.identity {
match body.appservice_info {
| Some(ref info) =>
if !info.is_user_match(&user_id) && !emergency_mode_enabled {
return Err!(Request(Exclusive(
@@ -125,7 +125,7 @@ pub(crate) async fn register_route(
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
}
let password = if body.identity.is_some() {
let password = if body.appservice_info.is_some() {
None
} else if let Some(password) = body.password.as_deref() {
Some(HashedPassword::new(password)?)
@@ -140,7 +140,9 @@ pub(crate) async fn register_route(
let mut displayname = user_id.localpart().to_owned();
// Apply the new user displayname suffix, if it's set
if !services.globals.new_user_displayname_suffix().is_empty() && body.identity.is_none() {
if !services.globals.new_user_displayname_suffix().is_empty()
&& body.appservice_info.is_none()
{
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)?;
}
@@ -207,7 +209,7 @@ pub(crate) async fn register_route(
let device_display_name = body.initial_device_display_name.as_deref().unwrap_or("");
if body.identity.is_none() {
if body.appservice_info.is_none() {
if !device_display_name.is_empty() {
let notice = format!(
"New user \"{user_id}\" registered on this server from IP {client} and device \
@@ -253,7 +255,7 @@ pub(crate) async fn register_route(
}
}
if body.identity.is_none() && !services.server.config.auto_join_rooms.is_empty() {
if body.appservice_info.is_none() && !services.server.config.auto_join_rooms.is_empty() {
for room in &services.server.config.auto_join_rooms {
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
error!(
+10 -17
View File
@@ -13,7 +13,7 @@ use ruma::{
};
use service::{mailer::messages, uiaa::Identity};
use crate::{Ruma, router::ClientIdentity};
use crate::Ruma;
/// # `GET _matrix/client/v3/account/3pid`
///
@@ -22,7 +22,7 @@ pub(crate) async fn third_party_route(
State(services): State<crate::State>,
body: Ruma<get_3pids::v3::Request>,
) -> Result<get_3pids::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut threepids = vec![];
if let Some(email) = services
@@ -53,14 +53,6 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
State(services): State<crate::State>,
body: Ruma<request_3pid_management_token_via_email::v3::Request>,
) -> Result<request_3pid_management_token_via_email::v3::Response> {
// Authentication for this endpoint is technically optional,
// but we require the user to be logged in
let sender_user = body
.identity
.as_ref()
.map(ClientIdentity::sender_user)
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
if !services.threepid.email_requirement().may_change() {
return Err!(Request(Forbidden("You may not change your email address.")));
}
@@ -84,7 +76,7 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
Mailbox::new(None, email),
|verification_link| messages::ChangeEmail {
server_name: services.config.server_name.as_str(),
user_id: Some(sender_user),
user_id: body.sender_user.as_deref(),
verification_link,
},
&body.client_secret,
@@ -115,6 +107,8 @@ pub(crate) async fn add_3pid_route(
State(services): State<crate::State>,
body: Ruma<add_3pid::v3::Request>,
) -> Result<add_3pid::v3::Response> {
let sender_user = body.sender_user();
if !services.threepid.email_requirement().may_change() {
return Err!(Request(Forbidden("You may not change your email address.")));
}
@@ -122,10 +116,7 @@ pub(crate) async fn add_3pid_route(
// Require password auth to add an email
let _ = services
.uiaa
.authenticate_password(
&body.auth,
Some(Identity::from_user_id(body.identity.sender_user())),
)
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
.await?;
let email = services
@@ -136,7 +127,7 @@ pub(crate) async fn add_3pid_route(
services
.threepid
.associate_localpart_email(body.identity.sender_user().localpart(), &email)
.associate_localpart_email(sender_user.localpart(), &email)
.await?;
Ok(add_3pid::v3::Response::new())
@@ -147,6 +138,8 @@ pub(crate) async fn delete_3pid_route(
State(services): State<crate::State>,
body: Ruma<delete_3pid::v3::Request>,
) -> Result<delete_3pid::v3::Response> {
let sender_user = body.sender_user();
if body.medium != Medium::Email {
return Ok(delete_3pid::v3::Response::new(ThirdPartyIdRemovalStatus::NoSupport));
}
@@ -157,7 +150,7 @@ pub(crate) async fn delete_3pid_route(
if services
.threepid
.disassociate_localpart_email(body.identity.sender_user().localpart())
.disassociate_localpart_email(sender_user.localpart())
.await
.is_none()
{
+8 -8
View File
@@ -22,9 +22,9 @@ pub(crate) async fn set_global_account_data_route(
State(services): State<crate::State>,
body: Ruma<set_global_account_data::v3::Request>,
) -> Result<set_global_account_data::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user != body.user_id && !body.identity.is_appservice() {
if sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot set account data for other users.")));
}
@@ -47,9 +47,9 @@ pub(crate) async fn set_room_account_data_route(
State(services): State<crate::State>,
body: Ruma<set_room_account_data::v3::Request>,
) -> Result<set_room_account_data::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user != body.user_id && !body.identity.is_appservice() {
if sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot set account data for other users.")));
}
@@ -72,9 +72,9 @@ pub(crate) async fn get_global_account_data_route(
State(services): State<crate::State>,
body: Ruma<get_global_account_data::v3::Request>,
) -> Result<get_global_account_data::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user != body.user_id && !body.identity.is_appservice() {
if sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot get account data of other users.")));
}
@@ -94,9 +94,9 @@ pub(crate) async fn get_room_account_data_route(
State(services): State<crate::State>,
body: Ruma<get_room_account_data::v3::Request>,
) -> Result<get_room_account_data::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user != body.user_id && !body.identity.is_appservice() {
if sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot get account data of other users.")));
}
+5 -6
View File
@@ -12,11 +12,10 @@ pub(crate) async fn get_suspended_status(
State(services): State<crate::State>,
body: Ruma<get_suspended::v1::Request>,
) -> Result<get_suspended::v1::Response> {
let (admin, active) = join(
services.users.is_admin(body.identity.sender_user()),
services.users.is_active(&body.user_id),
)
.await;
let sender_user = body.sender_user();
let (admin, active) =
join(services.users.is_admin(sender_user), services.users.is_active(&body.user_id)).await;
if !admin {
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
}
@@ -38,7 +37,7 @@ pub(crate) async fn put_suspended_status(
State(services): State<crate::State>,
body: Ruma<set_suspended::v1::Request>,
) -> Result<set_suspended::v1::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let (sender_admin, active, target_admin) = join3(
services.users.is_admin(sender_user),
+4 -4
View File
@@ -11,7 +11,7 @@ pub(crate) async fn create_alias_route(
State(services): State<crate::State>,
body: Ruma<create_alias::v3::Request>,
) -> Result<create_alias::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -19,7 +19,7 @@ pub(crate) async fn create_alias_route(
services
.rooms
.alias
.appservice_checks(&body.room_alias, body.identity.appservice_info())
.appservice_checks(&body.room_alias, &body.appservice_info)
.await?;
// this isn't apart of alias_checks or delete alias route because we should
@@ -59,7 +59,7 @@ pub(crate) async fn delete_alias_route(
State(services): State<crate::State>,
body: Ruma<delete_alias::v3::Request>,
) -> Result<delete_alias::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -67,7 +67,7 @@ pub(crate) async fn delete_alias_route(
services
.rooms
.alias
.appservice_checks(&body.room_alias, body.identity.appservice_info())
.appservice_checks(&body.room_alias, &body.appservice_info)
.await?;
services
+4 -2
View File
@@ -1,5 +1,5 @@
use axum::extract::State;
use conduwuit::{Err, Result};
use conduwuit::{Err, Result, err};
use ruma::{
api::{appservice::ping, client::appservice::request_ping},
assign,
@@ -15,7 +15,9 @@ pub(crate) async fn appservice_ping(
State(services): State<crate::State>,
body: Ruma<request_ping::v1::Request>,
) -> Result<request_ping::v1::Response> {
let appservice_info = &body.identity;
let appservice_info = body.appservice_info.as_ref().ok_or_else(|| {
err!(Request(Forbidden("This endpoint can only be called by appservices.")))
})?;
if body.appservice_id != appservice_info.registration.id {
return Err!(Request(Forbidden(
+26 -50
View File
@@ -25,7 +25,7 @@ pub(crate) async fn create_backup_version_route(
) -> Result<create_backup_version::v3::Response> {
let version = services
.key_backups
.create_backup(body.identity.sender_user(), &body.algorithm)?;
.create_backup(body.sender_user(), &body.algorithm)?;
Ok(create_backup_version::v3::Response::new(version))
}
@@ -40,7 +40,7 @@ pub(crate) async fn update_backup_version_route(
) -> Result<update_backup_version::v3::Response> {
services
.key_backups
.update_backup(body.identity.sender_user(), &body.version, &body.algorithm)
.update_backup(body.sender_user(), &body.version, &body.algorithm)
.await?;
Ok(update_backup_version::v3::Response::new())
@@ -55,11 +55,11 @@ pub(crate) async fn get_latest_backup_info_route(
) -> Result<get_latest_backup_info::v3::Response> {
let (version, algorithm) = services
.key_backups
.get_latest_backup(body.identity.sender_user())
.get_latest_backup(body.sender_user())
.await
.map_err(|_| err!(Request(NotFound("Key backup does not exist."))))?;
let (count, etag) = get_count_etag(&services, body.identity.sender_user(), &version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &version).await;
Ok(get_latest_backup_info::v3::Response::new(algorithm, count, etag, version))
}
@@ -73,14 +73,13 @@ pub(crate) async fn get_backup_info_route(
) -> Result<get_backup_info::v3::Response> {
let algorithm = services
.key_backups
.get_backup(body.identity.sender_user(), &body.version)
.get_backup(body.sender_user(), &body.version)
.await
.map_err(|_| {
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
})?;
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(get_backup_info::v3::Response::new(algorithm, count, etag, body.version.clone()))
}
@@ -97,7 +96,7 @@ pub(crate) async fn delete_backup_version_route(
) -> Result<delete_backup_version::v3::Response> {
services
.key_backups
.delete_backup(body.identity.sender_user(), &body.version)
.delete_backup(body.sender_user(), &body.version)
.await;
Ok(delete_backup_version::v3::Response::new())
@@ -117,7 +116,7 @@ pub(crate) async fn add_backup_keys_route(
) -> Result<add_backup_keys::v3::Response> {
if services
.key_backups
.get_latest_backup_version(body.identity.sender_user())
.get_latest_backup_version(body.sender_user())
.await
.is_ok_and(|version| version != body.version)
{
@@ -130,19 +129,12 @@ pub(crate) async fn add_backup_keys_route(
for (session_id, key_data) in &room.sessions {
services
.key_backups
.add_key(
body.identity.sender_user(),
&body.version,
room_id,
session_id,
key_data,
)
.add_key(body.sender_user(), &body.version, room_id, session_id, key_data)
.await?;
}
}
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(add_backup_keys::v3::Response::new(etag, count))
}
@@ -161,7 +153,7 @@ pub(crate) async fn add_backup_keys_for_room_route(
) -> Result<add_backup_keys_for_room::v3::Response> {
if services
.key_backups
.get_latest_backup_version(body.identity.sender_user())
.get_latest_backup_version(body.sender_user())
.await
.is_ok_and(|version| version != body.version)
{
@@ -173,18 +165,11 @@ pub(crate) async fn add_backup_keys_for_room_route(
for (session_id, key_data) in &body.sessions {
services
.key_backups
.add_key(
body.identity.sender_user(),
&body.version,
&body.room_id,
session_id,
key_data,
)
.add_key(body.sender_user(), &body.version, &body.room_id, session_id, key_data)
.await?;
}
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(add_backup_keys_for_room::v3::Response::new(etag, count))
}
@@ -203,7 +188,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
) -> Result<add_backup_keys_for_session::v3::Response> {
if services
.key_backups
.get_latest_backup_version(body.identity.sender_user())
.get_latest_backup_version(body.sender_user())
.await
.is_ok_and(|version| version != body.version)
{
@@ -216,7 +201,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
let mut ok_to_replace = true;
if let Some(old_key) = &services
.key_backups
.get_session(body.identity.sender_user(), &body.version, &body.room_id, &body.session_id)
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
.await
.ok()
{
@@ -275,7 +260,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
services
.key_backups
.add_key(
body.identity.sender_user(),
body.sender_user(),
&body.version,
&body.room_id,
&body.session_id,
@@ -284,8 +269,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
.await?;
}
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(add_backup_keys_for_session::v3::Response::new(etag, count))
}
@@ -299,7 +283,7 @@ pub(crate) async fn get_backup_keys_route(
) -> Result<get_backup_keys::v3::Response> {
let rooms = services
.key_backups
.get_all(body.identity.sender_user(), &body.version)
.get_all(body.sender_user(), &body.version)
.await;
Ok(get_backup_keys::v3::Response::new(rooms))
@@ -314,7 +298,7 @@ pub(crate) async fn get_backup_keys_for_room_route(
) -> Result<get_backup_keys_for_room::v3::Response> {
let sessions = services
.key_backups
.get_room(body.identity.sender_user(), &body.version, &body.room_id)
.get_room(body.sender_user(), &body.version, &body.room_id)
.await;
Ok(get_backup_keys_for_room::v3::Response::new(sessions))
@@ -329,7 +313,7 @@ pub(crate) async fn get_backup_keys_for_session_route(
) -> Result<get_backup_keys_for_session::v3::Response> {
let key_data = services
.key_backups
.get_session(body.identity.sender_user(), &body.version, &body.room_id, &body.session_id)
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
.await
.map_err(|_| {
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
@@ -347,11 +331,10 @@ pub(crate) async fn delete_backup_keys_route(
) -> Result<delete_backup_keys::v3::Response> {
services
.key_backups
.delete_all_keys(body.identity.sender_user(), &body.version)
.delete_all_keys(body.sender_user(), &body.version)
.await;
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(delete_backup_keys::v3::Response::new(etag, count))
}
@@ -365,11 +348,10 @@ pub(crate) async fn delete_backup_keys_for_room_route(
) -> Result<delete_backup_keys_for_room::v3::Response> {
services
.key_backups
.delete_room_keys(body.identity.sender_user(), &body.version, &body.room_id)
.delete_room_keys(body.sender_user(), &body.version, &body.room_id)
.await;
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(delete_backup_keys_for_room::v3::Response::new(etag, count))
}
@@ -383,16 +365,10 @@ pub(crate) async fn delete_backup_keys_for_session_route(
) -> Result<delete_backup_keys_for_session::v3::Response> {
services
.key_backups
.delete_room_key(
body.identity.sender_user(),
&body.version,
&body.room_id,
&body.session_id,
)
.delete_room_key(body.sender_user(), &body.version, &body.room_id, &body.session_id)
.await;
let (count, etag) =
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
Ok(delete_backup_keys_for_session::v3::Response::new(etag, count))
}
+5 -1
View File
@@ -48,7 +48,11 @@ pub(crate) async fn get_capabilities_route(
json!({"enabled": services.config.forget_forced_upon_leave}),
)?;
if services.users.is_admin(body.identity.sender_user()).await {
if services
.users
.is_admin(body.sender_user.as_ref().unwrap())
.await
{
// Advertise suspension API
capabilities.set("uk.timedout.msc4323", json!({"suspend": true, "lock": false}))?;
}
+3 -3
View File
@@ -37,8 +37,8 @@ pub(crate) async fn get_context_route(
State(services): State<crate::State>,
body: Ruma<get_context::v3::Request>,
) -> Result<get_context::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.sender_device();
let sender = body.sender();
let (sender_user, sender_device) = sender;
let room_id = &body.room_id;
let event_id = &body.event_id;
let filter = &body.filter;
@@ -143,7 +143,7 @@ pub(crate) async fn get_context_route(
let lazy_loading_context = lazy_loading::Context {
user_id: sender_user,
device_id: sender_device,
device_id: Some(sender_device),
room_id,
token: Some(base_count.into_unsigned()),
options: Some(&filter.lazy_load_options),
+10 -5
View File
@@ -25,11 +25,16 @@ pub(crate) async fn put_dehydrated_device_route(
ClientIp(client): ClientIp,
body: Ruma<put_dehydrated_device::Request>,
) -> Result<put_dehydrated_device::Response> {
let device_id = body.device_id.clone();
let sender_user = body
.sender_user
.as_deref()
.expect("AccessToken authentication required");
let device_id = body.body.device_id.clone();
services
.users
.set_dehydrated_device(body.identity.sender_user(), body.body)
.set_dehydrated_device(sender_user, body.body)
.await?;
Ok(put_dehydrated_device::Response::new(device_id))
@@ -44,7 +49,7 @@ pub(crate) async fn delete_dehydrated_device_route(
ClientIp(client): ClientIp,
body: Ruma<delete_dehydrated_device::Request>,
) -> Result<delete_dehydrated_device::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let device_id = services.users.get_dehydrated_device_id(sender_user).await?;
@@ -62,7 +67,7 @@ pub(crate) async fn get_dehydrated_device_route(
ClientIp(client): ClientIp,
body: Ruma<get_dehydrated_device::Request>,
) -> Result<get_dehydrated_device::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let device = services.users.get_dehydrated_device(sender_user).await?;
@@ -78,7 +83,7 @@ pub(crate) async fn get_dehydrated_events_route(
ClientIp(client): ClientIp,
body: Ruma<get_events::Request>,
) -> Result<get_events::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let device_id = &body.body.device_id;
let existing_id = services.users.get_dehydrated_device_id(sender_user).await;
+8 -8
View File
@@ -21,7 +21,7 @@ pub(crate) async fn get_devices_route(
) -> Result<get_devices::v3::Response> {
let devices: Vec<device::Device> = services
.users
.all_devices_metadata(body.identity.sender_user())
.all_devices_metadata(body.sender_user())
.collect()
.await;
@@ -37,7 +37,7 @@ pub(crate) async fn get_device_route(
) -> Result<get_device::v3::Response> {
let device = services
.users
.get_device_metadata(body.identity.sender_user(), &body.body.device_id)
.get_device_metadata(body.sender_user(), &body.body.device_id)
.await
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
@@ -53,8 +53,8 @@ pub(crate) async fn update_device_route(
ClientIp(client): ClientIp,
body: Ruma<update_device::v3::Request>,
) -> Result<update_device::v3::Response> {
let sender_user = body.identity.sender_user();
let appservice = body.identity.appservice_info();
let sender_user = body.sender_user();
let appservice = body.appservice_info.as_ref();
match services
.users
@@ -118,8 +118,8 @@ pub(crate) async fn delete_device_route(
State(services): State<crate::State>,
body: Ruma<delete_device::v3::Request>,
) -> Result<delete_device::v3::Response> {
let sender_user = body.identity.sender_user();
let appservice = body.identity.appservice_info();
let sender_user = body.sender_user();
let appservice = body.appservice_info.as_ref();
// Appservices get to skip UIAA for this endpoint
if appservice.is_none() {
@@ -154,8 +154,8 @@ pub(crate) async fn delete_devices_route(
State(services): State<crate::State>,
body: Ruma<delete_devices::v3::Request>,
) -> Result<delete_devices::v3::Response> {
let sender_user = body.identity.sender_user();
let appservice = body.identity.appservice_info();
let sender_user = body.sender_user();
let appservice = body.appservice_info.as_ref();
// Appservices get to skip UIAA for this endpoint
if appservice.is_none() {
+2 -2
View File
@@ -112,7 +112,7 @@ pub(crate) async fn set_room_visibility_route(
ClientIp(client): ClientIp,
body: Ruma<set_room_visibility::v3::Request>,
) -> Result<set_room_visibility::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services.rooms.metadata.exists(&body.room_id).await {
// Return 404 if the room doesn't exist
@@ -130,7 +130,7 @@ pub(crate) async fn set_room_visibility_route(
| room::Visibility::Public => {
if services.server.config.lockdown_public_room_directory
&& !services.users.is_admin(sender_user).await
&& !body.identity.is_appservice()
&& body.appservice_info.is_none()
{
info!(
"Non-admin user {sender_user} tried to publish {0} to the room directory \
+2 -2
View File
@@ -15,7 +15,7 @@ pub(crate) async fn get_filter_route(
) -> Result<get_filter::v3::Response> {
services
.users
.get_filter(body.identity.sender_user(), &body.filter_id)
.get_filter(body.sender_user(), &body.filter_id)
.await
.map(get_filter::v3::Response::new)
.map_err(|_| err!(Request(NotFound("Filter not found."))))
@@ -30,7 +30,7 @@ pub(crate) async fn create_filter_route(
) -> Result<create_filter::v3::Response> {
let filter_id = services
.users
.create_filter(body.identity.sender_user(), &body.filter);
.create_filter(body.sender_user(), &body.filter);
Ok(create_filter::v3::Response::new(filter_id))
}
+5 -6
View File
@@ -41,8 +41,7 @@ pub(crate) async fn upload_keys_route(
State(services): State<crate::State>,
body: Ruma<upload_keys::v3::Request>,
) -> Result<upload_keys::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.expect_sender_device()?;
let (sender_user, sender_device) = body.sender();
for (key_id, one_time_key) in &body.one_time_keys {
if one_time_key
@@ -155,7 +154,7 @@ pub(crate) async fn get_keys_route(
State(services): State<crate::State>,
body: Ruma<get_keys::v3::Request>,
) -> Result<get_keys::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
get_keys_helper(
&services,
@@ -192,7 +191,7 @@ pub(crate) async fn upload_signing_keys_route(
State(services): State<crate::State>,
body: Ruma<upload_signing_keys::v3::Request>,
) -> Result<upload_signing_keys::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if uiaa_needed_to_upload_keys(
services,
@@ -288,7 +287,7 @@ pub(crate) async fn upload_signatures_route(
return Ok(upload_signatures::v3::Response::new());
}
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
for (user_id, keys) in &body.signed_keys {
for (key_id, key) in keys {
@@ -341,7 +340,7 @@ pub(crate) async fn get_key_changes_route(
State(services): State<crate::State>,
body: Ruma<get_key_changes::v3::Request>,
) -> Result<get_key_changes::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut device_list_updates = HashSet::new();
+5 -5
View File
@@ -53,7 +53,7 @@ pub(crate) async fn create_content_route(
ClientIp(client): ClientIp,
body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> {
let user = body.identity.sender_user();
let user = body.sender_user();
if services.users.is_suspended(user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -92,7 +92,7 @@ pub(crate) async fn get_content_thumbnail_route(
ClientIp(client): ClientIp,
body: Ruma<get_content_thumbnail::v1::Request>,
) -> Result<get_content_thumbnail::v1::Response> {
let user = body.identity.sender_user();
let user = body.sender_user();
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
let mxc = Mxc {
@@ -142,7 +142,7 @@ pub(crate) async fn get_content_route(
ClientIp(client): ClientIp,
body: Ruma<get_content::v1::Request>,
) -> Result<get_content::v1::Response> {
let user = body.identity.sender_user();
let user = body.sender_user();
let mxc = Mxc {
server_name: &body.server_name,
@@ -189,7 +189,7 @@ pub(crate) async fn get_content_as_filename_route(
ClientIp(client): ClientIp,
body: Ruma<get_content_as_filename::v1::Request>,
) -> Result<get_content_as_filename::v1::Response> {
let user = body.identity.sender_user();
let user = body.sender_user();
let mxc = Mxc {
server_name: &body.server_name,
@@ -240,7 +240,7 @@ pub(crate) async fn get_media_preview_route(
ClientIp(client): ClientIp,
body: Ruma<get_media_preview::v1::Request>,
) -> Result<get_media_preview::v1::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let url = &body.url;
let url = Url::parse(&body.url).map_err(|e| {
+1 -1
View File
@@ -56,7 +56,7 @@ pub(crate) async fn get_media_preview_legacy_route(
ClientIp(client): ClientIp,
body: Ruma<get_media_preview::v3::Request>,
) -> Result<get_media_preview::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let url = &body.url;
let url = Url::parse(&body.url).map_err(|e| {
+1 -1
View File
@@ -15,7 +15,7 @@ pub(crate) async fn ban_user_route(
State(services): State<crate::State>,
body: Ruma<ban_user::v3::Request>,
) -> Result<ban_user::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user == body.user_id {
return Err!(Request(Forbidden("You cannot ban yourself.")));
+1 -1
View File
@@ -18,7 +18,7 @@ pub(crate) async fn forget_room_route(
State(services): State<crate::State>,
body: Ruma<forget_room::v3::Request>,
) -> Result<forget_room::v3::Response> {
let user_id = body.identity.sender_user();
let user_id = body.sender_user();
let room_id = &body.room_id;
let joined = services.rooms.state_cache.is_joined(user_id, room_id);
+1 -1
View File
@@ -29,7 +29,7 @@ pub(crate) async fn invite_user_route(
ClientIp(client): ClientIp,
body: Ruma<invite_user::v3::Request>,
) -> Result<invite_user::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
+2 -2
View File
@@ -28,7 +28,7 @@ pub(crate) async fn join_room_by_id_route(
ClientIp(client): ClientIp,
body: Ruma<join_room_by_id::v3::Request>,
) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -97,7 +97,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
ClientIp(client): ClientIp,
body: Ruma<join_room_by_id_or_alias::v3::Request>,
) -> Result<join_room_by_id_or_alias::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let body = &body.body;
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
+1 -1
View File
@@ -15,7 +15,7 @@ pub(crate) async fn kick_user_route(
State(services): State<crate::State>,
body: Ruma<kick_user::v3::Request>,
) -> Result<kick_user::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
+1 -1
View File
@@ -51,7 +51,7 @@ pub(crate) async fn knock_room_route(
ClientIp(client): ClientIp,
body: Ruma<knock_room::v3::Request>,
) -> Result<knock_room::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let body = &body.body;
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
+1 -1
View File
@@ -32,7 +32,7 @@ pub(crate) async fn leave_room_route(
State(services): State<crate::State>,
body: Ruma<leave_room::v3::Request>,
) -> Result<leave_room::v3::Response> {
leave_room(&services, body.identity.sender_user(), &body.room_id, body.reason.clone())
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone())
.boxed()
.await
.map(|()| leave_room::v3::Response::new())
+2 -2
View File
@@ -30,7 +30,7 @@ pub(crate) async fn get_member_events_route(
State(services): State<crate::State>,
body: Ruma<get_member_events::v3::Request>,
) -> Result<get_member_events::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let membership = body.membership.as_ref();
let not_membership = body.not_membership.as_ref();
@@ -72,7 +72,7 @@ pub(crate) async fn joined_members_route(
if !services
.rooms
.state_accessor
.user_can_see_state_events(body.identity.sender_user(), &body.room_id)
.user_can_see_state_events(body.sender_user(), &body.room_id)
.await
{
return Err!(Request(Forbidden("You don't have permission to view this room.")));
+6 -2
View File
@@ -40,7 +40,7 @@ pub(crate) async fn joined_rooms_route(
let joined_rooms = services
.rooms
.state_cache
.rooms_joined(body.identity.sender_user())
.rooms_joined(body.sender_user())
.collect()
.await;
@@ -105,7 +105,11 @@ pub(crate) async fn banned_room_check(
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
}
} else if let Some(server_name) = server_name {
if services.moderation.is_remote_server_forbidden(server_name) {
if services
.config
.forbidden_remote_server_names
.is_match(server_name.host())
{
warn!(
"User {user_id} who is not an admin tried joining a room which has the server \
name {server_name} that is globally forbidden. Rejecting.",
+1 -1
View File
@@ -14,7 +14,7 @@ pub(crate) async fn unban_user_route(
State(services): State<crate::State>,
body: Ruma<unban_user::v3::Request>,
) -> Result<unban_user::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
+15 -4
View File
@@ -23,7 +23,7 @@ use conduwuit_service::{
};
use futures::{FutureExt, StreamExt, TryFutureExt, future::OptionFuture, pin_mut};
use ruma::{
RoomId, UserId,
DeviceId, RoomId, UserId,
api::{
Direction,
client::{filter::RoomEventFilter, message::get_message_events},
@@ -37,6 +37,7 @@ use ruma::{
serde::Raw,
};
use ruminuwuity::invite_permission_config::FilterLevel;
use tracing::warn;
use crate::Ruma;
@@ -75,8 +76,8 @@ pub(crate) async fn get_message_events_route(
ClientIp(client_ip): ClientIp,
body: Ruma<get_message_events::v3::Request>,
) -> Result<get_message_events::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.sender_device();
let sender_user = body.sender_user();
let sender_device = body.sender_device.as_deref();
let room_id = &body.room_id;
let filter = &body.filter;
@@ -157,7 +158,17 @@ pub(crate) async fn get_message_events_route(
let lazy_loading_context = lazy_loading::Context {
user_id: sender_user,
device_id: sender_device,
device_id: sender_device.or_else(|| {
if let Some(registration) = body.appservice_info.as_ref() {
Some(<&DeviceId>::from(registration.registration.id.as_str()))
} else {
warn!(
"No device_id provided and no appservice registration found, this should be \
unreachable"
);
None
}
}),
room_id,
token: Some(from.into_unsigned()),
options: Some(&filter.lazy_load_options),
+1 -1
View File
@@ -15,7 +15,7 @@ pub(crate) async fn get_mutual_rooms_route(
State(services): State<crate::State>,
body: Ruma<mutual_rooms::unstable::Request>,
) -> Result<mutual_rooms::unstable::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user == body.user_id {
return Err!(Request(Unknown("You cannot request rooms in common with yourself.")));
+1 -1
View File
@@ -16,7 +16,7 @@ pub(crate) async fn create_openid_token_route(
State(services): State<crate::State>,
body: Ruma<account::request_openid_token::v3::Request>,
) -> Result<account::request_openid_token::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if sender_user != body.user_id {
return Err!(Request(InvalidParam(
+3 -9
View File
@@ -20,19 +20,13 @@ pub(crate) async fn set_presence_route(
return Err!(Request(Forbidden("Presence is disabled on this server")));
}
if body.identity.sender_user() != body.user_id && !body.identity.is_appservice() {
if body.sender_user() != body.user_id && body.appservice_info.is_none() {
return Err!(Request(InvalidParam("Not allowed to set presence of other users")));
}
services
.presence
.set_presence(
body.identity.sender_user(),
&body.presence,
None,
None,
body.status_msg.clone(),
)
.set_presence(body.sender_user(), &body.presence, None, None, body.status_msg.clone())
.await?;
Ok(set_presence::v3::Response::new())
@@ -55,7 +49,7 @@ pub(crate) async fn get_presence_route(
let has_shared_rooms = services
.rooms
.state_cache
.user_sees_user(body.identity.sender_user(), &body.user_id)
.user_sees_user(body.sender_user(), &body.user_id)
.await;
if has_shared_rooms {
+7 -13
View File
@@ -51,12 +51,9 @@ pub(crate) async fn set_profile_field_route(
State(services): State<crate::State>,
body: Ruma<set_profile_field::v3::Request>,
) -> Result<set_profile_field::v3::Response> {
if body.user_id != body.identity.sender_user()
&& !(body.identity.is_appservice()
|| services
.admin
.user_is_admin(body.identity.sender_user())
.await)
if body.user_id != body.sender_user()
&& !(body.appservice_info.is_some()
|| services.admin.user_is_admin(body.sender_user()).await)
{
return Err!(Request(Forbidden("You may not change other users' profile data.")));
}
@@ -75,12 +72,9 @@ pub(crate) async fn delete_profile_field_route(
State(services): State<crate::State>,
body: Ruma<delete_profile_field::v3::Request>,
) -> Result<delete_profile_field::v3::Response> {
if body.user_id != body.identity.sender_user()
&& !(body.identity.is_appservice()
|| services
.admin
.user_is_admin(body.identity.sender_user())
.await)
if body.user_id != body.sender_user()
&& !(body.appservice_info.is_some()
|| services.admin.user_is_admin(body.sender_user()).await)
{
return Err!(Request(Forbidden("You may not change other users' profile data.")));
}
@@ -335,7 +329,7 @@ async fn set_profile_field(
// If the user is local and changed their displayname or avatar_url, update it
// in all their joined rooms
if matches!(field_name, ProfileFieldName::AvatarUrl | ProfileFieldName::DisplayName)
&& services.globals.user_is_local(user_id)
&& services.users.is_active_local(user_id).await
{
let displayname = services.users.displayname(user_id).await.ok();
let avatar_url = services.users.avatar_url(user_id).await.ok();
+12 -13
View File
@@ -30,7 +30,7 @@ pub(crate) async fn get_pushrules_all_route(
State(services): State<crate::State>,
body: Ruma<get_pushrules_all::v3::Request>,
) -> Result<get_pushrules_all::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let Some(content_value) = services
.account_data
@@ -101,7 +101,7 @@ pub(crate) async fn get_pushrules_global_route(
State(services): State<crate::State>,
body: Ruma<get_pushrules_global_scope::v3::Request>,
) -> Result<get_pushrules_global_scope::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let Some(content_value) = services
.account_data
@@ -189,7 +189,7 @@ pub(crate) async fn get_pushrule_route(
State(services): State<crate::State>,
body: Ruma<get_pushrule::v3::Request>,
) -> Result<get_pushrule::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
// remove old deprecated mentions push rules as per MSC4210
#[allow(deprecated)]
@@ -226,7 +226,7 @@ pub(crate) async fn set_pushrule_route(
State(services): State<crate::State>,
body: Ruma<set_pushrule::v3::Request>,
) -> Result<set_pushrule::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let body = &body.body;
let mut account_data: PushRulesEvent = services
.account_data
@@ -282,7 +282,7 @@ pub(crate) async fn get_pushrule_actions_route(
State(services): State<crate::State>,
body: Ruma<get_pushrule_actions::v3::Request>,
) -> Result<get_pushrule_actions::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
// remove old deprecated mentions push rules as per MSC4210
#[allow(deprecated)]
@@ -316,7 +316,7 @@ pub(crate) async fn set_pushrule_actions_route(
State(services): State<crate::State>,
body: Ruma<set_pushrule_actions::v3::Request>,
) -> Result<set_pushrule_actions::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut account_data: PushRulesEvent = services
.account_data
@@ -349,7 +349,7 @@ pub(crate) async fn get_pushrule_enabled_route(
State(services): State<crate::State>,
body: Ruma<get_pushrule_enabled::v3::Request>,
) -> Result<get_pushrule_enabled::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
// remove old deprecated mentions push rules as per MSC4210
#[allow(deprecated)]
@@ -383,7 +383,7 @@ pub(crate) async fn set_pushrule_enabled_route(
State(services): State<crate::State>,
body: Ruma<set_pushrule_enabled::v3::Request>,
) -> Result<set_pushrule_enabled::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut account_data: PushRulesEvent = services
.account_data
@@ -416,7 +416,7 @@ pub(crate) async fn delete_pushrule_route(
State(services): State<crate::State>,
body: Ruma<delete_pushrule::v3::Request>,
) -> Result<delete_pushrule::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut account_data: PushRulesEvent = services
.account_data
@@ -458,7 +458,7 @@ pub(crate) async fn get_pushers_route(
State(services): State<crate::State>,
body: Ruma<get_pushers::v3::Request>,
) -> Result<get_pushers::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
Ok(get_pushers::v3::Response::new(services.pusher.get_pushers(sender_user).await))
}
@@ -472,12 +472,11 @@ pub(crate) async fn set_pushers_route(
State(services): State<crate::State>,
body: Ruma<set_pusher::v3::Request>,
) -> Result<set_pusher::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.expect_sender_device()?;
let sender_user = body.sender_user();
services
.pusher
.set_pusher(sender_user, sender_device, &body.action)
.set_pusher(sender_user, body.sender_device(), &body.action)
.await?;
Ok(set_pusher::v3::Response::new())
+3 -3
View File
@@ -26,7 +26,7 @@ pub(crate) async fn set_read_marker_route(
State(services): State<crate::State>,
body: Ruma<set_read_marker::v3::Request>,
) -> Result<set_read_marker::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if let Some(event) = &body.fully_read {
let fully_read_event = FullyReadEvent::new(FullyReadEventContent::new(event.to_owned()));
@@ -118,10 +118,10 @@ pub(crate) async fn create_receipt_route(
ClientIp(client_ip): ClientIp,
body: Ruma<create_receipt::v3::Request>,
) -> Result<create_receipt::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
services
.users
.update_device_last_seen(sender_user, body.identity.sender_device(), client_ip)
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
.await;
if matches!(
+2 -2
View File
@@ -17,10 +17,10 @@ pub(crate) async fn redact_event_route(
ClientIp(client_ip): ClientIp,
body: Ruma<redact_event::v3::Request>,
) -> Result<redact_event::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
services
.users
.update_device_last_seen(sender_user, body.identity.sender_device(), client_ip)
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
.await;
let body = &body.body;
if services.users.is_suspended(sender_user).await? {
+3 -3
View File
@@ -28,7 +28,7 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
paginate_relations_with_filter(
&services,
body.identity.sender_user(),
body.sender_user(),
&body.room_id,
&body.event_id,
body.event_type.clone().into(),
@@ -56,7 +56,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
) -> Result<get_relating_events_with_rel_type::v1::Response> {
paginate_relations_with_filter(
&services,
body.identity.sender_user(),
body.sender_user(),
&body.room_id,
&body.event_id,
None,
@@ -84,7 +84,7 @@ pub(crate) async fn get_relating_events_route(
) -> Result<get_relating_events::v1::Response> {
paginate_relations_with_filter(
&services,
body.identity.sender_user(),
body.sender_user(),
&body.room_id,
&body.event_id,
None,
+10 -10
View File
@@ -36,7 +36,7 @@ pub(crate) async fn report_room_route(
ClientIp(client): ClientIp,
body: Ruma<report_room::v3::Request>,
) -> Result<report_room::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -92,7 +92,7 @@ pub(crate) async fn report_event_route(
body: Ruma<report_content::v3::Request>,
) -> Result<report_content::v3::Response> {
// user authentication
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -135,8 +135,8 @@ pub(crate) async fn report_user_route(
ClientIp(client): ClientIp,
body: Ruma<report_user::v3::Request>,
) -> Result<report_user::v3::Response> {
let sender_user = body.identity.sender_user();
// user authentication
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
@@ -219,14 +219,14 @@ async fn is_event_report_valid(
fn build_report(report: Report) -> RoomMessageEventContent {
let mut text =
format!("@room New {} report received from {}:\n\n", report.report_type, report.sender);
if let Some(user_id) = report.user_id {
let _ = writeln!(text, "- Reported User ID: `{user_id}`");
if report.user_id.is_some() {
let _ = writeln!(text, "- Reported User ID: `{}`", report.user_id.unwrap());
}
if let Some(room_id) = report.room_id {
let _ = writeln!(text, "- Reported Room ID: `{room_id}`");
if report.room_id.is_some() {
let _ = writeln!(text, "- Reported Room ID: `{}`", report.room_id.unwrap());
}
if let Some(event_id) = report.event_id {
let _ = writeln!(text, "- Reported Event ID: `{event_id}`");
if report.event_id.is_some() {
let _ = writeln!(text, "- Reported Event ID: `{}`", report.event_id.unwrap());
}
let _ = writeln!(text, "- Report Reason: {}", report.reason);
+1 -1
View File
@@ -15,7 +15,7 @@ pub(crate) async fn get_room_aliases_route(
State(services): State<crate::State>,
body: Ruma<aliases::v3::Request>,
) -> Result<aliases::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services
.rooms
+36 -83
View File
@@ -10,7 +10,7 @@ use conduwuit_service::{Services, appservice::RegistrationInfo};
use futures::FutureExt;
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, Int, MilliSecondsSinceUnixEpoch, OwnedRoomAliasId,
OwnedUserId, RoomAliasId, RoomId, RoomVersionId, UserId,
OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomVersionId, UserId,
api::client::room::{self, create_room},
assign,
events::{
@@ -24,7 +24,6 @@ use ruma::{
member::{MembershipState, RoomMemberEventContent},
name::RoomNameEventContent,
power_levels::RoomPowerLevelsEventContent,
server_acl::RoomServerAclEventContent,
topic::RoomTopicEventContent,
},
},
@@ -61,10 +60,10 @@ pub(crate) async fn create_room_route(
) -> Result<create_room::v3::Response> {
use create_room::v3::RoomPreset;
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services.globals.allow_room_creation()
&& !body.identity.is_appservice()
&& body.appservice_info.is_none()
&& !services.users.is_admin(sender_user).await
{
return Err!(Request(Forbidden("Room creation has been disabled.",)));
@@ -87,41 +86,25 @@ pub(crate) async fn create_room_route(
};
let room_version_rules = room_version.rules().unwrap();
// For custom room IDs, if the user is creating a room with a v1 room ID format,
// we can just use that ID directly. However, if it's a custom *v2* room ID, we
// need to make sure that we don't generate one, which would in turn trick us
// into generating invalid v2 room events.
//
// expect_room_id is the custom room ID that the user is expecting - for v2
// formatted rooms, we check that the m.room.create event's generated room ID
// exactly matches this, and abort if it doesn't. Otherwise, we use it as the
// room ID itself.
let expect_room_id = {
let body_ref = body.json_body.as_ref().unwrap();
if let Some(CanonicalJsonValue::String(room_id)) = body_ref
.get("fi.mau.room_id")
.or_else(|| body_ref.get("room_id"))
{
Some(
RoomId::parse(room_id)
.map_err(|e| err!(Request(BadJson("Malformed custom room ID: {e}"))))?,
)
} else {
None
}
};
let room_id = match room_version_rules.room_id_format {
| RoomIdFormatVersion::V1 => Some(
expect_room_id
.clone()
.unwrap_or_else(|| RoomId::new_v1(services.globals.server_name())),
),
let room_id: Option<OwnedRoomId> = match room_version_rules.room_id_format {
| RoomIdFormatVersion::V1 => {
// Check for custom room ID field
if let Some(CanonicalJsonValue::String(room_id)) =
body.json_body.as_ref().unwrap().get("room_id")
{
Some(
RoomId::parse(room_id)
.map_err(|_| err!(Request(BadJson("Malformed custom room ID"))))?,
)
} else {
Some(RoomId::new_v1(services.globals.server_name()))
}
},
| _ => None,
};
// check if room ID doesn't already exist instead of erroring on auth check
if let Some(room_id) = room_id.as_ref().or(expect_room_id.as_ref()) {
if let Some(ref room_id) = room_id {
if services.rooms.short.get_shortroomid(room_id).await.is_ok() {
return Err!(Request(RoomInUse("Room with that custom room ID already exists",)));
}
@@ -130,7 +113,7 @@ pub(crate) async fn create_room_route(
if body.visibility == room::Visibility::Public
&& services.server.config.lockdown_public_room_directory
&& !services.users.is_admin(sender_user).await
&& !body.identity.is_appservice()
&& body.appservice_info.is_none()
{
warn!(
"Non-admin user {sender_user} tried to publish {room_id:?} to the room directory \
@@ -186,7 +169,7 @@ pub(crate) async fn create_room_route(
let alias: Option<OwnedRoomAliasId> = match body.room_alias_name.as_ref() {
| Some(alias) =>
Some(room_alias_check(&services, alias, body.identity.appservice_info()).await?),
Some(room_alias_check(&services, alias, body.appservice_info.as_ref()).await?),
| _ => None,
};
@@ -260,16 +243,15 @@ pub(crate) async fn create_room_route(
// Allow requesters to override the `origin_server_ts` to customize room ids
// from v12 onwards
let custom_origin_server_ts = {
let body_ref = body.json_body.as_ref().unwrap();
body_ref
.get("origin_server_ts")
.or_else(|| body_ref.get("fi.mau.origin_server_ts"))
.and_then(CanonicalJsonValue::as_integer)
.map(Into::into)
.and_then(|value: i64| value.try_into().ok())
.map(MilliSecondsSinceUnixEpoch)
};
let custom_origin_server_ts = body
.json_body
.as_ref()
.unwrap()
.get("origin_server_ts")
.and_then(CanonicalJsonValue::as_integer)
.map(Into::into)
.and_then(|value: i64| value.try_into().ok())
.map(MilliSecondsSinceUnixEpoch);
let create_event_id = services
.rooms
@@ -299,13 +281,6 @@ pub(crate) async fn create_room_route(
};
drop(state_lock);
debug!("Room created with ID {room_id}");
if let Some(expected_room_id) = expect_room_id
&& expected_room_id != room_id
{
return Err!(BadServerResponse(
"Room's final room ID was {room_id}, but expected {expected_room_id}"
));
}
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
// 2. Let the room creator join
@@ -478,32 +453,7 @@ pub(crate) async fn create_room_route(
.boxed()
.await?;
// 6. Initial state events provided by the homeserver
let mut server_initial_state: Vec<PartialPdu> = Vec::new();
if let Some(allow_list) = services.server.config.default_room_acl_allow.clone() {
server_initial_state.push(PartialPdu::state(
String::new(),
&RoomServerAclEventContent::new(true, allow_list, vec![]),
));
} else if let Some(deny_list) = services.server.config.default_room_acl_deny.clone() {
server_initial_state.push(PartialPdu::state(
String::new(),
&RoomServerAclEventContent::new(true, vec!["*".to_owned()], deny_list),
));
}
for pdu in server_initial_state {
services
.rooms
.timeline
.build_and_append_pdu(pdu, sender_user, Some(&room_id), &state_lock)
.boxed()
.await?;
}
// 7. Events listed in initial_state
// 6. Events listed in initial_state
for event in &body.initial_state {
let mut partial_pdu = event
.deserialize_as_unchecked::<PartialPdu>()
@@ -531,7 +481,7 @@ pub(crate) async fn create_room_route(
.await?;
}
// 8. Events implied by name and topic
// 7. Events implied by name and topic
if let Some(name) = &body.name {
services
.rooms
@@ -560,7 +510,7 @@ pub(crate) async fn create_room_route(
.await?;
}
// 9. Events implied by invite (and TODO: invite_3pid)
// 8. Events implied by invite (and TODO: invite_3pid)
drop(state_lock);
for recipient_user in &invitees {
if let Err(e) =
@@ -586,7 +536,10 @@ pub(crate) async fn create_room_route(
if services.server.config.admin_room_notices {
services
.admin
.send_text(&format!("{sender_user} made {room_id} public to the room directory"))
.send_text(&format!(
"{sender_user} made {} public to the room directory",
&room_id
))
.await;
}
info!("{sender_user} made {0} public to the room directory", &room_id);
+4 -4
View File
@@ -24,25 +24,25 @@ pub(crate) async fn get_room_event_route(
let visible = services
.rooms
.state_accessor
.user_can_see_event(body.identity.sender_user(), room_id, event_id)
.user_can_see_event(body.sender_user(), room_id, event_id)
.map(Ok);
let (mut event, visible) = try_join(event, visible).await?;
if !visible || is_ignored_pdu(services, &event, body.identity.sender_user()).await? {
if !visible || is_ignored_pdu(services, &event, body.sender_user()).await? {
return Err!(Request(Forbidden("You don't have permission to view this event.")));
}
if let Err(e) = services
.rooms
.pdu_metadata
.add_bundled_aggregations_to_pdu(body.identity.sender_user(), &mut event)
.add_bundled_aggregations_to_pdu(body.sender_user(), &mut event)
.await
{
debug_warn!("Failed to add bundled aggregations to event: {e}");
}
event.set_unsigned(Some(body.identity.sender_user()));
event.set_unsigned(body.sender_user.as_deref());
Ok(get_room_event::v3::Response::new(event.into_format()))
}
+12 -10
View File
@@ -22,7 +22,7 @@ pub(crate) async fn room_initial_sync_route(
if !services
.rooms
.state_accessor
.user_can_see_state_events(body.identity.sender_user(), room_id)
.user_can_see_state_events(body.sender_user(), room_id)
.await
{
return Err!(Request(Forbidden("No room preview available.")));
@@ -31,7 +31,7 @@ pub(crate) async fn room_initial_sync_route(
let membership = services
.rooms
.state_cache
.user_membership(body.identity.sender_user(), room_id)
.user_membership(body.sender_user(), room_id)
.map(Ok);
let visibility = services.rooms.directory.visibility(room_id).map(Ok);
@@ -52,14 +52,16 @@ pub(crate) async fn room_initial_sync_route(
.pdus_rev(room_id, None)
.try_take(limit)
.and_then(async |mut pdu| {
pdu.1.set_unsigned(Some(body.identity.sender_user()));
if let Err(e) = services
.rooms
.pdu_metadata
.add_bundled_aggregations_to_pdu(body.identity.sender_user(), &mut pdu.1)
.await
{
debug_warn!("Failed to add bundled aggregations: {e}");
pdu.1.set_unsigned(body.sender_user.as_deref());
if let Some(sender_user) = body.sender_user.as_deref() {
if let Err(e) = services
.rooms
.pdu_metadata
.add_bundled_aggregations_to_pdu(sender_user, &mut pdu.1)
.await
{
debug_warn!("Failed to add bundled aggregations: {e}");
}
}
Ok(pdu)
})
+2 -6
View File
@@ -4,7 +4,7 @@ use conduwuit::{Err, Result};
use ruma::api::client::room::get_summary;
use service::rooms::summary::Accessibility;
use crate::{Ruma, router::ClientIdentity};
use crate::Ruma;
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
///
@@ -28,11 +28,7 @@ pub(crate) async fn get_room_summary(
let summary = services
.rooms
.summary
.get_room_summary_for_user(
body.identity.as_ref().map(ClientIdentity::sender_user),
&room_id,
&servers,
)
.get_room_summary_for_user(body.sender_user.as_deref(), &room_id, &servers)
.await?;
match summary {
+272 -420
View File
@@ -2,267 +2,47 @@ use std::cmp::max;
use axum::extract::State;
use conduwuit::{
Err, Error, Event, Result, debug,
debug::DebugInspect,
err, error,
info::room_version::UNSTABLE_ROOM_VERSIONS,
Err, Error, Event, Result, debug, err, info,
matrix::{StateKey, pdu::PartialPdu},
};
use futures::{FutureExt, StreamExt};
use ruma::{
OwnedEventId, OwnedRoomId, RoomId, UserId,
CanonicalJsonObject, RoomId, RoomVersionId,
api::{client::room::upgrade_room, error::ErrorKind},
assign,
events::{
StateEventType,
StateEventType, TimelineEventType,
room::{
create::{PreviousRoom, RoomCreateEventContent},
create::PreviousRoom,
member::{MembershipState, RoomMemberEventContent},
power_levels::RoomPowerLevelsEventContent,
tombstone::RoomTombstoneEventContent,
},
space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
space::child::{RedactedSpaceChildEventContent, SpaceChildEventContent},
},
int,
room_version_rules::RoomIdFormatVersion,
};
use serde_json::value::to_raw_value;
use serde_json::{json, value::to_raw_value};
use crate::router::Ruma;
/// Recommended transferable state events list from the spec
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 11] = &[
StateEventType::RoomServerAcl,
StateEventType::RoomEncryption,
StateEventType::RoomName,
StateEventType::RoomAvatar,
StateEventType::RoomTopic,
StateEventType::RoomEncryption,
StateEventType::RoomGuestAccess,
StateEventType::RoomHistoryVisibility,
StateEventType::RoomJoinRules,
StateEventType::RoomName,
StateEventType::RoomPowerLevels,
StateEventType::SpaceParent,
StateEventType::RoomServerAcl,
StateEventType::RoomTopic,
// Not explicitly recommended in spec, but very useful.
StateEventType::SpaceChild,
StateEventType::SpaceParent, // TODO: m.room.policy?
];
/// Updates spaces that are marked as parents of old_room_id to instead point to
/// the new room ID.
///
/// See: https://github.com/matrix-org/matrix-spec-proposals/pull/4168
async fn update_parents(
services: &crate::State,
sender: &UserId,
old_room_id: &RoomId,
new_room_id: &RoomId,
) -> Result {
// Fetch the spaces which this room claims are its parents.
// In rooms that reference the old room via m.space.child events...
let parents = services
.rooms
.state_accessor
.room_state_keys(old_room_id, &StateEventType::SpaceParent)
.await
.debug_inspect(|k| debug!(?old_room_id, "Parents: {k:?}"))?;
for raw_parent_id in parents {
let parent_id = RoomId::parse(&raw_parent_id)?;
if !services
.rooms
.state_cache
.is_joined(sender, &parent_id)
.await
{
debug!(%parent_id, "Skipping space as sender is not joined");
continue; // Skip updating rooms the sender isn't in.
}
let state_lock = services.rooms.state.mutex.lock(parent_id.as_str()).await;
// We're now fetching state from the *space* that has the old room as a *child*.
// Follow along. This will be on the test.
let Ok(child) = services
.rooms
.state_accessor
.room_state_get_content::<SpaceChildEventContent>(
&parent_id,
&StateEventType::SpaceChild,
old_room_id.as_str(),
)
.await
.debug_inspect_err(|e| {
error!(
?parent_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to fetch m.space.child from parent"
);
})
else {
// If the space does not have a child event for this room, we can skip it
continue;
};
// ...the upgrading server SHOULD send a new m.space.child event with state_key
// set to the new room's ID, copying the order and suggested fields from the
// content of the m.space.child with state_key of the previous room ID.
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
new_room_id.as_str(),
&assign!(
SpaceChildEventContent::new(vec![sender.server_name().to_owned()]),
{
order: child.order,
suggested: child.suggested,
}
),
),
sender,
Some(&parent_id),
&state_lock,
)
.boxed()
.await
.debug_inspect_err(|e| {
error!(
?parent_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to send m.space.child to parent during room upgrade"
);
})
.ok();
drop(state_lock);
}
Ok(())
}
/// If the room being upgraded is a space, replace all m.space.parent references
/// in its children to point at the newly upgraded room ID, so that they point
/// at the new space.
///
/// See: https://github.com/matrix-org/matrix-spec-proposals/pull/4168
async fn update_children(
services: &crate::State,
sender: &UserId,
old_room_id: &RoomId,
new_room_id: &RoomId,
) -> Result {
// Fetch the children of this space.
// Note that this might not actually be a space, but just a room that has
// children.
// In rooms that reference the old room via m.space.parent events...
// NOTE: Doing that would be expensive. We'll instead fetch rooms which the
// space claims are children.
let parents = services
.rooms
.state_accessor
.room_state_keys(old_room_id, &StateEventType::SpaceChild)
.await
.debug_inspect(|k| debug!(?old_room_id, "Children: {k:?}"))?;
for raw_child_id in parents {
let child_id = RoomId::parse(&raw_child_id)?;
if !services
.rooms
.state_cache
.is_joined(sender, &child_id)
.await
{
debug!(%child_id, "Skipping child room as sender is not joined");
continue;
}
let state_lock = services.rooms.state.mutex.lock(child_id.as_str()).await;
// We're now fetching state from the *child* that has the old space as a
// *parent*. Follow along. This will also be on the test.
let Ok(ref parent) = services
.rooms
.state_accessor
.room_state_get_content::<SpaceParentEventContent>(
&child_id,
&StateEventType::SpaceParent,
old_room_id.as_str(),
)
.await
.debug_inspect_err(|e| {
error!(
?child_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to fetch m.space.parent from child"
);
})
else {
// If the child does not have a parent event for this room, we can skip it.
continue;
};
// ... the upgrading server SHOULD send a new m.space.parent event with
// state_key set to the new room's ID.
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
new_room_id.as_str(),
&assign!(SpaceParentEventContent::new(vec![sender.server_name().to_owned()]), { canonical: parent.canonical }),
),
sender,
Some(&child_id),
&state_lock,
)
.boxed()
.await
.debug_inspect_err(|e| error!(
child_id=?child_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to send updated m.space.parent to child during room upgrade"
))
.ok();
// If the previous m.space.parent event has canonical set to true in content,
// homeservers SHOULD update the old state event to set canonical to false,
// while setting it to true in the newly-sent m.space.parent event.
if parent.canonical {
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
old_room_id.as_str(),
&assign!(parent.clone(), {canonical: false}),
),
sender,
Some(&child_id),
&state_lock,
)
.boxed()
.await
.debug_inspect_err(|e| {
error!(
child_id=?child_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to send non-canonical m.space.parent to child room"
);
})
.ok();
}
drop(state_lock);
}
Ok(())
}
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
///
/// Upgrades the room.
@@ -277,14 +57,10 @@ pub(crate) async fn upgrade_room_route(
State(services): State<crate::State>,
body: Ruma<upgrade_room::v3::Request>,
) -> Result<upgrade_room::v3::Response> {
let sender_user = body.identity.sender_user();
// TODO[v12]: Handle additional creators
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let (supported, forbid_unstable, is_unstable) = (
services.server.supported_room_version(&body.new_version),
!services.config.allow_unstable_room_versions,
UNSTABLE_ROOM_VERSIONS.contains(&body.new_version),
);
if !supported || (forbid_unstable && is_unstable) {
if !services.server.supported_room_version(&body.new_version) {
return Err(Error::BadRequest(
ErrorKind::UnsupportedRoomVersion,
"This server does not support that room version.",
@@ -301,15 +77,17 @@ pub(crate) async fn upgrade_room_route(
return Err!(Request(Forbidden("Upgrading the admin room this way is not allowed.")));
}
// 1. Check that the user has permission to send m.room.tombstone events in the
// room.
// First, check if the user has permission to upgrade the room (send tombstone
// event)
let old_room_state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
// Check tombstone permission by attempting to create (but not send) the event.
services
// Check tombstone permission by attempting to create (but not send) the event
// Note that this does internally call the policy server with a fake room ID,
// which may not be good?
let tombstone_test_result = services
.rooms
.timeline
.create_event(
.create_hash_and_sign_event(
PartialPdu::state(
StateKey::new(),
&RoomTombstoneEventContent::new(
@@ -321,104 +99,157 @@ pub(crate) async fn upgrade_room_route(
Some(&body.room_id),
&old_room_state_lock,
)
.await
.map_err(|_| {
err!(Request(Forbidden("You do not have permission to upgrade this room.")))
})?;
.await;
if let Err(_e) = tombstone_test_result {
return Err!(Request(Forbidden("User does not have permission to upgrade this room.")));
}
drop(old_room_state_lock);
// Create a replacement room
let new_version_rules = body
let room_version_rules = body
.new_version
.rules()
.expect("new room version should have defined rules");
let last_event = if new_version_rules
.authorization
.room_create_event_id_as_room_id
{
None
let replacement_room_owned = if room_version_rules.room_id_format == RoomIdFormatVersion::V2 {
Some(RoomId::new_v1(services.globals.server_name()))
} else {
Some(
services
.rooms
.state
.get_forward_extremities(&body.room_id)
.collect::<Vec<OwnedEventId>>()
.await[0]
.clone(),
)
None
};
let old_create_event: RoomCreateEventContent = services
let replacement_room: Option<&RoomId> = replacement_room_owned.as_ref().map(AsRef::as_ref);
let replacement_room_tmp = match replacement_room {
| Some(v) => v,
| None => &RoomId::new_v1(services.globals.server_name()),
};
let _short_id = services
.rooms
.short
.get_or_create_shortroomid(replacement_room_tmp)
.await;
// For pre-v12 rooms, send tombstone before creating replacement room
let tombstone_event_id = if room_version_rules.room_id_format != RoomIdFormatVersion::V2 {
let state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
// Send a m.room.tombstone event to the old room to indicate that it is not
// intended to be used any further
let tombstone_event_id = services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
StateKey::new(),
&RoomTombstoneEventContent::new(
"This room has been replaced".to_owned(),
replacement_room.unwrap().to_owned(),
),
),
sender_user,
Some(&body.room_id),
&state_lock,
)
.boxed()
.await?;
// Change lock to replacement room
drop(state_lock);
Some(tombstone_event_id)
} else {
None
};
let state_lock = services
.rooms
.state
.mutex
.lock(replacement_room_tmp.as_str())
.await;
// Get the old room creation event
let mut create_event_content: CanonicalJsonObject = services
.rooms
.state_accessor
.room_state_get_content(&body.room_id, &StateEventType::RoomCreate, "")
.await
.map_err(|_| err!(Database("Found room without m.room.create event.")))?;
let create_event_content = if new_version_rules.authorization.use_room_create_sender {
RoomCreateEventContent::new_v1(sender_user.to_owned())
} else {
RoomCreateEventContent::new_v11()
};
#[allow(deprecated)]
let create_event_content = {
assign!(
create_event_content,
{
additional_creators: if new_version_rules.authorization.additional_room_creators {
body.additional_creators.clone()
} else { Vec::new() },
creator: if new_version_rules.authorization.use_room_create_sender {
None
} else { Some(sender_user.to_owned()) },
predecessor: Some(assign!(PreviousRoom::new(body.room_id.clone()), {
event_id: last_event,
})),
room_type: old_create_event.room_type.clone(),
room_version: body.new_version.clone(),
}
)
// Use the m.room.tombstone event as the predecessor
let predecessor = {
#[allow(deprecated, reason = "Clients still use event_id even though it's deprecated")]
Some(assign!(PreviousRoom::new(body.room_id.clone()), {
event_id: tombstone_event_id,
}))
};
let replacement_room_id: Option<OwnedRoomId> =
if new_version_rules.room_id_format == RoomIdFormatVersion::V2 {
None
} else {
Some(RoomId::new_v1(services.globals.server_name()))
};
// Send a m.room.create event containing a predecessor field and the applicable
// room_version
{
use RoomVersionId::*;
match body.new_version {
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
create_event_content.insert(
"creator".into(),
json!(&sender_user).try_into().map_err(|e| {
info!("Error forming creation event: {e}");
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
})?,
);
},
| _ => {
// "creator" key no longer exists in V11 rooms
create_event_content.remove("creator");
},
// TODO(hydra): additional_creators
}
}
create_event_content.insert(
"room_version".into(),
json!(&body.new_version)
.try_into()
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
);
create_event_content.insert(
"predecessor".into(),
json!(predecessor)
.try_into()
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
);
// Validate creation event content
if serde_json::from_str::<CanonicalJsonObject>(
to_raw_value(&create_event_content)
.expect("Error forming creation event")
.get(),
)
.is_err()
{
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
}
let new_room_state_lock = if let Some(new_room_id) = replacement_room_id.as_ref() {
services.rooms.state.mutex.lock(new_room_id.as_str()).await
} else {
// NOTE: Using a hardcoded room ID for the temporary mutex means only one room
// can be created at a time. This is actually beneficial, as it reduces the
// risk of concurrent in-flight collisions.
services.rooms.state.mutex.lock("!new-room").await
};
debug!("Upgrading {} to room version {}", &body.room_id, &body.new_version);
let create_event_id = services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(StateKey::new(), &create_event_content),
PartialPdu {
event_type: TimelineEventType::RoomCreate,
content: to_raw_value(&create_event_content)
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some(StateKey::new()),
redacts: None,
timestamp: None,
},
sender_user,
replacement_room_id.as_deref(),
&new_room_state_lock,
replacement_room,
&state_lock,
)
.boxed()
.await?;
drop(new_room_state_lock);
// re-acquire a new lock with the new room ID.
// We don't actually need a state lock for sending the m.room.create event, but
// we get one anyway because the function requires it and I can't be bothered
// refactoring it.
let (replacement_room_id, new_room_state_lock) =
if new_version_rules.room_id_format == RoomIdFormatVersion::V2 {
let parsed_room_id = RoomId::new_v2(
create_event_id
.as_str()
.strip_prefix("$")
.expect("event ID must start with $ sigil"),
)?;
let create_id = create_event_id.as_str().replace('$', "!");
let (replacement_room, state_lock) =
if room_version_rules.room_id_format == RoomIdFormatVersion::V2 {
let parsed_room_id = RoomId::parse(&create_id)?;
let lock = services
.rooms
.state
@@ -427,13 +258,9 @@ pub(crate) async fn upgrade_room_route(
.await;
(Some(parsed_room_id), lock)
} else {
let new_room_id =
replacement_room_id.expect("replacement room id should be known by now");
let lock = services.rooms.state.mutex.lock(new_room_id.as_str()).await;
(Some(new_room_id), lock)
(replacement_room.map(ToOwned::to_owned), state_lock)
};
debug!("Upgraded {} to {}", &body.room_id, replacement_room_id.as_deref().unwrap());
// Join the new room
services
.rooms
@@ -447,13 +274,13 @@ pub(crate) async fn upgrade_room_route(
}),
),
sender_user,
replacement_room_id.as_deref(),
&new_room_state_lock,
replacement_room.as_deref(),
&state_lock,
)
.boxed()
.await?;
// 3. Replicate transferable state events to the new room
// Replicate transferable state events to the new room
for event_type in TRANSFERABLE_STATE_EVENTS {
let state_keys = services
.rooms
@@ -470,45 +297,26 @@ pub(crate) async fn upgrade_room_route(
| Ok(v) => v.content().to_owned(),
| Err(_) => continue, // Skipping missing events.
};
if event_content.get() == "{}" {
// If the event content is empty, we skip it
continue;
}
// If this is a power levels event, and the new room version has creators,
// we need to make sure they dont appear in the users block of power levels.
if *event_type == StateEventType::RoomPowerLevels {
let creators = body
.additional_creators
.clone()
.iter()
.chain(std::iter::once(&sender_user.to_owned()))
.map(ToOwned::to_owned)
.collect::<Vec<_>>();
// TODO(v12): additional creators
let creators = vec![sender_user];
let mut power_levels_event_content: RoomPowerLevelsEventContent =
serde_json::from_str(event_content.get()).map_err(|_| {
err!(Request(BadJson("Power levels event content is not valid")))
})?;
for creator in creators {
if new_version_rules
.authorization
.explicitly_privilege_room_creators
{
power_levels_event_content.users.remove(&creator);
} else {
power_levels_event_content.users.insert(
creator.clone(),
max(
int!(100),
power_levels_event_content
.users
.get(&creator)
.copied()
.unwrap_or_default(),
),
);
}
power_levels_event_content.users.remove(creator);
}
event_content = to_raw_value(&power_levels_event_content)
.expect("event is valid, we just deserialized and modified it");
}
debug!(%event_type, ?state_key, "Transferring state event to new room");
services
.rooms
.timeline
@@ -520,15 +328,15 @@ pub(crate) async fn upgrade_room_route(
..Default::default()
},
sender_user,
replacement_room_id.as_deref(),
&new_room_state_lock,
replacement_room.as_deref(),
&state_lock,
)
.boxed()
.await?;
}
}
// 4. Move any local aliases to the new room
// Moves any local aliases to the new room
let mut local_aliases = services
.rooms
.alias
@@ -536,7 +344,6 @@ pub(crate) async fn upgrade_room_route(
.boxed();
while let Some(alias) = local_aliases.next().await {
debug!(?alias, "Migrating alias");
services
.rooms
.alias
@@ -545,31 +352,11 @@ pub(crate) async fn upgrade_room_route(
services.rooms.alias.set_alias(
&alias,
replacement_room_id.as_deref().unwrap(),
replacement_room.as_ref().unwrap(),
sender_user,
)?;
}
// 5. Send a `m.room.tombstone` event to the old room to indicate that it is not
// intended to be used any further.
debug!(target=?body.room_id, "Sending tombstone to old room");
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
StateKey::new(),
&RoomTombstoneEventContent::new(
"This room has been replaced".to_owned(),
replacement_room_id.clone().unwrap(),
),
),
sender_user,
Some(&body.room_id),
&old_room_state_lock,
)
.await?;
// Get the old room power levels
let mut power_levels = services
.rooms
@@ -591,10 +378,8 @@ pub(crate) async fn upgrade_room_route(
power_levels.events_default = new_level;
power_levels.invite = new_level;
// 6. Modify the power levels in the old room to prevent sending of events and
// Modify the power levels in the old room to prevent sending of events and
// inviting new users
// Spec dictates that this is allowed to fail.
debug!(target=?body.room_id, ?new_level, "Raising power level in old room to lock it");
services
.rooms
.timeline
@@ -605,50 +390,117 @@ pub(crate) async fn upgrade_room_route(
),
sender_user,
Some(&body.room_id),
&old_room_state_lock,
&state_lock,
)
.boxed()
.await
.ok();
.await?;
// MSC4168: Update spaces that reference this room to point at the new room.
debug!("Updating parent spaces");
update_parents(
&services,
sender_user,
&body.room_id,
replacement_room_id.as_deref().unwrap(),
)
.await
.inspect_err(|e| {
error!(
old_room_id=?body.room_id,
new_room_id=?replacement_room_id.as_deref().unwrap(),
%e,
"failed to update parent spaces during room upgrade"
);
})
.ok();
drop(state_lock);
// MSC4168: Update child rooms to point at the new space, where possible
debug!("Updating space children");
update_children(
&services,
sender_user,
&body.room_id,
replacement_room_id.as_deref().unwrap(),
)
.await
.inspect_err(|e| {
error!(
old_room_id=?body.room_id,
new_room_id=?replacement_room_id.as_deref().unwrap(),
%e,
"failed to update space children during room upgrade"
// For v12 rooms, send tombstone AFTER creating replacement room
if room_version_rules.room_id_format == RoomIdFormatVersion::V2 {
let old_room_state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
// For v12 rooms, no event reference in predecessor due to cyclic dependency -
// could best effort one maybe?
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
StateKey::new(),
&RoomTombstoneEventContent::new(
"This room has been replaced".to_owned(),
replacement_room.as_ref().unwrap().to_owned(),
),
),
sender_user,
Some(&body.room_id),
&old_room_state_lock,
)
.await?;
drop(old_room_state_lock);
}
// Check if the old room has a space parent, and if so, whether we should update
// it (m.space.parent, room_id)
let parents = services
.rooms
.state_accessor
.room_state_keys(&body.room_id, &StateEventType::SpaceParent)
.await?;
for raw_space_id in parents {
let space_id = RoomId::parse(&raw_space_id)?;
let Ok(child) = services
.rooms
.state_accessor
.room_state_get_content::<SpaceChildEventContent>(
&space_id,
&StateEventType::SpaceChild,
body.room_id.as_str(),
)
.await
else {
// If the space does not have a child event for this room, we can skip it
continue;
};
debug!(
"Updating space {space_id} child event for room {} to {}",
&body.room_id,
replacement_room.as_ref().unwrap()
);
})
.ok();
// First, drop the space's child event
let state_lock = services.rooms.state.mutex.lock(space_id.as_str()).await;
debug!("Removing space child event for room {} in space {space_id}", &body.room_id);
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu {
event_type: StateEventType::SpaceChild.into(),
content: to_raw_value(&RedactedSpaceChildEventContent::new())
.expect("event is valid, we just created it"),
state_key: Some(body.room_id.clone().as_str().into()),
..Default::default()
},
sender_user,
Some(&space_id),
&state_lock,
)
.boxed()
.await
.ok();
// Now, add a new child event for the replacement room
debug!(
"Adding space child event for room {} in space {space_id}",
replacement_room.as_ref().unwrap()
);
services
.rooms
.timeline
.build_and_append_pdu(
PartialPdu::state(
replacement_room.as_ref().unwrap().as_str(),
&assign!(SpaceChildEventContent::new(vec![sender_user.server_name().to_owned()]), {
order: child.order,
suggested: child.suggested,
}),
),
sender_user,
Some(&space_id),
&state_lock,
)
.boxed()
.await
.ok();
debug!(
"Finished updating space {space_id} child event for room {} to {}",
&body.room_id,
replacement_room.as_ref().unwrap()
);
drop(state_lock);
}
// Return the replacement room id
Ok(upgrade_room::v3::Response::new(replacement_room_id.unwrap()))
Ok(upgrade_room::v3::Response::new(replacement_room.as_ref().unwrap().to_owned()))
}
+1 -1
View File
@@ -43,7 +43,7 @@ pub(crate) async fn search_events_route(
State(services): State<crate::State>,
body: Ruma<Request>,
) -> Result<Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let next_batch = body.next_batch.as_deref();
let mut result_categories = ResultCategories::new();
+5 -9
View File
@@ -22,16 +22,16 @@ pub(crate) async fn send_message_event_route(
ClientIp(client_ip): ClientIp,
body: Ruma<send_message_event::v3::Request>,
) -> Result<send_message_event::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.sender_device();
let sender_user = body.sender_user();
let sender_device = body.sender_device.as_deref();
let appservice_info = body.appservice_info.as_ref();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
services
.users
.update_device_last_seen(sender_user, sender_device, client_ip)
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
.await;
// Forbid m.room.encrypted if encryption is disabled
@@ -83,11 +83,7 @@ pub(crate) async fn send_message_event_route(
event_type: body.event_type.clone().into(),
content,
unsigned: Some(unsigned),
timestamp: if body.identity.is_appservice() {
body.timestamp
} else {
None
},
timestamp: appservice_info.and(body.timestamp),
..Default::default()
},
sender_user,
+4 -6
View File
@@ -146,7 +146,7 @@ pub(crate) async fn login_route(
}) => {
debug!("Got appservice login type");
let Some(ref info) = body.identity else {
let Some(ref info) = body.appservice_info else {
return Err!(Request(MissingToken("Missing appservice token.")));
};
@@ -254,7 +254,7 @@ pub(crate) async fn login_token_route(
return Err!(Request(Forbidden("Login via an existing session is not enabled")));
}
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
// Prompt the user to confirm with their password using UIAA
let _ = services
@@ -286,9 +286,7 @@ pub(crate) async fn logout_route(
ClientIp(client): ClientIp,
body: Ruma<logout::v3::Request>,
) -> Result<logout::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.expect_sender_device()?;
let (sender_user, sender_device) = body.sender();
services
.users
.remove_device(sender_user, sender_device)
@@ -334,7 +332,7 @@ pub(crate) async fn logout_all_route(
ClientIp(client): ClientIp,
body: Ruma<logout_all::v3::Request>,
) -> Result<logout_all::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
services
.users
.all_device_ids(sender_user)
+1 -1
View File
@@ -27,7 +27,7 @@ pub(crate) async fn get_hierarchy_route(
.rooms
.summary
.get_room_hierarchy_for_user(
body.identity.sender_user(),
body.sender_user(),
body.room_id.clone(),
max_depth,
body.suggested_only,
+5 -5
View File
@@ -38,10 +38,10 @@ pub(crate) async fn send_state_event_for_key_route(
ClientIp(ip): ClientIp,
body: Ruma<send_state_event::v3::Request>,
) -> Result<send_state_event::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
services
.users
.update_device_last_seen(sender_user, body.identity.sender_device(), ip)
.update_device_last_seen(sender_user, body.sender_device.as_deref(), ip)
.await;
if services.users.is_suspended(sender_user).await? {
@@ -55,7 +55,7 @@ pub(crate) async fn send_state_event_for_key_route(
&body.event_type,
&body.body.body,
&body.state_key,
if body.identity.is_appservice() {
if body.appservice_info.is_some() {
body.timestamp
} else {
None
@@ -91,7 +91,7 @@ pub(crate) async fn get_state_events_route(
State(services): State<crate::State>,
body: Ruma<get_state_events::v3::Request>,
) -> Result<get_state_events::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services
.rooms
@@ -125,7 +125,7 @@ pub(crate) async fn get_state_event_for_key_route(
State(services): State<crate::State>,
body: Ruma<get_state_event_for_key::v3::Request>,
) -> Result<get_state_event_for_key::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
if !services
.rooms
-7
View File
@@ -48,13 +48,6 @@ async fn load_timeline(
ending_count: Option<PduCount>,
limit: usize,
) -> Result<TimelinePdus> {
if let (Some(starting_count), Some(ending_count)) = (starting_count, ending_count) {
debug_assert!(
starting_count <= ending_count,
"starting count {starting_count} > ending count {ending_count}"
);
}
let mut pdu_stream = match starting_count {
| Some(starting_count) => {
let last_timeline_count = services
+73 -76
View File
@@ -38,7 +38,6 @@ use ruma::{
uint,
};
use service::{account_data::AnyRawAccountDataEvent, rooms::short::ShortStateHash};
use tokio::pin;
use super::{load_timeline, share_encrypted_room};
use crate::client::{
@@ -97,19 +96,12 @@ pub(super) async fn load_joined_room(
);
}
let state_events =
StateEvents::with_events(state_events.into_iter().map(Event::into_format).collect());
let joined_room = assign!(JoinedRoom::new(), {
account_data,
summary: summary.unwrap_or_default(),
unread_notifications: notification_counts.unwrap_or_default(),
timeline,
state: if sync_context.use_state_after {
RoomState::After(state_events)
} else {
RoomState::Before(state_events)
},
state: RoomState::Before(StateEvents::with_events(state_events.into_iter().map(Event::into_format).collect())),
ephemeral,
unread_thread_notifications: BTreeMap::new(),
});
@@ -352,7 +344,7 @@ struct ShortStateHashes {
#[tracing::instrument(level = "debug", skip_all)]
async fn fetch_shortstatehashes(
services: &Services,
SyncContext { last_sync_end_count, .. }: SyncContext<'_>,
SyncContext { last_sync_end_count, current_count, .. }: SyncContext<'_>,
room_id: &RoomId,
) -> Result<ShortStateHashes> {
// the room state currently.
@@ -362,41 +354,46 @@ async fn fetch_shortstatehashes(
.rooms
.state
.get_room_shortstatehash(room_id)
.map_err(|_| err!(Database(error!("Room {room_id} has no state"))))
.await?;
.map_err(|_| err!(Database(error!("Room {room_id} has no state"))));
// The room state as of the end of the last sync.
// This will be None if we are doing an initial sync.
// the room state as of the end of the last sync.
// this will be None if we are doing an initial sync or if we just joined this
// room.
let last_sync_end_shortstatehash =
OptionFuture::from(last_sync_end_count.map(async |last_sync_end_count| {
pin! {
let pdus = services
.rooms
.timeline
.pdus(room_id, Some(PduCount::Normal(last_sync_end_count)))
.ignore_err();
}
match pdus.next().await {
| Some((_, pdu_after_last_sync_end)) => {
trace!(?pdu_after_last_sync_end.event_id, "pdu at last sync end");
services
.rooms
.state_accessor
.pdu_shortstatehash(&pdu_after_last_sync_end.event_id)
.await
.map_err(|err| err!("Last sync end PDU has no shortstatehash: {err}"))
},
| None => {
// No events have been sent since the last sync, or we just joined this room,
// so the state then is the same as the state now
Ok(current_shortstatehash)
},
}
OptionFuture::from(last_sync_end_count.map(|last_sync_end_count| {
// look up the shortstatehash saved by the last sync's call to
// `associate_token_shortstatehash`
services
.rooms
.user
.get_token_shortstatehash(room_id, last_sync_end_count)
.inspect_err(move |_| {
debug_warn!(
token = last_sync_end_count,
"Room has no shortstatehash for this token"
);
})
.ok()
}))
.await
.transpose()?;
.map(Option::flatten)
.map(Ok);
let (current_shortstatehash, last_sync_end_shortstatehash) =
try_join(current_shortstatehash, last_sync_end_shortstatehash).await?;
/*
associate the `current_count` with the `current_shortstatehash`, so we can
use it on the next sync as the `last_sync_end_shortstatehash`.
TODO: the table written to by this call grows extremely fast, gaining one new entry for each
joined room on _every single sync request_. we need to find a better way to remember the shortstatehash
between syncs.
*/
services
.rooms
.user
.associate_token_shortstatehash(room_id, current_count, current_shortstatehash)
.await;
Ok(ShortStateHashes {
current_shortstatehash,
@@ -455,7 +452,6 @@ async fn build_state_events(
syncing_user,
last_sync_end_count,
full_state,
use_state_after,
..
} = sync_context;
@@ -464,28 +460,32 @@ async fn build_state_events(
last_sync_end_shortstatehash,
} = shortstatehashes;
let timeline_start_shortstatehash = if let Some((count, pdu)) = timeline.pdus.front() {
if matches!(count, PduCount::Backfilled(_)) {
// We don't have shortstatehashes for backfilled PDUs, the best we can
// do is to use the current state
current_shortstatehash
} else {
services
// the spec states that the `state` property only includes state events up to
// the beginning of the timeline, so we determine the state of the syncing room
// as of the first timeline event. NOTE: this explanation is not entirely
// accurate; see the implementation of `build_state_incremental`.
let timeline_start_shortstatehash = async {
if let Some((_, pdu)) = timeline.pdus.front() {
if let Ok(shortstatehash) = services
.rooms
.state_accessor
.pdu_shortstatehash(&pdu.event_id)
.await
.map_err(|err| err!("Timeline start has no shortstatehash: {err}"))?
{
return shortstatehash;
}
}
} else {
// if the timeline is empty there can't possibly be any changes to the state
return Ok(vec![]);
current_shortstatehash
};
// the user IDs of members whose membership needs to be sent to the client, if
// lazy-loading is enabled.
let lazily_loaded_members =
prepare_lazily_loaded_members(services, sync_context, room_id, timeline.senders()).await;
prepare_lazily_loaded_members(services, sync_context, room_id, timeline.senders());
let (timeline_start_shortstatehash, lazily_loaded_members) =
join(timeline_start_shortstatehash, lazily_loaded_members).await;
// compute the state delta between the previous sync and this sync.
match (last_sync_end_count, last_sync_end_shortstatehash) {
@@ -494,15 +494,16 @@ async fn build_state_events(
is Some (meaning the syncing user didn't just join this room for the first time ever), and `full_state` is false,
then use `build_state_incremental`.
*/
| (Some(_), Some(last_sync_end_shortstatehash)) if !full_state =>
| (Some(last_sync_end_count), Some(last_sync_end_shortstatehash)) if !full_state =>
build_state_incremental(
services,
syncing_user,
room_id,
PduCount::Normal(last_sync_end_count),
last_sync_end_shortstatehash,
timeline_start_shortstatehash,
current_shortstatehash,
timeline,
use_state_after,
lazily_loaded_members.as_ref(),
)
.boxed()
@@ -517,8 +518,6 @@ async fn build_state_events(
services,
syncing_user,
timeline_start_shortstatehash,
current_shortstatehash,
use_state_after,
lazily_loaded_members.as_ref(),
)
.boxed()
@@ -599,25 +598,23 @@ async fn check_joined_since_last_sync(
ShortStateHashes { last_sync_end_shortstatehash, .. }: ShortStateHashes,
SyncContext { syncing_user, .. }: SyncContext<'_>,
) -> Result<bool> {
let Some(last_sync_end_shortstatehash) = last_sync_end_shortstatehash else {
// For initial syncs always return false, since there's no "last sync" for the
// user to have joined since.
return Ok(false);
// fetch the syncing user's membership event during the last sync.
// this will be None if `previous_sync_end_shortstatehash` is None.
let membership_during_previous_sync = match last_sync_end_shortstatehash {
| Some(last_sync_end_shortstatehash) => services
.rooms
.state_accessor
.state_get_content(
last_sync_end_shortstatehash,
&StateEventType::RoomMember,
syncing_user.as_str(),
)
.await
.inspect_err(|_| debug_warn!("User has no previous membership"))
.ok(),
| None => None,
};
// Fetch the syncing user's membership event during the last sync.
let membership_during_previous_sync = services
.rooms
.state_accessor
.state_get_content(
last_sync_end_shortstatehash,
&StateEventType::RoomMember,
syncing_user.as_str(),
)
.await
.inspect_err(|_| debug_warn!("User has no previous membership"))
.ok();
// TODO: If the requesting user got state-reset out of the room, this
// will be `true` when it shouldn't be. this function should never be called
// in that situation, but it may be if the membership cache didn't get updated.
+1 -10
View File
@@ -181,9 +181,6 @@ pub(super) async fn load_left_room(
.collect::<Vec<_>>()
.await;
let state_events =
StateEvents::with_events(state_events.into_iter().map(Event::into_format).collect());
Ok(Some(assign!(LeftRoom::new(), {
account_data: RoomAccountData::new(),
timeline: assign!(Timeline::new(), {
@@ -191,11 +188,7 @@ pub(super) async fn load_left_room(
prev_batch: Some(current_count.to_string()),
events: raw_timeline_pdus,
}),
state: if sync_context.use_state_after {
State::After(state_events)
} else {
State::Before(state_events)
},
state: State::Before(StateEvents::with_events(state_events.into_iter().map(Event::into_format).collect())),
})))
}
@@ -271,8 +264,6 @@ async fn build_left_state_and_timeline(
services,
syncing_user,
timeline_start_shortstatehash,
leave_shortstatehash,
sync_context.use_state_after,
lazily_loaded_members.as_ref(),
)
.await?;
+6 -11
View File
@@ -11,11 +11,12 @@ use std::{
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, at, error, extract_variant,
Err, Result, at, extract_variant,
utils::{
ReadyExt, TryFutureExtExt,
stream::{BroadbandExt, Tools, WidebandExt},
},
warn,
};
use conduwuit_service::Services;
use futures::{FutureExt, StreamExt, TryFutureExt, future::OptionFuture};
@@ -109,9 +110,6 @@ struct SyncContext<'a> {
/// The sync filter, which the client uses to specify what data should be
/// included in the sync response.
filter: &'a FilterDefinition,
/// Whether the state at the end of the timeline should be used when
/// calculating state diffs for sync.
use_state_after: bool,
}
impl<'a> SyncContext<'a> {
@@ -183,8 +181,7 @@ pub(crate) async fn sync_events_route(
ClientIp(client_ip): ClientIp,
body: Ruma<sync_events::v3::Request>,
) -> Result<sync_events::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.expect_sender_device()?;
let (sender_user, sender_device) = body.sender();
// Presence update
if services.config.allow_local_presence {
@@ -228,8 +225,7 @@ pub(crate) async fn build_sync_events(
services: &Services,
body: &Ruma<sync_events::v3::Request>,
) -> Result<sync_events::v3::Response> {
let syncing_user = body.identity.sender_user();
let syncing_device = body.identity.sender_device().expect("should have a device");
let (syncing_user, syncing_device) = body.sender();
let current_count = services.globals.current_count()?;
@@ -265,7 +261,6 @@ pub(crate) async fn build_sync_events(
current_count,
full_state,
filter: &filter,
use_state_after: body.use_state_after,
};
let joined_rooms = services
@@ -278,7 +273,7 @@ pub(crate) async fn build_sync_events(
match joined_room {
| Ok((room, updates)) => Some((room_id, room, updates)),
| Err(err) => {
error!(?err, %room_id, "error loading joined room");
warn!(?err, %room_id, "error loading joined room");
None
},
}
@@ -307,7 +302,7 @@ pub(crate) async fn build_sync_events(
| Ok(Some(left_room)) => Some((room_id, left_room)),
| Ok(None) => None,
| Err(err) => {
error!(?err, %room_id, "error loading joined room");
warn!(?err, %room_id, "error loading joined room");
None
},
}
+143 -60
View File
@@ -1,8 +1,11 @@
use std::collections::HashSet;
use std::{collections::BTreeSet, ops::ControlFlow};
use conduwuit::{
Result, at,
matrix::{Event, pdu::PduEvent},
Result, at, is_equal_to,
matrix::{
Event,
pdu::{PduCount, PduEvent},
},
utils::{
BoolExt, IterStream, ReadyExt, TryFutureExtExt,
stream::{BroadbandExt, TryIgnore},
@@ -13,7 +16,9 @@ use conduwuit_service::{
rooms::{lazy_loading::MemberSet, short::ShortStateHash},
};
use futures::{FutureExt, StreamExt};
use ruma::{OwnedEventId, UserId, events::StateEventType};
use itertools::Itertools;
use ruma::{OwnedEventId, RoomId, UserId, events::StateEventType};
use service::rooms::short::ShortEventId;
use tracing::trace;
use crate::client::TimelinePdus;
@@ -34,19 +39,13 @@ pub(super) async fn build_state_initial(
services: &Services,
sender_user: &UserId,
timeline_start_shortstatehash: ShortStateHash,
timeline_end_shortstatehash: ShortStateHash,
use_state_after: bool,
lazily_loaded_members: Option<&MemberSet>,
) -> Result<Vec<PduEvent>> {
// load the keys and event IDs of the state events at the start of the timeline
let (shortstatekeys, event_ids): (Vec<_>, Vec<_>) = services
.rooms
.state_accessor
.state_full_ids(if use_state_after {
timeline_end_shortstatehash
} else {
timeline_start_shortstatehash
})
.state_full_ids(timeline_start_shortstatehash)
.unzip()
.await;
@@ -93,34 +92,82 @@ pub(super) async fn build_state_initial(
pub(super) async fn build_state_incremental<'a>(
services: &Services,
sender_user: &'a UserId,
room_id: &RoomId,
last_sync_end_count: PduCount,
last_sync_end_shortstatehash: ShortStateHash,
timeline_start_shortstatehash: ShortStateHash,
timeline_end_shortstatehash: ShortStateHash,
timeline: &TimelinePdus,
use_state_after: bool,
lazily_loaded_members: Option<&'a MemberSet>,
) -> Result<Vec<PduEvent>> {
let mut state_event_ids: HashSet<OwnedEventId> = HashSet::new();
/*
NB: a limited sync is one where `timeline.limited == true`. Synapse calls this a "gappy" sync internally.
trace!(
%use_state_after,
%last_sync_end_shortstatehash,
%timeline_start_shortstatehash,
%timeline_end_shortstatehash,
"computing state for incremental sync"
);
The algorithm implemented in this function is, currently, quite different from the algorithm vaguely described
by the Matrix specification. This is because the specification's description of the `state` property does not accurately
reflect how Synapse behaves, and therefore how client SDKs behave. Notable differences include:
1. We do not compute the delta using the naive approach of "every state event from the end of the last sync
up to the start of this sync's timeline". see below for details.
2. If lazy-loading is enabled, we include lazily-loaded membership events. The specific users to include are determined
elsewhere and supplied to this function in the `lazily_loaded_members` parameter.
*/
// Fetch lazy-loaded membership events if lazy-loading is enabled
if let Some(lazily_loaded_members) = lazily_loaded_members
&& !lazily_loaded_members.is_empty()
{
trace!("including lazy membership events for members: {:?}", lazily_loaded_members);
/*
the `state` property of an incremental sync which isn't limited are _usually_ empty.
(note: the specification says that the `state` property is _always_ empty for limited syncs, which is incorrect.)
however, if an event in the timeline (`timeline.pdus`) merges a split in the room's DAG (i.e. has multiple `prev_events`),
the state at the _end_ of the timeline may include state events which were merged in and don't exist in the state
at the _start_ of the timeline. because this is uncommon, we check here to see if any events in the timeline
merged a split in the DAG.
services
see: https://github.com/element-hq/synapse/issues/16941
*/
let timeline_is_linear = timeline.pdus.is_empty() || {
let last_pdu_of_last_sync = services
.rooms
.short
.multi_get_eventid_from_short::<'_, OwnedEventId, _>(
lazily_loaded_members
.timeline
.pdus_rev(room_id, Some(last_sync_end_count.saturating_add(1)))
.boxed()
.next()
.await
.transpose()
.expect("last sync should have had some PDUs")
.map(at!(1));
// make sure the prev_events of each pdu in the timeline refer only to the
// previous pdu
timeline
.pdus
.iter()
.try_fold(last_pdu_of_last_sync.map(|pdu| pdu.event_id), |prev_event_id, (_, pdu)| {
if let Ok(pdu_prev_event_id) = pdu.prev_events.iter().exactly_one() {
if prev_event_id
.as_ref()
.is_none_or(is_equal_to!(pdu_prev_event_id))
{
return ControlFlow::Continue(Some(pdu_prev_event_id.to_owned()));
}
}
trace!(
"pdu {:?} has split prev_events (expected {:?}): {:?}",
pdu.event_id, prev_event_id, pdu.prev_events
);
ControlFlow::Break(())
})
.is_continue()
};
if timeline_is_linear && !timeline.limited {
// if there are no splits in the DAG and the timeline isn't limited, then
// `state` will always be empty unless lazy loading is enabled.
if let Some(lazily_loaded_members) = lazily_loaded_members {
if !timeline.pdus.is_empty() {
// lazy loading is enabled, so we return the membership events which were
// requested by the caller.
let lazy_membership_events: Vec<_> = lazily_loaded_members
.iter()
.stream()
.broad_filter_map(|user_id| async move {
@@ -131,24 +178,71 @@ pub(super) async fn build_state_incremental<'a>(
services
.rooms
.state_accessor
.state_get_shortid(
.state_get(
timeline_start_shortstatehash,
&StateEventType::RoomMember,
user_id.as_str(),
)
.ok()
.await
}),
)
.ignore_err()
.ready_for_each(|event_id| {
state_event_ids.insert(event_id);
})
.await;
})
.collect()
.await;
if !lazy_membership_events.is_empty() {
trace!(
"syncing lazy membership events for members: {:?}",
lazy_membership_events
.iter()
.map(|pdu| pdu.state_key().unwrap())
.collect::<Vec<_>>()
);
}
return Ok(lazy_membership_events);
}
}
// lazy loading is disabled, `state` is empty.
return Ok(vec![]);
}
// Fetch the state events added since the last sync.
services
/*
at this point, either the timeline is `limited` or the DAG has a split in it. this necessitates
computing the incremental state (which may be empty).
NOTE: this code path does not use the `lazy_membership_events` parameter. any changes to membership will be included
in the incremental state. therefore, the incremental state may include "redundant" membership events,
which we do not filter out because A. the spec forbids lazy-load filtering if the timeline is `limited`,
and B. DAG splits which require sending extra membership state events are (probably) uncommon enough that
the performance penalty is acceptable.
*/
trace!(%timeline_is_linear, %timeline.limited, "computing state for incremental sync");
// fetch the shorteventids of state events in the timeline
let state_events_in_timeline: BTreeSet<ShortEventId> = services
.rooms
.short
.multi_get_or_create_shorteventid(timeline.pdus.iter().filter_map(|(_, pdu)| {
if pdu.state_key().is_some() {
Some(pdu.event_id.as_ref())
} else {
None
}
}))
.collect()
.await;
trace!("{} state events in timeline", state_events_in_timeline.len());
/*
fetch the state events which were added since the last sync.
specifically we fetch the difference between the state at the last sync and the state at the _end_
of the timeline, and then we filter out state events in the timeline itself using the shorteventids we fetched.
this is necessary to account for splits in the DAG, as explained above.
*/
let state_diff = services
.rooms
.short
.multi_get_eventid_from_short::<'_, OwnedEventId, _>(
@@ -158,29 +252,18 @@ pub(super) async fn build_state_incremental<'a>(
.state_added((last_sync_end_shortstatehash, timeline_end_shortstatehash))
.await?
.stream()
.map(at!(1)),
.ready_filter_map(|(_, shorteventid)| {
if state_events_in_timeline.contains(&shorteventid) {
None
} else {
Some(shorteventid)
}
}),
)
.ignore_err()
.ready_for_each(|event_id| {
state_event_ids.insert(event_id);
})
.await;
.ignore_err();
if !use_state_after {
// If state_after isn't enabled, filter out state events which also exist
// in the timeline. If splits exist in the DAG, this may not be exactly the same
// thing as the state diff ending at the start of the timeline, but Synapse
// also does this and it's technically more useful behavior anyway.
// See: https://github.com/element-hq/synapse/issues/16941
for (_, pdu) in &timeline.pdus {
state_event_ids.remove(pdu.event_id());
}
}
// Finally, fetch the PDU contents and collect them into a vec
let state_diff_pdus = state_event_ids
.stream()
// finally, fetch the PDU contents and collect them into a vec
let state_diff_pdus = state_diff
.broad_filter_map(|event_id| async move {
services
.rooms
+10 -30
View File
@@ -15,7 +15,7 @@ use conduwuit::{
BoolExt, FutureBoolExt, IterStream, ReadyExt, TryFutureExtExt,
future::ReadyEqExt,
math::{ruma_from_usize, usize_from_ruma},
stream::{TryIgnore, WidebandExt},
stream::WidebandExt,
},
warn,
};
@@ -41,7 +41,6 @@ use ruma::{
uint,
};
use service::account_data::AnyRawAccountDataEvent;
use tokio::pin;
use super::share_encrypted_room;
use crate::{
@@ -71,8 +70,8 @@ pub(crate) async fn sync_events_v5_route(
body: Ruma<sync_events::v5::Request>,
) -> Result<sync_events::v5::Response> {
debug_assert!(DEFAULT_BUMP_TYPES.is_sorted(), "DEFAULT_BUMP_TYPES is not sorted");
let sender_user = body.identity.sender_user();
let sender_device = body.identity.expect_sender_device()?;
let ref sender_user = body.sender_user().to_owned();
let ref sender_device = body.sender_device().to_owned();
services
.users
@@ -94,7 +93,7 @@ pub(crate) async fn sync_events_v5_route(
.and_then(|string| string.parse().ok())
.unwrap_or(0);
let snake_key = into_snake_key(sender_user, sender_device.as_str(), conn_id);
let snake_key = into_snake_key(sender_user.as_ref(), sender_device.as_str(), conn_id);
if globalsince != 0 && !services.sync.snake_connection_cached(&snake_key) {
return Err!(Request(UnknownPos(
@@ -859,31 +858,12 @@ where
continue;
};
let since_shortstatehash = async {
pin! {
let pdus_rev = services
.rooms
.timeline
.pdus_rev(room_id, Some(PduCount::Normal(globalsince.saturating_sub(1))))
.ignore_err();
}
let (count, pdu_at_last_sync_end) = pdus_rev.next().await?;
if matches!(count, PduCount::Backfilled(_)) {
None
} else {
Some(
services
.rooms
.state_accessor
.pdu_shortstatehash(&pdu_at_last_sync_end.event_id)
.await
.expect("pdu should have a shortstatehash"),
)
}
}
.await;
let since_shortstatehash = services
.rooms
.user
.get_token_shortstatehash(room_id, globalsince)
.await
.ok();
let encrypted_room = services
.rooms
+3 -3
View File
@@ -21,7 +21,7 @@ pub(crate) async fn update_tag_route(
State(services): State<crate::State>,
body: Ruma<create_tag::v3::Request>,
) -> Result<create_tag::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut tags_event = services
.account_data
@@ -56,7 +56,7 @@ pub(crate) async fn delete_tag_route(
State(services): State<crate::State>,
body: Ruma<delete_tag::v3::Request>,
) -> Result<delete_tag::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let mut tags_event = services
.account_data
@@ -88,7 +88,7 @@ pub(crate) async fn get_tags_route(
State(services): State<crate::State>,
body: Ruma<get_tags::v3::Request>,
) -> Result<get_tags::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let tags_event = services
.account_data
+3 -3
View File
@@ -34,14 +34,14 @@ pub(crate) async fn get_threads_route(
let threads: Vec<(PduCount, PduEvent)> = services
.rooms
.threads
.threads_until(body.identity.sender_user(), &body.room_id, from, &body.include)
.threads_until(body.sender_user(), &body.room_id, from, &body.include)
.await?
.take(limit)
.filter_map(|(count, pdu)| async move {
services
.rooms
.state_accessor
.user_can_see_event(body.identity.sender_user(), &body.room_id, &pdu.event_id)
.user_can_see_event(body.sender_user(), &body.room_id, &pdu.event_id)
.await
.then_some((count, pdu))
})
@@ -49,7 +49,7 @@ pub(crate) async fn get_threads_route(
if let Err(e) = services
.rooms
.pdu_metadata
.add_bundled_aggregations_to_pdu(body.identity.sender_user(), &mut pdu)
.add_bundled_aggregations_to_pdu(body.sender_user(), &mut pdu)
.await
{
debug_warn!("Failed to add bundled aggregations to thread: {e}");
+2 -2
View File
@@ -22,8 +22,8 @@ pub(crate) async fn send_event_to_device_route(
State(services): State<crate::State>,
body: Ruma<send_event_to_device::v3::Request>,
) -> Result<send_event_to_device::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_device = body.identity.sender_device();
let sender_user = body.sender_user();
let sender_device = body.sender_device.as_deref();
// Check if this is a new transaction id
if services
+3 -3
View File
@@ -14,13 +14,13 @@ pub(crate) async fn create_typing_event_route(
body: Ruma<create_typing_event::v3::Request>,
) -> Result<create_typing_event::v3::Response> {
use create_typing_event::v3::Typing;
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
services
.users
.update_device_last_seen(sender_user, body.identity.sender_device(), ip)
.update_device_last_seen(sender_user, body.sender_device.as_deref(), ip)
.await;
if sender_user != body.user_id && !body.identity.is_appservice() {
if sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot update typing status of other users.")));
}
+1 -1
View File
@@ -26,7 +26,7 @@ pub(crate) async fn search_users_route(
State(services): State<crate::State>,
body: Ruma<search_users::v3::Request>,
) -> Result<search_users::v3::Response> {
let sender_user = body.identity.sender_user();
let sender_user = body.sender_user();
let limit = usize::try_from(body.limit)
.map_or(LIMIT_DEFAULT, usize::from)
.min(LIMIT_MAX);
+13 -3
View File
@@ -2,13 +2,15 @@ use std::time::{Duration, SystemTime};
use axum::extract::State;
use base64::{Engine as _, engine::general_purpose};
use conduwuit::{Err, Result};
use conduwuit::{Err, Result, utils};
use hmac::{Hmac, KeyInit, Mac};
use ruma::{SecondsSinceUnixEpoch, api::client::voip::get_turn_server_info};
use ruma::{SecondsSinceUnixEpoch, UserId, api::client::voip::get_turn_server_info};
use sha1::Sha1;
use crate::Ruma;
const RANDOM_USER_ID_LENGTH: usize = 10;
type HmacSha1 = Hmac<Sha1>;
/// # `GET /_matrix/client/r0/voip/turnServer`
@@ -33,7 +35,15 @@ pub(crate) async fn turn_server_route(
)
.expect("time is valid");
let username: String = format!("{}:{}", expiry.get(), body.identity.sender_user());
let user = body.sender_user.unwrap_or_else(|| {
UserId::parse_with_server_name(
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
&services.server.name,
)
.unwrap()
});
let username: String = format!("{}:{}", expiry.get(), user);
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
.expect("HMAC can take key of any size");
-16
View File
@@ -3,7 +3,6 @@ use conduwuit::{Err, Result};
use ruma::{
api::client::discovery::{
discover_homeserver::{self, HomeserverInfo},
discover_policy_server,
discover_support::{self, Contact, ContactRole},
},
assign,
@@ -115,18 +114,3 @@ pub(crate) async fn well_known_support(
Ok(assign!(discover_support::Response::with_contacts(contacts), { support_page }))
}
/// # `GET /.well-known/matrix/policy_server`
///
/// Advertises the policy server's public key, allowing clients to discover the
/// values to be set in m.room.policy. Introduced in spec v1.18.
pub(crate) async fn well_known_policy_server(
State(services): State<crate::State>,
_body: Ruma<discover_policy_server::Request>,
) -> Result<discover_policy_server::Response> {
if let Some(key) = services.config.well_known.policy_server_public_key.clone() {
Ok(discover_policy_server::Response::new(key))
} else {
Err!(Request(NotFound("No policy server available.")))
}
}
+1 -2
View File
@@ -15,7 +15,7 @@ pub(super) use conduwuit_service::state::State;
use http::{Uri, uri};
use self::handler::RouterExt;
pub(super) use self::{args::Args as Ruma, auth::ClientIdentity, response::RumaResponse};
pub(super) use self::{args::Args as Ruma, response::RumaResponse};
use crate::{admin, client, server};
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
@@ -183,7 +183,6 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
.ruma_route(&client::put_suspended_status)
.ruma_route(&client::well_known_support)
.ruma_route(&client::well_known_client)
.ruma_route(&client::well_known_policy_server)
.ruma_route(&client::get_rtc_transports)
.ruma_route(&client::room_initial_sync_route)
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
+68 -13
View File
@@ -6,14 +6,17 @@ use axum::{
extract::{FromRequest, Path, Query},
};
use conduwuit::{Error, Result, err};
use ruma::{CanonicalJsonObject, api::IncomingRequest};
use ruma::{
CanonicalJsonObject, DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, ServerName,
UserId, api::IncomingRequest,
};
use serde::Deserialize;
use crate::{State, router::auth::CheckAuth};
use crate::{State, router::auth::CheckAuth, service::appservice::RegistrationInfo};
/// Query parameters needed to authenticate requests
#[derive(Deserialize)]
pub(crate) struct AuthQueryParams {
pub(super) struct AuthQueryParams {
pub(super) user_id: Option<String>,
/// Device ID for appservice device masquerading (MSC3202/MSC4190).
/// Can be provided as `device_id` or `org.matrix.msc3202.device_id`.
@@ -22,22 +25,67 @@ pub(crate) struct AuthQueryParams {
}
/// Extractor for Ruma request structs
pub(crate) struct Args<R: IncomingRequest<Authentication: CheckAuth> + Send + Sync + 'static> {
pub(crate) struct Args<T> {
/// Request struct body
pub(crate) body: R,
pub(crate) body: T,
/// Parsed JSON body. None when body is not JSON.
/// Federation server authentication: X-Matrix origin
/// None when not a federation server.
pub(crate) origin: Option<OwnedServerName>,
/// Local user authentication: user_id.
/// None when not an authenticated local user.
pub(crate) sender_user: Option<OwnedUserId>,
/// Local user authentication: device_id.
/// None when not an authenticated local user or no device.
pub(crate) sender_device: Option<OwnedDeviceId>,
/// Appservice authentication; registration info.
/// None when not an appservice.
pub(crate) appservice_info: Option<RegistrationInfo>,
/// Parsed JSON content.
/// None when body is not a valid string
pub(crate) json_body: Option<CanonicalJsonObject>,
/// Identity of the requesting entity
pub(crate) identity: <R::Authentication as CheckAuth>::Identity,
}
impl<R> Deref for Args<R>
impl<T> Args<T>
where
R: IncomingRequest<Authentication: CheckAuth> + Send + Sync + 'static,
T: IncomingRequest + Send + Sync + 'static,
{
type Target = R;
#[inline]
pub(crate) fn sender(&self) -> (&UserId, &DeviceId) {
(self.sender_user(), self.sender_device())
}
#[inline]
pub(crate) fn sender_user(&self) -> &UserId {
self.sender_user
.as_deref()
.expect("user must be authenticated for this handler")
}
#[inline]
pub(crate) fn sender_device(&self) -> &DeviceId {
self.sender_device
.as_deref()
.expect("user must be authenticated and device identified")
}
#[inline]
pub(crate) fn origin(&self) -> &ServerName {
self.origin
.as_deref()
.expect("server must be authenticated for this handler")
}
}
impl<T> Deref for Args<T>
where
T: IncomingRequest + Send + Sync + 'static,
{
type Target = T;
fn deref(&self) -> &Self::Target { &self.body }
}
@@ -97,6 +145,13 @@ where
let body = R::try_from_http_request(request, &path)
.map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))?;
Ok(Self { body, json_body, identity: auth })
Ok(Self {
body,
origin: auth.origin,
sender_user: auth.sender_user,
sender_device: auth.sender_device,
appservice_info: auth.appservice_info,
json_body,
})
}
}
+99 -161
View File
@@ -2,14 +2,13 @@ use std::any::{Any, TypeId};
use conduwuit::{Err, Result, err};
use ruma::{
DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
api::{
IncomingRequest,
auth_scheme::{
AccessToken, AccessTokenOptional, AppserviceToken, AppserviceTokenOptional,
AuthScheme, NoAccessToken, NoAuthentication,
},
client,
federation::authentication::ServerSignatures,
},
};
@@ -20,57 +19,20 @@ use service::{
use crate::{router::args::AuthQueryParams, service::appservice::RegistrationInfo};
pub(crate) enum ClientIdentity {
User {
sender_user: OwnedUserId,
sender_device: OwnedDeviceId,
},
Appservice {
sender_user: OwnedUserId,
sender_device: Option<OwnedDeviceId>,
appservice_info: Box<RegistrationInfo>,
},
#[derive(Default)]
pub(super) struct Auth {
pub(super) origin: Option<OwnedServerName>,
pub(super) sender_user: Option<OwnedUserId>,
pub(super) sender_device: Option<OwnedDeviceId>,
pub(super) appservice_info: Option<RegistrationInfo>,
}
impl ClientIdentity {
pub(crate) fn sender_user(&self) -> &UserId {
match self {
| Self::User { sender_user, .. } | Self::Appservice { sender_user, .. } =>
sender_user,
}
}
pub(crate) fn sender_device(&self) -> Option<&DeviceId> {
match self {
| Self::User { sender_device, .. } => Some(sender_device),
| Self::Appservice { sender_device, .. } => sender_device.as_deref(),
}
}
pub(crate) fn expect_sender_device(&self) -> Result<&DeviceId> {
self.sender_device().ok_or_else(|| {
err!(Request(Forbidden("Appservices must masquerade to use this endpoint.")))
})
}
pub(crate) fn appservice_info(&self) -> Option<&RegistrationInfo> {
match self {
| Self::User { .. } => None,
| Self::Appservice { appservice_info, .. } => Some(appservice_info),
}
}
pub(crate) fn is_appservice(&self) -> bool { matches!(self, Self::Appservice { .. }) }
}
pub(crate) trait CheckAuth: AuthScheme {
type Identity: Send;
pub(super) trait CheckAuth: AuthScheme {
fn authenticate<R: IncomingRequest + Any, B: AsRef<[u8]> + Sync>(
services: &Services,
incoming_request: &hyper::Request<B>,
query: AuthQueryParams,
) -> impl Future<Output = Result<Self::Identity>> + Send {
) -> impl Future<Output = Result<Auth>> + Send {
async move {
let route = TypeId::of::<R>();
@@ -91,19 +53,17 @@ pub(crate) trait CheckAuth: AuthScheme {
request: &hyper::Request<B>,
query: AuthQueryParams,
route: TypeId,
) -> impl Future<Output = Result<Self::Identity>> + Send;
) -> impl Future<Output = Result<Auth>> + Send;
}
impl CheckAuth for ServerSignatures {
type Identity = OwnedServerName;
async fn verify<B: AsRef<[u8]> + Sync>(
services: &Services,
output: Self::Output,
request: &hyper::Request<B>,
_query: AuthQueryParams,
_route: TypeId,
) -> Result<Self::Identity> {
) -> Result<Auth> {
let destination = services.globals.server_name();
if output
.destination
@@ -125,18 +85,10 @@ impl CheckAuth for ServerSignatures {
let keys: PubKeyMap = [(output.origin.as_str().into(), keys)].into();
match output.verify_request(request, destination, &keys) {
| Ok(()) => {
if services
.moderation
.is_remote_server_forbidden(&output.origin)
{
return Err!(Request(Forbidden(
"You are blocked from federating with this server."
)));
}
Ok(output.origin)
},
| Ok(()) => Ok(Auth {
origin: Some(output.origin.clone()),
..Default::default()
}),
| Err(err) =>
Err!(Request(Unauthorized(warn!("Failed to verify X-Matrix header: {err}")))),
}
@@ -144,182 +96,168 @@ impl CheckAuth for ServerSignatures {
}
impl CheckAuth for AccessToken {
type Identity = ClientIdentity;
async fn verify<B: AsRef<[u8]> + Sync>(
services: &Services,
output: Self::Output,
_request: &hyper::Request<B>,
query: AuthQueryParams,
route: TypeId,
) -> Result<Self::Identity> {
if let Ok((sender_user, sender_device)) = services.users.find_from_token(&output).await {
// Locked users can only use /logout and /logout/all
if services
.users
.is_locked(&sender_user)
.await
.is_ok_and(std::convert::identity)
) -> Result<Auth> {
// Check for appservice tokens first
let (sender_user, sender_device, appservice_info) = {
if let Ok((sender_user, sender_device)) =
services.users.find_from_token(&output).await
{
if !(route == TypeId::of::<client::session::logout::v3::Request>()
|| route == TypeId::of::<client::session::logout_all::v3::Request>())
// Locked users can only use /logout and /logout/all
if services
.users
.is_locked(&sender_user)
.await
.is_ok_and(std::convert::identity)
{
return Err!(Request(Unauthorized("Your account is locked.")));
}
}
Ok(ClientIdentity::User { sender_user, sender_device })
} else if let Ok(appservice_info) = services.appservice.find_from_token(&output).await {
let Ok(sender_user) = query.user_id.clone().map_or_else(
|| {
UserId::parse_with_server_name(
appservice_info.registration.sender_localpart.as_str(),
services.globals.server_name(),
)
},
UserId::parse,
) else {
return Err!(Request(InvalidUsername("Username is invalid.")));
};
if !appservice_info.is_user_match(&sender_user) {
return Err!(Request(Exclusive("User is not in namespace.")));
}
// MSC3202/MSC4190: Handle device_id masquerading for appservices.
// The device_id can be provided via `device_id` or
// `org.matrix.msc3202.device_id` query parameter.
let sender_device =
if let Some(device_id) = query.device_id.as_deref().map(Into::into) {
// Verify the device exists for this user
if services
.users
.get_device_metadata(&sender_user, device_id)
.await
.is_err()
{
return Err!(Request(Forbidden(
"Device does not exist for user or appservice cannot masquerade as \
this device."
)));
if !(route == TypeId::of::<ruma::api::client::session::logout::v3::Request>()
|| route
== TypeId::of::<ruma::api::client::session::logout_all::v3::Request>(
)) {
return Err!(Request(Unauthorized("Your account is locked.")));
}
}
Some(device_id.to_owned())
} else {
None
(Some(sender_user), Some(sender_device), None)
} else if let Ok(appservice_info) = services.appservice.find_from_token(&output).await
{
let Ok(sender_user) = query.user_id.clone().map_or_else(
|| {
UserId::parse_with_server_name(
appservice_info.registration.sender_localpart.as_str(),
services.globals.server_name(),
)
},
UserId::parse,
) else {
return Err!(Request(InvalidUsername("Username is invalid.")));
};
Ok(ClientIdentity::Appservice {
sender_user,
sender_device,
appservice_info: Box::new(appservice_info),
})
} else {
Err!(Request(Unauthorized("Invalid access token.")))
}
if !appservice_info.is_user_match(&sender_user) {
return Err!(Request(Exclusive("User is not in namespace.")));
}
// MSC3202/MSC4190: Handle device_id masquerading for appservices.
// The device_id can be provided via `device_id` or
// `org.matrix.msc3202.device_id` query parameter.
let sender_device =
if let Some(device_id) = query.device_id.as_deref().map(Into::into) {
// Verify the device exists for this user
if services
.users
.get_device_metadata(&sender_user, device_id)
.await
.is_err()
{
return Err!(Request(Forbidden(
"Device does not exist for user or appservice cannot masquerade \
as this device."
)));
}
Some(device_id.to_owned())
} else {
None
};
(Some(sender_user), sender_device, Some(appservice_info))
} else {
return Err!(Request(Unauthorized("Invalid access token.")));
}
};
Ok(Auth {
sender_user,
sender_device,
appservice_info,
..Default::default()
})
}
}
impl CheckAuth for AccessTokenOptional {
type Identity = Option<ClientIdentity>;
async fn verify<B: AsRef<[u8]> + Sync>(
services: &Services,
output: Self::Output,
request: &hyper::Request<B>,
query: AuthQueryParams,
route: TypeId,
) -> Result<Self::Identity> {
) -> Result<Auth> {
match output {
| Some(token) =>
<AccessToken as CheckAuth>::verify(services, token, request, query, route)
.await
.map(Some),
| None => Ok(None),
<AccessToken as CheckAuth>::verify(services, token, request, query, route).await,
| None => Ok(Auth::default()),
}
}
}
impl CheckAuth for AppserviceToken {
type Identity = RegistrationInfo;
async fn verify<B: AsRef<[u8]> + Sync>(
services: &Services,
output: Self::Output,
_request: &hyper::Request<B>,
_query: AuthQueryParams,
_route: TypeId,
) -> Result<Self::Identity> {
) -> Result<Auth> {
let Ok(appservice_info) = services.appservice.find_from_token(&output).await else {
return Err!(Request(Unauthorized("Invalid appservice token.")));
};
Ok(appservice_info)
Ok(Auth {
appservice_info: Some(appservice_info),
..Default::default()
})
}
}
impl CheckAuth for AppserviceTokenOptional {
type Identity = Option<RegistrationInfo>;
async fn verify<B: AsRef<[u8]> + Sync>(
services: &Services,
output: Self::Output,
request: &hyper::Request<B>,
query: AuthQueryParams,
route: TypeId,
) -> Result<Self::Identity> {
) -> Result<Auth> {
match output {
| Some(token) =>
<AppserviceToken as CheckAuth>::verify(services, token, request, query, route)
.await
.map(Some),
| None => Ok(None),
.await,
| None => Ok(Auth::default()),
}
}
}
impl CheckAuth for NoAuthentication {
type Identity = ();
async fn verify<B: AsRef<[u8]> + Sync>(
_services: &Services,
_output: Self::Output,
_request: &hyper::Request<B>,
_query: AuthQueryParams,
_route: TypeId,
) -> Result<Self::Identity> {
Ok(())
) -> Result<Auth> {
Ok(Auth::default())
}
}
impl CheckAuth for NoAccessToken {
type Identity = Option<ClientIdentity>;
async fn verify<B: AsRef<[u8]> + Sync>(
services: &Services,
_output: Self::Output,
request: &hyper::Request<B>,
query: AuthQueryParams,
route: TypeId,
) -> Result<Self::Identity> {
) -> Result<Auth> {
// We handle these the same as AccessTokenOptional
let token = AccessTokenOptional::extract_authentication(request).map_err(|err| {
err!(Request(Unauthorized(warn!("Failed to extract authorization: {}", err))))
})?;
// Check special access restrictions
if (route == TypeId::of::<client::profile::get_avatar_url::v3::Request>()
|| route == TypeId::of::<client::profile::get_display_name::v3::Request>()
|| route == TypeId::of::<client::profile::get_profile_field::v3::Request>()
|| route == TypeId::of::<client::profile::get_profile::v3::Request>())
&& services.config.require_auth_for_profile_requests
&& token.is_none()
{
return Err!(Request(Unauthorized(
"This server requires authentication to access user profiles."
)));
}
<AccessTokenOptional as CheckAuth>::verify(services, token, request, query, route).await
}
}
+3 -3
View File
@@ -28,7 +28,7 @@ pub(crate) async fn get_backfill_route(
) -> Result<get_backfill::v1::Response> {
AccessCheck {
services: &services,
origin: &body.identity,
origin: body.origin(),
room_id: &body.room_id,
event_id: None,
}
@@ -41,7 +41,7 @@ pub(crate) async fn get_backfill_route(
.await
{
info!(
origin = body.identity.as_str(),
origin = body.origin().as_str(),
"Refusing to serve backfill for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));
@@ -76,7 +76,7 @@ pub(crate) async fn get_backfill_route(
Ok(services
.rooms
.state_accessor
.server_can_see_event(&body.identity, &pdu.room_id_or_hash(), &pdu.event_id)
.server_can_see_event(body.origin(), &pdu.room_id_or_hash(), &pdu.event_id)
.await
.then_some(pdu))
})
+2 -11
View File
@@ -22,15 +22,6 @@ pub(crate) async fn get_event_route(
.await
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
if services
.rooms
.pdu_metadata
.is_event_rejected(&body.event_id)
.await
{
return Err!(Request(NotFound("Event not found.")));
}
let room_id: &RoomId = event
.get("room_id")
.and_then(|val| val.as_str())
@@ -40,7 +31,7 @@ pub(crate) async fn get_event_route(
AccessCheck {
services: &services,
origin: &body.identity,
origin: body.origin(),
room_id,
event_id: Some(&body.event_id),
}
@@ -54,7 +45,7 @@ pub(crate) async fn get_event_route(
.await
{
info!(
origin = body.identity.as_str(),
origin = body.origin().as_str(),
"Refusing to serve state for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));

Some files were not shown because too many files have changed in this diff Show More