Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot] 39d6f5bac6 Update opentelemetry-rust monorepo 2024-07-15 19:18:11 +00:00
231 changed files with 5930 additions and 8747 deletions
+14 -254
View File
@@ -27,16 +27,6 @@ concurrency:
cancel-in-progress: true
env:
# sccache only on main repo
SCCACHE_GHA_ENABLED: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'true' || 'false' }}"
RUSTC_WRAPPER: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' }}"
SCCACHE_BUCKET: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' }}"
SCCACHE_S3_USE_SSL: ${{ vars.SCCACHE_S3_USE_SSL }}
SCCACHE_REGION: ${{ vars.SCCACHE_REGION }}
SCCACHE_ENDPOINT: ${{ vars.SCCACHE_ENDPOINT }}
SCCACHE_CACHE_MULTIARCH: ${{ vars.SCCACHE_CACHE_MULTIARCH }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# Required to make some things output color
TERM: ansi
# Publishing to my nix binary cache
@@ -48,11 +38,8 @@ env:
# Custom nix binary cache if fork is being used
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
NIX_CONFIG: |
show-trace = true
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
# Get error output from nix that we can actually use
NIX_CONFIG: show-trace = true
permissions:
packages: write
@@ -62,8 +49,6 @@ jobs:
tests:
name: Test
runs-on: ubuntu-latest
env:
CARGO_PROFILE: "test"
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
@@ -72,7 +57,7 @@ jobs:
uses: actions/checkout@v4
- name: Tag comparison check
if: ${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') }}
if: startsWith(github.ref, 'refs/tags/v')
run: |
# Tag mismatch with latest repo tag check to prevent potential downgrades
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
@@ -130,39 +115,15 @@ jobs:
- name: Prepare build environment
run: |
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
direnv allow
nix develop .#all-features --command true
- name: Cache CI dependencies
run: |
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache ci
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
bin/nix-build-and-cache ci
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
# use sccache for Rust
- name: Run sccache-cache
if: (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]')
uses: mozilla-actions/sccache-action@main
# use rust-cache
- uses: Swatinem/rust-cache@v2
with:
cache-all-crates: "true"
- name: Run CI tests
run: |
@@ -216,10 +177,6 @@ jobs:
echo '```' >> $GITHUB_STEP_SUMMARY
fi
- name: Run cargo clean test artifacts
run: |
cargo clean --profile test
build:
name: Build
runs-on: ubuntu-latest
@@ -230,9 +187,6 @@ jobs:
- target: aarch64-unknown-linux-musl
- target: x86_64-unknown-linux-musl
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
- name: Sync repository
uses: actions/checkout@v4
@@ -283,43 +237,16 @@ jobs:
- name: Prepare build environment
run: |
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
direnv allow
nix develop .#all-features --command true
# use sccache for Rust
- name: Run sccache-cache
if: (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]')
uses: mozilla-actions/sccache-action@main
# use rust-cache
- uses: Swatinem/rust-cache@v2
with:
cache-all-crates: "true"
- name: Build static ${{ matrix.target }}
run: |
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache just .#static-${{ matrix.target }}-all-features
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
bin/nix-build-and-cache just .#static-${{ matrix.target }}-all-features
mkdir -v -p target/release/
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
cp -v -f result/bin/conduit target/release/conduwuit
@@ -329,66 +256,6 @@ jobs:
mv -v target/release/conduwuit static-${{ matrix.target }}
mv -v target/release/${{ matrix.target }}.deb ${{ matrix.target }}.deb
# quick smoke test of the x86_64 static release binary
- name: Run x86_64 static release binary
run: |
# GH actions default runners are x86_64 only
if file result/bin/conduit | grep x86-64; then
result/bin/conduit --version
fi
- name: Build static debug ${{ matrix.target }}
run: |
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache just .#static-${{ matrix.target }}-all-features-debug
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
# > warning: dev profile is not supported and will be a hard error in the future. cargo-deb is for making releases, and it doesn't make sense to use it with dev profiles.
# so we need to coerce cargo-deb into thinking this is a release binary
mkdir -v -p target/release/
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
cp -v -f result/bin/conduit target/release/conduwuit
cp -v -f result/bin/conduit target/$CARGO_DEB_TARGET_TUPLE/release/conduwuit
# -p conduit is the main crate name
direnv exec . cargo deb --verbose --no-build --no-strip -p conduit --target=$CARGO_DEB_TARGET_TUPLE --output target/release/${{ matrix.target }}-debug.deb
mv -v target/release/conduwuit static-${{ matrix.target }}-debug
mv -v target/release/${{ matrix.target }}-debug.deb ${{ matrix.target }}-debug.deb
# quick smoke test of the x86_64 static debug binary
- name: Run x86_64 static debug binary
run: |
# GH actions default runners are x86_64 only
if file result/bin/conduit | grep x86-64; then
result/bin/conduit --version
fi
# check validity of produced deb package, invalid debs will error on these commands
- name: Validate produced deb package
run: |
# List contents
dpkg-deb --contents ${{ matrix.target }}.deb
dpkg-deb --contents ${{ matrix.target }}-debug.deb
# List info
dpkg-deb --info ${{ matrix.target }}.deb
dpkg-deb --info ${{ matrix.target }}-debug.deb
- name: Upload static-${{ matrix.target }}
uses: actions/upload-artifact@v4
with:
@@ -404,65 +271,11 @@ jobs:
if-no-files-found: error
compression-level: 0
- name: Upload static-${{ matrix.target }}-debug
uses: actions/upload-artifact@v4
with:
name: static-${{ matrix.target }}-debug
path: static-${{ matrix.target }}-debug
if-no-files-found: error
- name: Upload deb ${{ matrix.target }}-debug
uses: actions/upload-artifact@v4
with:
name: deb-${{ matrix.target }}-debug
path: ${{ matrix.target }}-debug.deb
if-no-files-found: error
compression-level: 0
- name: Build OCI image ${{ matrix.target }}
run: |
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}-all-features
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}-all-features
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
- name: Build debug OCI image ${{ matrix.target }}
run: |
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}-all-features-debug
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
cp -v -f result oci-image-${{ matrix.target }}-debug.tar.gz
- name: Upload OCI image ${{ matrix.target }}
uses: actions/upload-artifact@v4
with:
@@ -471,14 +284,6 @@ jobs:
if-no-files-found: error
compression-level: 0
- name: Upload OCI image ${{ matrix.target }}-debug
uses: actions/upload-artifact@v4
with:
name: oci-image-${{ matrix.target }}-debug
path: oci-image-${{ matrix.target }}-debug.tar.gz
if-no-files-found: error
compression-level: 0
docker:
name: Docker publish
runs-on: ubuntu-latest
@@ -488,15 +293,15 @@ jobs:
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
@@ -529,10 +334,8 @@ jobs:
- name: Move OCI images into position
run: |
mv -v oci-image-x86_64-unknown-linux-musl/*.tar.gz oci-image-amd64.tar.gz
mv -v oci-image-aarch64-unknown-linux-musl/*.tar.gz oci-image-arm64v8.tar.gz
mv -v oci-image-x86_64-unknown-linux-musl-debug/*.tar.gz oci-image-amd64-debug.tar.gz
mv -v oci-image-aarch64-unknown-linux-musl-debug/*.tar.gz oci-image-arm64v8-debug.tar.gz
mv -v oci-image-x86_64-*/*.tar.gz oci-image-amd64.tar.gz
mv -v oci-image-aarch64-*/*.tar.gz oci-image-arm64v8.tar.gz
- name: Load and push amd64 image
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
@@ -556,28 +359,6 @@ jobs:
docker push ${{ env.GHCR_ARM64 }}
docker push ${{ env.GLCR_ARM64 }}
- name: Load and push amd64 debug image
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
docker load -i oci-image-amd64-debug.tar.gz
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_AMD64 }}-debug
docker tag $(docker images -q conduit:main) ${{ env.GHCR_AMD64 }}-debug
docker tag $(docker images -q conduit:main) ${{ env.GLCR_AMD64 }}-debug
docker push ${{ env.DOCKER_AMD64 }}-debug
docker push ${{ env.GHCR_AMD64 }}-debug
docker push ${{ env.GLCR_AMD64 }}-debug
- name: Load and push arm64 debug image
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
docker load -i oci-image-arm64v8-debug.tar.gz
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_ARM64 }}-debug
docker tag $(docker images -q conduit:main) ${{ env.GHCR_ARM64 }}-debug
docker tag $(docker images -q conduit:main) ${{ env.GLCR_ARM64 }}-debug
docker push ${{ env.DOCKER_ARM64 }}-debug
docker push ${{ env.GHCR_ARM64 }}-debug
docker push ${{ env.GLCR_ARM64 }}-debug
- name: Create Docker combined manifests
run: |
# Dockerhub Container Registry
@@ -587,21 +368,9 @@ jobs:
docker manifest create ${{ env.GHCR_TAG }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
docker manifest create ${{ env.GHCR_BRANCH }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
# GitLab Container Registry
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GCCR_AMD64 }}
docker manifest create ${{ env.GLCR_BRANCH }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
- name: Create Docker combined debug manifests
run: |
# Dockerhub Container Registry
docker manifest create ${{ env.DOCKER_TAG }}-debug --amend ${{ env.DOCKER_ARM64 }}-debug --amend ${{ env.DOCKER_AMD64 }}-debug
docker manifest create ${{ env.DOCKER_BRANCH }}-debug --amend ${{ env.DOCKER_ARM64 }}-debug --amend ${{ env.DOCKER_AMD64 }}-debug
# GitHub Container Registry
docker manifest create ${{ env.GHCR_TAG }}-debug --amend ${{ env.GHCR_ARM64 }}-debug --amend ${{ env.GHCR_AMD64 }}-debug
docker manifest create ${{ env.GHCR_BRANCH }}-debug --amend ${{ env.GHCR_ARM64 }}-debug --amend ${{ env.GHCR_AMD64 }}-debug
# GitLab Container Registry
docker manifest create ${{ env.GLCR_TAG }}-debug --amend ${{ env.GLCR_ARM64 }}-debug --amend ${{ env.GLCR_AMD64 }}-debug
docker manifest create ${{ env.GLCR_BRANCH }}-debug --amend ${{ env.GLCR_ARM64 }}-debug --amend ${{ env.GLCR_AMD64 }}-debug
- name: Push manifests to Docker registries
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
run: |
@@ -611,12 +380,6 @@ jobs:
docker manifest push ${{ env.GHCR_BRANCH }}
docker manifest push ${{ env.GLCR_TAG }}
docker manifest push ${{ env.GLCR_BRANCH }}
docker manifest push ${{ env.DOCKER_TAG }}-debug
docker manifest push ${{ env.DOCKER_BRANCH }}-debug
docker manifest push ${{ env.GHCR_TAG }}-debug
docker manifest push ${{ env.GHCR_BRANCH }}-debug
docker manifest push ${{ env.GLCR_TAG }}-debug
docker manifest push ${{ env.GLCR_BRANCH }}-debug
- name: Add Image Links to Job Summary
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
@@ -624,6 +387,3 @@ jobs:
echo "- \`docker pull ${{ env.DOCKER_TAG }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.GHCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.GLCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.DOCKER_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.GHCR_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
echo "- \`docker pull ${{ env.GLCR_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
+6 -46
View File
@@ -21,11 +21,8 @@ env:
# Custom nix binary cache if fork is being used
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
NIX_CONFIG: |
show-trace = true
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
# Get error output from nix that we can actually use
NIX_CONFIG: show-trace = true
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
@@ -47,9 +44,6 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
- name: Sync repository
uses: actions/checkout@v4
@@ -104,29 +98,13 @@ jobs:
- name: Prepare build environment
run: |
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
direnv allow
nix develop --command true
- name: Cache CI dependencies
run: |
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache ci
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
bin/nix-build-and-cache ci
- name: Run lychee and markdownlint
run: |
@@ -135,26 +113,8 @@ jobs:
- name: Build documentation (book)
run: |
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
ATTEMPTS=3
SUCCESS=false
while (( ATTEMPTS-- > 0 ))
do
bin/nix-build-and-cache just .#book
if [[ $? == 0 ]]; then
SUCCESS=true
break
else
sleep 3
fi
done
if [[ $SUCCESS == "false" ]]; then
exit 1
fi
cp -r --dereference result public
./bin/nix-build-and-cache just .#book
cp -r --dereference result public
- name: Upload generated documentation (book) as normal artifact
uses: actions/upload-artifact@v4
with:
-1
View File
@@ -39,7 +39,6 @@ The following binaries are used in [`engage.toml`][engage.toml]:
- [`cargo-deb`][cargo-deb]
- [`lychee`][lychee]
- [`markdownlint-cli`][markdownlint-cli]
- `dpkg`
### Matrix tests
Generated
+150 -191
View File
File diff suppressed because it is too large Load Diff
+32 -72
View File
@@ -14,13 +14,13 @@ categories = ["network-programming"]
description = "a very cool fork of Conduit, a Matrix homeserver written in Rust"
edition = "2021"
homepage = "https://conduwuit.puppyirl.gay/"
keywords = ["chat", "matrix", "server", "uwu"]
keywords = ["chat", "matrix", "server"]
license = "Apache-2.0"
# See also `rust-toolchain.toml`
readme = "README.md"
repository = "https://github.com/girlbossceo/conduwuit"
rust-version = "1.80.0"
version = "0.4.6"
rust-version = "1.77.0"
version = "0.4.5"
[workspace.metadata.crane]
name = "conduit"
@@ -28,18 +28,6 @@ name = "conduit"
[workspace.dependencies.const-str]
version = "0.5.7"
[workspace.dependencies.ctor]
version = "0.2.8"
[workspace.dependencies.cargo_toml]
version = "0.20"
features = ["features"]
[workspace.dependencies.toml]
version = "0.8.14"
default-features = false
features = ["parse"]
[workspace.dependencies.sanitize-filename]
version = "0.5.0"
@@ -200,7 +188,7 @@ version = "0.3.30"
default-features = false
[workspace.dependencies.tokio]
version = "1.39.1"
version = "1.38.0"
features = [
"fs",
"net",
@@ -216,7 +204,7 @@ features = [
version = "0.3.1"
[workspace.dependencies.libloading]
version = "0.8.5"
version = "0.8.3"
# Validating urls in config, was already a transitive dependency
[workspace.dependencies.url]
@@ -230,7 +218,7 @@ features = ["alloc", "std"]
default-features = false
[workspace.dependencies.hyper]
version = "1.4.1"
version = "1.4.0"
features = [
"server",
"http1",
@@ -263,7 +251,7 @@ default-features = false
# Used for conduit::Error type
[workspace.dependencies.thiserror]
version = "1.0.63"
version = "1.0.62"
# Used when hashing the state
[workspace.dependencies.ring]
@@ -292,7 +280,7 @@ version = "0.1.2"
[workspace.dependencies.ruma]
git = "https://github.com/girlbossceo/ruwuma"
#branch = "conduwuit-changes"
rev = "c76e2873c1593a3308d4ba3e0e4a1db65acf8536"
rev = "c51ccb2c68d2e3557eb12b1a49036531711ec0e5"
features = [
"compat",
"rand",
@@ -305,7 +293,6 @@ features = [
"server-util",
"unstable-exhaustive-types",
"ring-compat",
"identifiers-validation",
"unstable-unspecified",
"unstable-msc2448",
"unstable-msc2666",
@@ -320,6 +307,10 @@ features = [
"unstable-extensible-events",
]
[workspace.dependencies.ruma-identifiers-validation]
git = "https://github.com/girlbossceo/ruwuma"
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
[workspace.dependencies.rust-rocksdb]
path = "deps/rust-rocksdb"
package = "rust-rocksdb-uwu"
@@ -342,7 +333,7 @@ version = "0.10.8"
# optional opentelemetry, performance measurements, flamegraphs, etc for performance measurements and monitoring
[workspace.dependencies.opentelemetry]
version = "0.21.0"
version = "0.24.0"
[workspace.dependencies.tracing-flame]
version = "0.2.0"
@@ -351,11 +342,11 @@ version = "0.2.0"
version = "0.22.0"
[workspace.dependencies.opentelemetry_sdk]
version = "0.21.2"
version = "0.24.0"
features = ["rt-tokio"]
[workspace.dependencies.opentelemetry-jaeger]
version = "0.20.0"
version = "0.22.0"
features = ["rt-tokio"]
# optional sentry metrics for crash/panic reporting
@@ -381,17 +372,16 @@ version = "0.34.0"
version = "0.34.0"
# jemalloc usage
# locked to 0.5.4 due to static binary linking breakage
[workspace.dependencies.tikv-jemalloc-sys]
version = "=0.5.4"
version = "0.5.4"
default-features = false
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
[workspace.dependencies.tikv-jemallocator]
version = "=0.5.4"
version = "0.5.4"
default-features = false
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
[workspace.dependencies.tikv-jemalloc-ctl]
version = "=0.5.4"
version = "0.5.4"
default-features = false
features = ["use_std"]
@@ -415,8 +405,8 @@ features = [
]
[workspace.dependencies.rustyline-async]
version = "0.4.2"
default-features = false
git = "https://github.com/girlbossceo/rustyline-async"
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
[workspace.dependencies.termimad]
version = "0.29.4"
@@ -425,16 +415,6 @@ default-features = false
[workspace.dependencies.checked_ops]
version = "0.1"
[workspace.dependencies.syn]
version = "2.0.72"
features = ["full", "extra-traits"]
[workspace.dependencies.quote]
version = "1.0.36"
[workspace.dependencies.proc-macro2]
version = "1.0.86"
#
# Patches
@@ -445,16 +425,16 @@ version = "1.0.86"
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
[patch.crates-io.tracing-subscriber]
git = "https://github.com/girlbossceo/tracing"
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
rev = "b348dca742af641c47bc390261f60711c2af573c"
[patch.crates-io.tracing]
git = "https://github.com/girlbossceo/tracing"
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
rev = "b348dca742af641c47bc390261f60711c2af573c"
[patch.crates-io.tracing-core]
git = "https://github.com/girlbossceo/tracing"
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
rev = "b348dca742af641c47bc390261f60711c2af573c"
[patch.crates-io.tracing-log]
git = "https://github.com/girlbossceo/tracing"
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
rev = "b348dca742af641c47bc390261f60711c2af573c"
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
@@ -462,12 +442,6 @@ rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
git = "https://github.com/girlbossceo/axum-server"
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
# adds a tab completion callback: https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
# adds event for CTRL+\: https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
[patch.crates-io.rustyline-async]
git = "https://github.com/girlbossceo/rustyline-async"
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
#
# Our crates
#
@@ -502,11 +476,6 @@ package = "conduit_core"
path = "src/core"
default-features = false
[workspace.dependencies.conduit-macros]
package = "conduit_macros"
path = "src/macros"
default-features = false
###############################################################################
#
# Release profiles
@@ -684,16 +653,7 @@ opt-level = 'z'
# '-Clink-arg=-Wl,-z,nodelete',
#]
# primarily used for CI
[profile.test]
inherits = "dev"
codegen-units = 16
incremental = false
[profile.test.package.'*']
inherits = "dev"
debug = 0
codegen-units = 16
incremental = false
###############################################################################
@@ -755,19 +715,19 @@ variant_size_differences = "allow"
[workspace.lints.clippy]
###################
cargo = { level = "warn", priority = -1 }
cargo = "warn"
## some sadness
multiple_crate_versions = { level = "allow", priority = 1 }
###################
complexity = { level = "warn", priority = -1 }
complexity = "warn"
###################
correctness = { level = "warn", priority = -1 }
correctness = "warn"
###################
nursery = { level = "warn", priority = -1 }
nursery = "warn"
## some sadness
missing_const_for_fn = { level = "allow", priority = 1 } # TODO
@@ -777,7 +737,7 @@ significant_drop_in_scrutinee = { level = "allow", priority = 1 } # TODO
significant_drop_tightening = { level = "allow", priority = 1 } # TODO
###################
pedantic = { level = "warn", priority = -1 }
pedantic = "warn"
## some sadness
doc_markdown = { level = "allow", priority = 1 }
@@ -796,7 +756,7 @@ unnecessary_wraps = { level = "allow", priority = 1 }
unused_async = { level = "allow", priority = 1 }
###################
perf = { level = "warn", priority = -1 }
perf = "warn"
###################
#restriction = "warn"
@@ -846,14 +806,14 @@ unseparated_literal_suffix = "warn"
verbose_file_reads = "warn"
###################
style = { level = "warn", priority = -1 }
style = "warn"
## some sadness
# trivial assertions are quite alright
assertions_on_constants = { level = "allow", priority = 1 }
###################
suspicious = { level = "warn", priority = -1 }
suspicious = "warn"
## some sadness
let_underscore_future = { level = "allow", priority = 1 }
+4 -8
View File
@@ -52,10 +52,9 @@
# Performance monitoring/tracing sample rate for Sentry.io
#
# Note that too high values may impact performance, and can be disabled by setting it to 0.0 (0%)
# This value is read as a percentage to Sentry, represented as a decimal
# Note that too high values may impact performance, and can be disabled by setting it to 0.0
#
# Defaults to 15% of traces (0.15)
# Defaults to 0.15
#sentry_traces_sample_rate = 0.15
# Whether to attach a stacktrace to Sentry reports.
@@ -402,7 +401,7 @@ allow_profile_lookup_federation_requests = true
# setting this to false may reduce startup time.
#
# Enabled by default.
#media_startup_check = true
#media_statup_check = true
# OpenID token expiration/TTL in seconds
#
@@ -422,11 +421,8 @@ allow_profile_lookup_federation_requests = true
# Set this to any float value to multiply conduwuit's in-memory LRU caches with.
# May be useful if you have significant memory to spare to increase performance.
#
# This was previously called `conduit_cache_capacity_modifier`
#
# Defaults to 1.0.
#cache_capacity_modifier = 1.0
#conduit_cache_capacity_modifier = 1.0
# Set this to any float value in megabytes for conduwuit to tell the database engine that this much memory is available for database-related caches.
# May be useful if you have significant memory to spare to increase performance.
+1 -1
View File
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
[dependencies.rust-rocksdb]
git = "https://github.com/zaidoon1/rust-rocksdb"
rev = "4056a3b0f823013fec49f6d0b3e5698856e6476a"
rev = "db1ba33c2b78ad228e2525e8902d059c24fc81a1"
#branch = "master"
default-features = false
+5 -3
View File
@@ -23,7 +23,7 @@ Otherwise, follow standard Rust project build guides (installing git and cloning
While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
you to make sure that the file permissions are correctly set up.
In Debian or Fedora/RHEL, you can use this command to create a conduwuit user:
In Debian or RHEL, you can use this command to create a conduwuit user:
```bash
sudo adduser --system conduwuit --group --disabled-login --no-create-home
@@ -53,11 +53,13 @@ RocksDB is the only supported database backend.
## Setting the correct file permissions
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this:
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
Debian or RHEL:
```bash
sudo chown -R root:root /etc/conduwuit
sudo chmod -R 755 /etc/conduwuit
sudo chmod 755 /etc/conduwuit
```
If you use the default database path you also need to run this:
+3 -22
View File
@@ -60,11 +60,6 @@ name = "markdownlint"
group = "versions"
script = "markdownlint --version"
[[task]]
name = "dpkg"
group = "versions"
script = "dpkg --version"
[[task]]
name = "cargo-audit"
group = "security"
@@ -73,9 +68,7 @@ script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked"
[[task]]
name = "cargo-fmt"
group = "lints"
script = """
cargo fmt --check -- --color=always
"""
script = "cargo fmt --check -- --color=always"
[[task]]
name = "cargo-doc"
@@ -86,7 +79,6 @@ env DIRENV_DEVSHELL=all-features \
direnv exec . \
cargo doc \
--workspace \
--profile test \
--all-features \
--no-deps \
--document-private-items \
@@ -99,7 +91,6 @@ group = "lints"
script = """
cargo clippy \
--workspace \
--profile test \
--all-targets \
--color=always \
-- \
@@ -114,7 +105,6 @@ env DIRENV_DEVSHELL=all-features \
direnv exec . \
cargo clippy \
--workspace \
--profile test \
--all-targets \
--all-features \
--color=always \
@@ -128,7 +118,6 @@ group = "lints"
script = """
cargo clippy \
--workspace \
--profile test \
--features jemalloc \
--all-targets \
--color=always \
@@ -167,7 +156,6 @@ env DIRENV_DEVSHELL=all-features \
direnv exec . \
cargo test \
--workspace \
--profile test \
--all-targets \
--all-features \
--color=always \
@@ -181,7 +169,6 @@ group = "tests"
script = """
cargo test \
--workspace \
--profile test \
--all-targets \
--color=always \
-- \
@@ -197,12 +184,6 @@ cargo test \
name = "nix-default"
group = "tests"
script = """
env DIRENV_DEVSHELL=dynamic \
CARGO_PROFILE="test" \
direnv exec . \
bin/nix-build-and-cache just .#default-test
env DIRENV_DEVSHELL=dynamic \
CARGO_PROFILE="test" \
direnv exec . \
nix run -L .#default-test -- --help && nix run -L .#default-test -- --version
bin/nix-build-and-cache just .#default
nix run -L .#default -- --help
"""
Generated
+25 -25
View File
@@ -9,11 +9,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1720542474,
"narHash": "sha256-aKjJ/4l2I9+wNGTaOGRsuS3M1+IoTibqgEMPDikXm04=",
"lastModified": 1717279440,
"narHash": "sha256-kH04ReTjxOpQumgWnqy40vvQLSnLGxWP6RF3nq5Esrk=",
"owner": "zhaofengli",
"repo": "attic",
"rev": "6139576a3ce6bb992e0f6c3022528ec233e45f00",
"rev": "717cc95983cdc357bc347d70be20ced21f935843",
"type": "github"
},
"original": {
@@ -81,11 +81,11 @@
"complement": {
"flake": false,
"locked": {
"lastModified": 1720637557,
"narHash": "sha256-oZz6nCmFmdJZpC+K1iOG2KkzTI6rlAmndxANPDVU7X0=",
"lastModified": 1719903368,
"narHash": "sha256-PPzgxM4Bir+Zh9FUV/v+RBxEYeJxYVmi/BYo3uqt268=",
"owner": "matrix-org",
"repo": "complement",
"rev": "0d14432e010482ea9e13a6f7c47c1533c0c9d62f",
"rev": "bc97f1ddc1cd7485faf80c8935ee2641f3e1b57c",
"type": "github"
},
"original": {
@@ -123,11 +123,11 @@
]
},
"locked": {
"lastModified": 1720546058,
"narHash": "sha256-iU2yVaPIZm5vMGdlT0+57vdB/aPq/V5oZFBRwYw+HBM=",
"lastModified": 1720226507,
"narHash": "sha256-yHVvNsgrpyNTXZBEokL8uyB2J6gB1wEx0KOJzoeZi1A=",
"owner": "ipetkov",
"repo": "crane",
"rev": "2d83156f23c43598cf44e152c33a59d3892f8b29",
"rev": "0aed560c5c0a61c9385bddff471a13036203e11c",
"type": "github"
},
"original": {
@@ -209,11 +209,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1720852044,
"narHash": "sha256-3NBYz8VuXuKU+8ONd9NFafCNjPEGHIZQ2Mdoam1a4mY=",
"lastModified": 1720420198,
"narHash": "sha256-OIuDb6pHDyGpo7YMFyuRzMLcHm7mRvlYOz0Ht7ps2sU=",
"owner": "nix-community",
"repo": "fenix",
"rev": "5087b12a595ee73131a944d922f24d81dae05725",
"rev": "abc0549e3560189462a7d394cc9d50af4608d103",
"type": "github"
},
"original": {
@@ -381,11 +381,11 @@
"liburing": {
"flake": false,
"locked": {
"lastModified": 1720798442,
"narHash": "sha256-gtPppAoksMLW4GuruQ36nf4EAqIA1Bs6V9Xcx8dBxrQ=",
"lastModified": 1719025212,
"narHash": "sha256-kD0yhjNStqC6uFqC1AxBwUpc/HlSFtiKrV+gwDyroDc=",
"owner": "axboe",
"repo": "liburing",
"rev": "1d674f83b7d0f07553ac44d99a401b05853d9dbe",
"rev": "7b3245583069bd481190c9da18f22e9fc8c3a805",
"type": "github"
},
"original": {
@@ -606,11 +606,11 @@
},
"nixpkgs_4": {
"locked": {
"lastModified": 1720768451,
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
"lastModified": 1720418205,
"narHash": "sha256-cPJoFPXU44GlhWg4pUk9oUPqurPlCFZ11ZQPk21GTPU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
"rev": "655a58a72a6601292512670343087c2d75d859c1",
"type": "github"
},
"original": {
@@ -673,16 +673,16 @@
"rocksdb": {
"flake": false,
"locked": {
"lastModified": 1720900786,
"narHash": "sha256-Vta9Um/RRuWwZ46BjXftV06iWLm/j/9MX39emXUvSAY=",
"lastModified": 1719949653,
"narHash": "sha256-DYx7XHH2GEh17GukKhXs6laM6l+eugCmRkF0adpi9wk=",
"owner": "girlbossceo",
"repo": "rocksdb",
"rev": "911f4243e69c2e320a7a209bf1f5f3ff5f825495",
"rev": "a935c0273e1ba44eacf88ce3685a9b9831486155",
"type": "github"
},
"original": {
"owner": "girlbossceo",
"ref": "v9.4.0",
"ref": "v9.3.1",
"repo": "rocksdb",
"type": "github"
}
@@ -705,11 +705,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1720717809,
"narHash": "sha256-6I+fm+nTLF/iaj7ffiFGlSY7POmubwUaPA/Wq0Bm53M=",
"lastModified": 1720344064,
"narHash": "sha256-STmaV9Zu74QtkGGrbr9uMhskwagfCjJqOAYapXabiuk=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "ffbc5ad993d5cd2f3b8bcf9a511165470944ab91",
"rev": "a5b21ea0aa644dffd7cf958b43f11f221d53404e",
"type": "github"
},
"original": {
+16 -167
View File
@@ -9,7 +9,8 @@
flake-utils.url = "github:numtide/flake-utils?ref=main";
nix-filter.url = "github:numtide/nix-filter?ref=main";
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.4.0"; flake = false; };
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.3.1"; flake = false; };
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
};
@@ -24,7 +25,7 @@
file = ./rust-toolchain.toml;
# See also `rust-toolchain.toml`
sha256 = "sha256-6eN/GKzjVSjEhGO9FhWObkRFaE1Jf+uqMSdQnb8lcB4=";
sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8";
};
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
@@ -44,34 +45,10 @@
# we have this already at https://github.com/girlbossceo/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
# unsetting this so i don't have to revert it and make this nix exclusive
patches = [];
# no real reason to have snappy, no one uses this
cmakeFlags = pkgs.lib.subtractLists
[
# no real reason to have snappy, no one uses this
"-DWITH_SNAPPY=1"
# we dont need to use ldb or sst_dump (core_tools)
"-DWITH_CORE_TOOLS=1"
# we dont need to build rocksdb tests
"-DWITH_TESTS=1"
# we use rust-rocksdb via C interface and dont need C++ RTTI
"-DUSE_RTTI=1"
]
old.cmakeFlags
++ [
# we dont need to use ldb or sst_dump (core_tools)
"-DWITH_CORE_TOOLS=0"
# we dont need trace tools
"-DWITH_TRACE_TOOLS=0"
# we dont need to build rocksdb tests
"-DWITH_TESTS=0"
# we use rust-rocksdb via C interface and dont need C++ RTTI
"-DUSE_RTTI=0"
];
# outputs has "tools" which we dont need or use
outputs = [ "out" ];
# preInstall hooks has stuff for messing with ldb/sst_dump which we dont need or use
preInstall = "";
[ "-DWITH_SNAPPY=1" ]
old.cmakeFlags;
});
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
liburing = pkgs.liburing.overrideAttrs (old: {
@@ -119,9 +96,6 @@
# Needed for producing Debian packages
cargo-deb
# Needed for CI to check validity of produced Debian packages (dpkg-deb)
dpkg
# Needed for Complement
go
@@ -136,8 +110,6 @@
# Useful for editing the book locally
mdbook
sccache
])
++ scope.main.buildInputs
++ scope.main.propagatedBuildInputs
@@ -149,35 +121,10 @@
{
packages = {
default = scopeHost.main;
default-debug = scopeHost.main.override {
profile = "dev";
# debug build users expect full logs
disable_release_max_log_level = true;
};
default-test = scopeHost.main.override {
profile = "test";
disable_release_max_log_level = true;
};
all-features = scopeHost.main.override {
all_features = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
};
all-features-debug = scopeHost.main.override {
profile = "dev";
all_features = true;
# debug build users expect full logs
disable_release_max_log_level = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
# this is non-functional on nix for some reason
disable_features = ["hardened_malloc"];
};
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
@@ -185,26 +132,8 @@
oci-image-all-features = scopeHost.oci-image.override {
main = scopeHost.main.override {
all_features = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
};
};
oci-image-all-features-debug = scopeHost.oci-image.override {
main = scopeHost.main.override {
profile = "dev";
all_features = true;
# debug build users expect full logs
disable_release_max_log_level = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
# this is non-functional on nix for some reason
disable_features = ["hardened_malloc"];
};
};
oci-image-hmalloc = scopeHost.oci-image.override {
@@ -241,54 +170,13 @@
value = scopeCrossStatic.main;
}
# An output for a statically-linked unstripped debug ("dev") binary
{
name = "${binaryName}-debug";
value = scopeCrossStatic.main.override {
profile = "dev";
# debug build users expect full logs
disable_release_max_log_level = true;
};
}
# An output for a statically-linked unstripped debug binary with the
# "test" profile (for CI usage only)
{
name = "${binaryName}-test";
value = scopeCrossStatic.main.override {
profile = "test";
disable_release_max_log_level = true;
};
}
# An output for a statically-linked binary with `--all-features`
{
name = "${binaryName}-all-features";
value = scopeCrossStatic.main.override {
all_features = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
};
}
# An output for a statically-linked unstripped debug ("dev") binary with `--all-features`
{
name = "${binaryName}-all-features-debug";
value = scopeCrossStatic.main.override {
profile = "dev";
all_features = true;
# debug build users expect full logs
disable_release_max_log_level = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
# this is non-functional on nix for some reason
disable_features = ["hardened_malloc"];
};
}
@@ -306,49 +194,14 @@
value = scopeCrossStatic.oci-image;
}
# An output for an OCI image based on that unstripped debug ("dev") binary
{
name = "oci-image-${crossSystem}-debug";
value = scopeCrossStatic.oci-image.override {
main = scopeCrossStatic.main.override {
profile = "dev";
# debug build users expect full logs
disable_release_max_log_level = true;
};
};
}
# An output for an OCI image based on that binary with `--all-features`
{
name = "oci-image-${crossSystem}-all-features";
value = scopeCrossStatic.oci-image.override {
main = scopeCrossStatic.main.override {
all_features = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
};
};
}
# An output for an OCI image based on that unstripped debug ("dev") binary with `--all-features`
{
name = "oci-image-${crossSystem}-all-features-debug";
value = scopeCrossStatic.oci-image.override {
main = scopeCrossStatic.main.override {
profile = "dev";
all_features = true;
# debug build users expect full logs
disable_release_max_log_level = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
# this is non-functional on nix for some reason
disable_features = ["hardened_malloc"];
};
};
}
@@ -376,12 +229,8 @@
(scopeHostStatic.overrideScope (final: prev: {
main = prev.main.override {
all_features = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
# this is non-functional on nix for some reason
disable_features = ["hardened_malloc"];
};
}));
devShells.no-features = mkDevShell
+1 -1
View File
@@ -14,7 +14,7 @@ yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
ip_range_denylist = []
url_preview_domain_contains_allowlist = ["*"]
media_compat_file_link = false
media_startup_check = false
media_statup_check = false
rocksdb_direct_io = false
[global.tls]
+2 -9
View File
@@ -14,15 +14,8 @@
let
main' = main.override {
profile = "test";
all_features = true;
disable_release_max_log_level = true;
disable_features = [
# this is non-functional on nix for some reason
"hardened_malloc"
# dont include experimental features
"experimental"
];
profile = "dev";
features = ["axum_dual_protocol"];
};
start = writeShellScriptBin "start" ''
-6
View File
@@ -13,12 +13,6 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic {
lib.concatStringsSep
" "
([]
++ lib.optionals
stdenv.targetPlatform.isx86_64
[ "-C" "target-cpu=x86-64-v2" ]
++ lib.optionals
stdenv.targetPlatform.isAarch64
[ "-C" "target-cpu=cortex-a55" ] # cortex-a55 == ARMv8.2-a
# This disables PIE for static builds, which isn't great in terms
# of security. Unfortunately, my hand is forced because nixpkgs'
# `libstdc++.a` is built without `-fPIE`, which precludes us from
+2 -31
View File
@@ -51,10 +51,6 @@ rust-jemalloc-sys' = (rust-jemalloc-sys.override {
unprefixed = true;
}).overrideAttrs (old: {
configureFlags = old.configureFlags ++
# we dont need docs
[ "--disable-doc" ] ++
# we dont need cxx/C++ integration
[ "--disable-cxx" ] ++
# tikv-jemalloc-sys/profiling feature
lib.optional (featureEnabled "jemalloc_prof") "--enable-prof";
});
@@ -75,29 +71,10 @@ buildDepsOnlyEnv =
# which breaks Darwin entirely
enableLiburing = enableLiburing;
}).overrideAttrs (old: {
# TODO: static rocksdb fails to build on darwin, also see <https://github.com/NixOS/nixpkgs/issues/320448>
# TODO: static rocksdb fails to build on darwin
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
enableLiburing = enableLiburing;
sse42Support = stdenv.targetPlatform.isx86_64;
cmakeFlags = if stdenv.targetPlatform.isx86_64
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
++ lib.optionals stdenv.targetPlatform.isx86_64 [
"-DPORTABLE=x86-64-v2"
"-DUSE_SSE=1"
"-DHAVE_SSE=1"
"-DHAVE_SSE42=1"
]
else if stdenv.targetPlatform.isAarch64
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
++ lib.optionals stdenv.targetPlatform.isAarch64 [
# cortex-a55 == ARMv8.2-a
"-DPORTABLE=armv8.2-a"
]
else old.cmakeFlags;
});
in
{
@@ -124,11 +101,7 @@ buildPackageEnv = {
# Only needed in static stdenv because these are transitive dependencies of rocksdb
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
+ lib.optionalString (enableLiburing && stdenv.hostPlatform.isStatic)
" -L${lib.getLib liburing}/lib -luring"
+ lib.optionalString stdenv.targetPlatform.isx86_64
" -Ctarget-cpu=x86-64-v2"
+ lib.optionalString stdenv.targetPlatform.isAarch64
" -Ctarget-cpu=cortex-a55"; # cortex-a55 == ARMv8.2-a
" -L${lib.getLib liburing}/lib -luring";
};
@@ -153,8 +126,6 @@ commonAttrs = {
];
};
dontStrip = profile == "dev" || profile == "test";
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
nativeBuildInputs = [
+1 -1
View File
@@ -11,7 +11,7 @@
# If you're having trouble making the relevant changes, bug a maintainer.
[toolchain]
channel = "1.80.0"
channel = "1.77.0"
components = [
# For rust-analyzer
"rust-src",
-1
View File
@@ -29,7 +29,6 @@ release_max_log_level = [
clap.workspace = true
conduit-api.workspace = true
conduit-core.workspace = true
conduit-macros.workspace = true
conduit-service.workspace = true
const-str.workspace = true
futures-util.workspace = true
-66
View File
@@ -1,66 +0,0 @@
use clap::Parser;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::{
appservice, appservice::AppserviceCommand, check, check::CheckCommand, command::Command, debug,
debug::DebugCommand, federation, federation::FederationCommand, media, media::MediaCommand, query,
query::QueryCommand, room, room::RoomCommand, server, server::ServerCommand, user, user::UserCommand,
};
#[derive(Debug, Parser)]
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
pub(super) enum AdminCommand {
#[command(subcommand)]
/// - Commands for managing appservices
Appservices(AppserviceCommand),
#[command(subcommand)]
/// - Commands for managing local users
Users(UserCommand),
#[command(subcommand)]
/// - Commands for managing rooms
Rooms(RoomCommand),
#[command(subcommand)]
/// - Commands for managing federation
Federation(FederationCommand),
#[command(subcommand)]
/// - Commands for managing the server
Server(ServerCommand),
#[command(subcommand)]
/// - Commands for managing media
Media(MediaCommand),
#[command(subcommand)]
/// - Commands for checking integrity
Check(CheckCommand),
#[command(subcommand)]
/// - Commands for debugging things
Debug(DebugCommand),
#[command(subcommand)]
/// - Low-level queries for database getters and iterators
Query(QueryCommand),
}
#[tracing::instrument(skip_all, name = "command")]
pub(super) async fn process(command: AdminCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
use AdminCommand::*;
Ok(match command {
Appservices(command) => appservice::process(command, context).await?,
Media(command) => media::process(command, context).await?,
Users(command) => user::process(command, context).await?,
Rooms(command) => room::process(command, context).await?,
Federation(command) => federation::process(command, context).await?,
Server(command) => server::process(command, context).await?,
Debug(command) => debug::process(command, context).await?,
Query(command) => query::process(command, context).await?,
Check(command) => check::process(command, context).await?,
})
}
+11 -18
View File
@@ -1,20 +1,18 @@
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
use crate::{admin_command, Result};
use crate::{services, Result};
#[admin_command]
pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
pub(super) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let appservice_config = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n");
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
match parsed_config {
Ok(yaml) => match self.services.appservice.register_appservice(yaml).await {
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
"Appservice registered with ID: {id}."
))),
@@ -28,10 +26,8 @@ pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
}
}
#[admin_command]
pub(super) async fn unregister(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
match self
.services
pub(super) async fn unregister(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
match services()
.appservice
.unregister_appservice(&appservice_identifier)
.await
@@ -43,10 +39,8 @@ pub(super) async fn unregister(&self, appservice_identifier: String) -> Result<R
}
}
#[admin_command]
pub(super) async fn show_appservice_config(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
match self
.services
pub(super) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
match services()
.appservice
.get_registration(&appservice_identifier)
.await
@@ -60,9 +54,8 @@ pub(super) async fn show_appservice_config(&self, appservice_identifier: String)
}
}
#[admin_command]
pub(super) async fn list_registered(&self) -> Result<RoomMessageEventContent> {
let appservices = self.services.appservice.iter_ids().await;
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let appservices = services().appservice.iter_ids().await;
let output = format!("Appservices ({}): {}", appservices.len(), appservices.join(", "));
Ok(RoomMessageEventContent::text_plain(output))
}
+19 -7
View File
@@ -2,11 +2,12 @@ mod commands;
use clap::Subcommand;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::admin_command_dispatch;
use self::commands::*;
#[derive(Debug, Subcommand)]
#[admin_command_dispatch]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum AppserviceCommand {
/// - Register an appservice using its registration YAML
///
@@ -28,13 +29,24 @@ pub(super) enum AppserviceCommand {
/// - Show an appservice's config using its ID
///
/// You can find the ID using the `list-appservices` command.
#[clap(alias("show"))]
ShowAppserviceConfig {
Show {
/// The appservice to show
appservice_identifier: String,
},
/// - List all the currently registered appservices
#[clap(alias("list"))]
ListRegistered,
List,
}
pub(super) async fn process(command: AppserviceCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
AppserviceCommand::Register => register(body).await?,
AppserviceCommand::Unregister {
appservice_identifier,
} => unregister(body, appservice_identifier).await?,
AppserviceCommand::Show {
appservice_identifier,
} => show(body, appservice_identifier).await?,
AppserviceCommand::List => list(body).await?,
})
}
+3 -5
View File
@@ -1,16 +1,14 @@
use conduit::Result;
use conduit_macros::implement;
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
use crate::services;
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
/// every user in our database (remote and local). Reports total count, any
/// errors if there were any, etc
#[implement(Command, params = "<'_>")]
pub(super) async fn check_all_users(&self) -> Result<RoomMessageEventContent> {
pub(super) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let timer = tokio::time::Instant::now();
let results = self.services.users.db.iter();
let results = services().users.db.iter();
let query_time = timer.elapsed();
let users = results.collect::<Vec<_>>();
+5 -4
View File
@@ -4,15 +4,16 @@ use clap::Subcommand;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
use self::commands::*;
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum CheckCommand {
AllUsers,
}
pub(super) async fn process(command: CheckCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
pub(super) async fn process(command: CheckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
CheckCommand::AllUsers => context.check_all_users().await?,
CheckCommand::AllUsers => check_all_users(body).await?,
})
}
-6
View File
@@ -1,6 +0,0 @@
use service::Services;
pub(crate) struct Command<'a> {
pub(crate) services: &'a Services,
pub(crate) body: &'a [&'a str],
}
+100 -236
View File
@@ -1,37 +1,33 @@
use std::{
collections::{BTreeMap, HashMap},
fmt::Write,
sync::{Arc, Mutex},
time::{Instant, SystemTime},
time::Instant,
};
use api::client::validate_and_add_event_id;
use conduit::{
debug, info, log,
log::{capture, Capture},
utils, warn, Error, PduEvent, Result,
warn, Error, Result,
};
use ruma::{
api::{client::error::ErrorKind, federation::event::get_room_state},
events::room::message::RoomMessageEventContent,
CanonicalJsonObject, EventId, OwnedRoomOrAliasId, RoomId, RoomVersionId, ServerName,
};
use service::{rooms::event_handler::parse_incoming_pdu, sending::resolve_actual_dest, services, PduEvent};
use tokio::sync::RwLock;
use tracing_subscriber::EnvFilter;
use crate::admin_command;
#[admin_command]
pub(super) async fn echo(&self, message: Vec<String>) -> Result<RoomMessageEventContent> {
pub(super) async fn echo(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
let message = message.join(" ");
Ok(RoomMessageEventContent::notice_plain(message))
}
#[admin_command]
pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
pub(super) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
let event_id = Arc::<EventId>::from(event_id);
if let Some(event) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
if let Some(event) = services().rooms.timeline.get_pdu_json(&event_id)? {
let room_id_str = event
.get("room_id")
.and_then(|val| val.as_str())
@@ -39,16 +35,13 @@ pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<Room
let room_id = <&RoomId>::try_from(room_id_str)
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
let start = Instant::now();
let count = self
.services
let count = services()
.rooms
.auth_chain
.event_ids_iter(room_id, vec![event_id])
.await?
.count();
let elapsed = start.elapsed();
Ok(RoomMessageEventContent::text_plain(format!(
"Loaded auth chain with length {count} in {elapsed:?}"
@@ -58,16 +51,14 @@ pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<Room
}
}
#[admin_command]
pub(super) async fn parse_pdu(&self) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
pub(super) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let string = self.body[1..self.body.len().saturating_sub(1)].join("\n");
let string = body[1..body.len().saturating_sub(1)].join("\n");
match serde_json::from_str(&string) {
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
Ok(hash) => {
@@ -88,17 +79,15 @@ pub(super) async fn parse_pdu(&self) -> Result<RoomMessageEventContent> {
}
}
#[admin_command]
pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
pub(super) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
let mut outlier = false;
let mut pdu_json = self
.services
let mut pdu_json = services()
.rooms
.timeline
.get_non_outlier_pdu_json(&event_id)?;
if pdu_json.is_none() {
outlier = true;
pdu_json = self.services.rooms.timeline.get_pdu_json(&event_id)?;
pdu_json = services().rooms.timeline.get_pdu_json(&event_id)?;
}
match pdu_json {
Some(json) => {
@@ -117,42 +106,38 @@ pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessage
}
}
#[admin_command]
pub(super) async fn get_remote_pdu_list(
&self, server: Box<ServerName>, force: bool,
body: Vec<&str>, server: Box<ServerName>, force: bool,
) -> Result<RoomMessageEventContent> {
if !self.services.globals.config.allow_federation {
if !services().globals.config.allow_federation {
return Ok(RoomMessageEventContent::text_plain(
"Federation is disabled on this homeserver.",
));
}
if server == self.services.globals.server_name() {
if server == services().globals.server_name() {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs from \
the database.",
));
}
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let list = self
.body
.iter()
.collect::<Vec<_>>()
.drain(1..self.body.len().saturating_sub(1))
let list = body
.clone()
.drain(1..body.len().checked_sub(1).unwrap())
.filter_map(|pdu| EventId::parse(pdu).ok())
.collect::<Vec<_>>();
for pdu in list {
if force {
if let Err(e) = self.get_remote_pdu(Box::from(pdu), server.clone()).await {
self.services
if let Err(e) = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await {
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Failed to get remote PDU, ignoring error: {e}"
@@ -161,31 +146,29 @@ pub(super) async fn get_remote_pdu_list(
warn!(%e, "Failed to get remote PDU, ignoring error");
}
} else {
self.get_remote_pdu(Box::from(pdu), server.clone()).await?;
get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?;
}
}
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
}
#[admin_command]
pub(super) async fn get_remote_pdu(
&self, event_id: Box<EventId>, server: Box<ServerName>,
_body: Vec<&str>, event_id: Box<EventId>, server: Box<ServerName>,
) -> Result<RoomMessageEventContent> {
if !self.services.globals.config.allow_federation {
if !services().globals.config.allow_federation {
return Ok(RoomMessageEventContent::text_plain(
"Federation is disabled on this homeserver.",
));
}
if server == self.services.globals.server_name() {
if server == services().globals.server_name() {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
));
}
match self
.services
match services()
.sending
.send_federation_request(
&server,
@@ -206,11 +189,7 @@ pub(super) async fn get_remote_pdu(
debug!("Attempting to parse PDU: {:?}", &response.pdu);
let parsed_pdu = {
let parsed_result = self
.services
.rooms
.event_handler
.parse_incoming_pdu(&response.pdu);
let parsed_result = parse_incoming_pdu(&response.pdu);
let (event_id, value, room_id) = match parsed_result {
Ok(t) => t,
Err(e) => {
@@ -228,7 +207,7 @@ pub(super) async fn get_remote_pdu(
let pub_key_map = RwLock::new(BTreeMap::new());
debug!("Attempting to fetch homeserver signing keys for {server}");
self.services
services()
.rooms
.event_handler
.fetch_required_signing_keys(parsed_pdu.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
@@ -238,7 +217,7 @@ pub(super) async fn get_remote_pdu(
});
info!("Attempting to handle event ID {event_id} as backfilled PDU");
self.services
services()
.rooms
.timeline
.backfill_pdu(&server, response.pdu, &pub_key_map)
@@ -257,11 +236,9 @@ pub(super) async fn get_remote_pdu(
}
}
#[admin_command]
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
let room_id = self.services.rooms.alias.resolve(&room).await?;
let room_state = self
.services
pub(super) async fn get_room_state(_body: Vec<&str>, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
let room_id = services().rooms.alias.resolve(&room).await?;
let room_state = services()
.rooms
.state_accessor
.room_state_full(&room_id)
@@ -286,9 +263,8 @@ pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<Ro
Ok(RoomMessageEventContent::notice_markdown(format!("```json\n{json}\n```")))
}
#[admin_command]
pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
if server == self.services.globals.server_name() {
pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
if server == services().globals.server_name() {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to send federation requests to ourselves.",
));
@@ -296,8 +272,7 @@ pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEv
let timer = tokio::time::Instant::now();
match self
.services
match services()
.sending
.send_federation_request(&server, ruma::api::federation::discovery::get_server_version::v1::Request {})
.await
@@ -326,23 +301,23 @@ pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEv
}
}
#[admin_command]
pub(super) async fn force_device_list_updates(&self) -> Result<RoomMessageEventContent> {
pub(super) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
// Force E2EE device list updates for all users
for user_id in self.services.users.iter().filter_map(Result::ok) {
self.services.users.mark_device_key_update(&user_id)?;
for user_id in services().users.iter().filter_map(Result::ok) {
services().users.mark_device_key_update(&user_id)?;
}
Ok(RoomMessageEventContent::text_plain(
"Marked all devices for all users as having new keys to update",
))
}
#[admin_command]
pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool) -> Result<RoomMessageEventContent> {
pub(super) async fn change_log_level(
_body: Vec<&str>, filter: Option<String>, reset: bool,
) -> Result<RoomMessageEventContent> {
let handles = &["console"];
if reset {
let old_filter_layer = match EnvFilter::try_new(&self.services.globals.config.log) {
let old_filter_layer = match EnvFilter::try_new(&services().globals.config.log) {
Ok(s) => s,
Err(e) => {
return Ok(RoomMessageEventContent::text_plain(format!(
@@ -351,8 +326,7 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
},
};
match self
.services
match services()
.server
.log
.reload
@@ -361,7 +335,7 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
Ok(()) => {
return Ok(RoomMessageEventContent::text_plain(format!(
"Successfully changed log level back to config value {}",
self.services.globals.config.log
services().globals.config.log
)));
},
Err(e) => {
@@ -382,8 +356,7 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
},
};
match self
.services
match services()
.server
.log
.reload
@@ -403,21 +376,19 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
Ok(RoomMessageEventContent::text_plain("No log level was specified."))
}
#[admin_command]
pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
pub(super) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
match serde_json::from_str(&string) {
Ok(mut value) => {
ruma::signatures::sign_json(
self.services.globals.server_name().as_str(),
self.services.globals.keypair(),
services().globals.server_name().as_str(),
services().globals.keypair(),
&mut value,
)
.expect("our request json is what ruma expects");
@@ -428,21 +399,19 @@ pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
}
}
#[admin_command]
pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
pub(super) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
match serde_json::from_str(&string) {
Ok(value) => {
let pub_key_map = RwLock::new(BTreeMap::new());
self.services
services()
.rooms
.event_handler
.fetch_required_signing_keys([&value], &pub_key_map)
@@ -460,22 +429,19 @@ pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
}
}
#[admin_command]
#[tracing::instrument(skip(self))]
pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
if !self
.services
#[tracing::instrument(skip(_body))]
pub(super) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
if !services()
.rooms
.state_cache
.server_in_room(&self.services.globals.config.server_name, &room_id)?
.server_in_room(&services().globals.config.server_name, &room_id)?
{
return Ok(RoomMessageEventContent::text_plain(
"We are not participating in the room / we don't know about the room ID.",
));
}
let first_pdu = self
.services
let first_pdu = services()
.rooms
.timeline
.first_pdu_in_room(&room_id)?
@@ -484,22 +450,19 @@ pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<Roo
Ok(RoomMessageEventContent::text_plain(format!("{first_pdu:?}")))
}
#[admin_command]
#[tracing::instrument(skip(self))]
pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
if !self
.services
#[tracing::instrument(skip(_body))]
pub(super) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
if !services()
.rooms
.state_cache
.server_in_room(&self.services.globals.config.server_name, &room_id)?
.server_in_room(&services().globals.config.server_name, &room_id)?
{
return Ok(RoomMessageEventContent::text_plain(
"We are not participating in the room / we don't know about the room ID.",
));
}
let latest_pdu = self
.services
let latest_pdu = services()
.rooms
.timeline
.latest_pdu_in_room(&room_id)?
@@ -508,36 +471,32 @@ pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<Ro
Ok(RoomMessageEventContent::text_plain(format!("{latest_pdu:?}")))
}
#[admin_command]
#[tracing::instrument(skip(self))]
#[tracing::instrument(skip(_body))]
pub(super) async fn force_set_room_state_from_server(
&self, room_id: Box<RoomId>, server_name: Box<ServerName>,
_body: Vec<&str>, server_name: Box<ServerName>, room_id: Box<RoomId>,
) -> Result<RoomMessageEventContent> {
if !self
.services
if !services()
.rooms
.state_cache
.server_in_room(&self.services.globals.config.server_name, &room_id)?
.server_in_room(&services().globals.config.server_name, &room_id)?
{
return Ok(RoomMessageEventContent::text_plain(
"We are not participating in the room / we don't know about the room ID.",
));
}
let first_pdu = self
.services
let first_pdu = services()
.rooms
.timeline
.latest_pdu_in_room(&room_id)?
.ok_or_else(|| Error::bad_database("Failed to find the latest PDU in database"))?;
let room_version = self.services.rooms.state.get_room_version(&room_id)?;
let room_version = services().rooms.state.get_room_version(&room_id)?;
let mut state: HashMap<u64, Arc<EventId>> = HashMap::new();
let pub_key_map = RwLock::new(BTreeMap::new());
let remote_state_response = self
.services
let remote_state_response = services()
.sending
.send_federation_request(
&server_name,
@@ -551,7 +510,7 @@ pub(super) async fn force_set_room_state_from_server(
let mut events = Vec::with_capacity(remote_state_response.pdus.len());
for pdu in remote_state_response.pdus.clone() {
events.push(match self.services.rooms.event_handler.parse_incoming_pdu(&pdu) {
events.push(match parse_incoming_pdu(&pdu) {
Ok(t) => t,
Err(e) => {
warn!("Could not parse PDU, ignoring: {e}");
@@ -561,7 +520,7 @@ pub(super) async fn force_set_room_state_from_server(
}
info!("Fetching required signing keys for all the state events we got");
self.services
services()
.rooms
.event_handler
.fetch_required_signing_keys(events.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
@@ -571,7 +530,7 @@ pub(super) async fn force_set_room_state_from_server(
for result in remote_state_response
.pdus
.iter()
.map(|pdu| validate_and_add_event_id(self.services, pdu, &room_version, &pub_key_map))
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
{
let Ok((event_id, value)) = result.await else {
continue;
@@ -582,13 +541,12 @@ pub(super) async fn force_set_room_state_from_server(
Error::BadServerResponse("Invalid PDU in send_join response.")
})?;
self.services
services()
.rooms
.outlier
.add_pdu_outlier(&event_id, &value)?;
if let Some(state_key) = &pdu.state_key {
let shortstatekey = self
.services
let shortstatekey = services()
.rooms
.short
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?;
@@ -600,34 +558,32 @@ pub(super) async fn force_set_room_state_from_server(
for result in remote_state_response
.auth_chain
.iter()
.map(|pdu| validate_and_add_event_id(self.services, pdu, &room_version, &pub_key_map))
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
{
let Ok((event_id, value)) = result.await else {
continue;
};
self.services
services()
.rooms
.outlier
.add_pdu_outlier(&event_id, &value)?;
}
let new_room_state = self
.services
let new_room_state = services()
.rooms
.event_handler
.resolve_state(room_id.clone().as_ref(), &room_version, state)
.await?;
info!("Forcing new room state");
let (short_state_hash, new, removed) = self
.services
let (short_state_hash, new, removed) = services()
.rooms
.state_compressor
.save_state(room_id.clone().as_ref(), new_room_state)?;
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
self.services
let state_lock = services().rooms.state.mutex.lock(&room_id).await;
services()
.rooms
.state
.force_state(room_id.clone().as_ref(), short_state_hash, new, removed, &state_lock)
@@ -637,10 +593,7 @@ pub(super) async fn force_set_room_state_from_server(
"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
.update_joined_count(&room_id)?;
services().rooms.state_cache.update_joined_count(&room_id)?;
drop(state_lock);
@@ -649,64 +602,16 @@ pub(super) async fn force_set_room_state_from_server(
))
}
#[admin_command]
pub(super) async fn get_signing_keys(
&self, server_name: Option<Box<ServerName>>, _cached: bool,
) -> Result<RoomMessageEventContent> {
let server_name = server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
let signing_keys = self.services.globals.signing_keys_for(&server_name)?;
Ok(RoomMessageEventContent::notice_markdown(format!(
"```rs\n{signing_keys:#?}\n```"
)))
}
#[admin_command]
#[allow(dead_code)]
pub(super) async fn get_verify_keys(
&self, server_name: Option<Box<ServerName>>, cached: bool,
) -> Result<RoomMessageEventContent> {
let server_name = server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
let mut out = String::new();
if cached {
writeln!(out, "| Key ID | VerifyKey |")?;
writeln!(out, "| --- | --- |")?;
for (key_id, verify_key) in self.services.globals.verify_keys_for(&server_name)? {
writeln!(out, "| {key_id} | {verify_key:?} |")?;
}
return Ok(RoomMessageEventContent::notice_markdown(out));
}
let signature_ids: Vec<String> = Vec::new();
let keys = self
.services
.rooms
.event_handler
.fetch_signing_keys_for_server(&server_name, signature_ids)
.await?;
writeln!(out, "| Key ID | Public Key |")?;
writeln!(out, "| --- | --- |")?;
for (key_id, key) in keys {
writeln!(out, "| {key_id} | {key} |")?;
}
Ok(RoomMessageEventContent::notice_markdown(out))
}
#[admin_command]
pub(super) async fn resolve_true_destination(
&self, server_name: Box<ServerName>, no_cache: bool,
_body: Vec<&str>, server_name: Box<ServerName>, no_cache: bool,
) -> Result<RoomMessageEventContent> {
if !self.services.globals.config.allow_federation {
if !services().globals.config.allow_federation {
return Ok(RoomMessageEventContent::text_plain(
"Federation is disabled on this homeserver.",
));
}
if server_name == self.services.globals.config.server_name {
if server_name == services().globals.config.server_name {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
));
@@ -718,16 +623,12 @@ pub(super) async fn resolve_true_destination(
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
};
let state = &self.services.server.log.capture;
let state = &services().server.log.capture;
let logs = Arc::new(Mutex::new(String::new()));
let capture = Capture::new(state, Some(filter), capture::fmt_markdown(logs.clone()));
let capture_scope = capture.start();
let actual = self
.services
.resolver
.resolve_actual_dest(&server_name, !no_cache)
.await?;
let actual = resolve_actual_dest(&server_name, !no_cache).await?;
drop(capture_scope);
let msg = format!(
@@ -739,26 +640,23 @@ pub(super) async fn resolve_true_destination(
Ok(RoomMessageEventContent::text_markdown(msg))
}
#[admin_command]
pub(super) async fn memory_stats(&self) -> Result<RoomMessageEventContent> {
#[must_use]
pub(super) fn memory_stats() -> RoomMessageEventContent {
let html_body = conduit::alloc::memory_stats();
if html_body.is_none() {
return Ok(RoomMessageEventContent::text_plain(
"malloc stats are not supported on your compiled malloc.",
));
return RoomMessageEventContent::text_plain("malloc stats are not supported on your compiled malloc.");
}
Ok(RoomMessageEventContent::text_html(
RoomMessageEventContent::text_html(
"This command's output can only be viewed by clients that render HTML.".to_owned(),
html_body.expect("string result"),
))
)
}
#[cfg(tokio_unstable)]
#[admin_command]
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
let out = self.services.server.metrics.runtime_metrics().map_or_else(
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let out = services().server.metrics.runtime_metrics().map_or_else(
|| "Runtime metrics are not available.".to_owned(),
|metrics| format!("```rs\n{metrics:#?}\n```"),
);
@@ -767,17 +665,15 @@ pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
}
#[cfg(not(tokio_unstable))]
#[admin_command]
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(RoomMessageEventContent::text_markdown(
"Runtime metrics require building with `tokio_unstable`.",
))
}
#[cfg(tokio_unstable)]
#[admin_command]
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
let out = self.services.server.metrics.runtime_interval().map_or_else(
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let out = services().server.metrics.runtime_interval().map_or_else(
|| "Runtime metrics are not available.".to_owned(),
|metrics| format!("```rs\n{metrics:#?}\n```"),
);
@@ -786,40 +682,8 @@ pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
}
#[cfg(not(tokio_unstable))]
#[admin_command]
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(RoomMessageEventContent::text_markdown(
"Runtime metrics require building with `tokio_unstable`.",
))
}
#[admin_command]
pub(super) async fn time(&self) -> Result<RoomMessageEventContent> {
let now = SystemTime::now();
Ok(RoomMessageEventContent::text_markdown(utils::time::format(now, "%+")))
}
#[admin_command]
pub(super) async fn list_dependencies(&self, names: bool) -> Result<RoomMessageEventContent> {
if names {
let out = info::cargo::dependencies_names().join(" ");
return Ok(RoomMessageEventContent::notice_markdown(out));
}
let deps = info::cargo::dependencies();
let mut out = String::new();
writeln!(out, "| name | version | features |")?;
writeln!(out, "| ---- | ------- | -------- |")?;
for (name, dep) in deps {
let version = dep.try_req().unwrap_or("*");
let feats = dep.req_features();
let feats = if !feats.is_empty() {
feats.join(" ")
} else {
String::new()
};
writeln!(out, "{name} | {version} | {feats}")?;
}
Ok(RoomMessageEventContent::notice_markdown(out))
}
+59 -23
View File
@@ -3,13 +3,13 @@ pub(crate) mod tester;
use clap::Subcommand;
use conduit::Result;
use ruma::{EventId, OwnedRoomOrAliasId, RoomId, ServerName};
use ruma::{events::room::message::RoomMessageEventContent, EventId, OwnedRoomOrAliasId, RoomId, ServerName};
use tester::TesterCommand;
use self::tester::TesterCommand;
use crate::admin_command_dispatch;
use self::commands::*;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum DebugCommand {
/// - Echo input of admin command
Echo {
@@ -76,14 +76,6 @@ pub(super) enum DebugCommand {
room_id: OwnedRoomOrAliasId,
},
/// - Get and display signing keys from local cache or remote server.
GetSigningKeys {
server_name: Option<Box<ServerName>>,
#[arg(short, long)]
cached: bool,
},
/// - Sends a federation request to the remote server's
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
/// took for the server to respond
@@ -175,17 +167,61 @@ pub(super) enum DebugCommand {
/// invocation.
RuntimeInterval,
/// - Print the current time
Time,
/// - List dependencies
ListDependencies {
#[arg(short, long)]
names: bool,
},
/// - Developer test stubs
#[command(subcommand)]
#[allow(non_snake_case)]
Tester(TesterCommand),
}
pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
DebugCommand::Echo {
message,
} => echo(body, message).await?,
DebugCommand::GetAuthChain {
event_id,
} => get_auth_chain(body, event_id).await?,
DebugCommand::ParsePdu => parse_pdu(body).await?,
DebugCommand::GetPdu {
event_id,
} => get_pdu(body, event_id).await?,
DebugCommand::GetRemotePdu {
event_id,
server,
} => get_remote_pdu(body, event_id, server).await?,
DebugCommand::GetRoomState {
room_id,
} => get_room_state(body, room_id).await?,
DebugCommand::Ping {
server,
} => ping(body, server).await?,
DebugCommand::ForceDeviceListUpdates => force_device_list_updates(body).await?,
DebugCommand::ChangeLogLevel {
filter,
reset,
} => change_log_level(body, filter, reset).await?,
DebugCommand::SignJson => sign_json(body).await?,
DebugCommand::VerifyJson => verify_json(body).await?,
DebugCommand::FirstPduInRoom {
room_id,
} => first_pdu_in_room(body, room_id).await?,
DebugCommand::LatestPduInRoom {
room_id,
} => latest_pdu_in_room(body, room_id).await?,
DebugCommand::GetRemotePduList {
server,
force,
} => get_remote_pdu_list(body, server, force).await?,
DebugCommand::ForceSetRoomStateFromServer {
room_id,
server_name,
} => force_set_room_state_from_server(body, server_name, room_id).await?,
DebugCommand::ResolveTrueDestination {
server_name,
no_cache,
} => resolve_true_destination(body, server_name, no_cache).await?,
DebugCommand::MemoryStats => memory_stats(),
DebugCommand::RuntimeMetrics => runtime_metrics(body).await?,
DebugCommand::RuntimeInterval => runtime_interval(body).await?,
DebugCommand::Tester(command) => tester::process(command, body).await?,
})
}
+13 -8
View File
@@ -1,29 +1,34 @@
use ruma::events::room::message::RoomMessageEventContent;
use crate::{admin_command, admin_command_dispatch, Result};
use crate::Result;
#[admin_command_dispatch]
#[derive(Debug, clap::Subcommand)]
#[derive(clap::Subcommand)]
#[cfg_attr(test, derive(Debug))]
pub(crate) enum TesterCommand {
Tester,
Timer,
}
pub(super) async fn process(command: TesterCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
TesterCommand::Tester => tester(body).await,
TesterCommand::Timer => timer(body).await,
}
}
#[inline(never)]
#[rustfmt::skip]
#[allow(unused_variables)]
#[admin_command]
async fn tester(&self) -> Result<RoomMessageEventContent> {
async fn tester(body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(RoomMessageEventContent::notice_plain("completed"))
}
#[inline(never)]
#[rustfmt::skip]
#[admin_command]
async fn timer(&self) -> Result<RoomMessageEventContent> {
async fn timer(body: Vec<&str>) -> Result<RoomMessageEventContent> {
let started = std::time::Instant::now();
timed(self.body);
timed(&body);
let elapsed = started.elapsed();
Ok(RoomMessageEventContent::notice_plain(format!("completed in {elapsed:#?}")))
+17 -23
View File
@@ -1,26 +1,21 @@
use std::fmt::Write;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
use crate::{admin_command, escape_html, get_room_info};
use crate::{escape_html, get_room_info, services, Result};
#[admin_command]
pub(super) async fn disable_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
self.services.rooms.metadata.disable_room(&room_id, true)?;
pub(super) async fn disable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
services().rooms.metadata.disable_room(&room_id, true)?;
Ok(RoomMessageEventContent::text_plain("Room disabled."))
}
#[admin_command]
pub(super) async fn enable_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
self.services.rooms.metadata.disable_room(&room_id, false)?;
pub(super) async fn enable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
services().rooms.metadata.disable_room(&room_id, false)?;
Ok(RoomMessageEventContent::text_plain("Room enabled."))
}
#[admin_command]
pub(super) async fn incoming_federation(&self) -> Result<RoomMessageEventContent> {
let map = self
.services
pub(super) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let map = services()
.rooms
.event_handler
.federation_handletime
@@ -36,10 +31,11 @@ pub(super) async fn incoming_federation(&self) -> Result<RoomMessageEventContent
Ok(RoomMessageEventContent::text_plain(&msg))
}
#[admin_command]
pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>) -> Result<RoomMessageEventContent> {
let response = self
.services
pub(super) async fn fetch_support_well_known(
_body: Vec<&str>, server_name: Box<ServerName>,
) -> Result<RoomMessageEventContent> {
let response = services()
.globals
.client
.default
.get(format!("https://{server_name}/.well-known/matrix/support"))
@@ -77,27 +73,25 @@ pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>
)))
}
#[admin_command]
pub(super) async fn remote_user_in_rooms(&self, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
if user_id.server_name() == self.services.globals.config.server_name {
pub(super) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
if user_id.server_name() == services().globals.config.server_name {
return Ok(RoomMessageEventContent::text_plain(
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
));
}
if !self.services.users.exists(&user_id)? {
if !services().users.exists(&user_id)? {
return Ok(RoomMessageEventContent::text_plain(
"Remote user does not exist in our database.",
));
}
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
.services
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.filter_map(Result::ok)
.map(|room_id| get_room_info(self.services, &room_id))
.map(|room_id| get_room_info(&room_id))
.collect();
if rooms.is_empty() {
+22 -4
View File
@@ -2,12 +2,12 @@ mod commands;
use clap::Subcommand;
use conduit::Result;
use ruma::{RoomId, ServerName, UserId};
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
use crate::admin_command_dispatch;
use self::commands::*;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum FederationCommand {
/// - List all rooms we are currently handling an incoming pdu from
IncomingFederation,
@@ -40,3 +40,21 @@ pub(super) enum FederationCommand {
user_id: Box<UserId>,
},
}
pub(super) async fn process(command: FederationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
FederationCommand::DisableRoom {
room_id,
} => disable_room(body, room_id).await?,
FederationCommand::EnableRoom {
room_id,
} => enable_room(body, room_id).await?,
FederationCommand::IncomingFederation => incoming_federation(body).await?,
FederationCommand::FetchSupportWellKnown {
server_name,
} => fetch_support_well_known(body, server_name).await?,
FederationCommand::RemoteUserInRooms {
user_id,
} => remote_user_in_rooms(body, user_id).await?,
})
}
+92 -34
View File
@@ -1,7 +1,7 @@
use std::{panic::AssertUnwindSafe, sync::Arc, time::Instant};
use std::{panic::AssertUnwindSafe, time::Instant};
use clap::{CommandFactory, Parser};
use conduit::{error, trace, utils::string::common_prefix, Error, Result};
use conduit::{error, trace, Error};
use futures_util::future::FutureExt;
use ruma::{
events::{
@@ -10,37 +10,82 @@ use ruma::{
},
OwnedEventId,
};
use service::{
admin::{CommandInput, CommandOutput, CommandResult, HandlerResult},
Services,
extern crate conduit_service as service;
use conduit::{utils::string::common_prefix, Result};
pub(crate) use service::admin::{Command, Service};
use service::admin::{CommandOutput, CommandResult, HandlerResult};
use crate::{
appservice, appservice::AppserviceCommand, check, check::CheckCommand, debug, debug::DebugCommand, federation,
federation::FederationCommand, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand,
server, server::ServerCommand, services, user, user::UserCommand,
};
pub(crate) const PAGE_SIZE: usize = 100;
use crate::{admin, admin::AdminCommand, Command};
#[derive(Parser)]
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
pub(crate) enum AdminCommand {
#[command(subcommand)]
/// - Commands for managing appservices
Appservices(AppserviceCommand),
#[must_use]
pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
#[command(subcommand)]
/// - Commands for managing local users
Users(UserCommand),
#[must_use]
pub(super) fn handle(services: Arc<Services>, command: CommandInput) -> HandlerResult {
Box::pin(handle_command(services, command))
#[command(subcommand)]
/// - Commands for managing rooms
Rooms(RoomCommand),
#[command(subcommand)]
/// - Commands for managing federation
Federation(FederationCommand),
#[command(subcommand)]
/// - Commands for managing the server
Server(ServerCommand),
#[command(subcommand)]
/// - Commands for managing media
Media(MediaCommand),
#[command(subcommand)]
/// - Commands for checking integrity
Check(CheckCommand),
#[command(subcommand)]
/// - Commands for debugging things
Debug(DebugCommand),
#[command(subcommand)]
/// - Low-level queries for database getters and iterators
Query(QueryCommand),
}
#[must_use]
pub(crate) fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
#[must_use]
pub(crate) fn complete(line: &str) -> String { complete_admin_command(AdminCommand::command(), line) }
#[tracing::instrument(skip_all, name = "admin")]
async fn handle_command(services: Arc<Services>, command: CommandInput) -> CommandResult {
AssertUnwindSafe(Box::pin(process_command(services, &command)))
async fn handle_command(command: Command) -> CommandResult {
AssertUnwindSafe(process_command(&command))
.catch_unwind()
.await
.map_err(Error::from_panic)
.or_else(|error| handle_panic(&error, command))
}
async fn process_command(services: Arc<Services>, command: &CommandInput) -> CommandOutput {
process(services, &command.command)
async fn process_command(command: &Command) -> CommandOutput {
process_admin_message(&command.command)
.await
.and_then(|content| reply(content, command.reply_id.clone()))
}
fn handle_panic(error: &Error, command: CommandInput) -> CommandResult {
fn handle_panic(error: &Error, command: Command) -> CommandResult {
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
let content = RoomMessageEventContent::notice_markdown(msg);
@@ -59,25 +104,21 @@ fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -
}
// Parse and process a message from the admin room
async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
async fn process_admin_message(msg: &str) -> CommandOutput {
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
let command = lines.next().expect("each string has at least one line");
let (parsed, body) = match parse_command(command) {
let body = lines.collect::<Vec<_>>();
let parsed = match parse_admin_command(command) {
Ok(parsed) => parsed,
Err(error) => {
let server_name = services.globals.server_name();
let server_name = services().globals.server_name();
let message = error.replace("server.name", server_name.as_str());
return Some(RoomMessageEventContent::notice_markdown(message));
},
};
let timer = Instant::now();
let body: Vec<&str> = body.iter().map(String::as_str).collect();
let context = Command {
services: &services,
body: &body,
};
let result = Box::pin(admin::process(parsed, &context)).await;
let result = process_admin_command(parsed, body).await;
let elapsed = timer.elapsed();
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
match result {
@@ -88,15 +129,31 @@ async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
}
}
// Parse chat messages from the admin room into an AdminCommand object
fn parse_command(command_line: &str) -> Result<(AdminCommand, Vec<String>), String> {
let argv = parse_line(command_line);
let com = AdminCommand::try_parse_from(&argv).map_err(|error| error.to_string())?;
Ok((com, argv))
#[tracing::instrument(skip_all, name = "command")]
async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
let reply_message_content = match command {
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
AdminCommand::Media(command) => media::process(command, body).await?,
AdminCommand::Users(command) => user::process(command, body).await?,
AdminCommand::Rooms(command) => room::process(command, body).await?,
AdminCommand::Federation(command) => federation::process(command, body).await?,
AdminCommand::Server(command) => server::process(command, body).await?,
AdminCommand::Debug(command) => debug::process(command, body).await?,
AdminCommand::Query(command) => query::process(command, body).await?,
AdminCommand::Check(command) => check::process(command, body).await?,
};
Ok(reply_message_content)
}
fn complete_command(mut cmd: clap::Command, line: &str) -> String {
let argv = parse_line(line);
// Parse chat messages from the admin room into an AdminCommand object
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
let argv = parse_command_line(command_line);
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
}
fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String {
let argv = parse_command_line(line);
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));
'token: for token in argv.into_iter().skip(1) {
@@ -139,7 +196,7 @@ fn complete_command(mut cmd: clap::Command, line: &str) -> String {
}
// Parse chat messages from the admin room into an AdminCommand object
fn parse_line(command_line: &str) -> Vec<String> {
fn parse_command_line(command_line: &str) -> Vec<String> {
let mut argv = command_line
.split_whitespace()
.map(str::to_owned)
@@ -151,7 +208,8 @@ fn parse_line(command_line: &str) -> Vec<String> {
}
// First indice has to be "admin" but for console convenience we add it here
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with('@') {
let server_user = services().globals.server_user.as_str();
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with(server_user) {
argv.insert(0, "admin".to_owned());
}
+17 -20
View File
@@ -1,11 +1,11 @@
use conduit::{debug, info, Result};
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
use tracing::{debug, info};
use crate::admin_command;
use crate::services;
#[admin_command]
pub(super) async fn delete(
&self, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
_body: Vec<&str>, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
) -> Result<RoomMessageEventContent> {
if event_id.is_some() && mxc.is_some() {
return Ok(RoomMessageEventContent::text_plain(
@@ -15,7 +15,7 @@ pub(super) async fn delete(
if let Some(mxc) = mxc {
debug!("Got MXC URL: {mxc}");
self.services.media.delete(mxc.as_ref()).await?;
services().media.delete(mxc.as_ref()).await?;
return Ok(RoomMessageEventContent::text_plain(
"Deleted the MXC from our database and on our filesystem.",
@@ -27,7 +27,7 @@ pub(super) async fn delete(
let mut mxc_deletion_count: usize = 0;
// parsing the PDU for any MXC URLs begins here
if let Some(event_json) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
if let Some(event_json) = services().rooms.timeline.get_pdu_json(&event_id)? {
if let Some(content_key) = event_json.get("content") {
debug!("Event ID has \"content\".");
let content_obj = content_key.as_object();
@@ -123,7 +123,7 @@ pub(super) async fn delete(
}
for mxc_url in mxc_urls {
self.services.media.delete(&mxc_url).await?;
services().media.delete(&mxc_url).await?;
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
}
@@ -138,26 +138,23 @@ pub(super) async fn delete(
))
}
#[admin_command]
pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
pub(super) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let mxc_list = self
.body
.to_vec()
.drain(1..self.body.len().checked_sub(1).unwrap())
let mxc_list = body
.clone()
.drain(1..body.len().checked_sub(1).unwrap())
.collect::<Vec<_>>();
let mut mxc_deletion_count: usize = 0;
for mxc in mxc_list {
debug!("Deleting MXC {mxc} in bulk");
self.services.media.delete(mxc).await?;
services().media.delete(mxc).await?;
mxc_deletion_count = mxc_deletion_count
.checked_add(1)
.expect("mxc_deletion_count should not get this high");
@@ -168,10 +165,10 @@ pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
)))
}
#[admin_command]
pub(super) async fn delete_past_remote_media(&self, duration: String, force: bool) -> Result<RoomMessageEventContent> {
let deleted_count = self
.services
pub(super) async fn delete_past_remote_media(
_body: Vec<&str>, duration: String, force: bool,
) -> Result<RoomMessageEventContent> {
let deleted_count = services()
.media
.delete_all_remote_media_at_after_time(duration, force)
.await?;
+18 -4
View File
@@ -2,12 +2,12 @@ mod commands;
use clap::Subcommand;
use conduit::Result;
use ruma::{EventId, MxcUri};
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
use crate::admin_command_dispatch;
use self::commands::*;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum MediaCommand {
/// - Deletes a single media file from our database and on the filesystem
/// via a single MXC URL
@@ -37,3 +37,17 @@ pub(super) enum MediaCommand {
force: bool,
},
}
pub(super) async fn process(command: MediaCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
MediaCommand::Delete {
mxc,
event_id,
} => delete(body, mxc, event_id).await?,
MediaCommand::DeleteList => delete_list(body).await?,
MediaCommand::DeletePastRemoteMedia {
duration,
force,
} => delete_past_remote_media(body, duration, force).await?,
})
}
+21 -22
View File
@@ -1,55 +1,54 @@
#![recursion_limit = "192"]
#![allow(clippy::wildcard_imports)]
#![allow(clippy::enum_glob_use)]
pub(crate) mod admin;
pub(crate) mod command;
pub(crate) mod handler;
mod tests;
pub(crate) mod utils;
pub(crate) mod appservice;
pub(crate) mod check;
pub(crate) mod debug;
pub(crate) mod federation;
pub(crate) mod handler;
pub(crate) mod media;
pub(crate) mod query;
pub(crate) mod room;
pub(crate) mod server;
mod tests;
pub(crate) mod user;
pub(crate) mod utils;
extern crate conduit_api as api;
extern crate conduit_core as conduit;
extern crate conduit_service as service;
pub(crate) use conduit::Result;
pub(crate) use conduit_macros::{admin_command, admin_command_dispatch};
pub(crate) use conduit::{mod_ctor, mod_dtor, Result};
pub(crate) use service::{services, user_is_local};
pub(crate) use crate::{
command::Command,
handler::Service,
utils::{escape_html, get_room_info},
};
pub(crate) const PAGE_SIZE: usize = 100;
conduit::mod_ctor! {}
conduit::mod_dtor! {}
conduit::rustc_flags_capture! {}
mod_ctor! {}
mod_dtor! {}
/// Install the admin command handler
pub async fn init(admin_service: &service::admin::Service) {
_ = admin_service
pub async fn init() {
_ = services()
.admin
.complete
.write()
.expect("locked for writing")
.insert(handler::complete);
_ = admin_service.handle.write().await.insert(handler::handle);
_ = services()
.admin
.handle
.write()
.await
.insert(handler::handle);
}
/// Uninstall the admin command handler
pub async fn fini(admin_service: &service::admin::Service) {
_ = admin_service.handle.write().await.take();
_ = admin_service
pub async fn fini() {
_ = services().admin.handle.write().await.take();
_ = services()
.admin
.complete
.write()
.expect("locked for writing")
+8 -38
View File
@@ -1,48 +1,18 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
RoomId, UserId,
};
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/account_data.rs
pub(crate) enum AccountDataCommand {
/// - Returns all changes to the account data that happened after `since`.
ChangesSince {
/// Full user ID
user_id: Box<UserId>,
/// UNIX timestamp since (u64)
since: u64,
/// Optional room ID of the account data
room_id: Option<Box<RoomId>>,
},
/// - Searches the account data for a specific kind.
Get {
/// Full user ID
user_id: Box<UserId>,
/// Account data event type
kind: RoomAccountDataEventType,
/// Optional room ID of the account data
room_id: Option<Box<RoomId>>,
},
}
use super::AccountData;
use crate::{services, Result};
/// All the getters and iterators from src/database/key_value/account_data.rs
pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageEventContent> {
match subcommand {
AccountDataCommand::ChangesSince {
AccountData::ChangesSince {
user_id,
since,
room_id,
} => {
let timer = tokio::time::Instant::now();
let results = services
let results = services()
.account_data
.changes_since(room_id.as_deref(), &user_id, since)?;
let query_time = timer.elapsed();
@@ -51,13 +21,13 @@ pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
AccountDataCommand::Get {
AccountData::Get {
user_id,
kind,
room_id,
} => {
let timer = tokio::time::Instant::now();
let results = services
let results = services()
.account_data
.get(room_id.as_deref(), &user_id, kind)?;
let query_time = timer.elapsed();
+7 -23
View File
@@ -1,32 +1,16 @@
use clap::Subcommand;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/appservice.rs
pub(crate) enum AppserviceCommand {
/// - Gets the appservice registration info/details from the ID as a string
GetRegistration {
/// Appservice registration ID
appservice_id: Box<str>,
},
/// - Gets all appservice registrations with their ID and registration info
All,
}
use super::Appservice;
use crate::{services, Result};
/// All the getters and iterators from src/database/key_value/appservice.rs
pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEventContent> {
match subcommand {
AppserviceCommand::GetRegistration {
Appservice::GetRegistration {
appservice_id,
} => {
let timer = tokio::time::Instant::now();
let results = services
let results = services()
.appservice
.db
.get_registration(appservice_id.as_ref());
@@ -36,9 +20,9 @@ pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
AppserviceCommand::All => {
Appservice::All => {
let timer = tokio::time::Instant::now();
let results = services.appservice.all();
let results = services().appservice.all();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
+14 -35
View File
@@ -1,73 +1,52 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, ServerName};
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/globals.rs
pub(crate) enum GlobalsCommand {
DatabaseVersion,
CurrentCount,
LastCheckForUpdatesId,
LoadKeypair,
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
/// for the server.
SigningKeysFor {
origin: Box<ServerName>,
},
}
use super::Globals;
use crate::{services, Result};
/// All the getters and iterators from src/database/key_value/globals.rs
pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn globals(subcommand: Globals) -> Result<RoomMessageEventContent> {
match subcommand {
GlobalsCommand::DatabaseVersion => {
Globals::DatabaseVersion => {
let timer = tokio::time::Instant::now();
let results = services.globals.db.database_version();
let results = services().globals.db.database_version();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
GlobalsCommand::CurrentCount => {
Globals::CurrentCount => {
let timer = tokio::time::Instant::now();
let results = services.globals.db.current_count();
let results = services().globals.db.current_count();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
GlobalsCommand::LastCheckForUpdatesId => {
Globals::LastCheckForUpdatesId => {
let timer = tokio::time::Instant::now();
let results = services.updates.last_check_for_updates_id();
let results = services().updates.last_check_for_updates_id();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
GlobalsCommand::LoadKeypair => {
Globals::LoadKeypair => {
let timer = tokio::time::Instant::now();
let results = services.globals.db.load_keypair();
let results = services().globals.db.load_keypair();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
GlobalsCommand::SigningKeysFor {
Globals::SigningKeysFor {
origin,
} => {
let timer = tokio::time::Instant::now();
let results = services.globals.db.verify_keys_for(&origin);
let results = services().globals.db.signing_keys_for(&origin);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
+278 -15
View File
@@ -10,51 +10,314 @@ mod users;
use clap::Subcommand;
use conduit::Result;
use room_state_cache::room_state_cache;
use ruma::{
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
OwnedServerName, RoomAliasId, RoomId, ServerName, UserId,
};
use self::{
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
presence::PresenceCommand, resolver::ResolverCommand, room_alias::RoomAliasCommand,
room_state_cache::RoomStateCacheCommand, sending::SendingCommand, users::UsersCommand,
account_data::account_data, appservice::appservice, globals::globals, presence::presence, resolver::resolver,
room_alias::room_alias, sending::sending, users::users,
};
use crate::admin_command_dispatch;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// Query tables from database
pub(super) enum QueryCommand {
/// - account_data.rs iterators and getters
#[command(subcommand)]
AccountData(AccountDataCommand),
AccountData(AccountData),
/// - appservice.rs iterators and getters
#[command(subcommand)]
Appservice(AppserviceCommand),
Appservice(Appservice),
/// - presence.rs iterators and getters
#[command(subcommand)]
Presence(PresenceCommand),
Presence(Presence),
/// - rooms/alias.rs iterators and getters
#[command(subcommand)]
RoomAlias(RoomAliasCommand),
RoomAlias(RoomAlias),
/// - rooms/state_cache iterators and getters
#[command(subcommand)]
RoomStateCache(RoomStateCacheCommand),
RoomStateCache(RoomStateCache),
/// - globals.rs iterators and getters
#[command(subcommand)]
Globals(GlobalsCommand),
Globals(Globals),
/// - sending.rs iterators and getters
#[command(subcommand)]
Sending(SendingCommand),
Sending(Sending),
/// - users.rs iterators and getters
#[command(subcommand)]
Users(UsersCommand),
Users(Users),
/// - resolver service
#[command(subcommand)]
Resolver(ResolverCommand),
Resolver(Resolver),
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/account_data.rs
pub(super) enum AccountData {
/// - Returns all changes to the account data that happened after `since`.
ChangesSince {
/// Full user ID
user_id: Box<UserId>,
/// UNIX timestamp since (u64)
since: u64,
/// Optional room ID of the account data
room_id: Option<Box<RoomId>>,
},
/// - Searches the account data for a specific kind.
Get {
/// Full user ID
user_id: Box<UserId>,
/// Account data event type
kind: RoomAccountDataEventType,
/// Optional room ID of the account data
room_id: Option<Box<RoomId>>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/appservice.rs
pub(super) enum Appservice {
/// - Gets the appservice registration info/details from the ID as a string
GetRegistration {
/// Appservice registration ID
appservice_id: Box<str>,
},
/// - Gets all appservice registrations with their ID and registration info
All,
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/presence.rs
pub(super) enum Presence {
/// - Returns the latest presence event for the given user.
GetPresence {
/// Full user ID
user_id: Box<UserId>,
},
/// - Iterator of the most recent presence updates that happened after the
/// event with id `since`.
PresenceSince {
/// UNIX timestamp since (u64)
since: u64,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/rooms/alias.rs
pub(super) enum RoomAlias {
ResolveLocalAlias {
/// Full room alias
alias: Box<RoomAliasId>,
},
/// - Iterator of all our local room aliases for the room ID
LocalAliasesForRoom {
/// Full room ID
room_id: Box<RoomId>,
},
/// - Iterator of all our local aliases in our database with their room IDs
AllLocalAliases,
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum RoomStateCache {
ServerInRoom {
server: Box<ServerName>,
room_id: Box<RoomId>,
},
RoomServers {
room_id: Box<RoomId>,
},
ServerRooms {
server: Box<ServerName>,
},
RoomMembers {
room_id: Box<RoomId>,
},
LocalUsersInRoom {
room_id: Box<RoomId>,
},
ActiveLocalUsersInRoom {
room_id: Box<RoomId>,
},
RoomJoinedCount {
room_id: Box<RoomId>,
},
RoomInvitedCount {
room_id: Box<RoomId>,
},
RoomUserOnceJoined {
room_id: Box<RoomId>,
},
RoomMembersInvited {
room_id: Box<RoomId>,
},
GetInviteCount {
room_id: Box<RoomId>,
user_id: Box<UserId>,
},
GetLeftCount {
room_id: Box<RoomId>,
user_id: Box<UserId>,
},
RoomsJoined {
user_id: Box<UserId>,
},
RoomsLeft {
user_id: Box<UserId>,
},
RoomsInvited {
user_id: Box<UserId>,
},
InviteState {
user_id: Box<UserId>,
room_id: Box<RoomId>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/globals.rs
pub(super) enum Globals {
DatabaseVersion,
CurrentCount,
LastCheckForUpdatesId,
LoadKeypair,
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
/// for the server.
SigningKeysFor {
origin: Box<ServerName>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/sending.rs
pub(super) enum Sending {
/// - Queries database for all `servercurrentevent_data`
ActiveRequests,
/// - Queries database for `servercurrentevent_data` but for a specific
/// destination
///
/// This command takes only *one* format of these arguments:
///
/// appservice_id
/// server_name
/// user_id AND push_key
///
/// See src/service/sending/mod.rs for the definition of the `Destination`
/// enum
ActiveRequestsFor {
#[arg(short, long)]
appservice_id: Option<String>,
#[arg(short, long)]
server_name: Option<Box<ServerName>>,
#[arg(short, long)]
user_id: Option<Box<UserId>>,
#[arg(short, long)]
push_key: Option<String>,
},
/// - Queries database for `servernameevent_data` which are the queued up
/// requests that will eventually be sent
///
/// This command takes only *one* format of these arguments:
///
/// appservice_id
/// server_name
/// user_id AND push_key
///
/// See src/service/sending/mod.rs for the definition of the `Destination`
/// enum
QueuedRequests {
#[arg(short, long)]
appservice_id: Option<String>,
#[arg(short, long)]
server_name: Option<Box<ServerName>>,
#[arg(short, long)]
user_id: Option<Box<UserId>>,
#[arg(short, long)]
push_key: Option<String>,
},
GetLatestEduCount {
server_name: Box<ServerName>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// All the getters and iterators from src/database/key_value/users.rs
pub(super) enum Users {
Iter,
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
/// Resolver service and caches
pub(super) enum Resolver {
/// Query the destinations cache
DestinationsCache {
server_name: Option<OwnedServerName>,
},
/// Query the overrides cache
OverridesCache {
name: Option<String>,
},
}
/// Processes admin query commands
pub(super) async fn process(command: QueryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
QueryCommand::AccountData(command) => account_data(command).await?,
QueryCommand::Appservice(command) => appservice(command).await?,
QueryCommand::Presence(command) => presence(command).await?,
QueryCommand::RoomAlias(command) => room_alias(command).await?,
QueryCommand::RoomStateCache(command) => room_state_cache(command).await?,
QueryCommand::Globals(command) => globals(command).await?,
QueryCommand::Sending(command) => sending(command).await?,
QueryCommand::Users(command) => users(command).await?,
QueryCommand::Resolver(command) => resolver(command).await?,
})
}
+8 -28
View File
@@ -1,47 +1,27 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, UserId};
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/presence.rs
pub(crate) enum PresenceCommand {
/// - Returns the latest presence event for the given user.
GetPresence {
/// Full user ID
user_id: Box<UserId>,
},
/// - Iterator of the most recent presence updates that happened after the
/// event with id `since`.
PresenceSince {
/// UNIX timestamp since (u64)
since: u64,
},
}
use super::Presence;
use crate::{services, Result};
/// All the getters and iterators in key_value/presence.rs
pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn presence(subcommand: Presence) -> Result<RoomMessageEventContent> {
match subcommand {
PresenceCommand::GetPresence {
Presence::GetPresence {
user_id,
} => {
let timer = tokio::time::Instant::now();
let results = services.presence.db.get_presence(&user_id)?;
let results = services().presence.db.get_presence(&user_id)?;
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
PresenceCommand::PresenceSince {
Presence::PresenceSince {
since,
} => {
let timer = tokio::time::Instant::now();
let results = services.presence.db.presence_since(since);
let results = services().presence.db.presence_since(since);
let presence_since: Vec<(_, _, _)> = results.collect();
let query_time = timer.elapsed();
+20 -27
View File
@@ -1,29 +1,25 @@
use std::fmt::Write;
use clap::Subcommand;
use conduit::{utils::time, Result};
use ruma::{events::room::message::RoomMessageEventContent, OwnedServerName};
use crate::{admin_command, admin_command_dispatch};
use super::Resolver;
use crate::services;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
/// Resolver service and caches
pub(crate) enum ResolverCommand {
/// Query the destinations cache
DestinationsCache {
server_name: Option<OwnedServerName>,
},
/// Query the overrides cache
OverridesCache {
name: Option<String>,
},
/// All the getters and iterators in key_value/users.rs
pub(super) async fn resolver(subcommand: Resolver) -> Result<RoomMessageEventContent> {
match subcommand {
Resolver::DestinationsCache {
server_name,
} => destinations_cache(server_name).await,
Resolver::OverridesCache {
name,
} => overrides_cache(name).await,
}
}
#[admin_command]
async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
use service::resolver::cache::CachedDest;
async fn destinations_cache(server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
use service::sending::CachedDest;
let mut out = String::new();
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
@@ -40,10 +36,9 @@ async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Resu
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
};
let map = self
.services
let map = services()
.globals
.resolver
.cache
.destinations
.read()
.expect("locked");
@@ -57,9 +52,8 @@ async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Resu
Ok(RoomMessageEventContent::notice_markdown(out))
}
#[admin_command]
async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessageEventContent> {
use service::resolver::cache::CachedOverride;
async fn overrides_cache(server_name: Option<String>) -> Result<RoomMessageEventContent> {
use service::sending::CachedOverride;
let mut out = String::new();
writeln!(out, "| Server Name | IP | Port | Expires |")?;
@@ -76,10 +70,9 @@ async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessa
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
};
let map = self
.services
let map = services()
.globals
.resolver
.cache
.overrides
.read()
.expect("locked");
+10 -31
View File
@@ -1,48 +1,27 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/rooms/alias.rs
pub(crate) enum RoomAliasCommand {
ResolveLocalAlias {
/// Full room alias
alias: Box<RoomAliasId>,
},
/// - Iterator of all our local room aliases for the room ID
LocalAliasesForRoom {
/// Full room ID
room_id: Box<RoomId>,
},
/// - Iterator of all our local aliases in our database with their room IDs
AllLocalAliases,
}
use super::RoomAlias;
use crate::{services, Result};
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEventContent> {
match subcommand {
RoomAliasCommand::ResolveLocalAlias {
RoomAlias::ResolveLocalAlias {
alias,
} => {
let timer = tokio::time::Instant::now();
let results = services.rooms.alias.resolve_local_alias(&alias);
let results = services().rooms.alias.resolve_local_alias(&alias);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomAliasCommand::LocalAliasesForRoom {
RoomAlias::LocalAliasesForRoom {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results = services.rooms.alias.local_aliases_for_room(&room_id);
let results = services().rooms.alias.local_aliases_for_room(&room_id);
let aliases: Vec<_> = results.collect();
let query_time = timer.elapsed();
@@ -50,9 +29,9 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
)))
},
RoomAliasCommand::AllLocalAliases => {
RoomAlias::AllLocalAliases => {
let timer = tokio::time::Instant::now();
let results = services.rooms.alias.all_local_aliases();
let results = services().rooms.alias.all_local_aliases();
let aliases: Vec<_> = results.collect();
let query_time = timer.elapsed();
+58 -112
View File
@@ -1,136 +1,71 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
pub(crate) enum RoomStateCacheCommand {
ServerInRoom {
server: Box<ServerName>,
room_id: Box<RoomId>,
},
RoomServers {
room_id: Box<RoomId>,
},
ServerRooms {
server: Box<ServerName>,
},
RoomMembers {
room_id: Box<RoomId>,
},
LocalUsersInRoom {
room_id: Box<RoomId>,
},
ActiveLocalUsersInRoom {
room_id: Box<RoomId>,
},
RoomJoinedCount {
room_id: Box<RoomId>,
},
RoomInvitedCount {
room_id: Box<RoomId>,
},
RoomUserOnceJoined {
room_id: Box<RoomId>,
},
RoomMembersInvited {
room_id: Box<RoomId>,
},
GetInviteCount {
room_id: Box<RoomId>,
user_id: Box<UserId>,
},
GetLeftCount {
room_id: Box<RoomId>,
user_id: Box<UserId>,
},
RoomsJoined {
user_id: Box<UserId>,
},
RoomsLeft {
user_id: Box<UserId>,
},
RoomsInvited {
user_id: Box<UserId>,
},
InviteState {
user_id: Box<UserId>,
room_id: Box<RoomId>,
},
}
pub(super) async fn process(
subcommand: RoomStateCacheCommand, context: &Command<'_>,
) -> Result<RoomMessageEventContent> {
let services = context.services;
use super::RoomStateCache;
use crate::{services, Result};
pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomMessageEventContent> {
match subcommand {
RoomStateCacheCommand::ServerInRoom {
RoomStateCache::ServerInRoom {
server,
room_id,
} => {
let timer = tokio::time::Instant::now();
let result = services.rooms.state_cache.server_in_room(&server, &room_id);
let result = services()
.rooms
.state_cache
.server_in_room(&server, &room_id);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
)))
},
RoomStateCacheCommand::RoomServers {
RoomStateCache::RoomServers {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services.rooms.state_cache.room_servers(&room_id).collect();
let results: Result<Vec<_>> = services()
.rooms
.state_cache
.room_servers(&room_id)
.collect();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::ServerRooms {
RoomStateCache::ServerRooms {
server,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services.rooms.state_cache.server_rooms(&server).collect();
let results: Result<Vec<_>> = services().rooms.state_cache.server_rooms(&server).collect();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomMembers {
RoomStateCache::RoomMembers {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services.rooms.state_cache.room_members(&room_id).collect();
let results: Result<Vec<_>> = services()
.rooms
.state_cache
.room_members(&room_id)
.collect();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::LocalUsersInRoom {
RoomStateCache::LocalUsersInRoom {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results: Vec<_> = services
let results: Vec<_> = services()
.rooms
.state_cache
.local_users_in_room(&room_id)
@@ -141,11 +76,11 @@ pub(super) async fn process(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::ActiveLocalUsersInRoom {
RoomStateCache::ActiveLocalUsersInRoom {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results: Vec<_> = services
let results: Vec<_> = services()
.rooms
.state_cache
.active_local_users_in_room(&room_id)
@@ -156,33 +91,33 @@ pub(super) async fn process(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomJoinedCount {
RoomStateCache::RoomJoinedCount {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results = services.rooms.state_cache.room_joined_count(&room_id);
let results = services().rooms.state_cache.room_joined_count(&room_id);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomInvitedCount {
RoomStateCache::RoomInvitedCount {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results = services.rooms.state_cache.room_invited_count(&room_id);
let results = services().rooms.state_cache.room_invited_count(&room_id);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomUserOnceJoined {
RoomStateCache::RoomUserOnceJoined {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services
let results: Result<Vec<_>> = services()
.rooms
.state_cache
.room_useroncejoined(&room_id)
@@ -193,11 +128,11 @@ pub(super) async fn process(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomMembersInvited {
RoomStateCache::RoomMembersInvited {
room_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services
let results: Result<Vec<_>> = services()
.rooms
.state_cache
.room_members_invited(&room_id)
@@ -208,12 +143,12 @@ pub(super) async fn process(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::GetInviteCount {
RoomStateCache::GetInviteCount {
room_id,
user_id,
} => {
let timer = tokio::time::Instant::now();
let results = services
let results = services()
.rooms
.state_cache
.get_invite_count(&room_id, &user_id);
@@ -223,12 +158,12 @@ pub(super) async fn process(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::GetLeftCount {
RoomStateCache::GetLeftCount {
room_id,
user_id,
} => {
let timer = tokio::time::Instant::now();
let results = services
let results = services()
.rooms
.state_cache
.get_left_count(&room_id, &user_id);
@@ -238,45 +173,56 @@ pub(super) async fn process(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomsJoined {
RoomStateCache::RoomsJoined {
user_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_joined(&user_id).collect();
let results: Result<Vec<_>> = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.collect();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomsInvited {
RoomStateCache::RoomsInvited {
user_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_invited(&user_id).collect();
let results: Result<Vec<_>> = services()
.rooms
.state_cache
.rooms_invited(&user_id)
.collect();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::RoomsLeft {
RoomStateCache::RoomsLeft {
user_id,
} => {
let timer = tokio::time::Instant::now();
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_left(&user_id).collect();
let results: Result<Vec<_>> = services().rooms.state_cache.rooms_left(&user_id).collect();
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
)))
},
RoomStateCacheCommand::InviteState {
RoomStateCache::InviteState {
user_id,
room_id,
} => {
let timer = tokio::time::Instant::now();
let results = services.rooms.state_cache.invite_state(&user_id, &room_id);
let results = services()
.rooms
.state_cache
.invite_state(&user_id, &room_id);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
+16 -75
View File
@@ -1,73 +1,14 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, ServerName, UserId};
use service::sending::Destination;
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/sending.rs
pub(crate) enum SendingCommand {
/// - Queries database for all `servercurrentevent_data`
ActiveRequests,
/// - Queries database for `servercurrentevent_data` but for a specific
/// destination
///
/// This command takes only *one* format of these arguments:
///
/// appservice_id
/// server_name
/// user_id AND push_key
///
/// See src/service/sending/mod.rs for the definition of the `Destination`
/// enum
ActiveRequestsFor {
#[arg(short, long)]
appservice_id: Option<String>,
#[arg(short, long)]
server_name: Option<Box<ServerName>>,
#[arg(short, long)]
user_id: Option<Box<UserId>>,
#[arg(short, long)]
push_key: Option<String>,
},
/// - Queries database for `servernameevent_data` which are the queued up
/// requests that will eventually be sent
///
/// This command takes only *one* format of these arguments:
///
/// appservice_id
/// server_name
/// user_id AND push_key
///
/// See src/service/sending/mod.rs for the definition of the `Destination`
/// enum
QueuedRequests {
#[arg(short, long)]
appservice_id: Option<String>,
#[arg(short, long)]
server_name: Option<Box<ServerName>>,
#[arg(short, long)]
user_id: Option<Box<UserId>>,
#[arg(short, long)]
push_key: Option<String>,
},
GetLatestEduCount {
server_name: Box<ServerName>,
},
}
use super::Sending;
use crate::{service::sending::Destination, services, Result};
/// All the getters and iterators in key_value/sending.rs
pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventContent> {
match subcommand {
SendingCommand::ActiveRequests => {
Sending::ActiveRequests => {
let timer = tokio::time::Instant::now();
let results = services.sending.db.active_requests();
let results = services().sending.db.active_requests();
let active_requests: Result<Vec<(_, _, _)>> = results.collect();
let query_time = timer.elapsed();
@@ -75,7 +16,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
)))
},
SendingCommand::QueuedRequests {
Sending::QueuedRequests {
appservice_id,
server_name,
user_id,
@@ -97,12 +38,12 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
));
}
services
services()
.sending
.db
.queued_requests(&Destination::Appservice(appservice_id))
},
(None, Some(server_name), None, None) => services
(None, Some(server_name), None, None) => services()
.sending
.db
.queued_requests(&Destination::Normal(server_name.into())),
@@ -114,7 +55,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
));
}
services
services()
.sending
.db
.queued_requests(&Destination::Push(user_id.into(), push_key))
@@ -140,7 +81,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
)))
},
SendingCommand::ActiveRequestsFor {
Sending::ActiveRequestsFor {
appservice_id,
server_name,
user_id,
@@ -163,12 +104,12 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
));
}
services
services()
.sending
.db
.active_requests_for(&Destination::Appservice(appservice_id))
},
(None, Some(server_name), None, None) => services
(None, Some(server_name), None, None) => services()
.sending
.db
.active_requests_for(&Destination::Normal(server_name.into())),
@@ -180,7 +121,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
));
}
services
services()
.sending
.db
.active_requests_for(&Destination::Push(user_id.into(), push_key))
@@ -206,11 +147,11 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
)))
},
SendingCommand::GetLatestEduCount {
Sending::GetLatestEduCount {
server_name,
} => {
let timer = tokio::time::Instant::now();
let results = services.sending.db.get_latest_educount(&server_name);
let results = services().sending.db.get_latest_educount(&server_name);
let query_time = timer.elapsed();
Ok(RoomMessageEventContent::notice_markdown(format!(
+5 -14
View File
@@ -1,23 +1,14 @@
use clap::Subcommand;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::Command;
#[derive(Debug, Subcommand)]
/// All the getters and iterators from src/database/key_value/users.rs
pub(crate) enum UsersCommand {
Iter,
}
use super::Users;
use crate::{services, Result};
/// All the getters and iterators in key_value/users.rs
pub(super) async fn process(subcommand: UsersCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn users(subcommand: Users) -> Result<RoomMessageEventContent> {
match subcommand {
UsersCommand::Iter => {
Users::Iter => {
let timer = tokio::time::Instant::now();
let results = services.users.db.iter();
let results = services().users.db.iter();
let users = results.collect::<Vec<_>>();
let query_time = timer.elapsed();
+160 -13
View File
@@ -1,23 +1,20 @@
mod alias;
mod commands;
mod directory;
mod info;
mod moderation;
mod room_alias_commands;
mod room_commands;
mod room_directory_commands;
mod room_info_commands;
mod room_moderation_commands;
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, RoomId, RoomOrAliasId};
use self::{
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand, moderation::RoomModerationCommand,
};
use crate::admin_command_dispatch;
use self::room_commands::list;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum RoomCommand {
/// - List all rooms the server knows about
#[clap(alias = "list")]
ListRooms {
List {
page: Option<usize>,
/// Excludes rooms that we have federation disabled with
@@ -45,3 +42,153 @@ pub(super) enum RoomCommand {
/// - Manage the room directory
Directory(RoomDirectoryCommand),
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum RoomInfoCommand {
/// - List joined members in a room
ListJoinedMembers {
room_id: Box<RoomId>,
},
/// - Displays room topic
///
/// Room topics can be huge, so this is in its
/// own separate command
ViewRoomTopic {
room_id: Box<RoomId>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum RoomAliasCommand {
/// - Make an alias point to a room.
Set {
#[arg(short, long)]
/// Set the alias even if a room is already using it
force: bool,
/// The room id to set the alias on
room_id: Box<RoomId>,
/// The alias localpart to use (`alias`, not `#alias:servername.tld`)
room_alias_localpart: String,
},
/// - Remove a local alias
Remove {
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
room_alias_localpart: String,
},
/// - Show which room is using an alias
Which {
/// The alias localpart to look up (`alias`, not
/// `#alias:servername.tld`)
room_alias_localpart: String,
},
/// - List aliases currently being used
List {
/// If set, only list the aliases for this room
room_id: Option<Box<RoomId>>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum RoomDirectoryCommand {
/// - Publish a room to the room directory
Publish {
/// The room id of the room to publish
room_id: Box<RoomId>,
},
/// - Unpublish a room to the room directory
Unpublish {
/// The room id of the room to unpublish
room_id: Box<RoomId>,
},
/// - List rooms that are published
List {
page: Option<usize>,
},
}
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum RoomModerationCommand {
/// - Bans a room from local users joining and evicts all our local users
/// from the room. Also blocks any invites (local and remote) for the
/// banned room.
///
/// Server admins (users in the conduwuit admin room) will not be evicted
/// and server admins can still join the room. To evict admins too, use
/// --force (also ignores errors) To disable incoming federation of the
/// room, use --disable-federation
BanRoom {
#[arg(short, long)]
/// Evicts admins out of the room and ignores any potential errors when
/// making our local users leave the room
force: bool,
#[arg(long)]
/// Disables incoming federation of the room after banning and evicting
/// users
disable_federation: bool,
/// The room in the format of `!roomid:example.com` or a room alias in
/// the format of `#roomalias:example.com`
room: Box<RoomOrAliasId>,
},
/// - Bans a list of rooms (room IDs and room aliases) from a newline
/// delimited codeblock similar to `user deactivate-all`
BanListOfRooms {
#[arg(short, long)]
/// Evicts admins out of the room and ignores any potential errors when
/// making our local users leave the room
force: bool,
#[arg(long)]
/// Disables incoming federation of the room after banning and evicting
/// users
disable_federation: bool,
},
/// - Unbans a room to allow local users to join again
///
/// To re-enable incoming federation of the room, use --enable-federation
UnbanRoom {
#[arg(long)]
/// Enables incoming federation of the room after unbanning
enable_federation: bool,
/// The room in the format of `!roomid:example.com` or a room alias in
/// the format of `#roomalias:example.com`
room: Box<RoomOrAliasId>,
},
/// - List of all rooms we have banned
ListBannedRooms,
}
pub(super) async fn process(command: RoomCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
RoomCommand::Info(command) => room_info_commands::process(command, body).await?,
RoomCommand::Alias(command) => room_alias_commands::process(command, body).await?,
RoomCommand::Directory(command) => room_directory_commands::process(command, body).await?,
RoomCommand::Moderation(command) => room_moderation_commands::process(command, body).await?,
RoomCommand::List {
page,
exclude_disabled,
exclude_banned,
} => list(body, page, exclude_disabled, exclude_banned).await?,
})
}
@@ -1,49 +1,12 @@
use std::fmt::Write;
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId};
use crate::{escape_html, Command};
use super::RoomAliasCommand;
use crate::{escape_html, services, Result};
#[derive(Debug, Subcommand)]
pub(crate) enum RoomAliasCommand {
/// - Make an alias point to a room.
Set {
#[arg(short, long)]
/// Set the alias even if a room is already using it
force: bool,
/// The room id to set the alias on
room_id: Box<RoomId>,
/// The alias localpart to use (`alias`, not `#alias:servername.tld`)
room_alias_localpart: String,
},
/// - Remove a local alias
Remove {
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
room_alias_localpart: String,
},
/// - Show which room is using an alias
Which {
/// The alias localpart to look up (`alias`, not
/// `#alias:servername.tld`)
room_alias_localpart: String,
},
/// - List aliases currently being used
List {
/// If set, only list the aliases for this room
room_id: Option<Box<RoomId>>,
},
}
pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
let server_user = &services.globals.server_user;
pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
let server_user = &services().globals.server_user;
match command {
RoomAliasCommand::Set {
@@ -56,7 +19,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
| RoomAliasCommand::Which {
ref room_alias_localpart,
} => {
let room_alias_str = format!("#{}:{}", room_alias_localpart, services.globals.server_name());
let room_alias_str = format!("#{}:{}", room_alias_localpart, services().globals.server_name());
let room_alias = match RoomAliasId::parse_box(room_alias_str) {
Ok(alias) => alias,
Err(err) => return Ok(RoomMessageEventContent::text_plain(format!("Failed to parse alias: {err}"))),
@@ -66,8 +29,8 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
force,
room_id,
..
} => match (force, services.rooms.alias.resolve_local_alias(&room_alias)) {
(true, Ok(Some(id))) => match services
} => match (force, services().rooms.alias.resolve_local_alias(&room_alias)) {
(true, Ok(Some(id))) => match services()
.rooms
.alias
.set_alias(&room_alias, &room_id, server_user)
@@ -80,7 +43,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
(false, Ok(Some(id))) => Ok(RoomMessageEventContent::text_plain(format!(
"Refusing to overwrite in use alias for {id}, use -f or --force to overwrite"
))),
(_, Ok(None)) => match services
(_, Ok(None)) => match services()
.rooms
.alias
.set_alias(&room_alias, &room_id, server_user)
@@ -92,8 +55,8 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
},
RoomAliasCommand::Remove {
..
} => match services.rooms.alias.resolve_local_alias(&room_alias) {
Ok(Some(id)) => match services
} => match services().rooms.alias.resolve_local_alias(&room_alias) {
Ok(Some(id)) => match services()
.rooms
.alias
.remove_alias(&room_alias, server_user)
@@ -107,7 +70,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
},
RoomAliasCommand::Which {
..
} => match services.rooms.alias.resolve_local_alias(&room_alias) {
} => match services().rooms.alias.resolve_local_alias(&room_alias) {
Ok(Some(id)) => Ok(RoomMessageEventContent::text_plain(format!("Alias resolves to {id}"))),
Ok(None) => Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {err}"))),
@@ -121,7 +84,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
room_id,
} => {
if let Some(room_id) = room_id {
let aliases = services
let aliases = services()
.rooms
.alias
.local_aliases_for_room(&room_id)
@@ -146,14 +109,14 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to list aliases: {err}"))),
}
} else {
let aliases = services
let aliases = services()
.rooms
.alias
.all_local_aliases()
.collect::<Result<Vec<_>, _>>();
match aliases {
Ok(aliases) => {
let server_name = services.globals.server_name();
let server_name = services().globals.server_name();
let plain_list = aliases
.iter()
.fold(String::new(), |mut output, (alias, id)| {
@@ -1,18 +1,15 @@
use std::fmt::Write;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::{admin_command, escape_html, get_room_info, PAGE_SIZE};
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
#[admin_command]
pub(super) async fn list_rooms(
&self, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
pub(super) async fn list(
_body: Vec<&str>, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
) -> Result<RoomMessageEventContent> {
// TODO: i know there's a way to do this with clap, but i can't seem to find it
let page = page.unwrap_or(1);
let mut rooms = self
.services
let mut rooms = services()
.rooms
.metadata
.iter_ids()
@@ -21,8 +18,7 @@ pub(super) async fn list_rooms(
.ok()
.filter(|room_id| {
if exclude_disabled
&& self
.services
&& services()
.rooms
.metadata
.is_disabled(room_id)
@@ -32,8 +28,7 @@ pub(super) async fn list_rooms(
}
if exclude_banned
&& self
.services
&& services()
.rooms
.metadata
.is_banned(room_id)
@@ -44,7 +39,7 @@ pub(super) async fn list_rooms(
true
})
.map(|room_id| get_room_info(self.services, &room_id))
.map(|room_id| get_room_info(&room_id))
})
.collect::<Vec<_>>();
rooms.sort_by_key(|r| r.1);
@@ -1,43 +1,21 @@
use std::fmt::Write;
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId};
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
use crate::{escape_html, get_room_info, Command, PAGE_SIZE};
use super::RoomDirectoryCommand;
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
#[derive(Debug, Subcommand)]
pub(crate) enum RoomDirectoryCommand {
/// - Publish a room to the room directory
Publish {
/// The room id of the room to publish
room_id: Box<RoomId>,
},
/// - Unpublish a room to the room directory
Unpublish {
/// The room id of the room to unpublish
room_id: Box<RoomId>,
},
/// - List rooms that are published
List {
page: Option<usize>,
},
}
pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
let services = context.services;
pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
RoomDirectoryCommand::Publish {
room_id,
} => match services.rooms.directory.set_public(&room_id) {
} => match services().rooms.directory.set_public(&room_id) {
Ok(()) => Ok(RoomMessageEventContent::text_plain("Room published")),
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
},
RoomDirectoryCommand::Unpublish {
room_id,
} => match services.rooms.directory.set_not_public(&room_id) {
} => match services().rooms.directory.set_not_public(&room_id) {
Ok(()) => Ok(RoomMessageEventContent::text_plain("Room unpublished")),
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
},
@@ -46,12 +24,12 @@ pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>
} => {
// TODO: i know there's a way to do this with clap, but i can't seem to find it
let page = page.unwrap_or(1);
let mut rooms = services
let mut rooms = services()
.rooms
.directory
.public_rooms()
.filter_map(Result::ok)
.map(|id: OwnedRoomId| get_room_info(services, &id))
.map(|id: OwnedRoomId| get_room_info(&id))
.collect::<Vec<_>>();
rooms.sort_by_key(|r| r.1);
rooms.reverse();
@@ -1,30 +1,22 @@
use clap::Subcommand;
use conduit::Result;
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
use service::services;
use crate::{admin_command, admin_command_dispatch};
use super::RoomInfoCommand;
use crate::Result;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
pub(crate) enum RoomInfoCommand {
/// - List joined members in a room
ListJoinedMembers {
room_id: Box<RoomId>,
},
/// - Displays room topic
///
/// Room topics can be huge, so this is in its
/// own separate command
ViewRoomTopic {
room_id: Box<RoomId>,
},
pub(super) async fn process(command: RoomInfoCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
RoomInfoCommand::ListJoinedMembers {
room_id,
} => list_joined_members(body, room_id).await,
RoomInfoCommand::ViewRoomTopic {
room_id,
} => view_room_topic(body, room_id).await,
}
}
#[admin_command]
async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
let room_name = self
.services
async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
let room_name = services()
.rooms
.state_accessor
.get_name(&room_id)
@@ -32,8 +24,7 @@ async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageE
.flatten()
.unwrap_or_else(|| room_id.to_string());
let members = self
.services
let members = services()
.rooms
.state_cache
.room_members(&room_id)
@@ -44,7 +35,7 @@ async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageE
.map(|user_id| {
(
user_id.clone(),
self.services
services()
.users
.displayname(&user_id)
.unwrap_or(None)
@@ -67,18 +58,12 @@ async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageE
Ok(RoomMessageEventContent::notice_markdown(output_plain))
}
#[admin_command]
async fn view_room_topic(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
let Some(room_topic) = self
.services
.rooms
.state_accessor
.get_room_topic(&room_id)?
else {
async fn view_room_topic(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
let Some(room_topic) = services().rooms.state_accessor.get_room_topic(&room_id)? else {
return Ok(RoomMessageEventContent::text_plain("Room does not have a room topic set."));
};
Ok(RoomMessageEventContent::notice_markdown(format!(
"Room topic:\n```\n{room_topic}\n```"
"Room topic:\n\n```{room_topic}\n```"
)))
}
@@ -1,77 +1,37 @@
use api::client::leave_room;
use clap::Subcommand;
use conduit::{debug, error, info, warn, Result};
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomAliasId, RoomId, RoomOrAliasId};
use tracing::{debug, error, info, warn};
use crate::{admin_command, admin_command_dispatch, get_room_info};
use super::{super::Service, RoomModerationCommand};
use crate::{get_room_info, services, user_is_local, Result};
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
pub(crate) enum RoomModerationCommand {
/// - Bans a room from local users joining and evicts all our local users
/// from the room. Also blocks any invites (local and remote) for the
/// banned room.
///
/// Server admins (users in the conduwuit admin room) will not be evicted
/// and server admins can still join the room. To evict admins too, use
/// --force (also ignores errors) To disable incoming federation of the
/// room, use --disable-federation
BanRoom {
#[arg(short, long)]
/// Evicts admins out of the room and ignores any potential errors when
/// making our local users leave the room
force: bool,
#[arg(long)]
/// Disables incoming federation of the room after banning and evicting
/// users
disable_federation: bool,
/// The room in the format of `!roomid:example.com` or a room alias in
/// the format of `#roomalias:example.com`
room: Box<RoomOrAliasId>,
},
/// - Bans a list of rooms (room IDs and room aliases) from a newline
/// delimited codeblock similar to `user deactivate-all`
BanListOfRooms {
#[arg(short, long)]
/// Evicts admins out of the room and ignores any potential errors when
/// making our local users leave the room
force: bool,
#[arg(long)]
/// Disables incoming federation of the room after banning and evicting
/// users
disable_federation: bool,
},
/// - Unbans a room to allow local users to join again
///
/// To re-enable incoming federation of the room, use --enable-federation
UnbanRoom {
#[arg(long)]
/// Enables incoming federation of the room after unbanning
enable_federation: bool,
/// The room in the format of `!roomid:example.com` or a room alias in
/// the format of `#roomalias:example.com`
room: Box<RoomOrAliasId>,
},
/// - List of all rooms we have banned
ListBannedRooms,
pub(super) async fn process(command: RoomModerationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command {
RoomModerationCommand::BanRoom {
force,
room,
disable_federation,
} => ban_room(body, force, room, disable_federation).await,
RoomModerationCommand::BanListOfRooms {
force,
disable_federation,
} => ban_list_of_rooms(body, force, disable_federation).await,
RoomModerationCommand::UnbanRoom {
room,
enable_federation,
} => unban_room(body, room, enable_federation).await,
RoomModerationCommand::ListBannedRooms => list_banned_rooms(body).await,
}
}
#[admin_command]
async fn ban_room(
&self, force: bool, disable_federation: bool, room: Box<RoomOrAliasId>,
_body: Vec<&str>, force: bool, room: Box<RoomOrAliasId>, disable_federation: bool,
) -> Result<RoomMessageEventContent> {
debug!("Got room alias or ID: {}", room);
let admin_room_alias = &self.services.globals.admin_alias;
let admin_room_alias = &services().globals.admin_alias;
if let Some(admin_room_id) = self.services.admin.get_admin_room()? {
if let Some(admin_room_id) = Service::get_admin_room()? {
if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) {
return Ok(RoomMessageEventContent::text_plain("Not allowed to ban the admin room."));
}
@@ -90,7 +50,7 @@ async fn ban_room(
debug!("Room specified is a room ID, banning room ID");
self.services.rooms.metadata.ban_room(&room_id, true)?;
services().rooms.metadata.ban_room(&room_id, true)?;
room_id
} else if room.is_room_alias_id() {
@@ -109,13 +69,12 @@ async fn ban_room(
get_alias_helper to fetch room ID remotely"
);
let room_id = if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
let room_id = if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
room_id
} else {
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
match self
.services
match services()
.rooms
.alias
.resolve_alias(&room_alias, None)
@@ -133,7 +92,7 @@ async fn ban_room(
}
};
self.services.rooms.metadata.ban_room(&room_id, true)?;
services().rooms.metadata.ban_room(&room_id, true)?;
room_id
} else {
@@ -145,21 +104,20 @@ async fn ban_room(
debug!("Making all users leave the room {}", &room);
if force {
for local_user in self
.services
for local_user in services()
.rooms
.state_cache
.room_members(&room_id)
.filter_map(|user| {
user.ok().filter(|local_user| {
self.services.globals.user_is_local(local_user)
user_is_local(local_user)
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would
// fail auth check)
&& (self.services.globals.user_is_local(local_user)
&& (user_is_local(local_user)
// since this is a force operation, assume user is an admin
// if somehow this fails
&& self.services
&& services()
.users
.is_admin(local_user)
.unwrap_or(true))
@@ -170,31 +128,30 @@ async fn ban_room(
&local_user, &room_id
);
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
if let Err(e) = leave_room(&local_user, &room_id, None).await {
warn!(%e, "Failed to leave room");
}
}
} else {
for local_user in self
.services
for local_user in services()
.rooms
.state_cache
.room_members(&room_id)
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == self.services.globals.server_name()
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote users
// who are in the admin room to the list of local users (would fail auth check)
&& (local_user.server_name()
== self.services.globals.server_name()
&& !self.services
== services().globals.server_name()
&& !services()
.users
.is_admin(local_user)
.unwrap_or(false))
})
}) {
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
if let Err(e) = leave_room(&local_user, &room_id, None).await {
error!(
"Error attempting to make local user {} leave room {} during room banning: {}",
&local_user, &room_id, e
@@ -209,7 +166,7 @@ async fn ban_room(
}
if disable_federation {
self.services.rooms.metadata.disable_room(&room_id, true)?;
services().rooms.metadata.disable_room(&room_id, true)?;
return Ok(RoomMessageEventContent::text_plain(
"Room banned, removed all our local users, and disabled incoming federation with room.",
));
@@ -221,22 +178,19 @@ async fn ban_room(
))
}
#[admin_command]
async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: bool) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let rooms_s = self
.body
.to_vec()
.drain(1..self.body.len().saturating_sub(1))
let rooms_s = body
.clone()
.drain(1..body.len().saturating_sub(1))
.collect::<Vec<_>>();
let admin_room_alias = &self.services.globals.admin_alias;
let admin_room_alias = &services().globals.admin_alias;
let mut room_ban_count: usize = 0;
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
@@ -244,7 +198,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
for &room in &rooms_s {
match <&RoomOrAliasId>::try_from(room) {
Ok(room_alias_or_id) => {
if let Some(admin_room_id) = self.services.admin.get_admin_room()? {
if let Some(admin_room_id) = Service::get_admin_room()? {
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(admin_room_alias) {
info!("User specified admin room in bulk ban list, ignoring");
continue;
@@ -277,7 +231,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
match RoomAliasId::parse(room_alias_or_id) {
Ok(room_alias) => {
let room_id =
if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
room_id
} else {
debug!(
@@ -285,8 +239,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
ID over federation"
);
match self
.services
match services()
.rooms
.alias
.resolve_alias(&room_alias, None)
@@ -350,35 +303,28 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
}
for room_id in room_ids {
if self
.services
.rooms
.metadata
.ban_room(&room_id, true)
.is_ok()
{
if services().rooms.metadata.ban_room(&room_id, true).is_ok() {
debug!("Banned {room_id} successfully");
room_ban_count = room_ban_count.saturating_add(1);
}
debug!("Making all users leave the room {}", &room_id);
if force {
for local_user in self
.services
for local_user in services()
.rooms
.state_cache
.room_members(&room_id)
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == self.services.globals.server_name()
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote
// users who are in the admin room to the list of local
// users (would fail auth check)
&& (local_user.server_name()
== self.services.globals.server_name()
== services().globals.server_name()
// since this is a force operation, assume user is an
// admin if somehow this fails
&& self.services
&& services()
.users
.is_admin(local_user)
.unwrap_or(true))
@@ -388,32 +334,31 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
&local_user, room_id
);
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
if let Err(e) = leave_room(&local_user, &room_id, None).await {
warn!(%e, "Failed to leave room");
}
}
} else {
for local_user in self
.services
for local_user in services()
.rooms
.state_cache
.room_members(&room_id)
.filter_map(|user| {
user.ok().filter(|local_user| {
local_user.server_name() == self.services.globals.server_name()
local_user.server_name() == services().globals.server_name()
// additional wrapped check here is to avoid adding remote
// users who are in the admin room to the list of local
// users (would fail auth check)
&& (local_user.server_name()
== self.services.globals.server_name()
&& !self.services
== services().globals.server_name()
&& !services()
.users
.is_admin(local_user)
.unwrap_or(false))
})
}) {
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
if let Err(e) = leave_room(&local_user, &room_id, None).await {
error!(
"Error attempting to make local user {} leave room {} during bulk room banning: {}",
&local_user, &room_id, e
@@ -429,7 +374,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
}
if disable_federation {
self.services.rooms.metadata.disable_room(&room_id, true)?;
services().rooms.metadata.disable_room(&room_id, true)?;
}
}
@@ -445,8 +390,9 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
}
}
#[admin_command]
async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) -> Result<RoomMessageEventContent> {
async fn unban_room(
_body: Vec<&str>, room: Box<RoomOrAliasId>, enable_federation: bool,
) -> Result<RoomMessageEventContent> {
let room_id = if room.is_room_id() {
let room_id = match RoomId::parse(&room) {
Ok(room_id) => room_id,
@@ -460,7 +406,7 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
debug!("Room specified is a room ID, unbanning room ID");
self.services.rooms.metadata.ban_room(&room_id, false)?;
services().rooms.metadata.ban_room(&room_id, false)?;
room_id
} else if room.is_room_alias_id() {
@@ -479,13 +425,12 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
get_alias_helper to fetch room ID remotely"
);
let room_id = if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
let room_id = if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
room_id
} else {
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
match self
.services
match services()
.rooms
.alias
.resolve_alias(&room_alias, None)
@@ -503,7 +448,7 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
}
};
self.services.rooms.metadata.ban_room(&room_id, false)?;
services().rooms.metadata.ban_room(&room_id, false)?;
room_id
} else {
@@ -514,7 +459,7 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
};
if enable_federation {
self.services.rooms.metadata.disable_room(&room_id, false)?;
services().rooms.metadata.disable_room(&room_id, false)?;
return Ok(RoomMessageEventContent::text_plain("Room unbanned."));
}
@@ -524,10 +469,8 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
))
}
#[admin_command]
async fn list_banned_rooms(&self) -> Result<RoomMessageEventContent> {
let rooms = self
.services
async fn list_banned_rooms(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let rooms = services()
.rooms
.metadata
.list_banned_rooms()
@@ -541,7 +484,7 @@ async fn list_banned_rooms(&self) -> Result<RoomMessageEventContent> {
let mut rooms = room_ids
.into_iter()
.map(|room_id| get_room_info(self.services, &room_id))
.map(|room_id| get_room_info(&room_id))
.collect::<Vec<_>>();
rooms.sort_by_key(|r| r.1);
rooms.reverse();
+31 -87
View File
@@ -1,14 +1,10 @@
use std::{fmt::Write, sync::Arc};
use conduit::{info, utils::time, warn, Err, Result};
use conduit::{utils::time, warn, Err, Result};
use ruma::events::room::message::RoomMessageEventContent;
use crate::admin_command;
use crate::services;
#[admin_command]
pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
let elapsed = self
.services
pub(super) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let elapsed = services()
.server
.started
.elapsed()
@@ -18,58 +14,14 @@ pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
Ok(RoomMessageEventContent::notice_plain(format!("{result}.")))
}
#[admin_command]
pub(super) async fn show_config(&self) -> Result<RoomMessageEventContent> {
pub(super) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
// Construct and send the response
Ok(RoomMessageEventContent::text_plain(format!("{}", self.services.globals.config)))
Ok(RoomMessageEventContent::text_plain(format!("{}", services().globals.config)))
}
#[admin_command]
pub(super) async fn list_features(
&self, available: bool, enabled: bool, comma: bool,
) -> Result<RoomMessageEventContent> {
let delim = if comma {
","
} else {
" "
};
if enabled && !available {
let features = info::rustc::features().join(delim);
let out = format!("```\n{features}\n```");
return Ok(RoomMessageEventContent::text_markdown(out));
}
if available && !enabled {
let features = info::cargo::features().join(delim);
let out = format!("```\n{features}\n```");
return Ok(RoomMessageEventContent::text_markdown(out));
}
let mut features = String::new();
let enabled = info::rustc::features();
let available = info::cargo::features();
for feature in available {
let active = enabled.contains(&feature.as_str());
let emoji = if active {
""
} else {
""
};
let remark = if active {
"[enabled]"
} else {
""
};
writeln!(features, "{emoji} {feature} {remark}")?;
}
Ok(RoomMessageEventContent::text_markdown(features))
}
#[admin_command]
pub(super) async fn memory_usage(&self) -> Result<RoomMessageEventContent> {
let services_usage = self.services.memory_usage().await?;
let database_usage = self.services.db.db.memory_usage()?;
pub(super) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let services_usage = services().memory_usage().await?;
let database_usage = services().db.db.memory_usage()?;
let allocator_usage = conduit::alloc::memory_usage().map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
Ok(RoomMessageEventContent::text_plain(format!(
@@ -77,16 +29,14 @@ pub(super) async fn memory_usage(&self) -> Result<RoomMessageEventContent> {
)))
}
#[admin_command]
pub(super) async fn clear_caches(&self) -> Result<RoomMessageEventContent> {
self.services.clear_cache().await;
pub(super) async fn clear_caches(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
services().clear_cache().await;
Ok(RoomMessageEventContent::text_plain("Done."))
}
#[admin_command]
pub(super) async fn list_backups(&self) -> Result<RoomMessageEventContent> {
let result = self.services.globals.db.backup_list()?;
pub(super) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let result = services().globals.db.backup_list()?;
if result.is_empty() {
Ok(RoomMessageEventContent::text_plain("No backups found."))
@@ -95,51 +45,46 @@ pub(super) async fn list_backups(&self) -> Result<RoomMessageEventContent> {
}
}
#[admin_command]
pub(super) async fn backup_database(&self) -> Result<RoomMessageEventContent> {
let globals = Arc::clone(&self.services.globals);
let mut result = self
.services
pub(super) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let mut result = services()
.server
.runtime()
.spawn_blocking(move || match globals.db.backup() {
.spawn_blocking(move || match services().globals.db.backup() {
Ok(()) => String::new(),
Err(e) => (*e).to_string(),
})
.await?;
.await
.unwrap();
if result.is_empty() {
result = self.services.globals.db.backup_list()?;
result = services().globals.db.backup_list()?;
}
Ok(RoomMessageEventContent::notice_markdown(result))
Ok(RoomMessageEventContent::text_plain(&result))
}
#[admin_command]
pub(super) async fn list_database_files(&self) -> Result<RoomMessageEventContent> {
let result = self.services.globals.db.file_list()?;
pub(super) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
let result = services().globals.db.file_list()?;
Ok(RoomMessageEventContent::notice_markdown(result))
}
#[admin_command]
pub(super) async fn admin_notice(&self, message: Vec<String>) -> Result<RoomMessageEventContent> {
pub(super) async fn admin_notice(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
let message = message.join(" ");
self.services.admin.send_text(&message).await;
services().admin.send_text(&message).await;
Ok(RoomMessageEventContent::notice_plain("Notice was sent to #admins"))
}
#[admin_command]
pub(super) async fn reload_mods(&self) -> Result<RoomMessageEventContent> {
self.services.server.reload()?;
#[cfg(conduit_mods)]
pub(super) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
services().server.reload()?;
Ok(RoomMessageEventContent::notice_plain("Reloading server..."))
}
#[admin_command]
#[cfg(unix)]
pub(super) async fn restart(&self, force: bool) -> Result<RoomMessageEventContent> {
pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessageEventContent> {
use conduit::utils::sys::current_exe_deleted;
if !force && current_exe_deleted() {
@@ -149,15 +94,14 @@ pub(super) async fn restart(&self, force: bool) -> Result<RoomMessageEventConten
);
}
self.services.server.restart()?;
services().server.restart()?;
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
}
#[admin_command]
pub(super) async fn shutdown(&self) -> Result<RoomMessageEventContent> {
pub(super) async fn shutdown(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
warn!("shutdown command");
self.services.server.shutdown()?;
services().server.shutdown()?;
Ok(RoomMessageEventContent::notice_plain("Shutting down server..."))
}
+28 -17
View File
@@ -2,11 +2,12 @@ mod commands;
use clap::Subcommand;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::admin_command_dispatch;
use self::commands::*;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum ServerCommand {
/// - Time elapsed since startup
Uptime,
@@ -14,18 +15,6 @@ pub(super) enum ServerCommand {
/// - Show configuration values
ShowConfig,
/// - List the features built into the server
ListFeatures {
#[arg(short, long)]
available: bool,
#[arg(short, long)]
enabled: bool,
#[arg(short, long)]
comma: bool,
},
/// - Print database memory usage statistics
MemoryUsage,
@@ -47,9 +36,9 @@ pub(super) enum ServerCommand {
message: Vec<String>,
},
#[cfg(conduit_mods)]
/// - Hot-reload the server
#[clap(alias = "reload")]
ReloadMods,
Reload,
#[cfg(unix)]
/// - Restart the server
@@ -61,3 +50,25 @@ pub(super) enum ServerCommand {
/// - Shutdown the server
Shutdown,
}
pub(super) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
ServerCommand::Uptime => uptime(body).await?,
ServerCommand::ShowConfig => show_config(body).await?,
ServerCommand::MemoryUsage => memory_usage(body).await?,
ServerCommand::ClearCaches => clear_caches(body).await?,
ServerCommand::ListBackups => list_backups(body).await?,
ServerCommand::BackupDatabase => backup_database(body).await?,
ServerCommand::ListDatabaseFiles => list_database_files(body).await?,
ServerCommand::AdminNotice {
message,
} => admin_notice(body, message).await?,
#[cfg(conduit_mods)]
ServerCommand::Reload => reload(body).await?,
#[cfg(unix)]
ServerCommand::Restart {
force,
} => restart(body, force).await?,
ServerCommand::Shutdown => shutdown(body).await?,
})
}
+1 -1
View File
@@ -12,7 +12,7 @@ fn get_help_subcommand() { get_help_inner("help"); }
fn get_help_inner(input: &str) {
use clap::Parser;
use crate::admin::AdminCommand;
use crate::handler::AdminCommand;
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
panic!("no error!");
+81 -113
View File
@@ -1,7 +1,7 @@
use std::{collections::BTreeMap, fmt::Write as _};
use api::client::{join_room_by_id_helper, leave_all_rooms, update_avatar_url, update_displayname};
use conduit::{error, info, utils, warn, Result};
use conduit::{utils, Result};
use ruma::{
events::{
room::message::RoomMessageEventContent,
@@ -10,17 +10,17 @@ use ruma::{
},
OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, RoomId,
};
use tracing::{error, info, warn};
use crate::{
admin_command, escape_html, get_room_info,
escape_html, get_room_info, services,
utils::{parse_active_local_user_id, parse_local_user_id},
};
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
#[admin_command]
pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
match self.services.users.list_local_users() {
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
match services().users.list_local_users() {
Ok(users) => {
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
plain_msg += users.join("\n").as_str();
@@ -32,51 +32,43 @@ pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
}
}
#[admin_command]
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result<RoomMessageEventContent> {
pub(super) async fn create(
_body: Vec<&str>, username: String, password: Option<String>,
) -> Result<RoomMessageEventContent> {
// Validate user id
let user_id = parse_local_user_id(self.services, &username)?;
let user_id = parse_local_user_id(&username)?;
if self.services.users.exists(&user_id)? {
if services().users.exists(&user_id)? {
return Ok(RoomMessageEventContent::text_plain(format!("Userid {user_id} already exists")));
}
if user_id.is_historical() {
return Ok(RoomMessageEventContent::text_plain(format!(
"User ID {user_id} does not conform to new Matrix identifier spec"
)));
}
let password = password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
// Create user
self.services
.users
.create(&user_id, Some(password.as_str()))?;
services().users.create(&user_id, Some(password.as_str()))?;
// Default to pretty displayname
let mut displayname = user_id.localpart().to_owned();
// If `new_user_displayname_suffix` is set, registration will push whatever
// content is set to the user's display name with a space before it
if !self
.services
if !services()
.globals
.config
.new_user_displayname_suffix
.is_empty()
{
write!(displayname, " {}", self.services.globals.config.new_user_displayname_suffix)
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
.expect("should be able to write to string buffer");
}
self.services
services()
.users
.set_displayname(&user_id, Some(displayname))
.await?;
// Initial account data
self.services.account_data.update(
services().account_data.update(
None,
&user_id,
ruma::events::GlobalAccountDataEventType::PushRules
@@ -90,13 +82,12 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
.expect("to json value always works"),
)?;
if !self.services.globals.config.auto_join_rooms.is_empty() {
for room in &self.services.globals.config.auto_join_rooms {
if !self
.services
if !services().globals.config.auto_join_rooms.is_empty() {
for room in &services().globals.config.auto_join_rooms {
if !services()
.rooms
.state_cache
.server_in_room(self.services.globals.server_name(), room)?
.server_in_room(services().globals.server_name(), room)?
{
warn!("Skipping room {room} to automatically join as we have never joined before.");
continue;
@@ -104,11 +95,10 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
if let Some(room_id_server_name) = room.server_name() {
match join_room_by_id_helper(
self.services,
&user_id,
room,
Some("Automatically joining this room upon registration".to_owned()),
&[room_id_server_name.to_owned(), self.services.globals.server_name().to_owned()],
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
None,
)
.await
@@ -133,38 +123,38 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
)))
}
#[admin_command]
pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) -> Result<RoomMessageEventContent> {
pub(super) async fn deactivate(
_body: Vec<&str>, no_leave_rooms: bool, user_id: String,
) -> Result<RoomMessageEventContent> {
// Validate user id
let user_id = parse_local_user_id(self.services, &user_id)?;
let user_id = parse_local_user_id(&user_id)?;
// don't deactivate the server service account
if user_id == self.services.globals.server_user {
if user_id == services().globals.server_user {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to deactivate the server service account.",
));
}
self.services.users.deactivate_account(&user_id)?;
services().users.deactivate_account(&user_id)?;
if !no_leave_rooms {
self.services
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Making {user_id} leave all rooms after deactivation..."
)))
.await;
let all_joined_rooms: Vec<OwnedRoomId> = self
.services
let all_joined_rooms: Vec<OwnedRoomId> = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.filter_map(Result::ok)
.collect();
update_displayname(self.services, user_id.clone(), None, all_joined_rooms.clone()).await?;
update_avatar_url(self.services, user_id.clone(), None, None, all_joined_rooms).await?;
leave_all_rooms(self.services, &user_id).await;
update_displayname(user_id.clone(), None, all_joined_rooms.clone()).await?;
update_avatar_url(user_id.clone(), None, None, all_joined_rooms).await?;
leave_all_rooms(&user_id).await;
}
Ok(RoomMessageEventContent::text_plain(format!(
@@ -172,11 +162,10 @@ pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) ->
)))
}
#[admin_command]
pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessageEventContent> {
let user_id = parse_local_user_id(self.services, &username)?;
pub(super) async fn reset_password(_body: Vec<&str>, username: String) -> Result<RoomMessageEventContent> {
let user_id = parse_local_user_id(&username)?;
if user_id == self.services.globals.server_user {
if user_id == services().globals.server_user {
return Ok(RoomMessageEventContent::text_plain(
"Not allowed to set the password for the server account. Please use the emergency password config option.",
));
@@ -184,8 +173,7 @@ pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessag
let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH);
match self
.services
match services()
.users
.set_password(&user_id, Some(new_password.as_str()))
{
@@ -198,29 +186,28 @@ pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessag
}
}
#[admin_command]
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result<RoomMessageEventContent> {
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
{
pub(super) async fn deactivate_all(
body: Vec<&str>, no_leave_rooms: bool, force: bool,
) -> Result<RoomMessageEventContent> {
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
return Ok(RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.",
));
}
let usernames = self
.body
.to_vec()
.drain(1..self.body.len().saturating_sub(1))
let usernames = body
.clone()
.drain(1..body.len().saturating_sub(1))
.collect::<Vec<_>>();
let mut user_ids: Vec<OwnedUserId> = Vec::with_capacity(usernames.len());
let mut admins = Vec::new();
for username in usernames {
match parse_active_local_user_id(self.services, username) {
match parse_active_local_user_id(username) {
Ok(user_id) => {
if self.services.users.is_admin(&user_id)? && !force {
self.services
if services().users.is_admin(&user_id)? && !force {
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"{username} is an admin and --force is not set, skipping over"
@@ -231,8 +218,8 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
}
// don't deactivate the server service account
if user_id == self.services.globals.server_user {
self.services
if user_id == services().globals.server_user {
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"{username} is the server service account, skipping over"
@@ -244,7 +231,7 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
user_ids.push(user_id);
},
Err(e) => {
self.services
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"{username} is not a valid username, skipping over: {e}"
@@ -258,25 +245,24 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
let mut deactivation_count: usize = 0;
for user_id in user_ids {
match self.services.users.deactivate_account(&user_id) {
match services().users.deactivate_account(&user_id) {
Ok(()) => {
deactivation_count = deactivation_count.saturating_add(1);
if !no_leave_rooms {
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
let all_joined_rooms: Vec<OwnedRoomId> = self
.services
let all_joined_rooms: Vec<OwnedRoomId> = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.filter_map(Result::ok)
.collect();
update_displayname(self.services, user_id.clone(), None, all_joined_rooms.clone()).await?;
update_avatar_url(self.services, user_id.clone(), None, None, all_joined_rooms).await?;
leave_all_rooms(self.services, &user_id).await;
update_displayname(user_id.clone(), None, all_joined_rooms.clone()).await?;
update_avatar_url(user_id.clone(), None, None, all_joined_rooms).await?;
leave_all_rooms(&user_id).await;
}
},
Err(e) => {
self.services
services()
.admin
.send_message(RoomMessageEventContent::text_plain(format!("Failed deactivating user: {e}")))
.await;
@@ -297,18 +283,16 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
}
}
#[admin_command]
pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result<RoomMessageEventContent> {
pub(super) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
// Validate user id
let user_id = parse_local_user_id(self.services, &user_id)?;
let user_id = parse_local_user_id(&user_id)?;
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
.services
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.filter_map(Result::ok)
.map(|room_id| get_room_info(self.services, &room_id))
.map(|room_id| get_room_info(&room_id))
.collect();
if rooms.is_empty() {
@@ -350,55 +334,41 @@ pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result<RoomMess
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
}
#[admin_command]
pub(super) async fn force_join_room(
&self, user_id: String, room_id: OwnedRoomOrAliasId,
_body: Vec<&str>, user_id: String, room_id: OwnedRoomOrAliasId,
) -> Result<RoomMessageEventContent> {
let user_id = parse_local_user_id(self.services, &user_id)?;
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
let user_id = parse_local_user_id(&user_id)?;
let room_id = services().rooms.alias.resolve(&room_id).await?;
assert!(
self.services.globals.user_is_local(&user_id),
"Parsed user_id must be a local user"
);
join_room_by_id_helper(self.services, &user_id, &room_id, None, &[], None).await?;
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
join_room_by_id_helper(&user_id, &room_id, None, &[], None).await?;
Ok(RoomMessageEventContent::notice_markdown(format!(
"{user_id} has been joined to {room_id}.",
)))
}
#[admin_command]
pub(super) async fn make_user_admin(&self, user_id: String) -> Result<RoomMessageEventContent> {
let user_id = parse_local_user_id(self.services, &user_id)?;
let displayname = self
.services
pub(super) async fn make_user_admin(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
let user_id = parse_local_user_id(&user_id)?;
let displayname = services()
.users
.displayname(&user_id)?
.unwrap_or_else(|| user_id.to_string());
assert!(
self.services.globals.user_is_local(&user_id),
"Parsed user_id must be a local user"
);
self.services
.admin
.make_user_admin(&user_id, displayname)
.await?;
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
service::admin::make_user_admin(&user_id, displayname).await?;
Ok(RoomMessageEventContent::notice_markdown(format!(
"{user_id} has been granted admin privileges.",
)))
}
#[admin_command]
pub(super) async fn put_room_tag(
&self, user_id: String, room_id: Box<RoomId>, tag: String,
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
) -> Result<RoomMessageEventContent> {
let user_id = parse_active_local_user_id(self.services, &user_id)?;
let user_id = parse_active_local_user_id(&user_id)?;
let event = self
.services
let event = services()
.account_data
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
@@ -416,7 +386,7 @@ pub(super) async fn put_room_tag(
.tags
.insert(tag.clone().into(), TagInfo::new());
self.services.account_data.update(
services().account_data.update(
Some(&room_id),
&user_id,
RoomAccountDataEventType::Tag,
@@ -428,14 +398,12 @@ pub(super) async fn put_room_tag(
)))
}
#[admin_command]
pub(super) async fn delete_room_tag(
&self, user_id: String, room_id: Box<RoomId>, tag: String,
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
) -> Result<RoomMessageEventContent> {
let user_id = parse_active_local_user_id(self.services, &user_id)?;
let user_id = parse_active_local_user_id(&user_id)?;
let event = self
.services
let event = services()
.account_data
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
@@ -450,7 +418,7 @@ pub(super) async fn delete_room_tag(
tags_event.content.tags.remove(&tag.clone().into());
self.services.account_data.update(
services().account_data.update(
Some(&room_id),
&user_id,
RoomAccountDataEventType::Tag,
@@ -462,12 +430,12 @@ pub(super) async fn delete_room_tag(
)))
}
#[admin_command]
pub(super) async fn get_room_tags(&self, user_id: String, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
let user_id = parse_active_local_user_id(self.services, &user_id)?;
pub(super) async fn get_room_tags(
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>,
) -> Result<RoomMessageEventContent> {
let user_id = parse_active_local_user_id(&user_id)?;
let event = self
.services
let event = services()
.account_data
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
+51 -8
View File
@@ -2,16 +2,15 @@ mod commands;
use clap::Subcommand;
use conduit::Result;
use ruma::{OwnedRoomOrAliasId, RoomId};
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId, RoomId};
use crate::admin_command_dispatch;
use self::commands::*;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
#[cfg_attr(test, derive(Debug))]
#[derive(Subcommand)]
pub(super) enum UserCommand {
/// - Create a new user
#[clap(alias = "create")]
CreateUser {
Create {
/// Username of the new user
username: String,
/// Password of the new user, if unspecified one is generated
@@ -58,8 +57,7 @@ pub(super) enum UserCommand {
},
/// - List local users in the database
#[clap(alias = "list")]
ListUsers,
List,
/// - Lists all the rooms (local and remote) that the specified user is
/// joined in
@@ -104,3 +102,48 @@ pub(super) enum UserCommand {
room_id: Box<RoomId>,
},
}
pub(super) async fn process(command: UserCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
Ok(match command {
UserCommand::List => list(body).await?,
UserCommand::Create {
username,
password,
} => create(body, username, password).await?,
UserCommand::Deactivate {
no_leave_rooms,
user_id,
} => deactivate(body, no_leave_rooms, user_id).await?,
UserCommand::ResetPassword {
username,
} => reset_password(body, username).await?,
UserCommand::DeactivateAll {
no_leave_rooms,
force,
} => deactivate_all(body, no_leave_rooms, force).await?,
UserCommand::ListJoinedRooms {
user_id,
} => list_joined_rooms(body, user_id).await?,
UserCommand::ForceJoinRoom {
user_id,
room_id,
} => force_join_room(body, user_id, room_id).await?,
UserCommand::MakeUserAdmin {
user_id,
} => make_user_admin(body, user_id).await?,
UserCommand::PutRoomTag {
user_id,
room_id,
tag,
} => put_room_tag(body, user_id, room_id, tag).await?,
UserCommand::DeleteRoomTag {
user_id,
room_id,
tag,
} => delete_room_tag(body, user_id, room_id, tag).await?,
UserCommand::GetRoomTags {
user_id,
room_id,
} => get_room_tags(body, user_id, room_id).await?,
})
}
+16 -14
View File
@@ -1,6 +1,8 @@
use conduit_core::{err, Err, Result};
use conduit_core::{err, Err};
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
use service::Services;
use service::user_is_local;
use crate::{services, Result};
pub(crate) fn escape_html(s: &str) -> String {
s.replace('&', "&amp;")
@@ -8,17 +10,17 @@ pub(crate) fn escape_html(s: &str) -> String {
.replace('>', "&gt;")
}
pub(crate) fn get_room_info(services: &Services, id: &RoomId) -> (OwnedRoomId, u64, String) {
pub(crate) fn get_room_info(id: &RoomId) -> (OwnedRoomId, u64, String) {
(
id.into(),
services
services()
.rooms
.state_cache
.room_joined_count(id)
.ok()
.flatten()
.unwrap_or(0),
services
services()
.rooms
.state_accessor
.get_name(id)
@@ -29,16 +31,16 @@ pub(crate) fn get_room_info(services: &Services, id: &RoomId) -> (OwnedRoomId, u
}
/// Parses user ID
pub(crate) fn parse_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
pub(crate) fn parse_user_id(user_id: &str) -> Result<OwnedUserId> {
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
.map_err(|e| err!("The supplied username is not a valid username: {e}"))
}
/// Parses user ID as our local user
pub(crate) fn parse_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
let user_id = parse_user_id(services, user_id)?;
pub(crate) fn parse_local_user_id(user_id: &str) -> Result<OwnedUserId> {
let user_id = parse_user_id(user_id)?;
if !services.globals.user_is_local(&user_id) {
if !user_is_local(&user_id) {
return Err!("User {user_id:?} does not belong to our server.");
}
@@ -46,14 +48,14 @@ pub(crate) fn parse_local_user_id(services: &Services, user_id: &str) -> Result<
}
/// Parses user ID that is an active (not guest or deactivated) local user
pub(crate) fn parse_active_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
let user_id = parse_local_user_id(services, user_id)?;
pub(crate) fn parse_active_local_user_id(user_id: &str) -> Result<OwnedUserId> {
let user_id = parse_local_user_id(user_id)?;
if !services.users.exists(&user_id)? {
if !services().users.exists(&user_id)? {
return Err!("User {user_id:?} does not exist on this server.");
}
if services.users.is_deactivated(&user_id)? {
if services().users.is_deactivated(&user_id)? {
return Err!("User {user_id:?} is deactivated.");
}
+75 -74
View File
@@ -1,8 +1,7 @@
use std::fmt::Write;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduit::{debug_info, error, info, utils, warn, Error, Result};
use conduit::debug_info;
use register::RegistrationKind;
use ruma::{
api::client::{
@@ -18,9 +17,15 @@ use ruma::{
events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType},
push, OwnedRoomId, UserId,
};
use tracing::{error, info, warn};
use super::{join_room_by_id_helper, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
use crate::Ruma;
use crate::{
service::user_is_local,
services,
utils::{self},
Error, Result, Ruma,
};
const RANDOM_USER_ID_LENGTH: usize = 10;
@@ -37,21 +42,20 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
/// invalid when trying to register
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
pub(crate) async fn get_register_available_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_username_availability::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_username_availability::v3::Request>,
) -> Result<get_username_availability::v3::Response> {
// Validate user id
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services.globals.server_name())
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services().globals.server_name())
.ok()
.filter(|user_id| !user_id.is_historical() && services.globals.user_is_local(user_id))
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
// Check if username is creative enough
if services.users.exists(&user_id)? {
if services().users.exists(&user_id)? {
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
}
if services
if services()
.globals
.forbidden_usernames()
.is_match(user_id.localpart())
@@ -87,9 +91,9 @@ pub(crate) async fn get_register_available_route(
#[allow(clippy::doc_markdown)]
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn register_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
) -> Result<register::v3::Response> {
if !services.globals.allow_registration() && body.appservice_info.is_none() {
if !services().globals.allow_registration() && body.appservice_info.is_none() {
info!(
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
{:?}",
@@ -101,8 +105,8 @@ pub(crate) async fn register_route(
let is_guest = body.kind == RegistrationKind::Guest;
if is_guest
&& (!services.globals.allow_guest_registration()
|| (services.globals.allow_registration() && services.globals.config.registration_token.is_some()))
&& (!services().globals.allow_guest_registration()
|| (services().globals.allow_registration() && services().globals.config.registration_token.is_some()))
{
info!(
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
@@ -117,7 +121,7 @@ pub(crate) async fn register_route(
// forbid guests from registering if there is not a real admin user yet. give
// generic user error.
if is_guest && services.users.count()? < 2 {
if is_guest && services().users.count()? < 2 {
warn!(
"Guest account attempted to register before a real admin user has been registered, rejecting \
registration. Guest's initial device name: {:?}",
@@ -129,16 +133,16 @@ pub(crate) async fn register_route(
let user_id = match (&body.username, is_guest) {
(Some(username), false) => {
let proposed_user_id =
UserId::parse_with_server_name(username.to_lowercase(), services.globals.server_name())
UserId::parse_with_server_name(username.to_lowercase(), services().globals.server_name())
.ok()
.filter(|user_id| !user_id.is_historical() && services.globals.user_is_local(user_id))
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
if services.users.exists(&proposed_user_id)? {
if services().users.exists(&proposed_user_id)? {
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
}
if services
if services()
.globals
.forbidden_usernames()
.is_match(proposed_user_id.localpart())
@@ -151,10 +155,10 @@ pub(crate) async fn register_route(
_ => loop {
let proposed_user_id = UserId::parse_with_server_name(
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
services.globals.server_name(),
services().globals.server_name(),
)
.unwrap();
if !services.users.exists(&proposed_user_id)? {
if !services().users.exists(&proposed_user_id)? {
break proposed_user_id;
}
},
@@ -168,13 +172,13 @@ pub(crate) async fn register_route(
} else {
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing appservice token."));
}
} else if services.appservice.is_exclusive_user_id(&user_id).await {
} else if services().appservice.is_exclusive_user_id(&user_id).await {
return Err(Error::BadRequest(ErrorKind::Exclusive, "User ID reserved by appservice."));
}
// UIAA
let mut uiaainfo;
let skip_auth = if services.globals.config.registration_token.is_some() {
let skip_auth = if services().globals.config.registration_token.is_some() {
// Registration token required
uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
@@ -202,8 +206,8 @@ pub(crate) async fn register_route(
if !skip_auth {
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services.uiaa.try_auth(
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
let (worked, uiaainfo) = services().uiaa.try_auth(
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
"".into(),
auth,
&uiaainfo,
@@ -214,8 +218,8 @@ pub(crate) async fn register_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services.uiaa.create(
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
services().uiaa.create(
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
"".into(),
&uiaainfo,
&json,
@@ -233,25 +237,25 @@ pub(crate) async fn register_route(
};
// Create user
services.users.create(&user_id, password)?;
services().users.create(&user_id, password)?;
// Default to pretty displayname
let mut displayname = user_id.localpart().to_owned();
// If `new_user_displayname_suffix` is set, registration will push whatever
// content is set to the user's display name with a space before it
if !services.globals.new_user_displayname_suffix().is_empty() {
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
if !services().globals.new_user_displayname_suffix().is_empty() {
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
.expect("should be able to write to string buffer");
}
services
services()
.users
.set_displayname(&user_id, Some(displayname.clone()))
.await?;
// Initial account data
services.account_data.update(
services().account_data.update(
None,
&user_id,
GlobalAccountDataEventType::PushRules.to_string().into(),
@@ -286,7 +290,7 @@ pub(crate) async fn register_route(
let token = utils::random_string(TOKEN_LENGTH);
// Create device for this account
services
services()
.users
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
@@ -295,7 +299,7 @@ pub(crate) async fn register_route(
// log in conduit admin channel if a non-guest user registered
if body.appservice_info.is_none() && !is_guest {
info!("New user \"{user_id}\" registered on this server.");
services
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"New user \"{user_id}\" registered on this server from IP {client}."
@@ -304,7 +308,7 @@ pub(crate) async fn register_route(
}
// log in conduit admin channel if a guest registered
if body.appservice_info.is_none() && is_guest && services.globals.log_guest_registrations() {
if body.appservice_info.is_none() && is_guest && services().globals.log_guest_registrations() {
info!("New guest user \"{user_id}\" registered on this server.");
if let Some(device_display_name) = &body.initial_device_display_name {
@@ -313,7 +317,7 @@ pub(crate) async fn register_route(
.as_ref()
.is_some_and(|device_display_name| !device_display_name.is_empty())
{
services
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
@@ -321,7 +325,7 @@ pub(crate) async fn register_route(
)))
.await;
} else {
services
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
@@ -330,7 +334,7 @@ pub(crate) async fn register_route(
.await;
}
} else {
services
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server from IP {client}.",
@@ -342,12 +346,14 @@ pub(crate) async fn register_route(
// If this is the first real user, grant them admin privileges except for guest
// users Note: the server user, @conduit:servername, is generated first
if !is_guest {
if let Some(admin_room) = services.admin.get_admin_room()? {
if services.rooms.state_cache.room_joined_count(&admin_room)? == Some(1) {
services
.admin
.make_user_admin(&user_id, displayname)
.await?;
if let Some(admin_room) = service::admin::Service::get_admin_room()? {
if services()
.rooms
.state_cache
.room_joined_count(&admin_room)?
== Some(1)
{
service::admin::make_user_admin(&user_id, displayname).await?;
warn!("Granting {user_id} admin privileges as the first user");
}
@@ -355,14 +361,14 @@ pub(crate) async fn register_route(
}
if body.appservice_info.is_none()
&& !services.globals.config.auto_join_rooms.is_empty()
&& (services.globals.allow_guests_auto_join_rooms() || !is_guest)
&& !services().globals.config.auto_join_rooms.is_empty()
&& (services().globals.allow_guests_auto_join_rooms() || !is_guest)
{
for room in &services.globals.config.auto_join_rooms {
if !services
for room in &services().globals.config.auto_join_rooms {
if !services()
.rooms
.state_cache
.server_in_room(services.globals.server_name(), room)?
.server_in_room(services().globals.server_name(), room)?
{
warn!("Skipping room {room} to automatically join as we have never joined before.");
continue;
@@ -370,11 +376,10 @@ pub(crate) async fn register_route(
if let Some(room_id_server_name) = room.server_name() {
if let Err(e) = join_room_by_id_helper(
&services,
&user_id,
room,
Some("Automatically joining this room upon registration".to_owned()),
&[room_id_server_name.to_owned(), services.globals.server_name().to_owned()],
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
None,
)
.await
@@ -416,8 +421,7 @@ pub(crate) async fn register_route(
/// - Triggers device list updates
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
pub(crate) async fn change_password_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<change_password::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<change_password::v3::Request>,
) -> Result<change_password::v3::Response> {
// Authentication for this endpoint was made optional, but we need
// authentication currently
@@ -438,7 +442,7 @@ pub(crate) async fn change_password_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services
let (worked, uiaainfo) = services()
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@@ -447,7 +451,7 @@ pub(crate) async fn change_password_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services
services()
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@@ -455,24 +459,24 @@ pub(crate) async fn change_password_route(
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
}
services
services()
.users
.set_password(sender_user, Some(&body.new_password))?;
if body.logout_devices {
// Logout all devices except the current one
for id in services
for id in services()
.users
.all_device_ids(sender_user)
.filter_map(Result::ok)
.filter(|id| id != sender_device)
{
services.users.remove_device(sender_user, &id)?;
services().users.remove_device(sender_user, &id)?;
}
}
info!("User {sender_user} changed their password.");
services
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {sender_user} changed their password."
@@ -487,16 +491,14 @@ pub(crate) async fn change_password_route(
/// Get `user_id` of the sender user.
///
/// Note: Also works for Application Services
pub(crate) async fn whoami_route(
State(services): State<crate::State>, body: Ruma<whoami::v3::Request>,
) -> Result<whoami::v3::Response> {
pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let device_id = body.sender_device.clone();
Ok(whoami::v3::Response {
user_id: sender_user.clone(),
device_id,
is_guest: services.users.is_deactivated(sender_user)? && body.appservice_info.is_none(),
is_guest: services().users.is_deactivated(sender_user)? && body.appservice_info.is_none(),
})
}
@@ -513,8 +515,7 @@ pub(crate) async fn whoami_route(
/// - Removes ability to log in again
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
pub(crate) async fn deactivate_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<deactivate::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
) -> Result<deactivate::v3::Response> {
// Authentication for this endpoint was made optional, but we need
// authentication currently
@@ -535,7 +536,7 @@ pub(crate) async fn deactivate_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services
let (worked, uiaainfo) = services()
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@@ -544,7 +545,7 @@ pub(crate) async fn deactivate_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services
services()
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@@ -553,23 +554,23 @@ pub(crate) async fn deactivate_route(
}
// Remove devices and mark account as deactivated
services.users.deactivate_account(sender_user)?;
services().users.deactivate_account(sender_user)?;
// Remove profile pictures and display name
let all_joined_rooms: Vec<OwnedRoomId> = services
let all_joined_rooms: Vec<OwnedRoomId> = services()
.rooms
.state_cache
.rooms_joined(sender_user)
.filter_map(Result::ok)
.collect();
super::update_displayname(&services, sender_user.clone(), None, all_joined_rooms.clone()).await?;
super::update_avatar_url(&services, sender_user.clone(), None, None, all_joined_rooms).await?;
super::update_displayname(sender_user.clone(), None, all_joined_rooms.clone()).await?;
super::update_avatar_url(sender_user.clone(), None, None, all_joined_rooms).await?;
// Make the user leave all rooms before deactivation
super::leave_all_rooms(&services, sender_user).await;
super::leave_all_rooms(sender_user).await;
info!("User {sender_user} deactivated their account.");
services
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {sender_user} deactivated their account."
@@ -631,9 +632,9 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
/// Currently does not have any ratelimiting, and this isn't very practical as
/// there is only one registration token allowed.
pub(crate) async fn check_registration_token_validity(
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
body: Ruma<check_registration_token_validity::v1::Request>,
) -> Result<check_registration_token_validity::v1::Response> {
let Some(reg_token) = services.globals.config.registration_token.clone() else {
let Some(reg_token) = services().globals.config.registration_token.clone() else {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Server does not allow token registration.",
+18 -34
View File
@@ -1,5 +1,3 @@
use axum::extract::State;
use conduit::{debug, Error, Result};
use rand::seq::SliceRandom;
use ruma::{
api::client::{
@@ -8,27 +6,21 @@ use ruma::{
},
OwnedServerName, RoomAliasId, RoomId,
};
use service::Services;
use tracing::debug;
use crate::Ruma;
use crate::{service::server_is_ours, services, Error, Result, Ruma};
/// # `PUT /_matrix/client/v3/directory/room/{roomAlias}`
///
/// Creates a new room alias on this server.
pub(crate) async fn create_alias_route(
State(services): State<crate::State>, body: Ruma<create_alias::v3::Request>,
) -> Result<create_alias::v3::Response> {
pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
.rooms
.alias
.appservice_checks(&body.room_alias, &body.appservice_info)
.await?;
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
// this isn't apart of alias_checks or delete alias route because we should
// allow removing forbidden room aliases
if services
if services()
.globals
.forbidden_alias_names()
.is_match(body.room_alias.alias())
@@ -36,7 +28,7 @@ pub(crate) async fn create_alias_route(
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room alias is forbidden."));
}
if services
if services()
.rooms
.alias
.resolve_local_alias(&body.room_alias)?
@@ -45,7 +37,7 @@ pub(crate) async fn create_alias_route(
return Err(Error::Conflict("Alias already exists."));
}
services
services()
.rooms
.alias
.set_alias(&body.room_alias, &body.room_id, sender_user)?;
@@ -58,18 +50,12 @@ pub(crate) async fn create_alias_route(
/// Deletes a room alias from this server.
///
/// - TODO: Update canonical alias event
pub(crate) async fn delete_alias_route(
State(services): State<crate::State>, body: Ruma<delete_alias::v3::Request>,
) -> Result<delete_alias::v3::Response> {
pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
.rooms
.alias
.appservice_checks(&body.room_alias, &body.appservice_info)
.await?;
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
if services
if services()
.rooms
.alias
.resolve_local_alias(&body.room_alias)?
@@ -78,7 +64,7 @@ pub(crate) async fn delete_alias_route(
return Err(Error::BadRequest(ErrorKind::NotFound, "Alias does not exist."));
}
services
services()
.rooms
.alias
.remove_alias(&body.room_alias, sender_user)
@@ -92,13 +78,11 @@ pub(crate) async fn delete_alias_route(
/// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
///
/// Resolve an alias locally or over federation.
pub(crate) async fn get_alias_route(
State(services): State<crate::State>, body: Ruma<get_alias::v3::Request>,
) -> Result<get_alias::v3::Response> {
pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Result<get_alias::v3::Response> {
let room_alias = body.body.room_alias;
let servers = None;
let Ok((room_id, pre_servers)) = services
let Ok((room_id, pre_servers)) = services()
.rooms
.alias
.resolve_alias(&room_alias, servers.as_ref())
@@ -107,17 +91,17 @@ pub(crate) async fn get_alias_route(
return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found."));
};
let servers = room_available_servers(&services, &room_id, &room_alias, &pre_servers);
let servers = room_available_servers(&room_id, &room_alias, &pre_servers);
debug!(?room_alias, ?room_id, "available servers: {servers:?}");
Ok(get_alias::v3::Response::new(room_id, servers))
}
fn room_available_servers(
services: &Services, room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
) -> Vec<OwnedServerName> {
// find active servers in room state cache to suggest
let mut servers: Vec<OwnedServerName> = services
let mut servers: Vec<OwnedServerName> = services()
.rooms
.state_cache
.room_servers(room_id)
@@ -140,10 +124,10 @@ fn room_available_servers(
// prefer the room alias server first
if let Some(server_index) = servers
.iter()
.position(|server_name| services.globals.server_is_ours(server_name))
.position(|server_name| server_is_ours(server_name))
{
servers.swap_remove(server_index);
servers.insert(0, services.globals.server_name().to_owned());
servers.insert(0, services().globals.server_name().to_owned());
} else if let Some(alias_server_index) = servers
.iter()
.position(|server| server == room_alias.server_name())
+62 -49
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use ruma::{
api::client::{
backup::{
@@ -12,16 +11,16 @@ use ruma::{
UInt,
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `POST /_matrix/client/r0/room_keys/version`
///
/// Creates a new backup.
pub(crate) async fn create_backup_version_route(
State(services): State<crate::State>, body: Ruma<create_backup_version::v3::Request>,
body: Ruma<create_backup_version::v3::Request>,
) -> Result<create_backup_version::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let version = services
let version = services()
.key_backups
.create_backup(sender_user, &body.algorithm)?;
@@ -35,10 +34,10 @@ pub(crate) async fn create_backup_version_route(
/// Update information about an existing backup. Only `auth_data` can be
/// modified.
pub(crate) async fn update_backup_version_route(
State(services): State<crate::State>, body: Ruma<update_backup_version::v3::Request>,
body: Ruma<update_backup_version::v3::Request>,
) -> Result<update_backup_version::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
services()
.key_backups
.update_backup(sender_user, &body.version, &body.algorithm)?;
@@ -49,20 +48,20 @@ pub(crate) async fn update_backup_version_route(
///
/// Get information about the latest backup version.
pub(crate) async fn get_latest_backup_info_route(
State(services): State<crate::State>, body: Ruma<get_latest_backup_info::v3::Request>,
body: Ruma<get_latest_backup_info::v3::Request>,
) -> Result<get_latest_backup_info::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let (version, algorithm) = services
let (version, algorithm) = services()
.key_backups
.get_latest_backup(sender_user)?
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
Ok(get_latest_backup_info::v3::Response {
algorithm,
count: (UInt::try_from(services.key_backups.count_keys(sender_user, &version)?)
count: (UInt::try_from(services().key_backups.count_keys(sender_user, &version)?)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &version)?,
etag: services().key_backups.get_etag(sender_user, &version)?,
version,
})
}
@@ -71,10 +70,10 @@ pub(crate) async fn get_latest_backup_info_route(
///
/// Get information about an existing backup.
pub(crate) async fn get_backup_info_route(
State(services): State<crate::State>, body: Ruma<get_backup_info::v3::Request>,
body: Ruma<get_backup_info::v3::Request>,
) -> Result<get_backup_info::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let algorithm = services
let algorithm = services()
.key_backups
.get_backup(sender_user, &body.version)?
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
@@ -82,12 +81,14 @@ pub(crate) async fn get_backup_info_route(
Ok(get_backup_info::v3::Response {
algorithm,
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
version: body.version.clone(),
})
}
@@ -99,11 +100,11 @@ pub(crate) async fn get_backup_info_route(
/// - Deletes both information about the backup, as well as all key data related
/// to the backup
pub(crate) async fn delete_backup_version_route(
State(services): State<crate::State>, body: Ruma<delete_backup_version::v3::Request>,
body: Ruma<delete_backup_version::v3::Request>,
) -> Result<delete_backup_version::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
services()
.key_backups
.delete_backup(sender_user, &body.version)?;
@@ -119,12 +120,12 @@ pub(crate) async fn delete_backup_version_route(
/// - Adds the keys to the backup
/// - Returns the new number of keys in this backup and the etag
pub(crate) async fn add_backup_keys_route(
State(services): State<crate::State>, body: Ruma<add_backup_keys::v3::Request>,
body: Ruma<add_backup_keys::v3::Request>,
) -> Result<add_backup_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if Some(&body.version)
!= services
!= services()
.key_backups
.get_latest_backup_version(sender_user)?
.as_ref()
@@ -137,7 +138,7 @@ pub(crate) async fn add_backup_keys_route(
for (room_id, room) in &body.rooms {
for (session_id, key_data) in &room.sessions {
services
services()
.key_backups
.add_key(sender_user, &body.version, room_id, session_id, key_data)?;
}
@@ -145,12 +146,14 @@ pub(crate) async fn add_backup_keys_route(
Ok(add_backup_keys::v3::Response {
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
})
}
@@ -163,12 +166,12 @@ pub(crate) async fn add_backup_keys_route(
/// - Adds the keys to the backup
/// - Returns the new number of keys in this backup and the etag
pub(crate) async fn add_backup_keys_for_room_route(
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_room::v3::Request>,
body: Ruma<add_backup_keys_for_room::v3::Request>,
) -> Result<add_backup_keys_for_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if Some(&body.version)
!= services
!= services()
.key_backups
.get_latest_backup_version(sender_user)?
.as_ref()
@@ -180,19 +183,21 @@ pub(crate) async fn add_backup_keys_for_room_route(
}
for (session_id, key_data) in &body.sessions {
services
services()
.key_backups
.add_key(sender_user, &body.version, &body.room_id, session_id, key_data)?;
}
Ok(add_backup_keys_for_room::v3::Response {
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
})
}
@@ -205,12 +210,12 @@ pub(crate) async fn add_backup_keys_for_room_route(
/// - Adds the keys to the backup
/// - Returns the new number of keys in this backup and the etag
pub(crate) async fn add_backup_keys_for_session_route(
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_session::v3::Request>,
body: Ruma<add_backup_keys_for_session::v3::Request>,
) -> Result<add_backup_keys_for_session::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if Some(&body.version)
!= services
!= services()
.key_backups
.get_latest_backup_version(sender_user)?
.as_ref()
@@ -221,18 +226,20 @@ pub(crate) async fn add_backup_keys_for_session_route(
));
}
services
services()
.key_backups
.add_key(sender_user, &body.version, &body.room_id, &body.session_id, &body.session_data)?;
Ok(add_backup_keys_for_session::v3::Response {
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
})
}
@@ -240,11 +247,11 @@ pub(crate) async fn add_backup_keys_for_session_route(
///
/// Retrieves all keys from the backup.
pub(crate) async fn get_backup_keys_route(
State(services): State<crate::State>, body: Ruma<get_backup_keys::v3::Request>,
body: Ruma<get_backup_keys::v3::Request>,
) -> Result<get_backup_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let rooms = services.key_backups.get_all(sender_user, &body.version)?;
let rooms = services().key_backups.get_all(sender_user, &body.version)?;
Ok(get_backup_keys::v3::Response {
rooms,
@@ -255,11 +262,11 @@ pub(crate) async fn get_backup_keys_route(
///
/// Retrieves all keys from the backup for a given room.
pub(crate) async fn get_backup_keys_for_room_route(
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_room::v3::Request>,
body: Ruma<get_backup_keys_for_room::v3::Request>,
) -> Result<get_backup_keys_for_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sessions = services
let sessions = services()
.key_backups
.get_room(sender_user, &body.version, &body.room_id)?;
@@ -272,11 +279,11 @@ pub(crate) async fn get_backup_keys_for_room_route(
///
/// Retrieves a key from the backup.
pub(crate) async fn get_backup_keys_for_session_route(
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_session::v3::Request>,
body: Ruma<get_backup_keys_for_session::v3::Request>,
) -> Result<get_backup_keys_for_session::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let key_data = services
let key_data = services()
.key_backups
.get_session(sender_user, &body.version, &body.room_id, &body.session_id)?
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Backup key not found for this user's session."))?;
@@ -290,22 +297,24 @@ pub(crate) async fn get_backup_keys_for_session_route(
///
/// Delete the keys from the backup.
pub(crate) async fn delete_backup_keys_route(
State(services): State<crate::State>, body: Ruma<delete_backup_keys::v3::Request>,
body: Ruma<delete_backup_keys::v3::Request>,
) -> Result<delete_backup_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
services()
.key_backups
.delete_all_keys(sender_user, &body.version)?;
Ok(delete_backup_keys::v3::Response {
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
})
}
@@ -313,22 +322,24 @@ pub(crate) async fn delete_backup_keys_route(
///
/// Delete the keys from the backup for a given room.
pub(crate) async fn delete_backup_keys_for_room_route(
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_room::v3::Request>,
body: Ruma<delete_backup_keys_for_room::v3::Request>,
) -> Result<delete_backup_keys_for_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
services()
.key_backups
.delete_room_keys(sender_user, &body.version, &body.room_id)?;
Ok(delete_backup_keys_for_room::v3::Response {
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
})
}
@@ -336,21 +347,23 @@ pub(crate) async fn delete_backup_keys_for_room_route(
///
/// Delete a key from the backup.
pub(crate) async fn delete_backup_keys_for_session_route(
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_session::v3::Request>,
body: Ruma<delete_backup_keys_for_session::v3::Request>,
) -> Result<delete_backup_keys_for_session::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services
services()
.key_backups
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)?;
Ok(delete_backup_keys_for_session::v3::Response {
count: (UInt::try_from(
services
services()
.key_backups
.count_keys(sender_user, &body.version)?,
)
.expect("user backup keys count should not be that high")),
etag: services.key_backups.get_etag(sender_user, &body.version)?,
etag: services()
.key_backups
.get_etag(sender_user, &body.version)?,
})
}
+5 -6
View File
@@ -1,30 +1,29 @@
use std::collections::BTreeMap;
use axum::extract::State;
use ruma::api::client::discovery::get_capabilities::{
self, Capabilities, RoomVersionStability, RoomVersionsCapability, ThirdPartyIdChangesCapability,
};
use crate::{Result, Ruma};
use crate::{services, Result, Ruma};
/// # `GET /_matrix/client/v3/capabilities`
///
/// Get information on the supported feature set and other relevent capabilities
/// of this server.
pub(crate) async fn get_capabilities_route(
State(services): State<crate::State>, _body: Ruma<get_capabilities::v3::Request>,
_body: Ruma<get_capabilities::v3::Request>,
) -> Result<get_capabilities::v3::Response> {
let mut available = BTreeMap::new();
for room_version in &services.globals.unstable_room_versions {
for room_version in &services().globals.unstable_room_versions {
available.insert(room_version.clone(), RoomVersionStability::Unstable);
}
for room_version in &services.globals.stable_room_versions {
for room_version in &services().globals.stable_room_versions {
available.insert(room_version.clone(), RoomVersionStability::Stable);
}
let mut capabilities = Capabilities::default();
capabilities.room_versions = RoomVersionsCapability {
default: services.globals.default_room_version(),
default: services().globals.default_room_version(),
available,
};
+10 -19
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use ruma::{
api::client::{
config::{get_global_account_data, get_room_account_data, set_global_account_data, set_room_account_data},
@@ -11,21 +10,15 @@ use ruma::{
use serde::Deserialize;
use serde_json::{json, value::RawValue as RawJsonValue};
use crate::{service::Services, Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}`
///
/// Sets some account data for the sender user.
pub(crate) async fn set_global_account_data_route(
State(services): State<crate::State>, body: Ruma<set_global_account_data::v3::Request>,
body: Ruma<set_global_account_data::v3::Request>,
) -> Result<set_global_account_data::v3::Response> {
set_account_data(
&services,
None,
&body.sender_user,
&body.event_type.to_string(),
body.data.json(),
)?;
set_account_data(None, &body.sender_user, &body.event_type.to_string(), body.data.json())?;
Ok(set_global_account_data::v3::Response {})
}
@@ -34,10 +27,9 @@ pub(crate) async fn set_global_account_data_route(
///
/// Sets some room account data for the sender user.
pub(crate) async fn set_room_account_data_route(
State(services): State<crate::State>, body: Ruma<set_room_account_data::v3::Request>,
body: Ruma<set_room_account_data::v3::Request>,
) -> Result<set_room_account_data::v3::Response> {
set_account_data(
&services,
Some(&body.room_id),
&body.sender_user,
&body.event_type.to_string(),
@@ -51,11 +43,11 @@ pub(crate) async fn set_room_account_data_route(
///
/// Gets some account data for the sender user.
pub(crate) async fn get_global_account_data_route(
State(services): State<crate::State>, body: Ruma<get_global_account_data::v3::Request>,
body: Ruma<get_global_account_data::v3::Request>,
) -> Result<get_global_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event: Box<RawJsonValue> = services
let event: Box<RawJsonValue> = services()
.account_data
.get(None, sender_user, body.event_type.to_string().into())?
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
@@ -73,11 +65,11 @@ pub(crate) async fn get_global_account_data_route(
///
/// Gets some room account data for the sender user.
pub(crate) async fn get_room_account_data_route(
State(services): State<crate::State>, body: Ruma<get_room_account_data::v3::Request>,
body: Ruma<get_room_account_data::v3::Request>,
) -> Result<get_room_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event: Box<RawJsonValue> = services
let event: Box<RawJsonValue> = services()
.account_data
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
@@ -92,15 +84,14 @@ pub(crate) async fn get_room_account_data_route(
}
fn set_account_data(
services: &Services, room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str,
data: &RawJsonValue,
room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str, data: &RawJsonValue,
) -> Result<()> {
let sender_user = sender_user.as_ref().expect("user is authenticated");
let data: serde_json::Value =
serde_json::from_str(data.get()).map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
services.account_data.update(
services().account_data.update(
room_id,
sender_user,
event_type.into(),
+18 -21
View File
@@ -1,13 +1,12 @@
use std::collections::HashSet;
use axum::extract::State;
use ruma::{
api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions},
events::StateEventType,
};
use tracing::error;
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `GET /_matrix/client/r0/rooms/{roomId}/context`
///
@@ -15,9 +14,7 @@ use crate::{Error, Result, Ruma};
///
/// - Only works if the user is joined (TODO: always allow, but only show events
/// if the user was joined, depending on history_visibility)
pub(crate) async fn get_context_route(
State(services): State<crate::State>, body: Ruma<get_context::v3::Request>,
) -> Result<get_context::v3::Response> {
pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> Result<get_context::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@@ -30,13 +27,13 @@ pub(crate) async fn get_context_route(
let mut lazy_loaded = HashSet::new();
let base_token = services
let base_token = services()
.rooms
.timeline
.get_pdu_count(&body.event_id)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Base event id not found."))?;
let base_event = services
let base_event = services()
.rooms
.timeline
.get_pdu(&body.event_id)?
@@ -44,7 +41,7 @@ pub(crate) async fn get_context_route(
let room_id = base_event.room_id.clone();
if !services
if !services()
.rooms
.state_accessor
.user_can_see_event(sender_user, &room_id, &body.event_id)?
@@ -55,7 +52,7 @@ pub(crate) async fn get_context_route(
));
}
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
sender_user,
sender_device,
&room_id,
@@ -70,14 +67,14 @@ pub(crate) async fn get_context_route(
let base_event = base_event.to_room_event();
let events_before: Vec<_> = services
let events_before: Vec<_> = services()
.rooms
.timeline
.pdus_until(sender_user, &room_id, base_token)?
.take(limit / 2)
.filter_map(Result::ok) // Remove buggy events
.filter(|(_, pdu)| {
services
services()
.rooms
.state_accessor
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
@@ -86,7 +83,7 @@ pub(crate) async fn get_context_route(
.collect();
for (_, event) in &events_before {
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
sender_user,
sender_device,
&room_id,
@@ -106,14 +103,14 @@ pub(crate) async fn get_context_route(
.map(|(_, pdu)| pdu.to_room_event())
.collect();
let events_after: Vec<_> = services
let events_after: Vec<_> = services()
.rooms
.timeline
.pdus_after(sender_user, &room_id, base_token)?
.take(limit / 2)
.filter_map(Result::ok) // Remove buggy events
.filter(|(_, pdu)| {
services
services()
.rooms
.state_accessor
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
@@ -122,7 +119,7 @@ pub(crate) async fn get_context_route(
.collect();
for (_, event) in &events_after {
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
sender_user,
sender_device,
&room_id,
@@ -133,7 +130,7 @@ pub(crate) async fn get_context_route(
}
}
let shortstatehash = services
let shortstatehash = services()
.rooms
.state_accessor
.pdu_shortstatehash(
@@ -142,7 +139,7 @@ pub(crate) async fn get_context_route(
.map_or(&*body.event_id, |(_, e)| &*e.event_id),
)?
.map_or(
services
services()
.rooms
.state
.get_room_shortstatehash(&room_id)?
@@ -150,7 +147,7 @@ pub(crate) async fn get_context_route(
|hash| hash,
);
let state_ids = services
let state_ids = services()
.rooms
.state_accessor
.state_full_ids(shortstatehash)
@@ -168,20 +165,20 @@ pub(crate) async fn get_context_route(
let mut state = Vec::with_capacity(state_ids.len());
for (shortstatekey, id) in state_ids {
let (event_type, state_key) = services
let (event_type, state_key) = services()
.rooms
.short
.get_statekey_from_short(shortstatekey)?;
if event_type != StateEventType::RoomMember {
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
state.push(pdu.to_state_event());
} else if !lazy_load_enabled || lazy_loaded.contains(&state_key) {
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
+18 -25
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use ruma::api::client::{
device::{self, delete_device, delete_devices, get_device, get_devices, update_device},
error::ErrorKind,
@@ -6,17 +5,15 @@ use ruma::api::client::{
};
use super::SESSION_ID_LENGTH;
use crate::{utils, Error, Result, Ruma};
use crate::{services, utils, Error, Result, Ruma};
/// # `GET /_matrix/client/r0/devices`
///
/// Get metadata on all devices of the sender user.
pub(crate) async fn get_devices_route(
State(services): State<crate::State>, body: Ruma<get_devices::v3::Request>,
) -> Result<get_devices::v3::Response> {
pub(crate) async fn get_devices_route(body: Ruma<get_devices::v3::Request>) -> Result<get_devices::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let devices: Vec<device::Device> = services
let devices: Vec<device::Device> = services()
.users
.all_devices_metadata(sender_user)
.filter_map(Result::ok) // Filter out buggy devices
@@ -30,12 +27,10 @@ pub(crate) async fn get_devices_route(
/// # `GET /_matrix/client/r0/devices/{deviceId}`
///
/// Get metadata on a single device of the sender user.
pub(crate) async fn get_device_route(
State(services): State<crate::State>, body: Ruma<get_device::v3::Request>,
) -> Result<get_device::v3::Response> {
pub(crate) async fn get_device_route(body: Ruma<get_device::v3::Request>) -> Result<get_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let device = services
let device = services()
.users
.get_device_metadata(sender_user, &body.body.device_id)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
@@ -48,19 +43,17 @@ pub(crate) async fn get_device_route(
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
///
/// Updates the metadata on a given device of the sender user.
pub(crate) async fn update_device_route(
State(services): State<crate::State>, body: Ruma<update_device::v3::Request>,
) -> Result<update_device::v3::Response> {
pub(crate) async fn update_device_route(body: Ruma<update_device::v3::Request>) -> Result<update_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let mut device = services
let mut device = services()
.users
.get_device_metadata(sender_user, &body.device_id)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
device.display_name.clone_from(&body.display_name);
services
services()
.users
.update_device_metadata(sender_user, &body.device_id, &device)?;
@@ -77,9 +70,7 @@ pub(crate) async fn update_device_route(
/// last seen ts)
/// - Forgets to-device events
/// - Triggers device list updates
pub(crate) async fn delete_device_route(
State(services): State<crate::State>, body: Ruma<delete_device::v3::Request>,
) -> Result<delete_device::v3::Response> {
pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>) -> Result<delete_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@@ -95,7 +86,7 @@ pub(crate) async fn delete_device_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services
let (worked, uiaainfo) = services()
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@@ -104,7 +95,7 @@ pub(crate) async fn delete_device_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services
services()
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@@ -112,7 +103,9 @@ pub(crate) async fn delete_device_route(
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
}
services.users.remove_device(sender_user, &body.device_id)?;
services()
.users
.remove_device(sender_user, &body.device_id)?;
Ok(delete_device::v3::Response {})
}
@@ -130,7 +123,7 @@ pub(crate) async fn delete_device_route(
/// - Forgets to-device events
/// - Triggers device list updates
pub(crate) async fn delete_devices_route(
State(services): State<crate::State>, body: Ruma<delete_devices::v3::Request>,
body: Ruma<delete_devices::v3::Request>,
) -> Result<delete_devices::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@@ -147,7 +140,7 @@ pub(crate) async fn delete_devices_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services
let (worked, uiaainfo) = services()
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@@ -156,7 +149,7 @@ pub(crate) async fn delete_devices_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services
services()
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@@ -165,7 +158,7 @@ pub(crate) async fn delete_devices_route(
}
for device_id in &body.devices {
services.users.remove_device(sender_user, device_id)?;
services().users.remove_device(sender_user, device_id)?;
}
Ok(delete_devices::v3::Response {})
+36 -48
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduit::{err, info, warn, Error, Result};
use ruma::{
@@ -20,9 +19,8 @@ use ruma::{
},
uint, RoomId, ServerName, UInt, UserId,
};
use service::Services;
use crate::Ruma;
use crate::{service::server_is_ours, services, Ruma};
/// # `POST /_matrix/client/v3/publicRooms`
///
@@ -31,11 +29,10 @@ use crate::Ruma;
/// - Rooms are ordered by the number of joined members
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
pub(crate) async fn get_public_rooms_filtered_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_public_rooms_filtered::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v3::Request>,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(server) = &body.server {
if services
if services()
.globals
.forbidden_remote_room_directory_server_names()
.contains(server)
@@ -48,7 +45,6 @@ pub(crate) async fn get_public_rooms_filtered_route(
}
let response = get_public_rooms_filtered_helper(
&services,
body.server.as_deref(),
body.limit,
body.since.as_deref(),
@@ -71,11 +67,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
/// - Rooms are ordered by the number of joined members
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
pub(crate) async fn get_public_rooms_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_public_rooms::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms::v3::Request>,
) -> Result<get_public_rooms::v3::Response> {
if let Some(server) = &body.server {
if services
if services()
.globals
.forbidden_remote_room_directory_server_names()
.contains(server)
@@ -88,7 +83,6 @@ pub(crate) async fn get_public_rooms_route(
}
let response = get_public_rooms_filtered_helper(
&services,
body.server.as_deref(),
body.limit,
body.since.as_deref(),
@@ -114,26 +108,20 @@ pub(crate) async fn get_public_rooms_route(
/// Sets the visibility of a given room in the room directory.
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
pub(crate) async fn set_room_visibility_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<set_room_visibility::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<set_room_visibility::v3::Request>,
) -> Result<set_room_visibility::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services.rooms.metadata.exists(&body.room_id)? {
if !services().rooms.metadata.exists(&body.room_id)? {
// Return 404 if the room doesn't exist
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
}
if !user_can_publish_room(&services, sender_user, &body.room_id)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"User is not allowed to publish this room",
));
}
user_can_publish_room(sender_user, &body.room_id)?;
match &body.visibility {
room::Visibility::Public => {
if services.globals.config.lockdown_public_room_directory && !services.users.is_admin(sender_user)? {
if services().globals.config.lockdown_public_room_directory && !services().users.is_admin(sender_user)? {
info!(
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
\"lockdown_public_room_directory\" is enabled",
@@ -146,10 +134,10 @@ pub(crate) async fn set_room_visibility_route(
));
}
services.rooms.directory.set_public(&body.room_id)?;
services().rooms.directory.set_public(&body.room_id)?;
info!("{sender_user} made {0} public", body.room_id);
},
room::Visibility::Private => services.rooms.directory.set_not_public(&body.room_id)?,
room::Visibility::Private => services().rooms.directory.set_not_public(&body.room_id)?,
_ => {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
@@ -165,15 +153,15 @@ pub(crate) async fn set_room_visibility_route(
///
/// Gets the visibility of a given room in the room directory.
pub(crate) async fn get_room_visibility_route(
State(services): State<crate::State>, body: Ruma<get_room_visibility::v3::Request>,
body: Ruma<get_room_visibility::v3::Request>,
) -> Result<get_room_visibility::v3::Response> {
if !services.rooms.metadata.exists(&body.room_id)? {
if !services().rooms.metadata.exists(&body.room_id)? {
// Return 404 if the room doesn't exist
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
}
Ok(get_room_visibility::v3::Response {
visibility: if services.rooms.directory.is_public_room(&body.room_id)? {
visibility: if services().rooms.directory.is_public_room(&body.room_id)? {
room::Visibility::Public
} else {
room::Visibility::Private
@@ -182,11 +170,10 @@ pub(crate) async fn get_room_visibility_route(
}
pub(crate) async fn get_public_rooms_filtered_helper(
services: &Services, server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter,
_network: &RoomNetwork,
server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter, _network: &RoomNetwork,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(other_server) = server.filter(|server_name| !services.globals.server_is_ours(server_name)) {
let response = services
if let Some(other_server) = server.filter(|server_name| !server_is_ours(server_name)) {
let response = services()
.sending
.send_federation_request(
other_server,
@@ -232,7 +219,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
}
}
let mut all_rooms: Vec<_> = services
let mut all_rooms: Vec<_> = services()
.rooms
.directory
.public_rooms()
@@ -240,12 +227,12 @@ pub(crate) async fn get_public_rooms_filtered_helper(
let room_id = room_id?;
let chunk = PublicRoomsChunk {
canonical_alias: services
canonical_alias: services()
.rooms
.state_accessor
.get_canonical_alias(&room_id)?,
name: services.rooms.state_accessor.get_name(&room_id)?,
num_joined_members: services
name: services().rooms.state_accessor.get_name(&room_id)?,
num_joined_members: services()
.rooms
.state_cache
.room_joined_count(&room_id)?
@@ -255,24 +242,24 @@ pub(crate) async fn get_public_rooms_filtered_helper(
})
.try_into()
.expect("user count should not be that big"),
topic: services
topic: services()
.rooms
.state_accessor
.get_room_topic(&room_id)
.unwrap_or(None),
world_readable: services.rooms.state_accessor.is_world_readable(&room_id)?,
guest_can_join: services
world_readable: services().rooms.state_accessor.is_world_readable(&room_id)?,
guest_can_join: services()
.rooms
.state_accessor
.guest_can_join(&room_id)?,
avatar_url: services
avatar_url: services()
.rooms
.state_accessor
.get_avatar(&room_id)?
.into_option()
.unwrap_or_default()
.url,
join_rule: services
join_rule: services()
.rooms
.state_accessor
.room_state_get(&room_id, &StateEventType::RoomJoinRules, "")?
@@ -290,7 +277,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
.transpose()?
.flatten()
.ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?,
room_type: services
room_type: services()
.rooms
.state_accessor
.get_room_type(&room_id)?,
@@ -369,11 +356,12 @@ pub(crate) async fn get_public_rooms_filtered_helper(
/// Check whether the user can publish to the room directory via power levels of
/// room history visibility event or room creator
fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
if let Some(event) = services
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result<bool> {
if let Some(event) =
services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
{
serde_json::from_str(event.content.get())
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
@@ -381,7 +369,7 @@ fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
})
} else if let Some(event) =
services
services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
@@ -389,8 +377,8 @@ fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId
Ok(event.sender == user_id)
} else {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"User is not allowed to publish this room",
ErrorKind::Unauthorized,
"You are not allowed to publish this room to the room directory",
));
}
}
+5 -10
View File
@@ -1,21 +1,18 @@
use axum::extract::State;
use ruma::api::client::{
error::ErrorKind,
filter::{create_filter, get_filter},
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}`
///
/// Loads a filter that was previously created.
///
/// - A user can only access their own filters
pub(crate) async fn get_filter_route(
State(services): State<crate::State>, body: Ruma<get_filter::v3::Request>,
) -> Result<get_filter::v3::Response> {
pub(crate) async fn get_filter_route(body: Ruma<get_filter::v3::Request>) -> Result<get_filter::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let Some(filter) = services.users.get_filter(sender_user, &body.filter_id)? else {
let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? else {
return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found."));
};
@@ -25,11 +22,9 @@ pub(crate) async fn get_filter_route(
/// # `PUT /_matrix/client/r0/user/{userId}/filter`
///
/// Creates a new filter to be used by other endpoints.
pub(crate) async fn create_filter_route(
State(services): State<crate::State>, body: Ruma<create_filter::v3::Request>,
) -> Result<create_filter::v3::Response> {
pub(crate) async fn create_filter_route(body: Ruma<create_filter::v3::Request>) -> Result<create_filter::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
Ok(create_filter::v3::Response::new(
services.users.create_filter(sender_user, &body.filter)?,
services().users.create_filter(sender_user, &body.filter)?,
))
}
+44 -50
View File
@@ -3,8 +3,7 @@ use std::{
time::Instant,
};
use axum::extract::State;
use conduit::{debug, utils, utils::math::continue_exponential_backoff_secs, Error, Result};
use conduit::{utils, utils::math::continue_exponential_backoff_secs, Error, Result};
use futures_util::{stream::FuturesUnordered, StreamExt};
use ruma::{
api::{
@@ -19,9 +18,11 @@ use ruma::{
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
};
use serde_json::json;
use service::user_is_local;
use tracing::debug;
use super::SESSION_ID_LENGTH;
use crate::{service::Services, Ruma};
use crate::{services, Ruma};
/// # `POST /_matrix/client/r0/keys/upload`
///
@@ -30,14 +31,12 @@ use crate::{service::Services, Ruma};
/// - Adds one time keys
/// - If there are no device keys yet: Adds device keys (TODO: merge with
/// existing keys?)
pub(crate) async fn upload_keys_route(
State(services): State<crate::State>, body: Ruma<upload_keys::v3::Request>,
) -> Result<upload_keys::v3::Response> {
pub(crate) async fn upload_keys_route(body: Ruma<upload_keys::v3::Request>) -> Result<upload_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
for (key_key, key_value) in &body.one_time_keys {
services
services()
.users
.add_one_time_key(sender_user, sender_device, key_key, key_value)?;
}
@@ -45,19 +44,19 @@ pub(crate) async fn upload_keys_route(
if let Some(device_keys) = &body.device_keys {
// TODO: merge this and the existing event?
// This check is needed to assure that signatures are kept
if services
if services()
.users
.get_device_keys(sender_user, sender_device)?
.is_none()
{
services
services()
.users
.add_device_keys(sender_user, sender_device, device_keys)?;
}
}
Ok(upload_keys::v3::Response {
one_time_key_counts: services
one_time_key_counts: services()
.users
.count_one_time_keys(sender_user, sender_device)?,
})
@@ -71,13 +70,10 @@ pub(crate) async fn upload_keys_route(
/// - Gets master keys, self-signing keys, user signing keys and device keys.
/// - The master and self-signing keys contain signatures that the user is
/// allowed to see
pub(crate) async fn get_keys_route(
State(services): State<crate::State>, body: Ruma<get_keys::v3::Request>,
) -> Result<get_keys::v3::Response> {
pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<get_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
get_keys_helper(
&services,
Some(sender_user),
&body.device_keys,
|u| u == sender_user,
@@ -89,10 +85,8 @@ pub(crate) async fn get_keys_route(
/// # `POST /_matrix/client/r0/keys/claim`
///
/// Claims one-time keys
pub(crate) async fn claim_keys_route(
State(services): State<crate::State>, body: Ruma<claim_keys::v3::Request>,
) -> Result<claim_keys::v3::Response> {
claim_keys_helper(&services, &body.one_time_keys).await
pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Result<claim_keys::v3::Response> {
claim_keys_helper(&body.one_time_keys).await
}
/// # `POST /_matrix/client/r0/keys/device_signing/upload`
@@ -101,7 +95,7 @@ pub(crate) async fn claim_keys_route(
///
/// - Requires UIAA to verify password
pub(crate) async fn upload_signing_keys_route(
State(services): State<crate::State>, body: Ruma<upload_signing_keys::v3::Request>,
body: Ruma<upload_signing_keys::v3::Request>,
) -> Result<upload_signing_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@@ -118,7 +112,7 @@ pub(crate) async fn upload_signing_keys_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services
let (worked, uiaainfo) = services()
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@@ -127,7 +121,7 @@ pub(crate) async fn upload_signing_keys_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services
services()
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@@ -136,7 +130,7 @@ pub(crate) async fn upload_signing_keys_route(
}
if let Some(master_key) = &body.master_key {
services.users.add_cross_signing_keys(
services().users.add_cross_signing_keys(
sender_user,
master_key,
&body.self_signing_key,
@@ -152,7 +146,7 @@ pub(crate) async fn upload_signing_keys_route(
///
/// Uploads end-to-end key signatures from the sender user.
pub(crate) async fn upload_signatures_route(
State(services): State<crate::State>, body: Ruma<upload_signatures::v3::Request>,
body: Ruma<upload_signatures::v3::Request>,
) -> Result<upload_signatures::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -179,7 +173,7 @@ pub(crate) async fn upload_signatures_route(
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature value."))?
.to_owned(),
);
services
services()
.users
.sign_key(user_id, key_id, signature, sender_user)?;
}
@@ -198,14 +192,14 @@ pub(crate) async fn upload_signatures_route(
///
/// - TODO: left users
pub(crate) async fn get_key_changes_route(
State(services): State<crate::State>, body: Ruma<get_key_changes::v3::Request>,
body: Ruma<get_key_changes::v3::Request>,
) -> Result<get_key_changes::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let mut device_list_updates = HashSet::new();
device_list_updates.extend(
services
services()
.users
.keys_changed(
sender_user.as_str(),
@@ -221,14 +215,14 @@ pub(crate) async fn get_key_changes_route(
.filter_map(Result::ok),
);
for room_id in services
for room_id in services()
.rooms
.state_cache
.rooms_joined(sender_user)
.filter_map(Result::ok)
{
device_list_updates.extend(
services
services()
.users
.keys_changed(
room_id.as_ref(),
@@ -251,8 +245,8 @@ pub(crate) async fn get_key_changes_route(
}
pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
services: &Services, sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
allowed_signatures: F, include_display_names: bool,
sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>, allowed_signatures: F,
include_display_names: bool,
) -> Result<get_keys::v3::Response> {
let mut master_keys = BTreeMap::new();
let mut self_signing_keys = BTreeMap::new();
@@ -264,7 +258,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
for (user_id, device_ids) in device_keys_input {
let user_id: &UserId = user_id;
if !services.globals.user_is_local(user_id) {
if !user_is_local(user_id) {
get_over_federation
.entry(user_id.server_name())
.or_insert_with(Vec::new)
@@ -274,10 +268,10 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
if device_ids.is_empty() {
let mut container = BTreeMap::new();
for device_id in services.users.all_device_ids(user_id) {
for device_id in services().users.all_device_ids(user_id) {
let device_id = device_id?;
if let Some(mut keys) = services.users.get_device_keys(user_id, &device_id)? {
let metadata = services
if let Some(mut keys) = services().users.get_device_keys(user_id, &device_id)? {
let metadata = services()
.users
.get_device_metadata(user_id, &device_id)?
.ok_or_else(|| Error::bad_database("all_device_keys contained nonexistent device."))?;
@@ -292,8 +286,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
} else {
for device_id in device_ids {
let mut container = BTreeMap::new();
if let Some(mut keys) = services.users.get_device_keys(user_id, device_id)? {
let metadata = services
if let Some(mut keys) = services().users.get_device_keys(user_id, device_id)? {
let metadata = services()
.users
.get_device_metadata(user_id, device_id)?
.ok_or(Error::BadRequest(
@@ -309,21 +303,21 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
}
}
if let Some(master_key) = services
if let Some(master_key) = services()
.users
.get_master_key(sender_user, user_id, &allowed_signatures)?
{
master_keys.insert(user_id.to_owned(), master_key);
}
if let Some(self_signing_key) =
services
services()
.users
.get_self_signing_key(sender_user, user_id, &allowed_signatures)?
{
self_signing_keys.insert(user_id.to_owned(), self_signing_key);
}
if Some(user_id) == sender_user {
if let Some(user_signing_key) = services.users.get_user_signing_key(user_id)? {
if let Some(user_signing_key) = services().users.get_user_signing_key(user_id)? {
user_signing_keys.insert(user_id.to_owned(), user_signing_key);
}
}
@@ -332,7 +326,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
let mut failures = BTreeMap::new();
let back_off = |id| async {
match services
match services()
.globals
.bad_query_ratelimiter
.write()
@@ -351,7 +345,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
let mut futures: FuturesUnordered<_> = get_over_federation
.into_iter()
.map(|(server, vec)| async move {
if let Some((time, tries)) = services
if let Some((time, tries)) = services()
.globals
.bad_query_ratelimiter
.read()
@@ -375,7 +369,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
let request = federation::keys::get_keys::v1::Request {
device_keys: device_keys_input_fed,
};
let response = services
let response = services()
.sending
.send_federation_request(server, request)
.await;
@@ -387,19 +381,19 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
while let Some((server, response)) = futures.next().await {
if let Ok(Ok(response)) = response {
for (user, masterkey) in response.master_keys {
let (master_key_id, mut master_key) = services.users.parse_master_key(&user, &masterkey)?;
let (master_key_id, mut master_key) = services().users.parse_master_key(&user, &masterkey)?;
if let Some(our_master_key) =
services
services()
.users
.get_key(&master_key_id, sender_user, &user, &allowed_signatures)?
{
let (_, our_master_key) = services.users.parse_master_key(&user, &our_master_key)?;
let (_, our_master_key) = services().users.parse_master_key(&user, &our_master_key)?;
master_key.signatures.extend(our_master_key.signatures);
}
let json = serde_json::to_value(master_key).expect("to_value always works");
let raw = serde_json::from_value(json).expect("Raw::from_value always works");
services.users.add_cross_signing_keys(
services().users.add_cross_signing_keys(
&user, &raw, &None, &None,
false, /* Dont notify. A notification would trigger another key request resulting in an
* endless loop */
@@ -450,14 +444,14 @@ fn add_unsigned_device_display_name(
}
pub(crate) async fn claim_keys_helper(
services: &Services, one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
) -> Result<claim_keys::v3::Response> {
let mut one_time_keys = BTreeMap::new();
let mut get_over_federation = BTreeMap::new();
for (user_id, map) in one_time_keys_input {
if !services.globals.user_is_local(user_id) {
if !user_is_local(user_id) {
get_over_federation
.entry(user_id.server_name())
.or_insert_with(Vec::new)
@@ -466,7 +460,7 @@ pub(crate) async fn claim_keys_helper(
let mut container = BTreeMap::new();
for (device_id, key_algorithm) in map {
if let Some(one_time_keys) = services
if let Some(one_time_keys) = services()
.users
.take_one_time_key(user_id, device_id, key_algorithm)?
{
@@ -489,7 +483,7 @@ pub(crate) async fn claim_keys_helper(
}
(
server,
services
services()
.sending
.send_federation_request(
server,
+96 -98
View File
@@ -1,18 +1,9 @@
#![allow(deprecated)]
use std::{io::Cursor, time::Duration};
use std::{io::Cursor, sync::Arc, time::Duration};
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduit::{
debug, debug_warn, error,
utils::{
self,
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
math::ruma_from_usize,
},
warn, Error, Result,
};
use conduit::{debug, error, utils::math::ruma_from_usize, warn};
use image::io::Reader as ImgReader;
use ipaddress::IPAddress;
use reqwest::Url;
@@ -23,13 +14,21 @@ use ruma::api::client::{
get_media_preview,
},
};
use service::{
media::{FileMeta, UrlPreviewData},
Services,
};
use webpage::HTML;
use crate::{Ruma, RumaResponse};
use crate::{
debug_warn,
service::{
media::{FileMeta, UrlPreviewData},
server_is_ours,
},
services,
utils::{
self,
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
},
Error, Result, Ruma, RumaResponse,
};
/// generated MXC ID (`media-id`) length
const MXC_LENGTH: usize = 32;
@@ -43,10 +42,10 @@ const CORP_CROSS_ORIGIN: &str = "cross-origin";
///
/// Returns max upload size.
pub(crate) async fn get_media_config_route(
State(services): State<crate::State>, _body: Ruma<get_media_config::v3::Request>,
_body: Ruma<get_media_config::v3::Request>,
) -> Result<get_media_config::v3::Response> {
Ok(get_media_config::v3::Response {
upload_size: ruma_from_usize(services.globals.config.max_request_size),
upload_size: ruma_from_usize(services().globals.config.max_request_size),
})
}
@@ -58,11 +57,9 @@ pub(crate) async fn get_media_config_route(
///
/// Returns max upload size.
pub(crate) async fn get_media_config_v1_route(
State(services): State<crate::State>, body: Ruma<get_media_config::v3::Request>,
body: Ruma<get_media_config::v3::Request>,
) -> Result<RumaResponse<get_media_config::v3::Response>> {
get_media_config_route(State(services), body)
.await
.map(RumaResponse)
get_media_config_route(body).await.map(RumaResponse)
}
/// # `GET /_matrix/media/v3/preview_url`
@@ -70,18 +67,17 @@ pub(crate) async fn get_media_config_v1_route(
/// Returns URL preview.
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
pub(crate) async fn get_media_preview_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_media_preview::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
) -> Result<get_media_preview::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let url = &body.url;
if !url_preview_allowed(&services, url) {
if !url_preview_allowed(url) {
warn!(%sender_user, "URL is not allowed to be previewed: {url}");
return Err(Error::BadRequest(ErrorKind::forbidden(), "URL is not allowed to be previewed"));
}
match get_url_preview(&services, url).await {
match get_url_preview(url).await {
Ok(preview) => {
let res = serde_json::value::to_raw_value(&preview).map_err(|e| {
error!(%sender_user, "Failed to convert UrlPreviewData into a serde json value: {e}");
@@ -119,10 +115,9 @@ pub(crate) async fn get_media_preview_route(
/// Returns URL preview.
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
pub(crate) async fn get_media_preview_v1_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_media_preview::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
get_media_preview_route(State(services), InsecureClientIp(client), body)
get_media_preview_route(InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -135,14 +130,17 @@ pub(crate) async fn get_media_preview_v1_route(
/// - Media will be saved in the media/ directory
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
pub(crate) async fn create_content_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<create_content::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let mxc = format!("mxc://{}/{}", services.globals.server_name(), utils::random_string(MXC_LENGTH));
let mxc = format!(
"mxc://{}/{}",
services().globals.server_name(),
utils::random_string(MXC_LENGTH)
);
services
services()
.media
.create(
Some(sender_user.clone()),
@@ -180,10 +178,9 @@ pub(crate) async fn create_content_route(
/// - Media will be saved in the media/ directory
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
pub(crate) async fn create_content_v1_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<create_content::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
) -> Result<RumaResponse<create_content::v3::Response>> {
create_content_route(State(services), InsecureClientIp(client), body)
create_content_route(InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -198,19 +195,17 @@ pub(crate) async fn create_content_v1_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
pub(crate) async fn get_content_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
) -> Result<get_content::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
if let Some(FileMeta {
content,
content_type,
file,
content_disposition,
}) = services.media.get(&mxc).await?
}) = services().media.get(&mxc).await?
{
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
let file = content.expect("content");
Ok(get_content::v3::Response {
file,
@@ -219,9 +214,8 @@ pub(crate) async fn get_content_route(
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
})
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
} else if !server_is_ours(&body.server_name) && body.allow_remote {
let response = get_remote_content(
&services,
&mxc,
&body.server_name,
body.media_id.clone(),
@@ -266,10 +260,9 @@ pub(crate) async fn get_content_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
pub(crate) async fn get_content_v1_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
) -> Result<RumaResponse<get_content::v3::Response>> {
get_content_route(State(services), InsecureClientIp(client), body)
get_content_route(InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -284,16 +277,15 @@ pub(crate) async fn get_content_v1_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
pub(crate) async fn get_content_as_filename_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_as_filename::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<get_content_as_filename::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
if let Some(FileMeta {
content,
content_type,
file,
content_disposition,
}) = services.media.get(&mxc).await?
}) = services().media.get(&mxc).await?
{
let content_disposition = Some(make_content_disposition(
&content_type,
@@ -301,7 +293,6 @@ pub(crate) async fn get_content_as_filename_route(
Some(body.filename.clone()),
));
let file = content.expect("content");
Ok(get_content_as_filename::v3::Response {
file,
content_type,
@@ -309,9 +300,8 @@ pub(crate) async fn get_content_as_filename_route(
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
})
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
} else if !server_is_ours(&body.server_name) && body.allow_remote {
match get_remote_content(
&services,
&mxc,
&body.server_name,
body.media_id.clone(),
@@ -359,10 +349,9 @@ pub(crate) async fn get_content_as_filename_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
pub(crate) async fn get_content_as_filename_v1_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_as_filename::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
get_content_as_filename_route(State(services), InsecureClientIp(client), body)
get_content_as_filename_route(InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -377,16 +366,15 @@ pub(crate) async fn get_content_as_filename_v1_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
pub(crate) async fn get_content_thumbnail_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_thumbnail::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<get_content_thumbnail::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
if let Some(FileMeta {
content,
content_type,
file,
content_disposition,
}) = services
}) = services()
.media
.get_thumbnail(
&mxc,
@@ -400,7 +388,6 @@ pub(crate) async fn get_content_thumbnail_route(
.await?
{
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
let file = content.expect("content");
Ok(get_content_thumbnail::v3::Response {
file,
@@ -409,8 +396,8 @@ pub(crate) async fn get_content_thumbnail_route(
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
content_disposition,
})
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
if services
} else if !server_is_ours(&body.server_name) && body.allow_remote {
if services()
.globals
.prevent_media_downloads_from()
.contains(&body.server_name)
@@ -421,7 +408,7 @@ pub(crate) async fn get_content_thumbnail_route(
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
}
match services
match services()
.sending
.send_federation_request(
&body.server_name,
@@ -440,7 +427,7 @@ pub(crate) async fn get_content_thumbnail_route(
.await
{
Ok(get_thumbnail_response) => {
services
services()
.media
.upload_thumbnail(
None,
@@ -491,19 +478,17 @@ pub(crate) async fn get_content_thumbnail_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
pub(crate) async fn get_content_thumbnail_v1_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_thumbnail::v3::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
get_content_thumbnail_route(State(services), InsecureClientIp(client), body)
get_content_thumbnail_route(InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
async fn get_remote_content(
services: &Services, mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool,
timeout_ms: Duration,
mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool, timeout_ms: Duration,
) -> Result<get_content::v3::Response, Error> {
if services
if services()
.globals
.prevent_media_downloads_from()
.contains(&server_name.to_owned())
@@ -514,7 +499,7 @@ async fn get_remote_content(
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
}
let content_response = services
let content_response = services()
.sending
.send_federation_request(
server_name,
@@ -534,7 +519,7 @@ async fn get_remote_content(
None,
));
services
services()
.media
.create(
None,
@@ -554,11 +539,15 @@ async fn get_remote_content(
})
}
async fn download_image(services: &Services, client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
async fn download_image(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
let image = client.get(url).send().await?.bytes().await?;
let mxc = format!("mxc://{}/{}", services.globals.server_name(), utils::random_string(MXC_LENGTH));
let mxc = format!(
"mxc://{}/{}",
services().globals.server_name(),
utils::random_string(MXC_LENGTH)
);
services
services()
.media
.create(None, &mxc, None, None, &image)
.await?;
@@ -580,18 +569,18 @@ async fn download_image(services: &Services, client: &reqwest::Client, url: &str
})
}
async fn download_html(services: &Services, client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
async fn download_html(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
let mut response = client.get(url).send().await?;
let mut bytes: Vec<u8> = Vec::new();
while let Some(chunk) = response.chunk().await? {
bytes.extend_from_slice(&chunk);
if bytes.len() > services.globals.url_preview_max_spider_size() {
if bytes.len() > services().globals.url_preview_max_spider_size() {
debug!(
"Response body from URL {} exceeds url_preview_max_spider_size ({}), not processing the rest of the \
response body and assuming our necessary data is in this range.",
url,
services.globals.url_preview_max_spider_size()
services().globals.url_preview_max_spider_size()
);
break;
}
@@ -603,7 +592,7 @@ async fn download_html(services: &Services, client: &reqwest::Client, url: &str)
let mut data = match html.opengraph.images.first() {
None => UrlPreviewData::default(),
Some(obj) => download_image(services, client, &obj.url).await?,
Some(obj) => download_image(client, &obj.url).await?,
};
let props = html.opengraph.properties;
@@ -615,19 +604,19 @@ async fn download_html(services: &Services, client: &reqwest::Client, url: &str)
Ok(data)
}
async fn request_url_preview(services: &Services, url: &str) -> Result<UrlPreviewData> {
async fn request_url_preview(url: &str) -> Result<UrlPreviewData> {
if let Ok(ip) = IPAddress::parse(url) {
if !services.globals.valid_cidr_range(&ip) {
if !services().globals.valid_cidr_range(&ip) {
return Err(Error::BadServerResponse("Requesting from this address is forbidden"));
}
}
let client = &services.client.url_preview;
let client = &services().globals.client.url_preview;
let response = client.head(url).send().await?;
if let Some(remote_addr) = response.remote_addr() {
if let Ok(ip) = IPAddress::parse(remote_addr.ip().to_string()) {
if !services.globals.valid_cidr_range(&ip) {
if !services().globals.valid_cidr_range(&ip) {
return Err(Error::BadServerResponse("Requesting from this address is forbidden"));
}
}
@@ -641,31 +630,40 @@ async fn request_url_preview(services: &Services, url: &str) -> Result<UrlPrevie
return Err(Error::BadRequest(ErrorKind::Unknown, "Unknown Content-Type"));
};
let data = match content_type {
html if html.starts_with("text/html") => download_html(services, client, url).await?,
img if img.starts_with("image/") => download_image(services, client, url).await?,
html if html.starts_with("text/html") => download_html(client, url).await?,
img if img.starts_with("image/") => download_image(client, url).await?,
_ => return Err(Error::BadRequest(ErrorKind::Unknown, "Unsupported Content-Type")),
};
services.media.set_url_preview(url, &data).await?;
services().media.set_url_preview(url, &data).await?;
Ok(data)
}
async fn get_url_preview(services: &Services, url: &str) -> Result<UrlPreviewData> {
if let Some(preview) = services.media.get_url_preview(url).await {
async fn get_url_preview(url: &str) -> Result<UrlPreviewData> {
if let Some(preview) = services().media.get_url_preview(url).await {
return Ok(preview);
}
// ensure that only one request is made per URL
let _request_lock = services.media.url_preview_mutex.lock(url).await;
let mutex_request = Arc::clone(
services()
.media
.url_preview_mutex
.write()
.await
.entry(url.to_owned())
.or_default(),
);
let _request_lock = mutex_request.lock().await;
match services.media.get_url_preview(url).await {
match services().media.get_url_preview(url).await {
Some(preview) => Ok(preview),
None => request_url_preview(services, url).await,
None => request_url_preview(url).await,
}
}
fn url_preview_allowed(services: &Services, url_str: &str) -> bool {
fn url_preview_allowed(url_str: &str) -> bool {
let url: Url = match Url::parse(url_str) {
Ok(u) => u,
Err(e) => {
@@ -690,10 +688,10 @@ fn url_preview_allowed(services: &Services, url_str: &str) -> bool {
Some(h) => h.to_owned(),
};
let allowlist_domain_contains = services.globals.url_preview_domain_contains_allowlist();
let allowlist_domain_explicit = services.globals.url_preview_domain_explicit_allowlist();
let denylist_domain_explicit = services.globals.url_preview_domain_explicit_denylist();
let allowlist_url_contains = services.globals.url_preview_url_contains_allowlist();
let allowlist_domain_contains = services().globals.url_preview_domain_contains_allowlist();
let allowlist_domain_explicit = services().globals.url_preview_domain_explicit_allowlist();
let denylist_domain_explicit = services().globals.url_preview_domain_explicit_denylist();
let allowlist_url_contains = services().globals.url_preview_url_contains_allowlist();
if allowlist_domain_contains.contains(&"*".to_owned())
|| allowlist_domain_explicit.contains(&"*".to_owned())
@@ -734,7 +732,7 @@ fn url_preview_allowed(services: &Services, url_str: &str) -> bool {
}
// check root domain if available and if user has root domain checks
if services.globals.url_preview_check_root_domain() {
if services().globals.url_preview_check_root_domain() {
debug!("Checking root domain");
match host.split_once('.') {
None => return false,
File diff suppressed because it is too large Load Diff
+27 -30
View File
@@ -1,6 +1,5 @@
use std::collections::{BTreeMap, HashSet};
use axum::extract::State;
use conduit::PduCount;
use ruma::{
api::client::{
@@ -13,10 +12,7 @@ use ruma::{
};
use serde_json::{from_str, Value};
use crate::{
service::{pdu::PduBuilder, Services},
utils, Error, PduEvent, Result, Ruma,
};
use crate::{service::pdu::PduBuilder, services, utils, Error, PduEvent, Result, Ruma};
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
///
@@ -28,19 +24,21 @@ use crate::{
/// - Tries to send the event into the room, auth rules will determine if it is
/// allowed
pub(crate) async fn send_message_event_route(
State(services): State<crate::State>, body: Ruma<send_message_event::v3::Request>,
body: Ruma<send_message_event::v3::Request>,
) -> Result<send_message_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_deref();
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
// Forbid m.room.encrypted if encryption is disabled
if MessageLikeEventType::RoomEncrypted == body.event_type && !services.globals.allow_encryption() {
if MessageLikeEventType::RoomEncrypted == body.event_type && !services().globals.allow_encryption() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
}
if body.event_type == MessageLikeEventType::CallInvite && services.rooms.directory.is_public_room(&body.room_id)? {
if body.event_type == MessageLikeEventType::CallInvite
&& services().rooms.directory.is_public_room(&body.room_id)?
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Room call invites are not allowed in public rooms",
@@ -48,7 +46,7 @@ pub(crate) async fn send_message_event_route(
}
// Check if this is a new transaction id
if let Some(response) = services
if let Some(response) = services()
.transaction_ids
.existing_txnid(sender_user, sender_device, &body.txn_id)?
{
@@ -73,7 +71,7 @@ pub(crate) async fn send_message_event_route(
let mut unsigned = BTreeMap::new();
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
let event_id = services
let event_id = services()
.rooms
.timeline
.build_and_append_pdu(
@@ -91,7 +89,7 @@ pub(crate) async fn send_message_event_route(
)
.await?;
services
services()
.transaction_ids
.add_txnid(sender_user, sender_device, &body.txn_id, event_id.as_bytes())?;
@@ -107,7 +105,7 @@ pub(crate) async fn send_message_event_route(
/// - Only works if the user is joined (TODO: always allow, but only show events
/// where the user was joined, depending on `history_visibility`)
pub(crate) async fn get_message_events_route(
State(services): State<crate::State>, body: Ruma<get_message_events::v3::Request>,
body: Ruma<get_message_events::v3::Request>,
) -> Result<get_message_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@@ -125,7 +123,7 @@ pub(crate) async fn get_message_events_route(
.as_ref()
.and_then(|t| PduCount::try_from_string(t).ok());
services
services()
.rooms
.lazy_loading
.lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)
@@ -141,12 +139,12 @@ pub(crate) async fn get_message_events_route(
match body.dir {
ruma::api::Direction::Forward => {
let events_after: Vec<_> = services
let events_after: Vec<_> = services()
.rooms
.timeline
.pdus_after(sender_user, &body.room_id, from)?
.filter_map(Result::ok) // Filter out buggy events
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(&services, pdu, sender_user, &body.room_id)
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)
})
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
@@ -159,7 +157,7 @@ pub(crate) async fn get_message_events_route(
* https://github.com/vector-im/element-web/issues/21034
*/
if !cfg!(feature = "element_hacks")
&& !services.rooms.lazy_loading.lazy_load_was_sent_before(
&& !services().rooms.lazy_loading.lazy_load_was_sent_before(
sender_user,
sender_device,
&body.room_id,
@@ -183,17 +181,17 @@ pub(crate) async fn get_message_events_route(
resp.chunk = events_after;
},
ruma::api::Direction::Backward => {
services
services()
.rooms
.timeline
.backfill_if_required(&body.room_id, from)
.await?;
let events_before: Vec<_> = services
let events_before: Vec<_> = services()
.rooms
.timeline
.pdus_until(sender_user, &body.room_id, from)?
.filter_map(Result::ok) // Filter out buggy events
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(&services, pdu, sender_user, &body.room_id)})
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)})
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
.take(limit)
.collect();
@@ -204,7 +202,7 @@ pub(crate) async fn get_message_events_route(
* https://github.com/vector-im/element-web/issues/21034
*/
if !cfg!(feature = "element_hacks")
&& !services.rooms.lazy_loading.lazy_load_was_sent_before(
&& !services().rooms.lazy_loading.lazy_load_was_sent_before(
sender_user,
sender_device,
&body.room_id,
@@ -231,12 +229,11 @@ pub(crate) async fn get_message_events_route(
resp.state = Vec::new();
for ll_id in &lazy_loaded {
if let Some(member_event) =
services
.rooms
.state_accessor
.room_state_get(&body.room_id, &StateEventType::RoomMember, ll_id.as_str())?
{
if let Some(member_event) = services().rooms.state_accessor.room_state_get(
&body.room_id,
&StateEventType::RoomMember,
ll_id.as_str(),
)? {
resp.state.push(member_event.to_state_event());
}
}
@@ -244,7 +241,7 @@ pub(crate) async fn get_message_events_route(
// remove the feature check when we are sure clients like element can handle it
if !cfg!(feature = "element_hacks") {
if let Some(next_token) = next_token {
services
services()
.rooms
.lazy_loading
.lazy_load_mark_sent(sender_user, sender_device, &body.room_id, lazy_loaded, next_token)
@@ -255,8 +252,8 @@ pub(crate) async fn get_message_events_route(
Ok(resp)
}
fn visibility_filter(services: &Services, pdu: &PduEvent, user_id: &UserId, room_id: &RoomId) -> bool {
services
fn visibility_filter(pdu: &PduEvent, user_id: &UserId, room_id: &RoomId) -> bool {
services()
.rooms
.state_accessor
.user_can_see_event(user_id, room_id, &pdu.event_id)
+4 -5
View File
@@ -1,6 +1,5 @@
use std::time::Duration;
use axum::extract::State;
use conduit::utils;
use ruma::{
api::client::{account, error::ErrorKind},
@@ -8,7 +7,7 @@ use ruma::{
};
use super::TOKEN_LENGTH;
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `POST /_matrix/client/v3/user/{userId}/openid/request_token`
///
@@ -16,7 +15,7 @@ use crate::{Error, Result, Ruma};
///
/// - The token generated is only valid for the OpenID API
pub(crate) async fn create_openid_token_route(
State(services): State<crate::State>, body: Ruma<account::request_openid_token::v3::Request>,
body: Ruma<account::request_openid_token::v3::Request>,
) -> Result<account::request_openid_token::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -29,14 +28,14 @@ pub(crate) async fn create_openid_token_route(
let access_token = utils::random_string(TOKEN_LENGTH);
let expires_in = services
let expires_in = services()
.users
.create_openid_token(&body.user_id, &access_token)?;
Ok(account::request_openid_token::v3::Response {
access_token,
token_type: TokenType::Bearer,
matrix_server_name: services.globals.config.server_name.clone(),
matrix_server_name: services().globals.config.server_name.clone(),
expires_in: Duration::from_secs(expires_in),
})
}
+8 -20
View File
@@ -1,32 +1,22 @@
use std::time::Duration;
use axum::extract::State;
use ruma::api::client::{
error::ErrorKind,
presence::{get_presence, set_presence},
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
///
/// Sets the presence state of the sender user.
pub(crate) async fn set_presence_route(
State(services): State<crate::State>, body: Ruma<set_presence::v3::Request>,
) -> Result<set_presence::v3::Response> {
if !services.globals.allow_local_presence() {
pub(crate) async fn set_presence_route(body: Ruma<set_presence::v3::Request>) -> Result<set_presence::v3::Response> {
if !services().globals.allow_local_presence() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
}
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if sender_user != &body.user_id && body.appservice_info.is_none() {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Not allowed to set presence of other users",
));
}
services
services()
.presence
.set_presence(sender_user, &body.presence, None, None, body.status_msg.clone())?;
@@ -38,10 +28,8 @@ pub(crate) async fn set_presence_route(
/// Gets the presence state of the given user.
///
/// - Only works if you share a room with the user
pub(crate) async fn get_presence_route(
State(services): State<crate::State>, body: Ruma<get_presence::v3::Request>,
) -> Result<get_presence::v3::Response> {
if !services.globals.allow_local_presence() {
pub(crate) async fn get_presence_route(body: Ruma<get_presence::v3::Request>) -> Result<get_presence::v3::Response> {
if !services().globals.allow_local_presence() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
}
@@ -49,12 +37,12 @@ pub(crate) async fn get_presence_route(
let mut presence_event = None;
for _room_id in services
for _room_id in services()
.rooms
.user
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
{
if let Some(presence) = services.presence.get_presence(&body.user_id)? {
if let Some(presence) = services().presence.get_presence(&body.user_id)? {
presence_event = Some(presence);
break;
}
+59 -64
View File
@@ -1,5 +1,3 @@
use axum::extract::State;
use conduit::{pdu::PduBuilder, warn, Error, Result};
use ruma::{
api::{
client::{
@@ -13,9 +11,12 @@ use ruma::{
OwnedMxcUri, OwnedRoomId, OwnedUserId,
};
use serde_json::value::to_raw_value;
use service::Services;
use tracing::warn;
use crate::Ruma;
use crate::{
service::{pdu::PduBuilder, user_is_local},
services, Error, Result, Ruma,
};
/// # `PUT /_matrix/client/r0/profile/{userId}/displayname`
///
@@ -23,21 +24,21 @@ use crate::Ruma;
///
/// - Also makes sure other users receive the update using presence EDUs
pub(crate) async fn set_displayname_route(
State(services): State<crate::State>, body: Ruma<set_display_name::v3::Request>,
body: Ruma<set_display_name::v3::Request>,
) -> Result<set_display_name::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let all_joined_rooms: Vec<OwnedRoomId> = services
let all_joined_rooms: Vec<OwnedRoomId> = services()
.rooms
.state_cache
.rooms_joined(sender_user)
.filter_map(Result::ok)
.collect();
update_displayname(&services, sender_user.clone(), body.displayname.clone(), all_joined_rooms).await?;
update_displayname(sender_user.clone(), body.displayname.clone(), all_joined_rooms).await?;
if services.globals.allow_local_presence() {
if services().globals.allow_local_presence() {
// Presence update
services
services()
.presence
.ping_presence(sender_user, &PresenceState::Online)?;
}
@@ -52,11 +53,11 @@ pub(crate) async fn set_displayname_route(
/// - If user is on another server and we do not have a local copy already fetch
/// displayname over federation
pub(crate) async fn get_displayname_route(
State(services): State<crate::State>, body: Ruma<get_display_name::v3::Request>,
body: Ruma<get_display_name::v3::Request>,
) -> Result<get_display_name::v3::Response> {
if !services.globals.user_is_local(&body.user_id) {
if !user_is_local(&body.user_id) {
// Create and update our local copy of the user
if let Ok(response) = services
if let Ok(response) = services()
.sending
.send_federation_request(
body.user_id.server_name(),
@@ -67,19 +68,19 @@ pub(crate) async fn get_displayname_route(
)
.await
{
if !services.users.exists(&body.user_id)? {
services.users.create(&body.user_id, None)?;
if !services().users.exists(&body.user_id)? {
services().users.create(&body.user_id, None)?;
}
services
services()
.users
.set_displayname(&body.user_id, response.displayname.clone())
.await?;
services
services()
.users
.set_avatar_url(&body.user_id, response.avatar_url.clone())
.await?;
services
services()
.users
.set_blurhash(&body.user_id, response.blurhash.clone())
.await?;
@@ -90,14 +91,14 @@ pub(crate) async fn get_displayname_route(
}
}
if !services.users.exists(&body.user_id)? {
if !services().users.exists(&body.user_id)? {
// Return 404 if this user doesn't exist and we couldn't fetch it over
// federation
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
}
Ok(get_display_name::v3::Response {
displayname: services.users.displayname(&body.user_id)?,
displayname: services().users.displayname(&body.user_id)?,
})
}
@@ -107,10 +108,10 @@ pub(crate) async fn get_displayname_route(
///
/// - Also makes sure other users receive the update using presence EDUs
pub(crate) async fn set_avatar_url_route(
State(services): State<crate::State>, body: Ruma<set_avatar_url::v3::Request>,
body: Ruma<set_avatar_url::v3::Request>,
) -> Result<set_avatar_url::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let all_joined_rooms: Vec<OwnedRoomId> = services
let all_joined_rooms: Vec<OwnedRoomId> = services()
.rooms
.state_cache
.rooms_joined(sender_user)
@@ -118,7 +119,6 @@ pub(crate) async fn set_avatar_url_route(
.collect();
update_avatar_url(
&services,
sender_user.clone(),
body.avatar_url.clone(),
body.blurhash.clone(),
@@ -126,9 +126,9 @@ pub(crate) async fn set_avatar_url_route(
)
.await?;
if services.globals.allow_local_presence() {
if services().globals.allow_local_presence() {
// Presence update
services
services()
.presence
.ping_presence(sender_user, &PresenceState::Online)?;
}
@@ -143,11 +143,11 @@ pub(crate) async fn set_avatar_url_route(
/// - If user is on another server and we do not have a local copy already fetch
/// `avatar_url` and blurhash over federation
pub(crate) async fn get_avatar_url_route(
State(services): State<crate::State>, body: Ruma<get_avatar_url::v3::Request>,
body: Ruma<get_avatar_url::v3::Request>,
) -> Result<get_avatar_url::v3::Response> {
if !services.globals.user_is_local(&body.user_id) {
if !user_is_local(&body.user_id) {
// Create and update our local copy of the user
if let Ok(response) = services
if let Ok(response) = services()
.sending
.send_federation_request(
body.user_id.server_name(),
@@ -158,19 +158,19 @@ pub(crate) async fn get_avatar_url_route(
)
.await
{
if !services.users.exists(&body.user_id)? {
services.users.create(&body.user_id, None)?;
if !services().users.exists(&body.user_id)? {
services().users.create(&body.user_id, None)?;
}
services
services()
.users
.set_displayname(&body.user_id, response.displayname.clone())
.await?;
services
services()
.users
.set_avatar_url(&body.user_id, response.avatar_url.clone())
.await?;
services
services()
.users
.set_blurhash(&body.user_id, response.blurhash.clone())
.await?;
@@ -182,15 +182,15 @@ pub(crate) async fn get_avatar_url_route(
}
}
if !services.users.exists(&body.user_id)? {
if !services().users.exists(&body.user_id)? {
// Return 404 if this user doesn't exist and we couldn't fetch it over
// federation
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
}
Ok(get_avatar_url::v3::Response {
avatar_url: services.users.avatar_url(&body.user_id)?,
blurhash: services.users.blurhash(&body.user_id)?,
avatar_url: services().users.avatar_url(&body.user_id)?,
blurhash: services().users.blurhash(&body.user_id)?,
})
}
@@ -200,12 +200,10 @@ pub(crate) async fn get_avatar_url_route(
///
/// - If user is on another server and we do not have a local copy already,
/// fetch profile over federation.
pub(crate) async fn get_profile_route(
State(services): State<crate::State>, body: Ruma<get_profile::v3::Request>,
) -> Result<get_profile::v3::Response> {
if !services.globals.user_is_local(&body.user_id) {
pub(crate) async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> Result<get_profile::v3::Response> {
if !user_is_local(&body.user_id) {
// Create and update our local copy of the user
if let Ok(response) = services
if let Ok(response) = services()
.sending
.send_federation_request(
body.user_id.server_name(),
@@ -216,19 +214,19 @@ pub(crate) async fn get_profile_route(
)
.await
{
if !services.users.exists(&body.user_id)? {
services.users.create(&body.user_id, None)?;
if !services().users.exists(&body.user_id)? {
services().users.create(&body.user_id, None)?;
}
services
services()
.users
.set_displayname(&body.user_id, response.displayname.clone())
.await?;
services
services()
.users
.set_avatar_url(&body.user_id, response.avatar_url.clone())
.await?;
services
services()
.users
.set_blurhash(&body.user_id, response.blurhash.clone())
.await?;
@@ -241,23 +239,23 @@ pub(crate) async fn get_profile_route(
}
}
if !services.users.exists(&body.user_id)? {
if !services().users.exists(&body.user_id)? {
// Return 404 if this user doesn't exist and we couldn't fetch it over
// federation
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
}
Ok(get_profile::v3::Response {
avatar_url: services.users.avatar_url(&body.user_id)?,
blurhash: services.users.blurhash(&body.user_id)?,
displayname: services.users.displayname(&body.user_id)?,
avatar_url: services().users.avatar_url(&body.user_id)?,
blurhash: services().users.blurhash(&body.user_id)?,
displayname: services().users.displayname(&body.user_id)?,
})
}
pub async fn update_displayname(
services: &Services, user_id: OwnedUserId, displayname: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
user_id: OwnedUserId, displayname: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
) -> Result<()> {
services
services()
.users
.set_displayname(&user_id, displayname.clone())
.await?;
@@ -273,7 +271,7 @@ pub async fn update_displayname(
displayname: displayname.clone(),
join_authorized_via_users_server: None,
..serde_json::from_str(
services
services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
@@ -296,20 +294,19 @@ pub async fn update_displayname(
.filter_map(Result::ok)
.collect();
update_all_rooms(services, all_joined_rooms, user_id).await;
update_all_rooms(all_joined_rooms, user_id).await;
Ok(())
}
pub async fn update_avatar_url(
services: &Services, user_id: OwnedUserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>,
all_joined_rooms: Vec<OwnedRoomId>,
user_id: OwnedUserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
) -> Result<()> {
services
services()
.users
.set_avatar_url(&user_id, avatar_url.clone())
.await?;
services
services()
.users
.set_blurhash(&user_id, blurhash.clone())
.await?;
@@ -326,7 +323,7 @@ pub async fn update_avatar_url(
blurhash: blurhash.clone(),
join_authorized_via_users_server: None,
..serde_json::from_str(
services
services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
@@ -349,17 +346,15 @@ pub async fn update_avatar_url(
.filter_map(Result::ok)
.collect();
update_all_rooms(services, all_joined_rooms, user_id).await;
update_all_rooms(all_joined_rooms, user_id).await;
Ok(())
}
pub async fn update_all_rooms(
services: &Services, all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId,
) {
pub async fn update_all_rooms(all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId) {
for (pdu_builder, room_id) in all_joined_rooms {
let state_lock = services.rooms.state.mutex.lock(room_id).await;
if let Err(e) = services
let state_lock = services().rooms.state.mutex.lock(room_id).await;
if let Err(e) = services()
.rooms
.timeline
.build_and_append_pdu(pdu_builder, &user_id, room_id, &state_lock)
+26 -35
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use ruma::{
api::client::{
error::ErrorKind,
@@ -11,18 +10,18 @@ use ruma::{
push::{InsertPushRuleError, RemovePushRuleError, Ruleset},
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `GET /_matrix/client/r0/pushrules/`
///
/// Retrieves the push rules event for this user.
pub(crate) async fn get_pushrules_all_route(
State(services): State<crate::State>, body: Ruma<get_pushrules_all::v3::Request>,
body: Ruma<get_pushrules_all::v3::Request>,
) -> Result<get_pushrules_all::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event =
services
services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?;
@@ -35,7 +34,7 @@ pub(crate) async fn get_pushrules_all_route(
global: account_data.global,
})
} else {
services.account_data.update(
services().account_data.update(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
@@ -56,12 +55,10 @@ pub(crate) async fn get_pushrules_all_route(
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
///
/// Retrieves a single specified push rule for this user.
pub(crate) async fn get_pushrule_route(
State(services): State<crate::State>, body: Ruma<get_pushrule::v3::Request>,
) -> Result<get_pushrule::v3::Response> {
pub(crate) async fn get_pushrule_route(body: Ruma<get_pushrule::v3::Request>) -> Result<get_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -87,9 +84,7 @@ pub(crate) async fn get_pushrule_route(
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
///
/// Creates a single specified push rule for this user.
pub(crate) async fn set_pushrule_route(
State(services): State<crate::State>, body: Ruma<set_pushrule::v3::Request>,
) -> Result<set_pushrule::v3::Response> {
pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) -> Result<set_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let body = body.body;
@@ -100,7 +95,7 @@ pub(crate) async fn set_pushrule_route(
));
}
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -139,7 +134,7 @@ pub(crate) async fn set_pushrule_route(
return Err(err);
}
services.account_data.update(
services().account_data.update(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
@@ -153,7 +148,7 @@ pub(crate) async fn set_pushrule_route(
///
/// Gets the actions of a single specified push rule for this user.
pub(crate) async fn get_pushrule_actions_route(
State(services): State<crate::State>, body: Ruma<get_pushrule_actions::v3::Request>,
body: Ruma<get_pushrule_actions::v3::Request>,
) -> Result<get_pushrule_actions::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -164,7 +159,7 @@ pub(crate) async fn get_pushrule_actions_route(
));
}
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -188,7 +183,7 @@ pub(crate) async fn get_pushrule_actions_route(
///
/// Sets the actions of a single specified push rule for this user.
pub(crate) async fn set_pushrule_actions_route(
State(services): State<crate::State>, body: Ruma<set_pushrule_actions::v3::Request>,
body: Ruma<set_pushrule_actions::v3::Request>,
) -> Result<set_pushrule_actions::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -199,7 +194,7 @@ pub(crate) async fn set_pushrule_actions_route(
));
}
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -216,7 +211,7 @@ pub(crate) async fn set_pushrule_actions_route(
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
}
services.account_data.update(
services().account_data.update(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
@@ -230,7 +225,7 @@ pub(crate) async fn set_pushrule_actions_route(
///
/// Gets the enabled status of a single specified push rule for this user.
pub(crate) async fn get_pushrule_enabled_route(
State(services): State<crate::State>, body: Ruma<get_pushrule_enabled::v3::Request>,
body: Ruma<get_pushrule_enabled::v3::Request>,
) -> Result<get_pushrule_enabled::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -241,7 +236,7 @@ pub(crate) async fn get_pushrule_enabled_route(
));
}
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -264,7 +259,7 @@ pub(crate) async fn get_pushrule_enabled_route(
///
/// Sets the enabled status of a single specified push rule for this user.
pub(crate) async fn set_pushrule_enabled_route(
State(services): State<crate::State>, body: Ruma<set_pushrule_enabled::v3::Request>,
body: Ruma<set_pushrule_enabled::v3::Request>,
) -> Result<set_pushrule_enabled::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -275,7 +270,7 @@ pub(crate) async fn set_pushrule_enabled_route(
));
}
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -292,7 +287,7 @@ pub(crate) async fn set_pushrule_enabled_route(
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
}
services.account_data.update(
services().account_data.update(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
@@ -306,7 +301,7 @@ pub(crate) async fn set_pushrule_enabled_route(
///
/// Deletes a single specified push rule for this user.
pub(crate) async fn delete_pushrule_route(
State(services): State<crate::State>, body: Ruma<delete_pushrule::v3::Request>,
body: Ruma<delete_pushrule::v3::Request>,
) -> Result<delete_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -317,7 +312,7 @@ pub(crate) async fn delete_pushrule_route(
));
}
let event = services
let event = services()
.account_data
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
@@ -341,7 +336,7 @@ pub(crate) async fn delete_pushrule_route(
return Err(err);
}
services.account_data.update(
services().account_data.update(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
@@ -354,13 +349,11 @@ pub(crate) async fn delete_pushrule_route(
/// # `GET /_matrix/client/r0/pushers`
///
/// Gets all currently active pushers for the sender user.
pub(crate) async fn get_pushers_route(
State(services): State<crate::State>, body: Ruma<get_pushers::v3::Request>,
) -> Result<get_pushers::v3::Response> {
pub(crate) async fn get_pushers_route(body: Ruma<get_pushers::v3::Request>) -> Result<get_pushers::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
Ok(get_pushers::v3::Response {
pushers: services.pusher.get_pushers(sender_user)?,
pushers: services().pusher.get_pushers(sender_user)?,
})
}
@@ -369,12 +362,10 @@ pub(crate) async fn get_pushers_route(
/// Adds a pusher for the sender user.
///
/// - TODO: Handle `append`
pub(crate) async fn set_pushers_route(
State(services): State<crate::State>, body: Ruma<set_pusher::v3::Request>,
) -> Result<set_pusher::v3::Response> {
pub(crate) async fn set_pushers_route(body: Ruma<set_pusher::v3::Request>) -> Result<set_pusher::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services.pusher.set_pusher(sender_user, &body.action)?;
services().pusher.set_pusher(sender_user, &body.action)?;
Ok(set_pusher::v3::Response::default())
}
+13 -14
View File
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use axum::extract::State;
use conduit::PduCount;
use ruma::{
api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt},
@@ -11,7 +10,7 @@ use ruma::{
MilliSecondsSinceUnixEpoch,
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
///
@@ -21,7 +20,7 @@ use crate::{Error, Result, Ruma};
/// - If `read_receipt` is set: Update private marker and public read receipt
/// EDU
pub(crate) async fn set_read_marker_route(
State(services): State<crate::State>, body: Ruma<set_read_marker::v3::Request>,
body: Ruma<set_read_marker::v3::Request>,
) -> Result<set_read_marker::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -31,7 +30,7 @@ pub(crate) async fn set_read_marker_route(
event_id: fully_read.clone(),
},
};
services.account_data.update(
services().account_data.update(
Some(&body.room_id),
sender_user,
RoomAccountDataEventType::FullyRead,
@@ -40,14 +39,14 @@ pub(crate) async fn set_read_marker_route(
}
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
services
services()
.rooms
.user
.reset_notification_counts(sender_user, &body.room_id)?;
}
if let Some(event) = &body.private_read_receipt {
let count = services
let count = services()
.rooms
.timeline
.get_pdu_count(event)?
@@ -61,7 +60,7 @@ pub(crate) async fn set_read_marker_route(
},
PduCount::Normal(c) => c,
};
services
services()
.rooms
.read_receipt
.private_read_set(&body.room_id, sender_user, count)?;
@@ -83,7 +82,7 @@ pub(crate) async fn set_read_marker_route(
let mut receipt_content = BTreeMap::new();
receipt_content.insert(event.to_owned(), receipts);
services.rooms.read_receipt.readreceipt_update(
services().rooms.read_receipt.readreceipt_update(
sender_user,
&body.room_id,
&ruma::events::receipt::ReceiptEvent {
@@ -100,7 +99,7 @@ pub(crate) async fn set_read_marker_route(
///
/// Sets private read marker and public read receipt EDU.
pub(crate) async fn create_receipt_route(
State(services): State<crate::State>, body: Ruma<create_receipt::v3::Request>,
body: Ruma<create_receipt::v3::Request>,
) -> Result<create_receipt::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -108,7 +107,7 @@ pub(crate) async fn create_receipt_route(
&body.receipt_type,
create_receipt::v3::ReceiptType::Read | create_receipt::v3::ReceiptType::ReadPrivate
) {
services
services()
.rooms
.user
.reset_notification_counts(sender_user, &body.room_id)?;
@@ -121,7 +120,7 @@ pub(crate) async fn create_receipt_route(
event_id: body.event_id.clone(),
},
};
services.account_data.update(
services().account_data.update(
Some(&body.room_id),
sender_user,
RoomAccountDataEventType::FullyRead,
@@ -143,7 +142,7 @@ pub(crate) async fn create_receipt_route(
let mut receipt_content = BTreeMap::new();
receipt_content.insert(body.event_id.clone(), receipts);
services.rooms.read_receipt.readreceipt_update(
services().rooms.read_receipt.readreceipt_update(
sender_user,
&body.room_id,
&ruma::events::receipt::ReceiptEvent {
@@ -153,7 +152,7 @@ pub(crate) async fn create_receipt_route(
)?;
},
create_receipt::v3::ReceiptType::ReadPrivate => {
let count = services
let count = services()
.rooms
.timeline
.get_pdu_count(&body.event_id)?
@@ -167,7 +166,7 @@ pub(crate) async fn create_receipt_route(
},
PduCount::Normal(c) => c,
};
services
services()
.rooms
.read_receipt
.private_read_set(&body.room_id, sender_user, count)?;
+4 -7
View File
@@ -1,26 +1,23 @@
use axum::extract::State;
use ruma::{
api::client::redact::redact_event,
events::{room::redaction::RoomRedactionEventContent, TimelineEventType},
};
use serde_json::value::to_raw_value;
use crate::{service::pdu::PduBuilder, Result, Ruma};
use crate::{service::pdu::PduBuilder, services, Result, Ruma};
/// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
///
/// Tries to send a redaction event into the room.
///
/// - TODO: Handle txn id
pub(crate) async fn redact_event_route(
State(services): State<crate::State>, body: Ruma<redact_event::v3::Request>,
) -> Result<redact_event::v3::Response> {
pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) -> Result<redact_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let body = body.body;
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
let event_id = services
let event_id = services()
.rooms
.timeline
.build_and_append_pdu(
+49 -41
View File
@@ -1,28 +1,30 @@
use axum::extract::State;
use ruma::api::client::relations::{
get_relating_events, get_relating_events_with_rel_type, get_relating_events_with_rel_type_and_event_type,
};
use crate::{Result, Ruma};
use crate::{services, Result, Ruma};
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let res = services.rooms.pdu_metadata.paginate_relations_with_filter(
sender_user,
&body.room_id,
&body.event_id,
&Some(body.event_type.clone()),
&Some(body.rel_type.clone()),
&body.from,
&body.to,
&body.limit,
body.recurse,
body.dir,
)?;
let res = services()
.rooms
.pdu_metadata
.paginate_relations_with_filter(
sender_user,
&body.room_id,
&body.event_id,
&Some(body.event_type.clone()),
&Some(body.rel_type.clone()),
&body.from,
&body.to,
&body.limit,
body.recurse,
body.dir,
)?;
Ok(get_relating_events_with_rel_type_and_event_type::v1::Response {
chunk: res.chunk,
@@ -34,22 +36,25 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
pub(crate) async fn get_relating_events_with_rel_type_route(
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type::v1::Request>,
body: Ruma<get_relating_events_with_rel_type::v1::Request>,
) -> Result<get_relating_events_with_rel_type::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let res = services.rooms.pdu_metadata.paginate_relations_with_filter(
sender_user,
&body.room_id,
&body.event_id,
&None,
&Some(body.rel_type.clone()),
&body.from,
&body.to,
&body.limit,
body.recurse,
body.dir,
)?;
let res = services()
.rooms
.pdu_metadata
.paginate_relations_with_filter(
sender_user,
&body.room_id,
&body.event_id,
&None,
&Some(body.rel_type.clone()),
&body.from,
&body.to,
&body.limit,
body.recurse,
body.dir,
)?;
Ok(get_relating_events_with_rel_type::v1::Response {
chunk: res.chunk,
@@ -61,20 +66,23 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
pub(crate) async fn get_relating_events_route(
State(services): State<crate::State>, body: Ruma<get_relating_events::v1::Request>,
body: Ruma<get_relating_events::v1::Request>,
) -> Result<get_relating_events::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services.rooms.pdu_metadata.paginate_relations_with_filter(
sender_user,
&body.room_id,
&body.event_id,
&None,
&None,
&body.from,
&body.to,
&body.limit,
body.recurse,
body.dir,
)
services()
.rooms
.pdu_metadata
.paginate_relations_with_filter(
sender_user,
&body.room_id,
&body.event_id,
&None,
&None,
&body.from,
&body.to,
&body.limit,
body.recurse,
body.dir,
)
}
+8 -22
View File
@@ -1,6 +1,5 @@
use std::time::Duration;
use axum::extract::State;
use rand::Rng;
use ruma::{
api::client::{error::ErrorKind, room::report_content},
@@ -10,18 +9,13 @@ use ruma::{
use tokio::time::sleep;
use tracing::info;
use crate::{
debug_info,
service::{pdu::PduEvent, Services},
utils::HtmlEscape,
Error, Result, Ruma,
};
use crate::{debug_info, service::pdu::PduEvent, services, utils::HtmlEscape, Error, Result, Ruma};
/// # `POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}`
///
/// Reports an inappropriate event to homeserver admins
pub(crate) async fn report_event_route(
State(services): State<crate::State>, body: Ruma<report_content::v3::Request>,
body: Ruma<report_content::v3::Request>,
) -> Result<report_content::v3::Response> {
// user authentication
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -32,26 +26,18 @@ pub(crate) async fn report_event_route(
);
// check if we know about the reported event ID or if it's invalid
let Some(pdu) = services.rooms.timeline.get_pdu(&body.event_id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else {
return Err(Error::BadRequest(
ErrorKind::NotFound,
"Event ID is not known to us or Event ID is invalid",
));
};
is_report_valid(
&services,
&pdu.event_id,
&body.room_id,
sender_user,
&body.reason,
body.score,
&pdu,
)?;
is_report_valid(&pdu.event_id, &body.room_id, sender_user, &body.reason, body.score, &pdu)?;
// send admin room message that we received the report with an @room ping for
// urgency
services
services()
.admin
.send_message(message::RoomMessageEventContent::text_html(
format!(
@@ -93,8 +79,8 @@ pub(crate) async fn report_event_route(
/// check if score is in valid range
/// check if report reasoning is less than or equal to 750 characters
fn is_report_valid(
services: &Services, event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: &Option<String>,
score: Option<ruma::Int>, pdu: &std::sync::Arc<PduEvent>,
event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: &Option<String>, score: Option<ruma::Int>,
pdu: &std::sync::Arc<PduEvent>,
) -> Result<bool> {
debug_info!("Checking if report from user {sender_user} for event {event_id} in room {room_id} is valid");
@@ -105,7 +91,7 @@ fn is_report_valid(
));
}
if !services
if !services()
.rooms
.state_cache
.room_members(&pdu.room_id)
+66 -73
View File
@@ -1,6 +1,5 @@
use std::{cmp::max, collections::BTreeMap};
use axum::extract::State;
use conduit::{debug_info, debug_warn};
use ruma::{
api::client::{
@@ -31,8 +30,8 @@ use tracing::{error, info, warn};
use super::invite_helper;
use crate::{
service::{appservice::RegistrationInfo, pdu::PduBuilder, Services},
Error, Result, Ruma,
service::{appservice::RegistrationInfo, pdu::PduBuilder},
services, Error, Result, Ruma,
};
/// Recommended transferable state events list from the spec
@@ -64,46 +63,44 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
/// - Send events listed in initial state
/// - Send events implied by `name` and `topic`
/// - Send invite events
pub(crate) async fn create_room_route(
State(services): State<crate::State>, body: Ruma<create_room::v3::Request>,
) -> Result<create_room::v3::Response> {
pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> Result<create_room::v3::Response> {
use create_room::v3::RoomPreset;
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services.globals.allow_room_creation()
if !services().globals.allow_room_creation()
&& body.appservice_info.is_none()
&& !services.users.is_admin(sender_user)?
&& !services().users.is_admin(sender_user)?
{
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room creation has been disabled."));
}
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
custom_room_id_check(&services, custom_room_id)?
custom_room_id_check(custom_room_id)?
} else {
RoomId::new(&services.globals.config.server_name)
RoomId::new(&services().globals.config.server_name)
};
// check if room ID doesn't already exist instead of erroring on auth check
if services.rooms.short.get_shortroomid(&room_id)?.is_some() {
if services().rooms.short.get_shortroomid(&room_id)?.is_some() {
return Err(Error::BadRequest(
ErrorKind::RoomInUse,
"Room with that custom room ID already exists",
));
}
let _short_id = services.rooms.short.get_or_create_shortroomid(&room_id)?;
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
let _short_id = services().rooms.short.get_or_create_shortroomid(&room_id)?;
let state_lock = services().rooms.state.mutex.lock(&room_id).await;
let alias: Option<OwnedRoomAliasId> = if let Some(alias) = &body.room_alias_name {
Some(room_alias_check(&services, alias, &body.appservice_info).await?)
Some(room_alias_check(alias, &body.appservice_info).await?)
} else {
None
};
let room_version = match body.room_version.clone() {
Some(room_version) => {
if services
if services()
.globals
.supported_room_versions()
.contains(&room_version)
@@ -116,7 +113,7 @@ pub(crate) async fn create_room_route(
));
}
},
None => services.globals.default_room_version(),
None => services().globals.default_room_version(),
};
let content = match &body.creation_content {
@@ -187,7 +184,7 @@ pub(crate) async fn create_room_route(
};
// 1. The room create event
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -205,7 +202,7 @@ pub(crate) async fn create_room_route(
.await?;
// 2. Let the room creator join
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -213,11 +210,11 @@ pub(crate) async fn create_room_route(
event_type: TimelineEventType::RoomMember,
content: to_raw_value(&RoomMemberEventContent {
membership: MembershipState::Join,
displayname: services.users.displayname(sender_user)?,
avatar_url: services.users.avatar_url(sender_user)?,
displayname: services().users.displayname(sender_user)?,
avatar_url: services().users.avatar_url(sender_user)?,
is_direct: Some(body.is_direct),
third_party_invite: None,
blurhash: services.users.blurhash(sender_user)?,
blurhash: services().users.blurhash(sender_user)?,
reason: None,
join_authorized_via_users_server: None,
})
@@ -252,7 +249,7 @@ pub(crate) async fn create_room_route(
let power_levels_content =
default_power_levels_content(&body.power_level_content_override, &body.visibility, users)?;
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -271,7 +268,7 @@ pub(crate) async fn create_room_route(
// 4. Canonical room alias
if let Some(room_alias_id) = &alias {
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -296,7 +293,7 @@ pub(crate) async fn create_room_route(
// 5. Events set by preset
// 5.1 Join Rules
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -319,7 +316,7 @@ pub(crate) async fn create_room_route(
.await?;
// 5.2 History Visibility
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -338,7 +335,7 @@ pub(crate) async fn create_room_route(
.await?;
// 5.3 Guest Access
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -381,11 +378,11 @@ pub(crate) async fn create_room_route(
pdu_builder.state_key.get_or_insert_with(String::new);
// Silently skip encryption events if they are not allowed
if pdu_builder.event_type == TimelineEventType::RoomEncryption && !services.globals.allow_encryption() {
if pdu_builder.event_type == TimelineEventType::RoomEncryption && !services().globals.allow_encryption() {
continue;
}
services
services()
.rooms
.timeline
.build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock)
@@ -394,7 +391,7 @@ pub(crate) async fn create_room_route(
// 7. Events implied by name and topic
if let Some(name) = &body.name {
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -414,7 +411,7 @@ pub(crate) async fn create_room_route(
}
if let Some(topic) = &body.topic {
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -438,21 +435,21 @@ pub(crate) async fn create_room_route(
// 8. Events implied by invite (and TODO: invite_3pid)
drop(state_lock);
for user_id in &body.invite {
if let Err(e) = invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct).await {
if let Err(e) = invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await {
warn!(%e, "Failed to send invite");
}
}
// Homeserver specific stuff
if let Some(alias) = alias {
services
services()
.rooms
.alias
.set_alias(&alias, &room_id, sender_user)?;
}
if body.visibility == room::Visibility::Public {
services.rooms.directory.set_public(&room_id)?;
services().rooms.directory.set_public(&room_id)?;
}
info!("{sender_user} created a room with room ID {room_id}");
@@ -467,11 +464,11 @@ pub(crate) async fn create_room_route(
/// - You have to currently be joined to the room (TODO: Respect history
/// visibility)
pub(crate) async fn get_room_event_route(
State(services): State<crate::State>, body: Ruma<get_room_event::v3::Request>,
body: Ruma<get_room_event::v3::Request>,
) -> Result<get_room_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services
let event = services()
.rooms
.timeline
.get_pdu(&body.event_id)?
@@ -480,7 +477,7 @@ pub(crate) async fn get_room_event_route(
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
})?;
if !services
if !services()
.rooms
.state_accessor
.user_can_see_event(sender_user, &event.room_id, &body.event_id)?
@@ -505,12 +502,10 @@ pub(crate) async fn get_room_event_route(
///
/// - Only users joined to the room are allowed to call this, or if
/// `history_visibility` is world readable in the room
pub(crate) async fn get_room_aliases_route(
State(services): State<crate::State>, body: Ruma<aliases::v3::Request>,
) -> Result<aliases::v3::Response> {
pub(crate) async fn get_room_aliases_route(body: Ruma<aliases::v3::Request>) -> Result<aliases::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services
if !services()
.rooms
.state_accessor
.user_can_see_state_events(sender_user, &body.room_id)?
@@ -522,7 +517,7 @@ pub(crate) async fn get_room_aliases_route(
}
Ok(aliases::v3::Response {
aliases: services
aliases: services()
.rooms
.alias
.local_aliases_for_room(&body.room_id)
@@ -541,12 +536,10 @@ pub(crate) async fn get_room_aliases_route(
/// - Transfers some state events
/// - Moves local aliases
/// - Modifies old room power levels to prevent users from speaking
pub(crate) async fn upgrade_room_route(
State(services): State<crate::State>, body: Ruma<upgrade_room::v3::Request>,
) -> Result<upgrade_room::v3::Response> {
pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) -> Result<upgrade_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services
if !services()
.globals
.supported_room_versions()
.contains(&body.new_version)
@@ -558,19 +551,19 @@ pub(crate) async fn upgrade_room_route(
}
// Create a replacement room
let replacement_room = RoomId::new(services.globals.server_name());
let replacement_room = RoomId::new(services().globals.server_name());
let _short_id = services
let _short_id = services()
.rooms
.short
.get_or_create_shortroomid(&replacement_room)?;
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
// Send a m.room.tombstone event to the old room to indicate that it is not
// intended to be used any further Fail if the sender does not have the required
// permissions
let tombstone_event_id = services
let tombstone_event_id = services()
.rooms
.timeline
.build_and_append_pdu(
@@ -593,11 +586,11 @@ pub(crate) async fn upgrade_room_route(
// Change lock to replacement room
drop(state_lock);
let state_lock = services.rooms.state.mutex.lock(&replacement_room).await;
let state_lock = services().rooms.state.mutex.lock(&replacement_room).await;
// Get the old room creation event
let mut create_event_content = serde_json::from_str::<CanonicalJsonObject>(
services
services()
.rooms
.state_accessor
.room_state_get(&body.room_id, &StateEventType::RoomCreate, "")?
@@ -665,7 +658,7 @@ pub(crate) async fn upgrade_room_route(
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
}
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -683,7 +676,7 @@ pub(crate) async fn upgrade_room_route(
.await?;
// Join the new room
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -691,11 +684,11 @@ pub(crate) async fn upgrade_room_route(
event_type: TimelineEventType::RoomMember,
content: to_raw_value(&RoomMemberEventContent {
membership: MembershipState::Join,
displayname: services.users.displayname(sender_user)?,
avatar_url: services.users.avatar_url(sender_user)?,
displayname: services().users.displayname(sender_user)?,
avatar_url: services().users.avatar_url(sender_user)?,
is_direct: None,
third_party_invite: None,
blurhash: services.users.blurhash(sender_user)?,
blurhash: services().users.blurhash(sender_user)?,
reason: None,
join_authorized_via_users_server: None,
})
@@ -712,7 +705,7 @@ pub(crate) async fn upgrade_room_route(
// Replicate transferable state events to the new room
for event_type in TRANSFERABLE_STATE_EVENTS {
let event_content = match services
let event_content = match services()
.rooms
.state_accessor
.room_state_get(&body.room_id, event_type, "")?
@@ -721,7 +714,7 @@ pub(crate) async fn upgrade_room_route(
None => continue, // Skipping missing events.
};
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -740,13 +733,13 @@ pub(crate) async fn upgrade_room_route(
}
// Moves any local aliases to the new room
for alias in services
for alias in services()
.rooms
.alias
.local_aliases_for_room(&body.room_id)
.filter_map(Result::ok)
{
services
services()
.rooms
.alias
.set_alias(&alias, &replacement_room, sender_user)?;
@@ -754,7 +747,7 @@ pub(crate) async fn upgrade_room_route(
// Get the old room power levels
let mut power_levels_event_content: RoomPowerLevelsEventContent = serde_json::from_str(
services
services()
.rooms
.state_accessor
.room_state_get(&body.room_id, &StateEventType::RoomPowerLevels, "")?
@@ -779,7 +772,7 @@ pub(crate) async fn upgrade_room_route(
// Modify the power levels in the old room to prevent sending of events and
// inviting new users
services
services()
.rooms
.timeline
.build_and_append_pdu(
@@ -848,7 +841,7 @@ fn default_power_levels_content(
/// if a room is being created with a room alias, run our checks
async fn room_alias_check(
services: &Services, room_alias_name: &str, appservice_info: &Option<RegistrationInfo>,
room_alias_name: &str, appservice_info: &Option<RegistrationInfo>,
) -> Result<OwnedRoomAliasId> {
// Basic checks on the room alias validity
if room_alias_name.contains(':') {
@@ -865,7 +858,7 @@ async fn room_alias_check(
}
// check if room alias is forbidden
if services
if services()
.globals
.forbidden_alias_names()
.is_match(room_alias_name)
@@ -873,13 +866,13 @@ async fn room_alias_check(
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
}
let full_room_alias = RoomAliasId::parse(format!("#{}:{}", room_alias_name, services.globals.config.server_name))
let full_room_alias = RoomAliasId::parse(format!("#{}:{}", room_alias_name, services().globals.config.server_name))
.map_err(|e| {
info!("Failed to parse room alias {room_alias_name}: {e}");
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
})?;
info!("Failed to parse room alias {room_alias_name}: {e}");
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
})?;
if services
if services()
.rooms
.alias
.resolve_local_alias(&full_room_alias)?
@@ -892,7 +885,7 @@ async fn room_alias_check(
if !info.aliases.is_match(full_room_alias.as_str()) {
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
}
} else if services
} else if services()
.appservice
.is_exclusive_alias(&full_room_alias)
.await
@@ -906,9 +899,9 @@ async fn room_alias_check(
}
/// if a room is being created with a custom room ID, run our checks against it
fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<OwnedRoomId> {
fn custom_room_id_check(custom_room_id: &str) -> Result<OwnedRoomId> {
// apply forbidden room alias checks to custom room IDs too
if services
if services()
.globals
.forbidden_alias_names()
.is_match(custom_room_id)
@@ -929,7 +922,7 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
));
}
let full_room_id = format!("!{}:{}", custom_room_id, services.globals.config.server_name);
let full_room_id = format!("!{}:{}", custom_room_id, services().globals.config.server_name);
debug_info!("Full custom room ID: {full_room_id}");
+18 -13
View File
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use axum::extract::State;
use ruma::{
api::client::{
error::ErrorKind,
@@ -15,7 +14,7 @@ use ruma::{
};
use tracing::debug;
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `POST /_matrix/client/r0/search`
///
@@ -23,9 +22,7 @@ use crate::{Error, Result, Ruma};
///
/// - Only works if the user is currently joined to the room (TODO: Respect
/// history visibility)
pub(crate) async fn search_events_route(
State(services): State<crate::State>, body: Ruma<search_events::v3::Request>,
) -> Result<search_events::v3::Response> {
pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>) -> Result<search_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
@@ -33,7 +30,7 @@ pub(crate) async fn search_events_route(
let include_state = &search_criteria.include_state;
let room_ids = filter.rooms.clone().unwrap_or_else(|| {
services
services()
.rooms
.state_cache
.rooms_joined(sender_user)
@@ -53,7 +50,11 @@ pub(crate) async fn search_events_route(
if include_state.is_some_and(|include_state| include_state) {
for room_id in &room_ids {
if !services.rooms.state_cache.is_joined(sender_user, room_id)? {
if !services()
.rooms
.state_cache
.is_joined(sender_user, room_id)?
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"You don't have permission to view this room.",
@@ -61,12 +62,12 @@ pub(crate) async fn search_events_route(
}
// check if sender_user can see state events
if services
if services()
.rooms
.state_accessor
.user_can_see_state_events(sender_user, room_id)?
{
let room_state = services
let room_state = services()
.rooms
.state_accessor
.room_state_full(room_id)
@@ -90,14 +91,18 @@ pub(crate) async fn search_events_route(
let mut searches = Vec::new();
for room_id in &room_ids {
if !services.rooms.state_cache.is_joined(sender_user, room_id)? {
if !services()
.rooms
.state_cache
.is_joined(sender_user, room_id)?
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"You don't have permission to view this room.",
));
}
if let Some(search) = services
if let Some(search) = services()
.rooms
.search
.search_pdus(room_id, &search_criteria.search_term)?
@@ -130,14 +135,14 @@ pub(crate) async fn search_events_route(
.iter()
.skip(skip)
.filter_map(|result| {
services
services()
.rooms
.timeline
.get_pdu_from_id(result)
.ok()?
.filter(|pdu| {
!pdu.is_redacted()
&& services
&& services()
.rooms
.state_accessor
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
+19 -26
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use ruma::{
api::client::{
error::ErrorKind,
@@ -21,7 +20,7 @@ use serde::Deserialize;
use tracing::{debug, info, warn};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use crate::{utils, utils::hash, Error, Result, Ruma};
use crate::{services, utils, utils::hash, Error, Result, Ruma};
#[derive(Debug, Deserialize)]
struct Claims {
@@ -56,9 +55,7 @@ pub(crate) async fn get_login_types_route(
/// Note: You can use [`GET
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
/// supported login types.
pub(crate) async fn login_route(
State(services): State<crate::State>, body: Ruma<login::v3::Request>,
) -> Result<login::v3::Response> {
pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
// Validate login method
// TODO: Other login methods
let user_id = match &body.login_info {
@@ -71,7 +68,7 @@ pub(crate) async fn login_route(
}) => {
debug!("Got password login type");
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
} else if let Some(user) = user {
UserId::parse(user)
} else {
@@ -80,7 +77,7 @@ pub(crate) async fn login_route(
}
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
let hash = services
let hash = services()
.users
.password_hash(&user_id)?
.ok_or(Error::BadRequest(ErrorKind::forbidden(), "Wrong username or password."))?;
@@ -99,7 +96,7 @@ pub(crate) async fn login_route(
token,
}) => {
debug!("Got token login type");
if let Some(jwt_decoding_key) = services.globals.jwt_decoding_key() {
if let Some(jwt_decoding_key) = services().globals.jwt_decoding_key() {
let token =
jsonwebtoken::decode::<Claims>(token, jwt_decoding_key, &jsonwebtoken::Validation::default())
.map_err(|e| {
@@ -109,7 +106,7 @@ pub(crate) async fn login_route(
let username = token.claims.sub.to_lowercase();
UserId::parse_with_server_name(username, services.globals.server_name()).map_err(|e| {
UserId::parse_with_server_name(username, services().globals.server_name()).map_err(|e| {
warn!("Failed to parse username from user logging in: {e}");
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
})?
@@ -127,7 +124,7 @@ pub(crate) async fn login_route(
}) => {
debug!("Got appservice login type");
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
} else if let Some(user) = user {
UserId::parse(user)
} else {
@@ -167,22 +164,22 @@ pub(crate) async fn login_route(
// Determine if device_id was provided and exists in the db for this user
let device_exists = body.device_id.as_ref().map_or(false, |device_id| {
services
services()
.users
.all_device_ids(&user_id)
.any(|x| x.as_ref().map_or(false, |v| v == device_id))
});
if device_exists {
services.users.set_token(&user_id, &device_id, &token)?;
services().users.set_token(&user_id, &device_id, &token)?;
} else {
services
services()
.users
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
}
// send client well-known if specified so the client knows to reconfigure itself
let client_discovery_info: Option<DiscoveryInfo> = services
let client_discovery_info: Option<DiscoveryInfo> = services()
.globals
.well_known_client()
.as_ref()
@@ -200,7 +197,7 @@ pub(crate) async fn login_route(
device_id,
well_known: client_discovery_info,
expires_in: None,
home_server: Some(services.globals.server_name().to_owned()),
home_server: Some(services().globals.server_name().to_owned()),
refresh_token: None,
})
}
@@ -214,16 +211,14 @@ pub(crate) async fn login_route(
/// last seen ts)
/// - Forgets to-device events
/// - Triggers device list updates
pub(crate) async fn logout_route(
State(services): State<crate::State>, body: Ruma<logout::v3::Request>,
) -> Result<logout::v3::Response> {
pub(crate) async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logout::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
services.users.remove_device(sender_user, sender_device)?;
services().users.remove_device(sender_user, sender_device)?;
// send device list update for user after logout
services.users.mark_device_key_update(sender_user)?;
services().users.mark_device_key_update(sender_user)?;
Ok(logout::v3::Response::new())
}
@@ -241,17 +236,15 @@ pub(crate) async fn logout_route(
/// Note: This is equivalent to calling [`GET
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
/// user.
pub(crate) async fn logout_all_route(
State(services): State<crate::State>, body: Ruma<logout_all::v3::Request>,
) -> Result<logout_all::v3::Response> {
pub(crate) async fn logout_all_route(body: Ruma<logout_all::v3::Request>) -> Result<logout_all::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
for device_id in services.users.all_device_ids(sender_user).flatten() {
services.users.remove_device(sender_user, &device_id)?;
for device_id in services().users.all_device_ids(sender_user).flatten() {
services().users.remove_device(sender_user, &device_id)?;
}
// send device list update for user after logout
services.users.mark_device_key_update(sender_user)?;
services().users.mark_device_key_update(sender_user)?;
Ok(logout_all::v3::Response::new())
}
+3 -6
View File
@@ -1,20 +1,17 @@
use std::str::FromStr;
use axum::extract::State;
use ruma::{
api::client::{error::ErrorKind, space::get_hierarchy},
UInt,
};
use crate::{service::rooms::spaces::PaginationToken, Error, Result, Ruma};
use crate::{service::rooms::spaces::PaginationToken, services, Error, Result, Ruma};
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
///
/// Paginates over the space tree in a depth-first manner to locate child rooms
/// of a given space.
pub(crate) async fn get_hierarchy_route(
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
) -> Result<get_hierarchy::v1::Response> {
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let limit = body
@@ -42,7 +39,7 @@ pub(crate) async fn get_hierarchy_route(
}
}
services
services()
.rooms
.spaces
.get_client_hierarchy(
+26 -31
View File
@@ -1,7 +1,6 @@
use std::sync::Arc;
use axum::extract::State;
use conduit::{debug_info, error, pdu::PduBuilder, Error, Result};
use conduit::{debug_info, error};
use ruma::{
api::client::{
error::ErrorKind,
@@ -18,9 +17,11 @@ use ruma::{
serde::Raw,
EventId, RoomId, UserId,
};
use service::Services;
use crate::{Ruma, RumaResponse};
use crate::{
service::{pdu::PduBuilder, server_is_ours},
services, Error, Result, Ruma, RumaResponse,
};
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`
///
@@ -31,13 +32,12 @@ use crate::{Ruma, RumaResponse};
/// allowed
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
pub(crate) async fn send_state_event_for_key_route(
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
body: Ruma<send_state_event::v3::Request>,
) -> Result<send_state_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
Ok(send_state_event::v3::Response {
event_id: send_state_event_for_key_helper(
&services,
sender_user,
&body.room_id,
&body.event_type,
@@ -58,11 +58,9 @@ pub(crate) async fn send_state_event_for_key_route(
/// allowed
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
pub(crate) async fn send_state_event_for_empty_key_route(
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
body: Ruma<send_state_event::v3::Request>,
) -> Result<RumaResponse<send_state_event::v3::Response>> {
send_state_event_for_key_route(State(services), body)
.await
.map(RumaResponse)
send_state_event_for_key_route(body).await.map(RumaResponse)
}
/// # `GET /_matrix/client/v3/rooms/{roomid}/state`
@@ -72,11 +70,11 @@ pub(crate) async fn send_state_event_for_empty_key_route(
/// - If not joined: Only works if current room history visibility is world
/// readable
pub(crate) async fn get_state_events_route(
State(services): State<crate::State>, body: Ruma<get_state_events::v3::Request>,
body: Ruma<get_state_events::v3::Request>,
) -> Result<get_state_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services
if !services()
.rooms
.state_accessor
.user_can_see_state_events(sender_user, &body.room_id)?
@@ -88,7 +86,7 @@ pub(crate) async fn get_state_events_route(
}
Ok(get_state_events::v3::Response {
room_state: services
room_state: services()
.rooms
.state_accessor
.room_state_full(&body.room_id)
@@ -108,11 +106,11 @@ pub(crate) async fn get_state_events_route(
/// - If not joined: Only works if current room history visibility is world
/// readable
pub(crate) async fn get_state_events_for_key_route(
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
body: Ruma<get_state_events_for_key::v3::Request>,
) -> Result<get_state_events_for_key::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services
if !services()
.rooms
.state_accessor
.user_can_see_state_events(sender_user, &body.room_id)?
@@ -123,7 +121,7 @@ pub(crate) async fn get_state_events_for_key_route(
));
}
let event = services
let event = services()
.rooms
.state_accessor
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
@@ -163,20 +161,17 @@ pub(crate) async fn get_state_events_for_key_route(
/// - If not joined: Only works if current room history visibility is world
/// readable
pub(crate) async fn get_state_events_for_empty_key_route(
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
body: Ruma<get_state_events_for_key::v3::Request>,
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
get_state_events_for_key_route(State(services), body)
.await
.map(RumaResponse)
get_state_events_for_key_route(body).await.map(RumaResponse)
}
async fn send_state_event_for_key_helper(
services: &Services, sender: &UserId, room_id: &RoomId, event_type: &StateEventType,
json: &Raw<AnyStateEventContent>, state_key: String,
sender: &UserId, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>, state_key: String,
) -> Result<Arc<EventId>> {
allowed_to_send_state_event(services, room_id, event_type, json).await?;
let state_lock = services.rooms.state.mutex.lock(room_id).await;
let event_id = services
allowed_to_send_state_event(room_id, event_type, json).await?;
let state_lock = services().rooms.state.mutex.lock(room_id).await;
let event_id = services()
.rooms
.timeline
.build_and_append_pdu(
@@ -197,18 +192,18 @@ async fn send_state_event_for_key_helper(
}
async fn allowed_to_send_state_event(
services: &Services, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
) -> Result<()> {
match event_type {
// Forbid m.room.encryption if encryption is disabled
StateEventType::RoomEncryption => {
if !services.globals.allow_encryption() {
if !services().globals.allow_encryption() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
}
},
// admin room is a sensitive room, it should not ever be made public
StateEventType::RoomJoinRules => {
if let Some(admin_room_id) = services.admin.get_admin_room()? {
if let Some(admin_room_id) = service::admin::Service::get_admin_room()? {
if admin_room_id == room_id {
if let Ok(join_rule) = serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get()) {
if join_rule.join_rule == JoinRule::Public {
@@ -223,7 +218,7 @@ async fn allowed_to_send_state_event(
},
// admin room is a sensitive room, it should not ever be made world readable
StateEventType::RoomHistoryVisibility => {
if let Some(admin_room_id) = services.admin.get_admin_room()? {
if let Some(admin_room_id) = service::admin::Service::get_admin_room()? {
if admin_room_id == room_id {
if let Ok(visibility_content) =
serde_json::from_str::<RoomHistoryVisibilityEventContent>(json.json().get())
@@ -248,8 +243,8 @@ async fn allowed_to_send_state_event(
}
for alias in aliases {
if !services.globals.server_is_ours(alias.server_name())
|| services
if !server_is_ours(alias.server_name())
|| services()
.rooms
.alias
.resolve_local_alias(&alias)?
+139 -149
View File
@@ -5,7 +5,6 @@ use std::{
time::Duration,
};
use axum::extract::State;
use conduit::{
error,
utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated},
@@ -18,7 +17,7 @@ use ruma::{
self,
v3::{
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence,
RoomAccountData, RoomSummary, Rooms, State as RoomState, Timeline, ToDevice,
RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice,
},
v4::SlidingOp,
DeviceLists, UnreadNotificationsCount,
@@ -35,10 +34,7 @@ use ruma::{
};
use tracing::{Instrument as _, Span};
use crate::{
service::{pdu::EventHash, Services},
utils, Error, PduEvent, Result, Ruma, RumaResponse,
};
use crate::{service::pdu::EventHash, services, utils, Error, PduEvent, Result, Ruma, RumaResponse};
/// # `GET /_matrix/client/r0/sync`
///
@@ -76,23 +72,23 @@ use crate::{
/// - If the user left after `since`: `prev_batch` token, empty state (TODO:
/// subset of the state at the point of the leave)
pub(crate) async fn sync_events_route(
State(services): State<crate::State>, body: Ruma<sync_events::v3::Request>,
body: Ruma<sync_events::v3::Request>,
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
let sender_user = body.sender_user.expect("user is authenticated");
let sender_device = body.sender_device.expect("user is authenticated");
let body = body.body;
// Presence update
if services.globals.allow_local_presence() {
services
if services().globals.allow_local_presence() {
services()
.presence
.ping_presence(&sender_user, &body.set_presence)?;
}
// Setup watchers, so if there's no response, we can wait for them
let watcher = services.globals.watch(&sender_user, &sender_device);
let watcher = services().globals.watch(&sender_user, &sender_device);
let next_batch = services.globals.current_count()?;
let next_batch = services().globals.current_count()?;
let next_batchcount = PduCount::Normal(next_batch);
let next_batch_string = next_batch.to_string();
@@ -100,7 +96,7 @@ pub(crate) async fn sync_events_route(
let filter = match body.filter {
None => FilterDefinition::default(),
Some(Filter::FilterDefinition(filter)) => filter,
Some(Filter::FilterId(filter_id)) => services
Some(Filter::FilterId(filter_id)) => services()
.users
.get_filter(&sender_user, &filter_id)?
.unwrap_or_default(),
@@ -130,29 +126,28 @@ pub(crate) async fn sync_events_route(
// Look for device list updates of this account
device_list_updates.extend(
services
services()
.users
.keys_changed(sender_user.as_ref(), since, None)
.filter_map(Result::ok),
);
if services.globals.allow_local_presence() {
process_presence_updates(&services, &mut presence_updates, since, &sender_user).await?;
if services().globals.allow_local_presence() {
process_presence_updates(&mut presence_updates, since, &sender_user).await?;
}
let all_joined_rooms = services
let all_joined_rooms = services()
.rooms
.state_cache
.rooms_joined(&sender_user)
.collect::<Vec<_>>();
// Coalesce database writes for the remainder of this scope.
let _cork = services.db.cork_and_flush();
let _cork = services().db.cork_and_flush();
for room_id in all_joined_rooms {
let room_id = room_id?;
if let Ok(joined_room) = load_joined_room(
&services,
&sender_user,
&sender_device,
&room_id,
@@ -175,14 +170,13 @@ pub(crate) async fn sync_events_route(
}
let mut left_rooms = BTreeMap::new();
let all_left_rooms: Vec<_> = services
let all_left_rooms: Vec<_> = services()
.rooms
.state_cache
.rooms_left(&sender_user)
.collect();
for result in all_left_rooms {
handle_left_room(
&services,
since,
&result?.0,
&sender_user,
@@ -196,7 +190,7 @@ pub(crate) async fn sync_events_route(
}
let mut invited_rooms = BTreeMap::new();
let all_invited_rooms: Vec<_> = services
let all_invited_rooms: Vec<_> = services()
.rooms
.state_cache
.rooms_invited(&sender_user)
@@ -205,10 +199,10 @@ pub(crate) async fn sync_events_route(
let (room_id, invite_state_events) = result?;
// Get and drop the lock to wait for remaining operations to finish
let insert_lock = services.rooms.timeline.mutex_insert.lock(&room_id).await;
let insert_lock = services().rooms.timeline.mutex_insert.lock(&room_id).await;
drop(insert_lock);
let invite_count = services
let invite_count = services()
.rooms
.state_cache
.get_invite_count(&room_id, &sender_user)?;
@@ -229,14 +223,14 @@ pub(crate) async fn sync_events_route(
}
for user_id in left_encrypted_users {
let dont_share_encrypted_room = services
let dont_share_encrypted_room = services()
.rooms
.user
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
.filter_map(Result::ok)
.filter_map(|other_room_id| {
Some(
services
services()
.rooms
.state_accessor
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
@@ -253,7 +247,7 @@ pub(crate) async fn sync_events_route(
}
// Remove all to-device events the device received *last time*
services
services()
.users
.remove_to_device_events(&sender_user, &sender_device, since)?;
@@ -272,7 +266,7 @@ pub(crate) async fn sync_events_route(
.collect(),
},
account_data: GlobalAccountData {
events: services
events: services()
.account_data
.changes_since(None, &sender_user, since)?
.into_iter()
@@ -287,11 +281,11 @@ pub(crate) async fn sync_events_route(
changed: device_list_updates.into_iter().collect(),
left: device_list_left.into_iter().collect(),
},
device_one_time_keys_count: services
device_one_time_keys_count: services()
.users
.count_one_time_keys(&sender_user, &sender_device)?,
to_device: ToDevice {
events: services
events: services()
.users
.get_to_device_events(&sender_user, &sender_device)?,
},
@@ -317,18 +311,16 @@ pub(crate) async fn sync_events_route(
Ok(response)
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id), name = "left_room")]
async fn handle_left_room(
services: &Services, since: u64, room_id: &RoomId, sender_user: &UserId,
left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>, next_batch_string: &str, full_state: bool,
lazy_load_enabled: bool,
since: u64, room_id: &RoomId, sender_user: &UserId, left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>,
next_batch_string: &str, full_state: bool, lazy_load_enabled: bool,
) -> Result<()> {
// Get and drop the lock to wait for remaining operations to finish
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
drop(insert_lock);
let left_count = services
let left_count = services()
.rooms
.state_cache
.get_left_count(room_id, sender_user)?;
@@ -338,11 +330,11 @@ async fn handle_left_room(
return Ok(());
}
if !services.rooms.metadata.exists(room_id)? {
if !services().rooms.metadata.exists(room_id)? {
// This is just a rejected invite, not a room we know
// Insert a leave event anyways
let event = PduEvent {
event_id: EventId::new(services.globals.server_name()).into(),
event_id: EventId::new(services().globals.server_name()).into(),
sender: sender_user.to_owned(),
origin: None,
origin_server_ts: utils::millis_since_unix_epoch()
@@ -375,7 +367,7 @@ async fn handle_left_room(
prev_batch: Some(next_batch_string.to_owned()),
events: Vec::new(),
},
state: RoomState {
state: State {
events: vec![event.to_sync_state_event()],
},
},
@@ -385,27 +377,27 @@ async fn handle_left_room(
let mut left_state_events = Vec::new();
let since_shortstatehash = services
let since_shortstatehash = services()
.rooms
.user
.get_token_shortstatehash(room_id, since)?;
let since_state_ids = match since_shortstatehash {
Some(s) => services.rooms.state_accessor.state_full_ids(s).await?,
Some(s) => services().rooms.state_accessor.state_full_ids(s).await?,
None => HashMap::new(),
};
let Some(left_event_id) =
services
.rooms
.state_accessor
.room_state_get_id(room_id, &StateEventType::RoomMember, sender_user.as_str())?
let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id(
room_id,
&StateEventType::RoomMember,
sender_user.as_str(),
)?
else {
error!("Left room but no left state event");
return Ok(());
};
let Some(left_shortstatehash) = services
let Some(left_shortstatehash) = services()
.rooms
.state_accessor
.pdu_shortstatehash(&left_event_id)?
@@ -414,13 +406,13 @@ async fn handle_left_room(
return Ok(());
};
let mut left_state_ids = services
let mut left_state_ids = services()
.rooms
.state_accessor
.state_full_ids(left_shortstatehash)
.await?;
let leave_shortstatekey = services
let leave_shortstatekey = services()
.rooms
.short
.get_or_create_shortstatekey(&StateEventType::RoomMember, sender_user.as_str())?;
@@ -430,7 +422,7 @@ async fn handle_left_room(
let mut i: u8 = 0;
for (key, id) in left_state_ids {
if full_state || since_state_ids.get(&key) != Some(&id) {
let (event_type, state_key) = services.rooms.short.get_statekey_from_short(key)?;
let (event_type, state_key) = services().rooms.short.get_statekey_from_short(key)?;
if !lazy_load_enabled
|| event_type != StateEventType::RoomMember
@@ -438,7 +430,7 @@ async fn handle_left_room(
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|| (cfg!(feature = "element_hacks") && *sender_user == state_key)
{
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
@@ -464,7 +456,7 @@ async fn handle_left_room(
prev_batch: Some(next_batch_string.to_owned()),
events: Vec::new(),
},
state: RoomState {
state: State {
events: left_state_events,
},
},
@@ -473,11 +465,13 @@ async fn handle_left_room(
}
async fn process_presence_updates(
services: &Services, presence_updates: &mut HashMap<OwnedUserId, PresenceEvent>, since: u64, syncing_user: &UserId,
presence_updates: &mut HashMap<OwnedUserId, PresenceEvent>, since: u64, syncing_user: &UserId,
) -> Result<()> {
use crate::service::presence::Presence;
// Take presence updates
for (user_id, _, presence_bytes) in services.presence.presence_since(since) {
if !services
for (user_id, _, presence_bytes) in services().presence.presence_since(since) {
if !services()
.rooms
.state_cache
.user_sees_user(syncing_user, &user_id)?
@@ -485,9 +479,7 @@ async fn process_presence_updates(
continue;
}
let presence_event = services
.presence
.from_json_bytes_to_event(&presence_bytes, &user_id)?;
let presence_event = Presence::from_json_bytes_to_event(&presence_bytes, &user_id)?;
match presence_updates.entry(user_id) {
Entry::Vacant(slot) => {
slot.insert(presence_event);
@@ -521,20 +513,19 @@ async fn process_presence_updates(
#[allow(clippy::too_many_arguments)]
async fn load_joined_room(
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, since: u64,
sincecount: PduCount, next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool,
lazy_load_send_redundant: bool, full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>,
left_encrypted_users: &mut HashSet<OwnedUserId>,
sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, since: u64, sincecount: PduCount,
next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool, lazy_load_send_redundant: bool,
full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>, left_encrypted_users: &mut HashSet<OwnedUserId>,
) -> Result<JoinedRoom> {
// Get and drop the lock to wait for remaining operations to finish
// This will make sure the we have all events until next_batch
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
drop(insert_lock);
let (timeline_pdus, limited) = load_timeline(services, sender_user, room_id, sincecount, 10)?;
let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?;
let send_notification_counts = !timeline_pdus.is_empty()
|| services
|| services()
.rooms
.user
.last_notification_read(sender_user, room_id)?
@@ -545,7 +536,7 @@ async fn load_joined_room(
timeline_users.insert(event.sender.as_str().to_owned());
}
services
services()
.rooms
.lazy_loading
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount)
@@ -553,11 +544,11 @@ async fn load_joined_room(
// Database queries:
let Some(current_shortstatehash) = services.rooms.state.get_room_shortstatehash(room_id)? else {
let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
return Err!(Database(error!("Room {room_id} has no state")));
};
let since_shortstatehash = services
let since_shortstatehash = services()
.rooms
.user
.get_token_shortstatehash(room_id, since)?;
@@ -569,12 +560,12 @@ async fn load_joined_room(
} else {
// Calculates joined_member_count, invited_member_count and heroes
let calculate_counts = || {
let joined_member_count = services
let joined_member_count = services()
.rooms
.state_cache
.room_joined_count(room_id)?
.unwrap_or(0);
let invited_member_count = services
let invited_member_count = services()
.rooms
.state_cache
.room_invited_count(room_id)?
@@ -587,7 +578,7 @@ async fn load_joined_room(
// Go through all PDUs and for each member event, check if the user is still
// joined or invited until we have 5 or we reach the end
for hero in services
for hero in services()
.rooms
.timeline
.all_pdus(sender_user, room_id)?
@@ -603,8 +594,8 @@ async fn load_joined_room(
// The membership was and still is invite or join
if matches!(content.membership, MembershipState::Join | MembershipState::Invite)
&& (services.rooms.state_cache.is_joined(&user_id, room_id)?
|| services.rooms.state_cache.is_invited(&user_id, room_id)?)
&& (services().rooms.state_cache.is_joined(&user_id, room_id)?
|| services().rooms.state_cache.is_invited(&user_id, room_id)?)
{
Ok::<_, Error>(Some(user_id))
} else {
@@ -631,7 +622,7 @@ async fn load_joined_room(
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
.and_then(|shortstatehash| {
services
services()
.rooms
.state_accessor
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
@@ -652,7 +643,7 @@ async fn load_joined_room(
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?;
let current_state_ids = services
let current_state_ids = services()
.rooms
.state_accessor
.state_full_ids(current_shortstatehash)
@@ -663,13 +654,13 @@ async fn load_joined_room(
let mut i: u8 = 0;
for (shortstatekey, id) in current_state_ids {
let (event_type, state_key) = services
let (event_type, state_key) = services()
.rooms
.short
.get_statekey_from_short(shortstatekey)?;
if event_type != StateEventType::RoomMember {
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
@@ -685,7 +676,7 @@ async fn load_joined_room(
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|| (cfg!(feature = "element_hacks") && *sender_user == state_key)
{
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
@@ -704,14 +695,14 @@ async fn load_joined_room(
}
// Reset lazy loading because this is an initial sync
services
services()
.rooms
.lazy_loading
.lazy_load_reset(sender_user, sender_device, room_id)?;
// The state_events above should contain all timeline_users, let's mark them as
// lazy loaded.
services
services()
.rooms
.lazy_loading
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
@@ -725,12 +716,12 @@ async fn load_joined_room(
let mut delta_state_events = Vec::new();
if since_shortstatehash != current_shortstatehash {
let current_state_ids = services
let current_state_ids = services()
.rooms
.state_accessor
.state_full_ids(current_shortstatehash)
.await?;
let since_state_ids = services
let since_state_ids = services()
.rooms
.state_accessor
.state_full_ids(since_shortstatehash)
@@ -738,7 +729,7 @@ async fn load_joined_room(
for (key, id) in current_state_ids {
if full_state || since_state_ids.get(&key) != Some(&id) {
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
@@ -749,13 +740,13 @@ async fn load_joined_room(
}
}
let encrypted_room = services
let encrypted_room = services()
.rooms
.state_accessor
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
.is_some();
let since_encryption = services.rooms.state_accessor.state_get(
let since_encryption = services().rooms.state_accessor.state_get(
since_shortstatehash,
&StateEventType::RoomEncryption,
"",
@@ -790,7 +781,7 @@ async fn load_joined_room(
match new_membership {
MembershipState::Join => {
// A new user joined an encrypted room
if !share_encrypted_room(services, sender_user, &user_id, room_id)? {
if !share_encrypted_room(sender_user, &user_id, room_id)? {
device_list_updates.insert(user_id);
}
},
@@ -807,7 +798,7 @@ async fn load_joined_room(
if joined_since_last_sync && encrypted_room || new_encrypted_room {
// If the user is in a new encrypted room, give them all joined users
device_list_updates.extend(
services
services()
.rooms
.state_cache
.room_members(room_id)
@@ -819,7 +810,7 @@ async fn load_joined_room(
.filter(|user_id| {
// Only send keys if the sender doesn't share an encrypted room with the target
// already
!share_encrypted_room(services, sender_user, user_id, room_id).unwrap_or(false)
!share_encrypted_room(sender_user, user_id, room_id).unwrap_or(false)
}),
);
}
@@ -857,14 +848,14 @@ async fn load_joined_room(
continue;
}
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
sender_user,
sender_device,
room_id,
&event.sender,
)? || lazy_load_send_redundant
{
if let Some(member_event) = services.rooms.state_accessor.room_state_get(
if let Some(member_event) = services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomMember,
event.sender.as_str(),
@@ -875,7 +866,7 @@ async fn load_joined_room(
}
}
services
services()
.rooms
.lazy_loading
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
@@ -893,7 +884,7 @@ async fn load_joined_room(
// Look for device list updates in this room
device_list_updates.extend(
services
services()
.users
.keys_changed(room_id.as_ref(), since, None)
.filter_map(Result::ok),
@@ -901,7 +892,7 @@ async fn load_joined_room(
let notification_count = if send_notification_counts {
Some(
services
services()
.rooms
.user
.notification_count(sender_user, room_id)?
@@ -914,7 +905,7 @@ async fn load_joined_room(
let highlight_count = if send_notification_counts {
Some(
services
services()
.rooms
.user
.highlight_count(sender_user, room_id)?
@@ -942,7 +933,7 @@ async fn load_joined_room(
.map(|(_, pdu)| pdu.to_sync_room_event())
.collect();
let mut edus: Vec<_> = services
let mut edus: Vec<_> = services()
.rooms
.read_receipt
.readreceipts_since(room_id, since)
@@ -950,10 +941,10 @@ async fn load_joined_room(
.map(|(_, _, v)| v)
.collect();
if services.rooms.typing.last_typing_update(room_id).await? > since {
if services().rooms.typing.last_typing_update(room_id).await? > since {
edus.push(
serde_json::from_str(
&serde_json::to_string(&services.rooms.typing.typings_all(room_id).await?)
&serde_json::to_string(&services().rooms.typing.typings_all(room_id).await?)
.expect("event is valid, we just created it"),
)
.expect("event is valid, we just created it"),
@@ -962,14 +953,14 @@ async fn load_joined_room(
// Save the state after this sync so we can send the correct state diff next
// sync
services
services()
.rooms
.user
.associate_token_shortstatehash(room_id, next_batch, current_shortstatehash)?;
Ok(JoinedRoom {
account_data: RoomAccountData {
events: services
events: services()
.account_data
.changes_since(Some(room_id), sender_user, since)?
.into_iter()
@@ -994,7 +985,7 @@ async fn load_joined_room(
prev_batch,
events: room_events,
},
state: RoomState {
state: State {
events: state_events
.iter()
.map(|pdu| pdu.to_sync_state_event())
@@ -1008,16 +999,16 @@ async fn load_joined_room(
}
fn load_timeline(
services: &Services, sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount, limit: u64,
sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount, limit: u64,
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
let timeline_pdus;
let limited = if services
let limited = if services()
.rooms
.timeline
.last_timeline_count(sender_user, room_id)?
> roomsincecount
{
let mut non_timeline_pdus = services
let mut non_timeline_pdus = services()
.rooms
.timeline
.pdus_until(sender_user, room_id, PduCount::max())?
@@ -1049,10 +1040,8 @@ fn load_timeline(
Ok((timeline_pdus, limited))
}
fn share_encrypted_room(
services: &Services, sender_user: &UserId, user_id: &UserId, ignore_room: &RoomId,
) -> Result<bool> {
Ok(services
fn share_encrypted_room(sender_user: &UserId, user_id: &UserId, ignore_room: &RoomId) -> Result<bool> {
Ok(services()
.rooms
.user
.get_shared_rooms(vec![sender_user.to_owned(), user_id.to_owned()])?
@@ -1060,7 +1049,7 @@ fn share_encrypted_room(
.filter(|room_id| room_id != ignore_room)
.filter_map(|other_room_id| {
Some(
services
services()
.rooms
.state_accessor
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
@@ -1075,15 +1064,15 @@ fn share_encrypted_room(
///
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
pub(crate) async fn sync_events_v4_route(
State(services): State<crate::State>, body: Ruma<sync_events::v4::Request>,
body: Ruma<sync_events::v4::Request>,
) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
let sender_user = body.sender_user.expect("user is authenticated");
let sender_device = body.sender_device.expect("user is authenticated");
let mut body = body.body;
// Setup watchers, so if there's no response, we can wait for them
let watcher = services.globals.watch(&sender_user, &sender_device);
let watcher = services().globals.watch(&sender_user, &sender_device);
let next_batch = services.globals.next_count()?;
let next_batch = services().globals.next_count()?;
let globalsince = body
.pos
@@ -1093,19 +1082,21 @@ pub(crate) async fn sync_events_v4_route(
if globalsince == 0 {
if let Some(conn_id) = &body.conn_id {
services
.users
.forget_sync_request_connection(sender_user.clone(), sender_device.clone(), conn_id.clone());
services().users.forget_sync_request_connection(
sender_user.clone(),
sender_device.clone(),
conn_id.clone(),
);
}
}
// Get sticky parameters from cache
let known_rooms =
services
services()
.users
.update_sync_request_with_cache(sender_user.clone(), sender_device.clone(), &mut body);
let all_joined_rooms = services
let all_joined_rooms = services()
.rooms
.state_cache
.rooms_joined(&sender_user)
@@ -1113,7 +1104,7 @@ pub(crate) async fn sync_events_v4_route(
.collect::<Vec<_>>();
if body.extensions.to_device.enabled.unwrap_or(false) {
services
services()
.users
.remove_to_device_events(&sender_user, &sender_device, globalsince)?;
}
@@ -1125,26 +1116,26 @@ pub(crate) async fn sync_events_v4_route(
if body.extensions.e2ee.enabled.unwrap_or(false) {
// Look for device list updates of this account
device_list_changes.extend(
services
services()
.users
.keys_changed(sender_user.as_ref(), globalsince, None)
.filter_map(Result::ok),
);
for room_id in &all_joined_rooms {
let Some(current_shortstatehash) = services.rooms.state.get_room_shortstatehash(room_id)? else {
let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
error!("Room {} has no state", room_id);
continue;
};
let since_shortstatehash = services
let since_shortstatehash = services()
.rooms
.user
.get_token_shortstatehash(room_id, globalsince)?;
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
.and_then(|shortstatehash| {
services
services()
.rooms
.state_accessor
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
@@ -1157,7 +1148,7 @@ pub(crate) async fn sync_events_v4_route(
.ok()
});
let encrypted_room = services
let encrypted_room = services()
.rooms
.state_accessor
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
@@ -1169,7 +1160,7 @@ pub(crate) async fn sync_events_v4_route(
continue;
}
let since_encryption = services.rooms.state_accessor.state_get(
let since_encryption = services().rooms.state_accessor.state_get(
since_shortstatehash,
&StateEventType::RoomEncryption,
"",
@@ -1180,12 +1171,12 @@ pub(crate) async fn sync_events_v4_route(
let new_encrypted_room = encrypted_room && since_encryption.is_none();
if encrypted_room {
let current_state_ids = services
let current_state_ids = services()
.rooms
.state_accessor
.state_full_ids(current_shortstatehash)
.await?;
let since_state_ids = services
let since_state_ids = services()
.rooms
.state_accessor
.state_full_ids(since_shortstatehash)
@@ -1193,7 +1184,7 @@ pub(crate) async fn sync_events_v4_route(
for (key, id) in current_state_ids {
if since_state_ids.get(&key) != Some(&id) {
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
@@ -1214,7 +1205,7 @@ pub(crate) async fn sync_events_v4_route(
match new_membership {
MembershipState::Join => {
// A new user joined an encrypted room
if !share_encrypted_room(&services, &sender_user, &user_id, room_id)? {
if !share_encrypted_room(&sender_user, &user_id, room_id)? {
device_list_changes.insert(user_id);
}
},
@@ -1231,7 +1222,7 @@ pub(crate) async fn sync_events_v4_route(
if joined_since_last_sync || new_encrypted_room {
// If the user is in a new encrypted room, give them all joined users
device_list_changes.extend(
services
services()
.rooms
.state_cache
.room_members(room_id)
@@ -1243,7 +1234,7 @@ pub(crate) async fn sync_events_v4_route(
.filter(|user_id| {
// Only send keys if the sender doesn't share an encrypted room with the target
// already
!share_encrypted_room(&services, &sender_user, user_id, room_id).unwrap_or(false)
!share_encrypted_room(&sender_user, user_id, room_id).unwrap_or(false)
}),
);
}
@@ -1251,21 +1242,21 @@ pub(crate) async fn sync_events_v4_route(
}
// Look for device list updates in this room
device_list_changes.extend(
services
services()
.users
.keys_changed(room_id.as_ref(), globalsince, None)
.filter_map(Result::ok),
);
}
for user_id in left_encrypted_users {
let dont_share_encrypted_room = services
let dont_share_encrypted_room = services()
.rooms
.user
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
.filter_map(Result::ok)
.filter_map(|other_room_id| {
Some(
services
services()
.rooms
.state_accessor
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
@@ -1345,7 +1336,7 @@ pub(crate) async fn sync_events_v4_route(
);
if let Some(conn_id) = &body.conn_id {
services.users.update_sync_known_rooms(
services().users.update_sync_known_rooms(
sender_user.clone(),
sender_device.clone(),
conn_id.clone(),
@@ -1358,7 +1349,7 @@ pub(crate) async fn sync_events_v4_route(
let mut known_subscription_rooms = BTreeSet::new();
for (room_id, room) in &body.room_subscriptions {
if !services.rooms.metadata.exists(room_id)? {
if !services().rooms.metadata.exists(room_id)? {
continue;
}
let todo_room = todo_rooms
@@ -1384,7 +1375,7 @@ pub(crate) async fn sync_events_v4_route(
}
if let Some(conn_id) = &body.conn_id {
services.users.update_sync_known_rooms(
services().users.update_sync_known_rooms(
sender_user.clone(),
sender_device.clone(),
conn_id.clone(),
@@ -1395,7 +1386,7 @@ pub(crate) async fn sync_events_v4_route(
}
if let Some(conn_id) = &body.conn_id {
services.users.update_sync_subscriptions(
services().users.update_sync_subscriptions(
sender_user.clone(),
sender_device.clone(),
conn_id.clone(),
@@ -1407,8 +1398,7 @@ pub(crate) async fn sync_events_v4_route(
for (room_id, (required_state_request, timeline_limit, roomsince)) in &todo_rooms {
let roomsincecount = PduCount::Normal(*roomsince);
let (timeline_pdus, limited) =
load_timeline(&services, &sender_user, room_id, roomsincecount, *timeline_limit)?;
let (timeline_pdus, limited) = load_timeline(&sender_user, room_id, roomsincecount, *timeline_limit)?;
if roomsince != &0 && timeline_pdus.is_empty() {
continue;
@@ -1441,7 +1431,7 @@ pub(crate) async fn sync_events_v4_route(
let required_state = required_state_request
.iter()
.map(|state| {
services
services()
.rooms
.state_accessor
.room_state_get(room_id, &state.0, &state.1)
@@ -1452,7 +1442,7 @@ pub(crate) async fn sync_events_v4_route(
.collect();
// Heroes
let heroes = services
let heroes = services()
.rooms
.state_cache
.room_members(room_id)
@@ -1460,7 +1450,7 @@ pub(crate) async fn sync_events_v4_route(
.filter(|member| member != &sender_user)
.map(|member| {
Ok::<_, Error>(
services
services()
.rooms
.state_accessor
.get_member(room_id, &member)?
@@ -1501,11 +1491,11 @@ pub(crate) async fn sync_events_v4_route(
rooms.insert(
room_id.clone(),
sync_events::v4::SlidingSyncRoom {
name: services.rooms.state_accessor.get_name(room_id)?.or(name),
name: services().rooms.state_accessor.get_name(room_id)?.or(name),
avatar: if let Some(heroes_avatar) = heroes_avatar {
ruma::JsOption::Some(heroes_avatar)
} else {
match services.rooms.state_accessor.get_avatar(room_id)? {
match services().rooms.state_accessor.get_avatar(room_id)? {
ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
ruma::JsOption::Null => ruma::JsOption::Null,
ruma::JsOption::Undefined => ruma::JsOption::Undefined,
@@ -1516,7 +1506,7 @@ pub(crate) async fn sync_events_v4_route(
invite_state: None,
unread_notifications: UnreadNotificationsCount {
highlight_count: Some(
services
services()
.rooms
.user
.highlight_count(&sender_user, room_id)?
@@ -1524,7 +1514,7 @@ pub(crate) async fn sync_events_v4_route(
.expect("notification count can't go that high"),
),
notification_count: Some(
services
services()
.rooms
.user
.notification_count(&sender_user, room_id)?
@@ -1537,7 +1527,7 @@ pub(crate) async fn sync_events_v4_route(
prev_batch,
limited,
joined_count: Some(
services
services()
.rooms
.state_cache
.room_joined_count(room_id)?
@@ -1546,7 +1536,7 @@ pub(crate) async fn sync_events_v4_route(
.unwrap_or_else(|_| uint!(0)),
),
invited_count: Some(
services
services()
.rooms
.state_cache
.room_invited_count(room_id)?
@@ -1581,7 +1571,7 @@ pub(crate) async fn sync_events_v4_route(
extensions: sync_events::v4::Extensions {
to_device: if body.extensions.to_device.enabled.unwrap_or(false) {
Some(sync_events::v4::ToDevice {
events: services
events: services()
.users
.get_to_device_events(&sender_user, &sender_device)?,
next_batch: next_batch.to_string(),
@@ -1594,7 +1584,7 @@ pub(crate) async fn sync_events_v4_route(
changed: device_list_changes.into_iter().collect(),
left: device_list_left.into_iter().collect(),
},
device_one_time_keys_count: services
device_one_time_keys_count: services()
.users
.count_one_time_keys(&sender_user, &sender_device)?,
// Fallback keys are not yet supported
@@ -1602,7 +1592,7 @@ pub(crate) async fn sync_events_v4_route(
},
account_data: sync_events::v4::AccountData {
global: if body.extensions.account_data.enabled.unwrap_or(false) {
services
services()
.account_data
.changes_since(None, &sender_user, globalsince)?
.into_iter()
+9 -16
View File
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use axum::extract::State;
use ruma::{
api::client::tag::{create_tag, delete_tag, get_tags},
events::{
@@ -9,19 +8,17 @@ use ruma::{
},
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}`
///
/// Adds a tag to the room.
///
/// - Inserts the tag into the tag event of the room account data.
pub(crate) async fn update_tag_route(
State(services): State<crate::State>, body: Ruma<create_tag::v3::Request>,
) -> Result<create_tag::v3::Response> {
pub(crate) async fn update_tag_route(body: Ruma<create_tag::v3::Request>) -> Result<create_tag::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services
let event = services()
.account_data
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
@@ -41,7 +38,7 @@ pub(crate) async fn update_tag_route(
.tags
.insert(body.tag.clone().into(), body.tag_info.clone());
services.account_data.update(
services().account_data.update(
Some(&body.room_id),
sender_user,
RoomAccountDataEventType::Tag,
@@ -56,12 +53,10 @@ pub(crate) async fn update_tag_route(
/// Deletes a tag from the room.
///
/// - Removes the tag from the tag event of the room account data.
pub(crate) async fn delete_tag_route(
State(services): State<crate::State>, body: Ruma<delete_tag::v3::Request>,
) -> Result<delete_tag::v3::Response> {
pub(crate) async fn delete_tag_route(body: Ruma<delete_tag::v3::Request>) -> Result<delete_tag::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services
let event = services()
.account_data
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
@@ -78,7 +73,7 @@ pub(crate) async fn delete_tag_route(
tags_event.content.tags.remove(&body.tag.clone().into());
services.account_data.update(
services().account_data.update(
Some(&body.room_id),
sender_user,
RoomAccountDataEventType::Tag,
@@ -93,12 +88,10 @@ pub(crate) async fn delete_tag_route(
/// Returns tags on the room.
///
/// - Gets the tag event of the room account data.
pub(crate) async fn get_tags_route(
State(services): State<crate::State>, body: Ruma<get_tags::v3::Request>,
) -> Result<get_tags::v3::Response> {
pub(crate) async fn get_tags_route(body: Ruma<get_tags::v3::Request>) -> Result<get_tags::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services
let event = services()
.account_data
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
+4 -7
View File
@@ -1,15 +1,12 @@
use axum::extract::State;
use ruma::{
api::client::{error::ErrorKind, threads::get_threads},
uint,
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
pub(crate) async fn get_threads_route(
State(services): State<crate::State>, body: Ruma<get_threads::v1::Request>,
) -> Result<get_threads::v1::Response> {
pub(crate) async fn get_threads_route(body: Ruma<get_threads::v1::Request>) -> Result<get_threads::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
// Use limit or else 10, with maximum 100
@@ -27,14 +24,14 @@ pub(crate) async fn get_threads_route(
u64::MAX
};
let threads = services
let threads = services()
.rooms
.threads
.threads_until(sender_user, &body.room_id, from, &body.include)?
.take(limit)
.filter_map(Result::ok)
.filter(|(_, pdu)| {
services
services()
.rooms
.state_accessor
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
+10 -12
View File
@@ -1,7 +1,5 @@
use std::collections::BTreeMap;
use axum::extract::State;
use conduit::{Error, Result};
use ruma::{
api::{
client::{error::ErrorKind, to_device::send_event_to_device},
@@ -10,19 +8,19 @@ use ruma::{
to_device::DeviceIdOrAllDevices,
};
use crate::Ruma;
use crate::{services, user_is_local, Error, Result, Ruma};
/// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}`
///
/// Send a to-device event to a set of client devices.
pub(crate) async fn send_event_to_device_route(
State(services): State<crate::State>, body: Ruma<send_event_to_device::v3::Request>,
body: Ruma<send_event_to_device::v3::Request>,
) -> Result<send_event_to_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_deref();
// Check if this is a new transaction id
if services
if services()
.transaction_ids
.existing_txnid(sender_user, sender_device, &body.txn_id)?
.is_some()
@@ -32,14 +30,14 @@ pub(crate) async fn send_event_to_device_route(
for (target_user_id, map) in &body.messages {
for (target_device_id_maybe, event) in map {
if !services.globals.user_is_local(target_user_id) {
if !user_is_local(target_user_id) {
let mut map = BTreeMap::new();
map.insert(target_device_id_maybe.clone(), event.clone());
let mut messages = BTreeMap::new();
messages.insert(target_user_id.clone(), map);
let count = services.globals.next_count()?;
let count = services().globals.next_count()?;
services.sending.send_edu_server(
services().sending.send_edu_server(
target_user_id.server_name(),
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
sender: sender_user.clone(),
@@ -55,7 +53,7 @@ pub(crate) async fn send_event_to_device_route(
match target_device_id_maybe {
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
services.users.add_to_device_event(
services().users.add_to_device_event(
sender_user,
target_user_id,
target_device_id,
@@ -67,8 +65,8 @@ pub(crate) async fn send_event_to_device_route(
},
DeviceIdOrAllDevices::AllDevices => {
for target_device_id in services.users.all_device_ids(target_user_id) {
services.users.add_to_device_event(
for target_device_id in services().users.all_device_ids(target_user_id) {
services().users.add_to_device_event(
sender_user,
target_user_id,
&target_device_id?,
@@ -84,7 +82,7 @@ pub(crate) async fn send_event_to_device_route(
}
// Save transaction id with empty data
services
services()
.transaction_ids
.add_txnid(sender_user, sender_device, &body.txn_id, &[])?;
+7 -8
View File
@@ -1,19 +1,18 @@
use axum::extract::State;
use ruma::api::client::{error::ErrorKind, typing::create_typing_event};
use crate::{utils, Error, Result, Ruma};
use crate::{services, utils, Error, Result, Ruma};
/// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
///
/// Sets the typing state of the sender user.
pub(crate) async fn create_typing_event_route(
State(services): State<crate::State>, body: Ruma<create_typing_event::v3::Request>,
body: Ruma<create_typing_event::v3::Request>,
) -> Result<create_typing_event::v3::Response> {
use create_typing_event::v3::Typing;
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services
if !services()
.rooms
.state_cache
.is_joined(sender_user, &body.room_id)?
@@ -24,20 +23,20 @@ pub(crate) async fn create_typing_event_route(
if let Typing::Yes(duration) = body.state {
let duration = utils::clamp(
duration.as_millis().try_into().unwrap_or(u64::MAX),
services
services()
.globals
.config
.typing_client_timeout_min_s
.checked_mul(1000)
.unwrap(),
services
services()
.globals
.config
.typing_client_timeout_max_s
.checked_mul(1000)
.unwrap(),
);
services
services()
.rooms
.typing
.typing_add(
@@ -49,7 +48,7 @@ pub(crate) async fn create_typing_event_route(
)
.await?;
} else {
services
services()
.rooms
.typing
.typing_remove(sender_user, &body.room_id)
+26 -26
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduit::warn;
use ruma::{
@@ -7,7 +6,7 @@ use ruma::{
OwnedRoomId,
};
use crate::{Error, Result, Ruma, RumaResponse};
use crate::{services, Error, Result, Ruma, RumaResponse};
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
///
@@ -18,8 +17,7 @@ use crate::{Error, Result, Ruma, RumaResponse};
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
pub(crate) async fn get_mutual_rooms_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<mutual_rooms::unstable::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<mutual_rooms::unstable::Request>,
) -> Result<mutual_rooms::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@@ -30,14 +28,14 @@ pub(crate) async fn get_mutual_rooms_route(
));
}
if !services.users.exists(&body.user_id)? {
if !services().users.exists(&body.user_id)? {
return Ok(mutual_rooms::unstable::Response {
joined: vec![],
next_batch_token: None,
});
}
let mutual_rooms: Vec<OwnedRoomId> = services
let mutual_rooms: Vec<OwnedRoomId> = services()
.rooms
.user
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
@@ -60,10 +58,9 @@ pub(crate) async fn get_mutual_rooms_route(
///
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
pub(crate) async fn get_room_summary_legacy(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_summary::msc3266::Request>,
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
get_room_summary(State(services), InsecureClientIp(client), body)
get_room_summary(InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -77,19 +74,22 @@ pub(crate) async fn get_room_summary_legacy(
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
pub(crate) async fn get_room_summary(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_summary::msc3266::Request>,
) -> Result<get_summary::msc3266::Response> {
let sender_user = body.sender_user.as_ref();
let room_id = services.rooms.alias.resolve(&body.room_id_or_alias).await?;
let room_id = services()
.rooms
.alias
.resolve(&body.room_id_or_alias)
.await?;
if !services.rooms.metadata.exists(&room_id)? {
if !services().rooms.metadata.exists(&room_id)? {
return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server"));
}
if sender_user.is_none()
&& !services
&& !services()
.rooms
.state_accessor
.is_world_readable(&room_id)
@@ -103,25 +103,25 @@ pub(crate) async fn get_room_summary(
Ok(get_summary::msc3266::Response {
room_id: room_id.clone(),
canonical_alias: services
canonical_alias: services()
.rooms
.state_accessor
.get_canonical_alias(&room_id)
.unwrap_or(None),
avatar_url: services
avatar_url: services()
.rooms
.state_accessor
.get_avatar(&room_id)?
.into_option()
.unwrap_or_default()
.url,
guest_can_join: services.rooms.state_accessor.guest_can_join(&room_id)?,
name: services
guest_can_join: services().rooms.state_accessor.guest_can_join(&room_id)?,
name: services()
.rooms
.state_accessor
.get_name(&room_id)
.unwrap_or(None),
num_joined_members: services
num_joined_members: services()
.rooms
.state_cache
.room_joined_count(&room_id)
@@ -132,21 +132,21 @@ pub(crate) async fn get_room_summary(
})
.try_into()
.expect("user count should not be that big"),
topic: services
topic: services()
.rooms
.state_accessor
.get_room_topic(&room_id)
.unwrap_or(None),
world_readable: services
world_readable: services()
.rooms
.state_accessor
.is_world_readable(&room_id)
.unwrap_or(false),
join_rule: services.rooms.state_accessor.get_join_rule(&room_id)?.0,
room_type: services.rooms.state_accessor.get_room_type(&room_id)?,
room_version: Some(services.rooms.state.get_room_version(&room_id)?),
join_rule: services().rooms.state_accessor.get_join_rule(&room_id)?.0,
room_type: services().rooms.state_accessor.get_room_type(&room_id)?,
room_version: Some(services().rooms.state.get_room_version(&room_id)?),
membership: if let Some(sender_user) = sender_user {
services
services()
.rooms
.state_accessor
.get_member(&room_id, sender_user)?
@@ -154,7 +154,7 @@ pub(crate) async fn get_room_summary(
} else {
None
},
encryption: services
encryption: services()
.rooms
.state_accessor
.get_room_encryption(&room_id)
+14 -16
View File
@@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use axum::{extract::State, response::IntoResponse, Json};
use axum::{response::IntoResponse, Json};
use ruma::api::client::{
discovery::{
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
@@ -10,7 +10,7 @@ use ruma::api::client::{
error::ErrorKind,
};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `GET /_matrix/client/versions`
///
@@ -62,9 +62,9 @@ pub(crate) async fn get_supported_versions_route(
///
/// Returns the .well-known URL if it is configured, otherwise returns 404.
pub(crate) async fn well_known_client(
State(services): State<crate::State>, _body: Ruma<discover_homeserver::Request>,
_body: Ruma<discover_homeserver::Request>,
) -> Result<discover_homeserver::Response> {
let client_url = match services.globals.well_known_client() {
let client_url = match services().globals.well_known_client() {
Some(url) => url.to_string(),
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
};
@@ -84,24 +84,22 @@ pub(crate) async fn well_known_client(
/// # `GET /.well-known/matrix/support`
///
/// Server support contact and support page of a homeserver's domain.
pub(crate) async fn well_known_support(
State(services): State<crate::State>, _body: Ruma<discover_support::Request>,
) -> Result<discover_support::Response> {
let support_page = services
pub(crate) async fn well_known_support(_body: Ruma<discover_support::Request>) -> Result<discover_support::Response> {
let support_page = services()
.globals
.well_known_support_page()
.as_ref()
.map(ToString::to_string);
let role = services.globals.well_known_support_role().clone();
let role = services().globals.well_known_support_role().clone();
// support page or role must be either defined for this to be valid
if support_page.is_none() && role.is_none() {
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
}
let email_address = services.globals.well_known_support_email().clone();
let matrix_id = services.globals.well_known_support_mxid().clone();
let email_address = services().globals.well_known_support_email().clone();
let matrix_id = services().globals.well_known_support_mxid().clone();
// if a role is specified, an email address or matrix id is required
if role.is_some() && (email_address.is_none() && matrix_id.is_none()) {
@@ -136,10 +134,10 @@ pub(crate) async fn well_known_support(
///
/// Endpoint provided by sliding sync proxy used by some clients such as Element
/// Web as a non-standard health check.
pub(crate) async fn syncv3_client_server_json(State(services): State<crate::State>) -> Result<impl IntoResponse> {
let server_url = match services.globals.well_known_client() {
pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
let server_url = match services().globals.well_known_client() {
Some(url) => url.to_string(),
None => match services.globals.well_known_server() {
None => match services().globals.well_known_server() {
Some(url) => url.to_string(),
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
},
@@ -167,8 +165,8 @@ pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
/// conduwuit-specific API to return the amount of users registered on this
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
/// only includes active users (not deactivated, no guests, etc)
pub(crate) async fn conduwuit_local_user_count(State(services): State<crate::State>) -> Result<impl IntoResponse> {
let user_count = services.users.list_local_users()?.len();
pub(crate) async fn conduwuit_local_user_count() -> Result<impl IntoResponse> {
let user_count = services().users.list_local_users()?.len();
Ok(Json(serde_json::json!({
"count": user_count
+8 -11
View File
@@ -1,4 +1,3 @@
use axum::extract::State;
use ruma::{
api::client::user_directory::search_users,
events::{
@@ -7,7 +6,7 @@ use ruma::{
},
};
use crate::{Result, Ruma};
use crate::{services, Result, Ruma};
/// # `POST /_matrix/client/r0/user_directory/search`
///
@@ -15,20 +14,18 @@ use crate::{Result, Ruma};
///
/// - Hides any local users that aren't in any public rooms (i.e. those that
/// have the join rule set to public) and don't share a room with the sender
pub(crate) async fn search_users_route(
State(services): State<crate::State>, body: Ruma<search_users::v3::Request>,
) -> Result<search_users::v3::Response> {
pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) -> Result<search_users::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let limit = usize::try_from(body.limit).unwrap_or(10); // default limit is 10
let mut users = services.users.iter().filter_map(|user_id| {
let mut users = services().users.iter().filter_map(|user_id| {
// Filter out buggy users (they should not exist, but you never know...)
let user_id = user_id.ok()?;
let user = search_users::v3::User {
user_id: user_id.clone(),
display_name: services.users.displayname(&user_id).ok()?,
avatar_url: services.users.avatar_url(&user_id).ok()?,
display_name: services().users.displayname(&user_id).ok()?,
avatar_url: services().users.avatar_url(&user_id).ok()?,
};
let user_id_matches = user
@@ -53,13 +50,13 @@ pub(crate) async fn search_users_route(
// It's a matching user, but is the sender allowed to see them?
let mut user_visible = false;
let user_is_in_public_rooms = services
let user_is_in_public_rooms = services()
.rooms
.state_cache
.rooms_joined(&user_id)
.filter_map(Result::ok)
.any(|room| {
services
services()
.rooms
.state_accessor
.room_state_get(&room, &StateEventType::RoomJoinRules, "")
@@ -74,7 +71,7 @@ pub(crate) async fn search_users_route(
if user_is_in_public_rooms {
user_visible = true;
} else {
let user_is_in_shared_rooms = services
let user_is_in_shared_rooms = services()
.rooms
.user
.get_shared_rooms(vec![sender_user.clone(), user_id])
+9 -10
View File
@@ -1,13 +1,12 @@
use std::time::{Duration, SystemTime};
use axum::extract::State;
use base64::{engine::general_purpose, Engine as _};
use conduit::utils;
use hmac::{Hmac, Mac};
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch, UserId};
use sha1::Sha1;
use crate::{Result, Ruma};
use crate::{services, Result, Ruma};
const RANDOM_USER_ID_LENGTH: usize = 10;
@@ -17,14 +16,14 @@ type HmacSha1 = Hmac<Sha1>;
///
/// TODO: Returns information about the recommended turn server.
pub(crate) async fn turn_server_route(
State(services): State<crate::State>, body: Ruma<get_turn_server_info::v3::Request>,
body: Ruma<get_turn_server_info::v3::Request>,
) -> Result<get_turn_server_info::v3::Response> {
let turn_secret = services.globals.turn_secret().clone();
let turn_secret = services().globals.turn_secret().clone();
let (username, password) = if !turn_secret.is_empty() {
let expiry = SecondsSinceUnixEpoch::from_system_time(
SystemTime::now()
.checked_add(Duration::from_secs(services.globals.turn_ttl()))
.checked_add(Duration::from_secs(services().globals.turn_ttl()))
.expect("TURN TTL should not get this high"),
)
.expect("time is valid");
@@ -32,7 +31,7 @@ pub(crate) async fn turn_server_route(
let user = body.sender_user.unwrap_or_else(|| {
UserId::parse_with_server_name(
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
&services.globals.config.server_name,
&services().globals.config.server_name,
)
.unwrap()
});
@@ -47,15 +46,15 @@ pub(crate) async fn turn_server_route(
(username, password)
} else {
(
services.globals.turn_username().clone(),
services.globals.turn_password().clone(),
services().globals.turn_username().clone(),
services().globals.turn_password().clone(),
)
};
Ok(get_turn_server_info::v3::Response {
username,
password,
uris: services.globals.turn_uris().to_vec(),
ttl: Duration::from_secs(services.globals.turn_ttl()),
uris: services().globals.turn_uris().to_vec(),
ttl: Duration::from_secs(services().globals.turn_ttl()),
})
}
+5 -7
View File
@@ -1,17 +1,15 @@
#![recursion_limit = "192"]
pub mod client;
pub mod router;
mod router;
pub mod routes;
pub mod server;
extern crate conduit_core as conduit;
extern crate conduit_service as service;
pub(crate) use conduit::{debug_info, pdu::PduEvent, utils, Error, Result};
pub(crate) use service::services;
pub(crate) use conduit::{debug_info, debug_warn, utils, Error, Result};
pub(crate) use service::{pdu::PduEvent, services, user_is_local};
pub use crate::router::State;
pub(crate) use crate::router::{Ruma, RumaResponse};
pub(crate) use self::router::{Ruma, RumaResponse};
conduit::mod_ctor! {}
conduit::mod_dtor! {}
+17 -19
View File
@@ -6,17 +6,17 @@ use axum_extra::{
typed_header::TypedHeaderRejectionReason,
TypedHeader,
};
use conduit::{warn, Err, Error, Result};
use conduit::Err;
use http::uri::PathAndQuery;
use ruma::{
api::{client::error::ErrorKind, AuthScheme, Metadata},
server_util::authorization::XMatrix,
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
};
use service::Services;
use tracing::warn;
use super::request::Request;
use crate::service::appservice::RegistrationInfo;
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
enum Token {
Appservice(Box<RegistrationInfo>),
@@ -33,7 +33,7 @@ pub(super) struct Auth {
}
pub(super) async fn auth(
services: &Services, request: &mut Request, json_body: &Option<CanonicalJsonValue>, metadata: &Metadata,
request: &mut Request, json_body: &Option<CanonicalJsonValue>, metadata: &Metadata,
) -> Result<Auth> {
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
let token = match &bearer {
@@ -42,9 +42,9 @@ pub(super) async fn auth(
};
let token = if let Some(token) = token {
if let Some(reg_info) = services.appservice.find_from_token(token).await {
if let Some(reg_info) = services().appservice.find_from_token(token).await {
Token::Appservice(Box::new(reg_info))
} else if let Some((user_id, device_id)) = services.users.find_from_token(token)? {
} else if let Some((user_id, device_id)) = services().users.find_from_token(token)? {
Token::User((user_id, OwnedDeviceId::from(device_id)))
} else {
Token::Invalid
@@ -57,7 +57,7 @@ pub(super) async fn auth(
match request.parts.uri.path() {
// TODO: can we check this better?
"/_matrix/client/v3/publicRooms" | "/_matrix/client/r0/publicRooms" => {
if !services
if !services()
.globals
.config
.allow_public_room_directory_without_auth
@@ -98,7 +98,7 @@ pub(super) async fn auth(
))
}
},
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(services, request, info)?),
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(request, info)?),
(AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
Ok(Auth {
origin: None,
@@ -110,7 +110,7 @@ pub(super) async fn auth(
(AuthScheme::AccessToken, Token::None) => match request.parts.uri.path() {
// TODO: can we check this better?
"/_matrix/client/v3/voip/turnServer" | "/_matrix/client/r0/voip/turnServer" => {
if services.globals.config.turn_allow_guests {
if services().globals.config.turn_allow_guests {
Ok(Auth {
origin: None,
sender_user: None,
@@ -132,7 +132,7 @@ pub(super) async fn auth(
sender_device: Some(device_id),
appservice_info: None,
}),
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(services, request, json_body).await?),
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(request, json_body).await?),
(AuthScheme::None | AuthScheme::AppserviceToken | AuthScheme::AccessTokenOptional, Token::None) => Ok(Auth {
sender_user: None,
sender_device: None,
@@ -150,7 +150,7 @@ pub(super) async fn auth(
}
}
fn auth_appservice(services: &Services, request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
let user_id = request
.query
.user_id
@@ -159,7 +159,7 @@ fn auth_appservice(services: &Services, request: &Request, info: Box<Registratio
|| {
UserId::parse_with_server_name(
info.registration.sender_localpart.as_str(),
services.globals.server_name(),
services().globals.server_name(),
)
},
UserId::parse,
@@ -170,7 +170,7 @@ fn auth_appservice(services: &Services, request: &Request, info: Box<Registratio
return Err(Error::BadRequest(ErrorKind::Exclusive, "User is not in namespace."));
}
if !services.users.exists(&user_id)? {
if !services().users.exists(&user_id)? {
return Err(Error::BadRequest(ErrorKind::forbidden(), "User does not exist."));
}
@@ -182,10 +182,8 @@ fn auth_appservice(services: &Services, request: &Request, info: Box<Registratio
})
}
async fn auth_server(
services: &Services, request: &mut Request, json_body: &Option<CanonicalJsonValue>,
) -> Result<Auth> {
if !services.globals.allow_federation() {
async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValue>) -> Result<Auth> {
if !services().globals.allow_federation() {
return Err!(Config("allow_federation", "Federation is disabled."));
}
@@ -218,7 +216,7 @@ async fn auth_server(
),
)]);
let server_destination = services.globals.server_name().as_str().to_owned();
let server_destination = services().globals.server_name().as_str().to_owned();
if let Some(destination) = x_matrix.destination.as_ref() {
if destination != &server_destination {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Invalid authorization."));
@@ -249,7 +247,7 @@ async fn auth_server(
request_map.insert("content".to_owned(), json_body.clone());
};
let keys_result = services
let keys_result = services()
.rooms
.event_handler
.fetch_signing_keys_for_server(origin, vec![x_matrix.key.to_string()])
+8 -8
View File
@@ -10,7 +10,7 @@ use conduit::Result;
use http::Method;
use ruma::api::IncomingRequest;
use super::{Ruma, RumaResponse, State};
use super::{Ruma, RumaResponse};
pub(in super::super) trait RouterExt {
fn ruma_route<H, T>(self, handler: H) -> Self
@@ -18,7 +18,7 @@ pub(in super::super) trait RouterExt {
H: RumaHandler<T>;
}
impl RouterExt for Router<State> {
impl RouterExt for Router {
fn ruma_route<H, T>(self, handler: H) -> Self
where
H: RumaHandler<T>,
@@ -28,9 +28,9 @@ impl RouterExt for Router<State> {
}
pub(in super::super) trait RumaHandler<T> {
fn add_routes(&self, router: Router<State>) -> Router<State>;
fn add_routes(&self, router: Router) -> Router;
fn add_route(&self, router: Router<State>, path: &str) -> Router<State>;
fn add_route(&self, router: Router, path: &str) -> Router;
}
macro_rules! ruma_handler {
@@ -41,17 +41,17 @@ macro_rules! ruma_handler {
Req: IncomingRequest + Send + 'static,
Ret: IntoResponse,
Fut: Future<Output = Result<Req::OutgoingResponse, Ret>> + Send,
Fun: FnOnce($($tx,)* Ruma<Req>,) -> Fut + Clone + Send + Sync + 'static,
$( $tx: FromRequestParts<State> + Send + 'static, )*
Fun: FnOnce($($tx,)* Ruma<Req>) -> Fut + Clone + Send + Sync + 'static,
$( $tx: FromRequestParts<()> + Send + 'static, )*
{
fn add_routes(&self, router: Router<State>) -> Router<State> {
fn add_routes(&self, router: Router) -> Router {
Req::METADATA
.history
.all_paths()
.fold(router, |router, path| self.add_route(router, path))
}
fn add_route(&self, router: Router<State>, path: &str) -> Router<State> {
fn add_route(&self, router: Router, path: &str) -> Router {
let handle = self.clone();
let method = method_to_filter(&Req::METADATA.method);
let action = |$($tx,)* req| async { handle($($tx,)* req).await.map(RumaResponse) };
@@ -1,16 +1,24 @@
mod auth;
mod handler;
mod request;
mod response;
use std::{mem, ops::Deref};
use axum::{async_trait, body::Body, extract::FromRequest};
use bytes::{BufMut, BytesMut};
use conduit::{debug, err, trace, utils::string::EMPTY, Error, Result};
use ruma::{api::IncomingRequest, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId};
use service::Services;
use conduit::{debug, debug_warn, trace, warn};
use ruma::{
api::{client::error::ErrorKind, IncomingRequest},
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
};
use super::{auth, auth::Auth, request, request::Request};
use crate::{service::appservice::RegistrationInfo, State};
use self::{auth::Auth, request::Request};
pub(super) use self::{handler::RouterExt, response::RumaResponse};
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
/// Extractor for Ruma request structs
pub(crate) struct Args<T> {
pub(crate) struct Ruma<T> {
/// Request struct body
pub(crate) body: T,
@@ -36,18 +44,18 @@ pub(crate) struct Args<T> {
}
#[async_trait]
impl<T> FromRequest<State, Body> for Args<T>
impl<T, S> FromRequest<S, Body> for Ruma<T>
where
T: IncomingRequest,
{
type Rejection = Error;
async fn from_request(request: hyper::Request<Body>, services: &State) -> Result<Self, Self::Rejection> {
let mut request = request::from(services, request).await?;
async fn from_request(request: hyper::Request<Body>, _: &S) -> Result<Self, Self::Rejection> {
let mut request = request::from(request).await?;
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&request.body).ok();
let auth = auth::auth(services, &mut request, &json_body, &T::METADATA).await?;
let auth = auth::auth(&mut request, &json_body, &T::METADATA).await?;
Ok(Self {
body: make_body::<T>(services, &mut request, &mut json_body, &auth)?,
body: make_body::<T>(&mut request, &mut json_body, &auth)?,
origin: auth.origin,
sender_user: auth.sender_user,
sender_device: auth.sender_device,
@@ -57,22 +65,19 @@ where
}
}
impl<T> Deref for Args<T> {
impl<T> Deref for Ruma<T> {
type Target = T;
fn deref(&self) -> &Self::Target { &self.body }
}
fn make_body<T>(
services: &Services, request: &mut Request, json_body: &mut Option<CanonicalJsonValue>, auth: &Auth,
) -> Result<T>
fn make_body<T>(request: &mut Request, json_body: &mut Option<CanonicalJsonValue>, auth: &Auth) -> Result<T>
where
T: IncomingRequest,
{
let body = if let Some(CanonicalJsonValue::Object(json_body)) = json_body {
let user_id = auth.sender_user.clone().unwrap_or_else(|| {
let server_name = services.globals.server_name();
UserId::parse_with_server_name(EMPTY, server_name).expect("valid user_id")
UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid")
});
let uiaa_request = json_body
@@ -81,9 +86,9 @@ where
.and_then(|auth| auth.get("session"))
.and_then(|session| session.as_str())
.and_then(|session| {
services.uiaa.get_uiaa_request(
services().uiaa.get_uiaa_request(
&user_id,
&auth.sender_device.clone().unwrap_or_else(|| EMPTY.into()),
&auth.sender_device.clone().unwrap_or_else(|| "".into()),
session,
)
});
@@ -104,14 +109,21 @@ where
let mut http_request = hyper::Request::builder()
.uri(request.parts.uri.clone())
.method(request.parts.method.clone());
*http_request.headers_mut().expect("mutable http headers") = request.parts.headers.clone();
let http_request = http_request.body(body).expect("http request body");
*http_request.headers_mut().unwrap() = request.parts.headers.clone();
let http_request = http_request.body(body).unwrap();
debug!(
"{:?} {:?} {:?}",
http_request.method(),
http_request.uri(),
http_request.headers()
);
let headers = http_request.headers();
let method = http_request.method();
let uri = http_request.uri();
debug!("{method:?} {uri:?} {headers:?}");
trace!("{method:?} {uri:?} {json_body:?}");
trace!("{:?} {:?} {:?}", http_request.method(), http_request.uri(), json_body);
let body = T::try_from_http_request(http_request, &request.path).map_err(|e| {
warn!("try_from_http_request failed: {e:?}",);
debug_warn!("JSON body: {:?}", json_body);
Error::BadRequest(ErrorKind::BadJson, "Failed to deserialize request.")
})?;
T::try_from_http_request(http_request, &request.path).map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))
Ok(body)
}
+8 -8
View File
@@ -2,10 +2,11 @@ use std::str;
use axum::{extract::Path, RequestExt, RequestPartsExt};
use bytes::Bytes;
use conduit::{err, Result};
use http::request::Parts;
use ruma::api::client::error::ErrorKind;
use serde::Deserialize;
use service::Services;
use crate::{services, Error, Result};
#[derive(Deserialize)]
pub(super) struct QueryParams {
@@ -20,20 +21,19 @@ pub(super) struct Request {
pub(super) parts: Parts,
}
pub(super) async fn from(services: &Services, request: hyper::Request<axum::body::Body>) -> Result<Request> {
pub(super) async fn from(request: hyper::Request<axum::body::Body>) -> Result<Request> {
let limited = request.with_limited_body();
let (mut parts, body) = limited.into_parts();
let path: Path<Vec<String>> = parts.extract().await?;
let query = parts.uri.query().unwrap_or_default();
let query =
serde_html_form::from_str(query).map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
let query = serde_html_form::from_str(parts.uri.query().unwrap_or_default())
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to read query parameters"))?;
let max_body_size = services.globals.config.max_request_size;
let max_body_size = services().globals.config.max_request_size;
let body = axum::body::to_bytes(body, max_body_size)
.await
.map_err(|e| err!(Request(TooLarge("Request body too large: {e}"))))?;
.map_err(|_| Error::BadRequest(ErrorKind::TooLarge, "Request body too large"))?;
Ok(Request {
path,
+15 -26
View File
@@ -1,26 +1,15 @@
mod args;
mod auth;
mod handler;
mod request;
mod response;
use std::sync::Arc;
use axum::{
response::IntoResponse,
routing::{any, get, post},
Router,
};
use conduit::{err, Server};
use conduit::{err, Error, Server};
use http::Uri;
use ruma::api::client::error::ErrorKind;
use self::handler::RouterExt;
pub(super) use self::{args::Args as Ruma, response::RumaResponse};
use crate::{client, server};
use crate::{client, router::RouterExt, server};
pub type State = Arc<service::Services>;
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
pub fn build(router: Router, server: &Server) -> Router {
let config = &server.config;
let router = router
.ruma_route(client::get_supported_versions_route)
@@ -106,7 +95,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
.ruma_route(client::get_member_events_route)
.ruma_route(client::get_protocols_route)
.route("/_matrix/client/unstable/thirdparty/protocols",
get(client::get_protocols_route_unstable))
get(client::get_protocols_route_unstable))
.ruma_route(client::send_message_event_route)
.ruma_route(client::send_state_event_for_key_route)
.ruma_route(client::get_state_events_route)
@@ -191,15 +180,15 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
.ruma_route(client::get_relating_events_with_rel_type_route)
.ruma_route(client::get_relating_events_route)
.ruma_route(client::get_hierarchy_route)
.ruma_route(client::get_mutual_rooms_route)
.ruma_route(client::get_room_summary)
.route(
"/_matrix/client/unstable/im.nheko.summary/rooms/:room_id_or_alias/summary",
get(client::get_room_summary_legacy)
)
.ruma_route(client::well_known_support)
.ruma_route(client::well_known_client)
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
.ruma_route(client::get_mutual_rooms_route)
.ruma_route(client::get_room_summary)
.route(
"/_matrix/client/unstable/im.nheko.summary/rooms/:room_id_or_alias/summary",
get(client::get_room_summary_legacy)
)
.ruma_route(client::well_known_support)
.ruma_route(client::well_known_client)
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
.route("/_matrix/client/r0/rooms/:room_id/initialSync", get(initial_sync))
.route("/_matrix/client/v3/rooms/:room_id/initialSync", get(initial_sync))
.route("/client/server.json", get(client::syncv3_client_server_json));
@@ -244,7 +233,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
}
async fn initial_sync(_uri: Uri) -> impl IntoResponse {
err!(Request(GuestAccessForbidden("Guest access not implemented")))
Error::BadRequest(ErrorKind::GuestAccessForbidden, "Guest access not implemented")
}
async fn federation_disabled() -> impl IntoResponse { err!(Config("allow_federation", "Federation is disabled.")) }
+11 -13
View File
@@ -1,9 +1,9 @@
use axum::extract::State;
use conduit::{Error, Result};
use ruma::{
api::{client::error::ErrorKind, federation::backfill::get_backfill},
uint, user_id, MilliSecondsSinceUnixEpoch,
};
use service::{sending::convert_to_outgoing_federation_event, services};
use crate::Ruma;
@@ -11,21 +11,19 @@ use crate::Ruma;
///
/// Retrieves events from before the sender joined the room, if the room's
/// history visibility allows.
pub(crate) async fn get_backfill_route(
State(services): State<crate::State>, body: Ruma<get_backfill::v1::Request>,
) -> Result<get_backfill::v1::Response> {
pub(crate) async fn get_backfill_route(body: Ruma<get_backfill::v1::Request>) -> Result<get_backfill::v1::Response> {
let origin = body.origin.as_ref().expect("server is authenticated");
services
services()
.rooms
.event_handler
.acl_check(origin, &body.room_id)?;
if !services
if !services()
.rooms
.state_accessor
.is_world_readable(&body.room_id)?
&& !services
&& !services()
.rooms
.state_cache
.server_in_room(origin, &body.room_id)?
@@ -36,7 +34,7 @@ pub(crate) async fn get_backfill_route(
let until = body
.v
.iter()
.map(|event_id| services.rooms.timeline.get_pdu_count(event_id))
.map(|event_id| services().rooms.timeline.get_pdu_count(event_id))
.filter_map(|r| r.ok().flatten())
.max()
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event not found."))?;
@@ -47,7 +45,7 @@ pub(crate) async fn get_backfill_route(
.try_into()
.expect("UInt could not be converted to usize");
let all_events = services
let all_events = services()
.rooms
.timeline
.pdus_until(user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)?
@@ -57,20 +55,20 @@ pub(crate) async fn get_backfill_route(
.filter_map(Result::ok)
.filter(|(_, e)| {
matches!(
services
services()
.rooms
.state_accessor
.server_can_see_event(origin, &e.room_id, &e.event_id,),
Ok(true),
)
})
.map(|(_, pdu)| services.rooms.timeline.get_pdu_json(&pdu.event_id))
.map(|(_, pdu)| services().rooms.timeline.get_pdu_json(&pdu.event_id))
.filter_map(|r| r.ok().flatten())
.map(|pdu| services.sending.convert_to_outgoing_federation_event(pdu))
.map(convert_to_outgoing_federation_event)
.collect();
Ok(get_backfill::v1::Response {
origin: services.globals.server_name().to_owned(),
origin: services().globals.server_name().to_owned(),
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
pdus: events,
})
+11 -10
View File
@@ -1,9 +1,9 @@
use axum::extract::State;
use conduit::{Error, Result};
use ruma::{
api::{client::error::ErrorKind, federation::event::get_event},
MilliSecondsSinceUnixEpoch, RoomId,
};
use service::{sending::convert_to_outgoing_federation_event, services};
use crate::Ruma;
@@ -13,12 +13,10 @@ use crate::Ruma;
///
/// - Only works if a user of this server is currently invited or joined the
/// room
pub(crate) async fn get_event_route(
State(services): State<crate::State>, body: Ruma<get_event::v1::Request>,
) -> Result<get_event::v1::Response> {
pub(crate) async fn get_event_route(body: Ruma<get_event::v1::Request>) -> Result<get_event::v1::Response> {
let origin = body.origin.as_ref().expect("server is authenticated");
let event = services
let event = services()
.rooms
.timeline
.get_pdu_json(&body.event_id)?
@@ -32,13 +30,16 @@ pub(crate) async fn get_event_route(
let room_id =
<&RoomId>::try_from(room_id_str).map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
if !services.rooms.state_accessor.is_world_readable(room_id)?
&& !services.rooms.state_cache.server_in_room(origin, room_id)?
if !services().rooms.state_accessor.is_world_readable(room_id)?
&& !services()
.rooms
.state_cache
.server_in_room(origin, room_id)?
{
return Err(Error::BadRequest(ErrorKind::forbidden(), "Server is not in room."));
}
if !services
if !services()
.rooms
.state_accessor
.server_can_see_event(origin, room_id, &body.event_id)?
@@ -47,8 +48,8 @@ pub(crate) async fn get_event_route(
}
Ok(get_event::v1::Response {
origin: services.globals.server_name().to_owned(),
origin: services().globals.server_name().to_owned(),
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
pdu: services.sending.convert_to_outgoing_federation_event(event),
pdu: convert_to_outgoing_federation_event(event),
})
}
+9 -9
View File
@@ -1,11 +1,11 @@
use std::sync::Arc;
use axum::extract::State;
use conduit::{Error, Result};
use ruma::{
api::{client::error::ErrorKind, federation::authorization::get_event_authorization},
RoomId,
};
use service::{sending::convert_to_outgoing_federation_event, services};
use crate::Ruma;
@@ -15,20 +15,20 @@ use crate::Ruma;
///
/// - This does not include the event itself
pub(crate) async fn get_event_authorization_route(
State(services): State<crate::State>, body: Ruma<get_event_authorization::v1::Request>,
body: Ruma<get_event_authorization::v1::Request>,
) -> Result<get_event_authorization::v1::Response> {
let origin = body.origin.as_ref().expect("server is authenticated");
services
services()
.rooms
.event_handler
.acl_check(origin, &body.room_id)?;
if !services
if !services()
.rooms
.state_accessor
.is_world_readable(&body.room_id)?
&& !services
&& !services()
.rooms
.state_cache
.server_in_room(origin, &body.room_id)?
@@ -36,7 +36,7 @@ pub(crate) async fn get_event_authorization_route(
return Err(Error::BadRequest(ErrorKind::forbidden(), "Server is not in room."));
}
let event = services
let event = services()
.rooms
.timeline
.get_pdu_json(&body.event_id)?
@@ -50,7 +50,7 @@ pub(crate) async fn get_event_authorization_route(
let room_id =
<&RoomId>::try_from(room_id_str).map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
let auth_chain_ids = services
let auth_chain_ids = services()
.rooms
.auth_chain
.event_ids_iter(room_id, vec![Arc::from(&*body.event_id)])
@@ -58,8 +58,8 @@ pub(crate) async fn get_event_authorization_route(
Ok(get_event_authorization::v1::Response {
auth_chain: auth_chain_ids
.filter_map(|id| services.rooms.timeline.get_pdu_json(&id).ok()?)
.map(|pdu| services.sending.convert_to_outgoing_federation_event(pdu))
.filter_map(|id| services().rooms.timeline.get_pdu_json(&id).ok()?)
.map(convert_to_outgoing_federation_event)
.collect(),
})
}
+8 -8
View File
@@ -1,9 +1,9 @@
use axum::extract::State;
use conduit::{Error, Result};
use ruma::{
api::{client::error::ErrorKind, federation::event::get_missing_events},
OwnedEventId, RoomId,
};
use service::{sending::convert_to_outgoing_federation_event, services};
use crate::Ruma;
@@ -11,20 +11,20 @@ use crate::Ruma;
///
/// Retrieves events that the sender is missing.
pub(crate) async fn get_missing_events_route(
State(services): State<crate::State>, body: Ruma<get_missing_events::v1::Request>,
body: Ruma<get_missing_events::v1::Request>,
) -> Result<get_missing_events::v1::Response> {
let origin = body.origin.as_ref().expect("server is authenticated");
services
services()
.rooms
.event_handler
.acl_check(origin, &body.room_id)?;
if !services
if !services()
.rooms
.state_accessor
.is_world_readable(&body.room_id)?
&& !services
&& !services()
.rooms
.state_cache
.server_in_room(origin, &body.room_id)?
@@ -43,7 +43,7 @@ pub(crate) async fn get_missing_events_route(
let mut i: usize = 0;
while i < queued_events.len() && events.len() < limit {
if let Some(pdu) = services.rooms.timeline.get_pdu_json(&queued_events[i])? {
if let Some(pdu) = services().rooms.timeline.get_pdu_json(&queued_events[i])? {
let room_id_str = pdu
.get("room_id")
.and_then(|val| val.as_str())
@@ -61,7 +61,7 @@ pub(crate) async fn get_missing_events_route(
continue;
}
if !services
if !services()
.rooms
.state_accessor
.server_can_see_event(origin, &body.room_id, &queued_events[i])?
@@ -81,7 +81,7 @@ pub(crate) async fn get_missing_events_route(
)
.map_err(|_| Error::bad_database("Invalid prev_events in event in database."))?,
);
events.push(services.sending.convert_to_outgoing_federation_event(pdu));
events.push(convert_to_outgoing_federation_event(pdu));
}
i = i.saturating_add(1);
}
+4 -7
View File
@@ -1,19 +1,16 @@
use axum::extract::State;
use ruma::api::{client::error::ErrorKind, federation::space::get_hierarchy};
use crate::{Error, Result, Ruma};
use crate::{services, Error, Result, Ruma};
/// # `GET /_matrix/federation/v1/hierarchy/{roomId}`
///
/// Gets the space tree in a depth-first manner to locate child rooms of a given
/// space.
pub(crate) async fn get_hierarchy_route(
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
) -> Result<get_hierarchy::v1::Response> {
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
let origin = body.origin.as_ref().expect("server is authenticated");
if services.rooms.metadata.exists(&body.room_id)? {
services
if services().rooms.metadata.exists(&body.room_id)? {
services()
.rooms
.spaces
.get_federation_hierarchy(&body.room_id, origin, body.suggested_only)

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