Compare commits
103 commits
Author | SHA1 | Date | |
---|---|---|---|
|
99ab234f40 | ||
|
e83416bb5a | ||
|
726b6f0fa6 | ||
|
d7fd89df49 | ||
|
f4e57fdb22 | ||
|
4f096adcfa | ||
21a5fa3ef0 | |||
b27e9ea95c | |||
8aa915acb9 | |||
ace9637bc2 | |||
|
be1e2e9307 | ||
|
1c6a4b1b24 | ||
976a73a0e5 | |||
4c06f329c4 | |||
d841b81c56 | |||
e707084345 | |||
|
6dcc8b6cf1 | ||
|
a2ac491c54 | ||
|
72a13d8353 | ||
|
3a63f9dfb6 | ||
|
f4f2d05b5b | ||
|
c3c7bcb2ed | ||
|
d6c57f9b2e | ||
|
7fb9e99649 | ||
|
1274b48ebb | ||
|
0a281e81a5 | ||
|
a43bde69fa | ||
|
986343877c | ||
|
2d47710b55 | ||
|
10542a1d70 | ||
|
c167f7a6ad | ||
|
5787a70bab | ||
|
cf8f1f2546 | ||
|
3c2fc4a4c6 | ||
|
dffd771e7c | ||
|
4da8c7e282 | ||
|
0df5d18fd6 | ||
|
825ceac1c3 | ||
|
3e389256f5 | ||
|
a7892a28ec | ||
|
9453dbc740 | ||
|
bf48c10d28 | ||
|
7c1a3e41d9 | ||
|
2a04a361e0 | ||
|
0e8e4f1083 | ||
|
81ae579b25 | ||
|
3a3cafe912 | ||
|
d29591d47d | ||
|
67d280dd2e | ||
|
3ac9be5a78 | ||
|
52954f7a11 | ||
|
692a31620d | ||
|
cf4015b830 | ||
|
9cef03127b | ||
|
249fc7769d | ||
|
5cc53c9e14 | ||
|
bdc46f6392 | ||
|
6ae776218c | ||
|
bd2b146d5d | ||
|
f7cc4fb3bb | ||
|
ca198c51fa | ||
|
fe86d28428 | ||
|
c86f9a5c5b | ||
|
e0358a9de5 | ||
|
69d0003222 | ||
|
5cf9f3df48 | ||
|
0b7ed5adc9 | ||
|
4de54db305 | ||
|
02781e4f9b | ||
|
f8bdfd82b0 | ||
|
7e66d2e2c0 | ||
|
ffd03a256b | ||
|
9d592d60d2 | ||
|
25ceb5ebd8 | ||
|
6f052fff98 | ||
|
e8ac881b2f | ||
|
0d17aedae5 | ||
|
ab1fff2642 | ||
|
92c5b6b86c | ||
|
dc2f53e773 | ||
|
2475995102 | ||
|
835f4ad8cf | ||
|
ca6219723b | ||
|
40c7c248fb | ||
|
8f3f5c01f9 | ||
|
9d7f7b871b | ||
|
30f0871e21 | ||
|
98e81c6217 | ||
|
f3b6b3e222 | ||
|
3bfdae795d | ||
|
75c80df271 | ||
|
094cb888d4 | ||
|
fa725a14e2 | ||
|
9b3664aeeb | ||
|
90fea00dc7 | ||
|
20924a44f1 | ||
|
38d6426b0e | ||
|
9b55ce933a | ||
|
f73a657a23 | ||
|
6dfb262ddf | ||
|
75cdc3a1f6 | ||
|
11103a92ed | ||
|
ce2017a10e |
68 changed files with 2338 additions and 1845 deletions
4
.envrc
4
.envrc
|
@ -1 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
use flake
|
use flake
|
||||||
|
|
||||||
|
PATH_add bin
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -68,3 +68,6 @@ cached_target
|
||||||
|
|
||||||
# Direnv cache
|
# Direnv cache
|
||||||
/.direnv
|
/.direnv
|
||||||
|
|
||||||
|
# Gitlab CI cache
|
||||||
|
/.gitlab-ci.d
|
||||||
|
|
398
.gitlab-ci.yml
398
.gitlab-ci.yml
|
@ -1,244 +1,180 @@
|
||||||
stages:
|
stages:
|
||||||
- build
|
- ci
|
||||||
- build docker image
|
- artifacts
|
||||||
- test
|
- publish
|
||||||
- upload artifacts
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
# Make GitLab CI go fast:
|
# Makes some things print in color
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
TERM: ansi
|
||||||
FF_USE_FASTZIP: 1
|
|
||||||
CACHE_COMPRESSION_LEVEL: fastest
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------- #
|
before_script:
|
||||||
# Create and publish docker image #
|
# Enable nix-command and flakes
|
||||||
# --------------------------------------------------------------------- #
|
- if command -v nix > /dev/null; then echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf; fi
|
||||||
|
|
||||||
.docker-shared-settings:
|
# Add our own binary cache
|
||||||
stage: "build docker image"
|
- if command -v nix > /dev/null; then echo "extra-substituters = https://nix.computer.surgery/conduit" >> /etc/nix/nix.conf; fi
|
||||||
needs: []
|
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=" >> /etc/nix/nix.conf; fi
|
||||||
tags: [ "docker" ]
|
|
||||||
variables:
|
# Add crane binary cache
|
||||||
# Docker in Docker:
|
- if command -v nix > /dev/null; then echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf; fi
|
||||||
DOCKER_BUILDKIT: 1
|
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf; fi
|
||||||
image:
|
|
||||||
name: docker.io/docker
|
# Add nix-community binary cache
|
||||||
|
- if command -v nix > /dev/null; then echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf; fi
|
||||||
|
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf; fi
|
||||||
|
|
||||||
|
# Install direnv and nix-direnv
|
||||||
|
- if command -v nix > /dev/null; then nix-env -iA nixpkgs.direnv nixpkgs.nix-direnv; fi
|
||||||
|
|
||||||
|
# Allow .envrc
|
||||||
|
- if command -v nix > /dev/null; then direnv allow; fi
|
||||||
|
|
||||||
|
# Set CARGO_HOME to a cacheable path
|
||||||
|
- export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo"
|
||||||
|
|
||||||
|
ci:
|
||||||
|
stage: ci
|
||||||
|
image: nixos/nix:2.19.2
|
||||||
|
script:
|
||||||
|
- direnv exec . engage
|
||||||
|
cache:
|
||||||
|
key: nix
|
||||||
|
paths:
|
||||||
|
- target
|
||||||
|
- .gitlab-ci.d
|
||||||
|
|
||||||
|
static:x86_64-unknown-linux-musl:
|
||||||
|
stage: artifacts
|
||||||
|
image: nixos/nix:2.19.2
|
||||||
|
script:
|
||||||
|
# Push artifacts and build requirements to binary cache
|
||||||
|
- ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# Make the output less difficult to find
|
||||||
|
- cp result/bin/conduit conduit
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- conduit
|
||||||
|
|
||||||
|
static:aarch64-unknown-linux-musl:
|
||||||
|
stage: artifacts
|
||||||
|
image: nixos/nix:2.19.2
|
||||||
|
script:
|
||||||
|
# Push artifacts and build requirements to binary cache
|
||||||
|
- ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl
|
||||||
|
|
||||||
|
# Make the output less difficult to find
|
||||||
|
- cp result/bin/conduit conduit
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- conduit
|
||||||
|
|
||||||
|
# Note that although we have an `oci-image-x86_64-unknown-linux-musl` output,
|
||||||
|
# we don't build it because it would be largely redundant to this one since it's
|
||||||
|
# all containerized anyway.
|
||||||
|
oci-image:x86_64-unknown-linux-gnu:
|
||||||
|
stage: artifacts
|
||||||
|
image: nixos/nix:2.19.2
|
||||||
|
script:
|
||||||
|
# Push artifacts and build requirements to binary cache
|
||||||
|
#
|
||||||
|
# Since the OCI image package is based on the binary package, this has the
|
||||||
|
# fun side effect of uploading the normal binary too. Conduit users who are
|
||||||
|
# deploying with Nix can leverage this fact by adding our binary cache to
|
||||||
|
# their systems.
|
||||||
|
- ./bin/nix-build-and-cache .#oci-image
|
||||||
|
|
||||||
|
# Make the output less difficult to find
|
||||||
|
- cp result oci-image-amd64.tar.gz
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- oci-image-amd64.tar.gz
|
||||||
|
|
||||||
|
oci-image:aarch64-unknown-linux-musl:
|
||||||
|
stage: artifacts
|
||||||
|
needs:
|
||||||
|
# Wait for the static binary job to finish before starting so we don't have
|
||||||
|
# to build that twice for no reason
|
||||||
|
- static:aarch64-unknown-linux-musl
|
||||||
|
image: nixos/nix:2.19.2
|
||||||
|
script:
|
||||||
|
# Push artifacts and build requirements to binary cache
|
||||||
|
- ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl
|
||||||
|
|
||||||
|
# Make the output less difficult to find
|
||||||
|
- cp result oci-image-arm64v8.tar.gz
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- oci-image-arm64v8.tar.gz
|
||||||
|
|
||||||
|
debian:x86_64-unknown-linux-gnu:
|
||||||
|
stage: artifacts
|
||||||
|
# See also `rust-toolchain.toml`
|
||||||
|
image: rust:1.75.0
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get install -y --no-install-recommends libclang-dev
|
||||||
|
- cargo install cargo-deb
|
||||||
|
- cargo deb
|
||||||
|
|
||||||
|
# Make the output less difficult to find
|
||||||
|
- mv target/debian/*.deb conduit.deb
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- conduit.deb
|
||||||
|
cache:
|
||||||
|
key: debian
|
||||||
|
paths:
|
||||||
|
- target
|
||||||
|
- .gitlab-ci.d
|
||||||
|
|
||||||
|
.push-oci-image:
|
||||||
|
stage: publish
|
||||||
|
image: docker:25.0.0
|
||||||
services:
|
services:
|
||||||
- name: docker.io/docker:dind
|
- docker:25.0.0-dind
|
||||||
alias: docker
|
variables:
|
||||||
|
IMAGE_SUFFIX_AMD64: amd64
|
||||||
|
IMAGE_SUFFIX_ARM64V8: arm64v8
|
||||||
script:
|
script:
|
||||||
- apk add openssh-client
|
- docker load -i oci-image-amd64.tar.gz
|
||||||
- eval $(ssh-agent -s)
|
- IMAGE_ID_AMD64=$(docker images -q conduit:next)
|
||||||
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
- docker load -i oci-image-arm64v8.tar.gz
|
||||||
- printf "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config
|
- IMAGE_ID_ARM64V8=$(docker images -q conduit:next)
|
||||||
- sh .gitlab/setup-buildx-remote-builders.sh
|
# Tag and push the architecture specific images
|
||||||
# Authorize against this project's own image registry:
|
- docker tag $IMAGE_ID_AMD64 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64
|
||||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
- docker tag $IMAGE_ID_ARM64V8 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||||
# Build multiplatform image and push to temporary tag:
|
- docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64
|
||||||
- >
|
- docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||||
docker buildx build
|
# Tag the multi-arch image
|
||||||
--platform "linux/arm/v7,linux/arm64,linux/amd64"
|
- docker manifest create $IMAGE_NAME:$CI_COMMIT_SHA --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||||
--pull
|
- docker manifest push $IMAGE_NAME:$CI_COMMIT_SHA
|
||||||
--tag "$CI_REGISTRY_IMAGE/temporary-ci-images:$CI_JOB_ID"
|
# Tag and push the git ref
|
||||||
--push
|
- docker manifest create $IMAGE_NAME:$CI_COMMIT_REF_NAME --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||||
--provenance=false
|
- docker manifest push $IMAGE_NAME:$CI_COMMIT_REF_NAME
|
||||||
--file "Dockerfile" .
|
# Tag git tags as 'latest'
|
||||||
# Build multiplatform image to deb stage and extract their .deb files:
|
- |
|
||||||
- >
|
if [[ -n "$CI_COMMIT_TAG" ]]; then
|
||||||
docker buildx build
|
docker manifest create $IMAGE_NAME:latest --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||||
--platform "linux/arm/v7,linux/arm64,linux/amd64"
|
docker manifest push $IMAGE_NAME:latest
|
||||||
--target "packager-result"
|
fi
|
||||||
--output="type=local,dest=/tmp/build-output"
|
dependencies:
|
||||||
--provenance=false
|
- oci-image:x86_64-unknown-linux-gnu
|
||||||
--file "Dockerfile" .
|
- oci-image:aarch64-unknown-linux-musl
|
||||||
# Build multiplatform image to binary stage and extract their binaries:
|
only:
|
||||||
- >
|
- next
|
||||||
docker buildx build
|
- master
|
||||||
--platform "linux/arm/v7,linux/arm64,linux/amd64"
|
- tags
|
||||||
--target "builder-result"
|
|
||||||
--output="type=local,dest=/tmp/build-output"
|
|
||||||
--provenance=false
|
|
||||||
--file "Dockerfile" .
|
|
||||||
# Copy to GitLab container registry:
|
|
||||||
- >
|
|
||||||
docker buildx imagetools create
|
|
||||||
--tag "$CI_REGISTRY_IMAGE/$TAG"
|
|
||||||
--tag "$CI_REGISTRY_IMAGE/$TAG-bullseye"
|
|
||||||
--tag "$CI_REGISTRY_IMAGE/$TAG-commit-$CI_COMMIT_SHORT_SHA"
|
|
||||||
"$CI_REGISTRY_IMAGE/temporary-ci-images:$CI_JOB_ID"
|
|
||||||
# if DockerHub credentials exist, also copy to dockerhub:
|
|
||||||
- if [ -n "${DOCKER_HUB}" ]; then docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD" "$DOCKER_HUB"; fi
|
|
||||||
- >
|
|
||||||
if [ -n "${DOCKER_HUB}" ]; then
|
|
||||||
docker buildx imagetools create
|
|
||||||
--tag "$DOCKER_HUB_IMAGE/$TAG"
|
|
||||||
--tag "$DOCKER_HUB_IMAGE/$TAG-bullseye"
|
|
||||||
--tag "$DOCKER_HUB_IMAGE/$TAG-commit-$CI_COMMIT_SHORT_SHA"
|
|
||||||
"$CI_REGISTRY_IMAGE/temporary-ci-images:$CI_JOB_ID"
|
|
||||||
; fi
|
|
||||||
- mv /tmp/build-output ./
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- "./build-output/"
|
|
||||||
|
|
||||||
docker:next:
|
oci-image:push-gitlab:
|
||||||
extends: .docker-shared-settings
|
extends: .push-oci-image
|
||||||
rules:
|
|
||||||
- if: '$BUILD_SERVER_SSH_PRIVATE_KEY && $CI_COMMIT_BRANCH == "next"'
|
|
||||||
variables:
|
variables:
|
||||||
TAG: "matrix-conduit:next"
|
IMAGE_NAME: $CI_REGISTRY_IMAGE/matrix-conduit
|
||||||
|
|
||||||
docker:master:
|
|
||||||
extends: .docker-shared-settings
|
|
||||||
rules:
|
|
||||||
- if: '$BUILD_SERVER_SSH_PRIVATE_KEY && $CI_COMMIT_BRANCH == "master"'
|
|
||||||
variables:
|
|
||||||
TAG: "matrix-conduit:latest"
|
|
||||||
|
|
||||||
docker:tags:
|
|
||||||
extends: .docker-shared-settings
|
|
||||||
rules:
|
|
||||||
- if: "$BUILD_SERVER_SSH_PRIVATE_KEY && $CI_COMMIT_TAG"
|
|
||||||
variables:
|
|
||||||
TAG: "matrix-conduit:$CI_COMMIT_TAG"
|
|
||||||
|
|
||||||
|
|
||||||
docker build debugging:
|
|
||||||
extends: .docker-shared-settings
|
|
||||||
rules:
|
|
||||||
- if: "$CI_MERGE_REQUEST_TITLE =~ /.*[Dd]ocker.*/"
|
|
||||||
variables:
|
|
||||||
TAG: "matrix-conduit-docker-tests:latest"
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
# Run tests #
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
|
|
||||||
cargo check:
|
|
||||||
stage: test
|
|
||||||
image: docker.io/rust:1.70.0-bullseye
|
|
||||||
needs: []
|
|
||||||
interruptible: true
|
|
||||||
before_script:
|
before_script:
|
||||||
- "rustup show && rustc --version && cargo --version" # Print version info for debugging
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
- apt-get update && apt-get -y --no-install-recommends install libclang-dev # dependency for rocksdb
|
|
||||||
script:
|
|
||||||
- cargo check
|
|
||||||
|
|
||||||
|
oci-image:push-dockerhub:
|
||||||
.test-shared-settings:
|
extends: .push-oci-image
|
||||||
stage: "test"
|
|
||||||
needs: []
|
|
||||||
image: "registry.gitlab.com/jfowl/conduit-containers/rust-with-tools:latest"
|
|
||||||
tags: ["docker"]
|
|
||||||
variables:
|
variables:
|
||||||
CARGO_INCREMENTAL: "false" # https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow
|
IMAGE_NAME: matrixconduit/matrix-conduit
|
||||||
interruptible: true
|
|
||||||
|
|
||||||
test:cargo:
|
|
||||||
extends: .test-shared-settings
|
|
||||||
before_script:
|
before_script:
|
||||||
- apt-get update && apt-get -y --no-install-recommends install libclang-dev # dependency for rocksdb
|
- docker login -u $DOCKER_HUB_USER -p $DOCKER_HUB_PASSWORD
|
||||||
script:
|
|
||||||
- rustc --version && cargo --version # Print version info for debugging
|
|
||||||
- "cargo test --color always --workspace --verbose --locked --no-fail-fast"
|
|
||||||
|
|
||||||
test:clippy:
|
|
||||||
extends: .test-shared-settings
|
|
||||||
allow_failure: true
|
|
||||||
before_script:
|
|
||||||
- rustup component add clippy
|
|
||||||
- apt-get update && apt-get -y --no-install-recommends install libclang-dev # dependency for rocksdb
|
|
||||||
script:
|
|
||||||
- rustc --version && cargo --version # Print version info for debugging
|
|
||||||
- "cargo clippy --color always --verbose --message-format=json | gitlab-report -p clippy > $CI_PROJECT_DIR/gl-code-quality-report.json"
|
|
||||||
artifacts:
|
|
||||||
when: always
|
|
||||||
reports:
|
|
||||||
codequality: gl-code-quality-report.json
|
|
||||||
|
|
||||||
test:format:
|
|
||||||
extends: .test-shared-settings
|
|
||||||
before_script:
|
|
||||||
- rustup component add rustfmt
|
|
||||||
script:
|
|
||||||
- cargo fmt --all -- --check
|
|
||||||
|
|
||||||
test:audit:
|
|
||||||
extends: .test-shared-settings
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- cargo audit --color always || true
|
|
||||||
- cargo audit --stale --json | gitlab-report -p audit > gl-sast-report.json
|
|
||||||
artifacts:
|
|
||||||
when: always
|
|
||||||
reports:
|
|
||||||
sast: gl-sast-report.json
|
|
||||||
|
|
||||||
test:dockerlint:
|
|
||||||
stage: "test"
|
|
||||||
needs: []
|
|
||||||
image: "ghcr.io/hadolint/hadolint@sha256:6c4b7c23f96339489dd35f21a711996d7ce63047467a9a562287748a03ad5242" # 2.8.0-alpine
|
|
||||||
interruptible: true
|
|
||||||
script:
|
|
||||||
- hadolint --version
|
|
||||||
# First pass: Print for CI log:
|
|
||||||
- >
|
|
||||||
hadolint
|
|
||||||
--no-fail --verbose
|
|
||||||
./Dockerfile
|
|
||||||
# Then output the results into a json for GitLab to pretty-print this in the MR:
|
|
||||||
- >
|
|
||||||
hadolint
|
|
||||||
--format gitlab_codeclimate
|
|
||||||
--failure-threshold error
|
|
||||||
./Dockerfile > dockerlint.json
|
|
||||||
artifacts:
|
|
||||||
when: always
|
|
||||||
reports:
|
|
||||||
codequality: dockerlint.json
|
|
||||||
paths:
|
|
||||||
- dockerlint.json
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_REF_NAME != "master"'
|
|
||||||
changes:
|
|
||||||
- docker/*Dockerfile
|
|
||||||
- Dockerfile
|
|
||||||
- .gitlab-ci.yml
|
|
||||||
- if: '$CI_COMMIT_REF_NAME == "master"'
|
|
||||||
- if: '$CI_COMMIT_REF_NAME == "next"'
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
# Store binaries as package so they have download urls #
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
|
|
||||||
# DISABLED FOR NOW, NEEDS TO BE FIXED AT A LATER TIME:
|
|
||||||
|
|
||||||
#publish:package:
|
|
||||||
# stage: "upload artifacts"
|
|
||||||
# needs:
|
|
||||||
# - "docker:tags"
|
|
||||||
# rules:
|
|
||||||
# - if: "$CI_COMMIT_TAG"
|
|
||||||
# image: curlimages/curl:latest
|
|
||||||
# tags: ["docker"]
|
|
||||||
# variables:
|
|
||||||
# GIT_STRATEGY: "none" # Don't need a clean copy of the code, we just operate on artifacts
|
|
||||||
# script:
|
|
||||||
# - 'BASE_URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/conduit-${CI_COMMIT_REF_SLUG}/build-${CI_PIPELINE_ID}"'
|
|
||||||
# - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_amd64/conduit "${BASE_URL}/conduit-x86_64-unknown-linux-gnu"'
|
|
||||||
# - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm_v7/conduit "${BASE_URL}/conduit-armv7-unknown-linux-gnu"'
|
|
||||||
# - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm64/conduit "${BASE_URL}/conduit-aarch64-unknown-linux-gnu"'
|
|
||||||
# - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_amd64/conduit.deb "${BASE_URL}/conduit-x86_64-unknown-linux-gnu.deb"'
|
|
||||||
# - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm_v7/conduit.deb "${BASE_URL}/conduit-armv7-unknown-linux-gnu.deb"'
|
|
||||||
# - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm64/conduit.deb "${BASE_URL}/conduit-aarch64-unknown-linux-gnu.deb"'
|
|
||||||
|
|
||||||
# Avoid duplicate pipelines
|
|
||||||
# See: https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines
|
|
||||||
workflow:
|
|
||||||
rules:
|
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
||||||
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
|
||||||
when: never
|
|
||||||
- if: "$CI_COMMIT_BRANCH"
|
|
||||||
- if: "$CI_COMMIT_TAG"
|
|
1649
Cargo.lock
generated
1649
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
38
Cargo.toml
38
Cargo.toml
|
@ -1,3 +1,14 @@
|
||||||
|
# Keep alphabetically sorted
|
||||||
|
[workspace.lints.rust]
|
||||||
|
explicit_outlives_requirements = "warn"
|
||||||
|
unused_qualifications = "warn"
|
||||||
|
|
||||||
|
# Keep alphabetically sorted
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
cloned_instead_of_copied = "warn"
|
||||||
|
dbg_macro = "warn"
|
||||||
|
str_to_string = "warn"
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
description = "A Matrix homeserver written in Rust"
|
description = "A Matrix homeserver written in Rust"
|
||||||
|
@ -6,17 +17,17 @@ authors = ["timokoesters <timo@koesters.xyz>"]
|
||||||
homepage = "https://conduit.rs"
|
homepage = "https://conduit.rs"
|
||||||
repository = "https://gitlab.com/famedly/conduit"
|
repository = "https://gitlab.com/famedly/conduit"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.6.0"
|
version = "0.7.0-alpha"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# When changing this, make sure to update the `flake.lock` file by running
|
# See also `rust-toolchain.toml`
|
||||||
# `nix flake update`. If you don't have Nix installed or otherwise don't know
|
rust-version = "1.75.0"
|
||||||
# how to do this, ping `@charles:computer.surgery` or `@dusk:gaze.systems` in
|
|
||||||
# the matrix room.
|
|
||||||
rust-version = "1.70.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Web framework
|
# Web framework
|
||||||
axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"], optional = true }
|
axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"], optional = true }
|
||||||
|
@ -26,7 +37,7 @@ tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitiv
|
||||||
|
|
||||||
# Used for matrix spec type definitions and helpers
|
# Used for matrix spec type definitions and helpers
|
||||||
#ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
#ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||||
ruma = { git = "https://github.com/ruma/ruma", rev = "3bd58e3c899457c2d55c45268dcb8a65ae682d54", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
ruma = { git = "https://github.com/ruma/ruma", rev = "1a1c61ee1e8f0936e956a3b69c931ce12ee28475", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
||||||
#ruma = { git = "https://github.com/timokoesters/ruma", rev = "4ec9c69bb7e09391add2382b3ebac97b6e8f4c64", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
#ruma = { git = "https://github.com/timokoesters/ruma", rev = "4ec9c69bb7e09391add2382b3ebac97b6e8f4c64", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
||||||
#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
||||||
|
|
||||||
|
@ -53,7 +64,8 @@ rand = "0.8.5"
|
||||||
# Used to hash passwords
|
# Used to hash passwords
|
||||||
rust-argon2 = "1.0.0"
|
rust-argon2 = "1.0.0"
|
||||||
# Used to send requests
|
# Used to send requests
|
||||||
reqwest = { default-features = false, features = ["rustls-tls-native-roots", "socks"], git = "https://github.com/timokoesters/reqwest", rev = "57b7cf4feb921573dfafad7d34b9ac6e44ead0bd" }
|
hyper = "0.14.26"
|
||||||
|
reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] }
|
||||||
# Used for conduit::Error type
|
# Used for conduit::Error type
|
||||||
thiserror = "1.0.40"
|
thiserror = "1.0.40"
|
||||||
# Used to generate thumbnails for images
|
# Used to generate thumbnails for images
|
||||||
|
@ -61,13 +73,13 @@ image = { version = "0.24.6", default-features = false, features = ["jpeg", "png
|
||||||
# Used to encode server public key
|
# Used to encode server public key
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.2"
|
||||||
# Used when hashing the state
|
# Used when hashing the state
|
||||||
ring = "0.16.20"
|
ring = "0.17.7"
|
||||||
# Used when querying the SRV record of other servers
|
# Used when querying the SRV record of other servers
|
||||||
trust-dns-resolver = "0.22.0"
|
trust-dns-resolver = "0.22.0"
|
||||||
# Used to find matching events for appservices
|
# Used to find matching events for appservices
|
||||||
regex = "1.8.1"
|
regex = "1.8.1"
|
||||||
# jwt jsonwebtokens
|
# jwt jsonwebtokens
|
||||||
jsonwebtoken = "8.3.0"
|
jsonwebtoken = "9.2.0"
|
||||||
# Performance measurements
|
# Performance measurements
|
||||||
tracing = { version = "0.1.37", features = [] }
|
tracing = { version = "0.1.37", features = [] }
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||||
|
@ -78,10 +90,10 @@ tracing-opentelemetry = "0.18.0"
|
||||||
lru-cache = "0.1.2"
|
lru-cache = "0.1.2"
|
||||||
rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] }
|
rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] }
|
||||||
parking_lot = { version = "0.12.1", optional = true }
|
parking_lot = { version = "0.12.1", optional = true }
|
||||||
crossbeam = { version = "0.8.2", optional = true }
|
# crossbeam = { version = "0.8.2", optional = true }
|
||||||
num_cpus = "1.15.0"
|
num_cpus = "1.15.0"
|
||||||
threadpool = "1.8.1"
|
threadpool = "1.8.1"
|
||||||
heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true }
|
# heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true }
|
||||||
# Used for ruma wrapper
|
# Used for ruma wrapper
|
||||||
serde_html_form = "0.2.0"
|
serde_html_form = "0.2.0"
|
||||||
|
|
||||||
|
@ -112,7 +124,7 @@ default = ["conduit_bin", "backend_sqlite", "backend_rocksdb", "systemd"]
|
||||||
#backend_sled = ["sled"]
|
#backend_sled = ["sled"]
|
||||||
backend_persy = ["persy", "parking_lot"]
|
backend_persy = ["persy", "parking_lot"]
|
||||||
backend_sqlite = ["sqlite"]
|
backend_sqlite = ["sqlite"]
|
||||||
backend_heed = ["heed", "crossbeam"]
|
#backend_heed = ["heed", "crossbeam"]
|
||||||
backend_rocksdb = ["rocksdb"]
|
backend_rocksdb = ["rocksdb"]
|
||||||
jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"]
|
jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"]
|
||||||
sqlite = ["rusqlite", "parking_lot", "tokio/signal"]
|
sqlite = ["rusqlite", "parking_lot", "tokio/signal"]
|
||||||
|
|
30
DEPLOY.md
30
DEPLOY.md
|
@ -12,11 +12,13 @@ only offer Linux binaries.
|
||||||
|
|
||||||
You may simply download the binary that fits your machine. Run `uname -m` to see what you need. Now copy the appropriate url:
|
You may simply download the binary that fits your machine. Run `uname -m` to see what you need. Now copy the appropriate url:
|
||||||
|
|
||||||
| CPU Architecture | Download stable version | Download development version |
|
**Stable versions:**
|
||||||
| ------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------- |
|
|
||||||
| x84_64 / amd64 (Most servers and computers) | [Binary][x84_64-glibc-master] / [.deb][x84_64-glibc-master-deb] | [Binary][x84_64-glibc-next] / [.deb][x84_64-glibc-next-deb] |
|
| CPU Architecture | Download stable version |
|
||||||
| armv7 (e.g. Raspberry Pi by default) | [Binary][armv7-glibc-master] / [.deb][armv7-glibc-master-deb] | [Binary][armv7-glibc-next] / [.deb][armv7-glibc-next-deb] |
|
| ------------------------------------------- | --------------------------------------------------------------- |
|
||||||
| armv8 / aarch64 | [Binary][armv8-glibc-master] / [.deb][armv8-glibc-master-deb] | [Binary][armv8-glibc-next] / [.deb][armv8-glibc-next-deb] |
|
| x84_64 / amd64 (Most servers and computers) | [Binary][x84_64-glibc-master] / [.deb][x84_64-glibc-master-deb] |
|
||||||
|
| armv7 (e.g. Raspberry Pi by default) | [Binary][armv7-glibc-master] / [.deb][armv7-glibc-master-deb] |
|
||||||
|
| armv8 / aarch64 | [Binary][armv8-glibc-master] / [.deb][armv8-glibc-master-deb] |
|
||||||
|
|
||||||
These builds were created on and linked against the glibc version shipped with Debian bullseye.
|
These builds were created on and linked against the glibc version shipped with Debian bullseye.
|
||||||
If you use a system with an older glibc version (e.g. RHEL8), you might need to compile Conduit yourself.
|
If you use a system with an older glibc version (e.g. RHEL8), you might need to compile Conduit yourself.
|
||||||
|
@ -24,15 +26,19 @@ If you use a system with an older glibc version (e.g. RHEL8), you might need to
|
||||||
[x84_64-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit?job=docker:master
|
[x84_64-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit?job=docker:master
|
||||||
[armv7-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit?job=docker:master
|
[armv7-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit?job=docker:master
|
||||||
[armv8-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit?job=docker:master
|
[armv8-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit?job=docker:master
|
||||||
[x84_64-glibc-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/build-output/linux_amd64/conduit?job=docker:next
|
|
||||||
[armv7-glibc-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/build-output/linux_arm_v7/conduit?job=docker:next
|
|
||||||
[armv8-glibc-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/build-output/linux_arm64/conduit?job=docker:next
|
|
||||||
[x84_64-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit.deb?job=docker:master
|
[x84_64-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit.deb?job=docker:master
|
||||||
[armv7-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit.deb?job=docker:master
|
[armv7-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit.deb?job=docker:master
|
||||||
[armv8-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit.deb?job=docker:master
|
[armv8-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit.deb?job=docker:master
|
||||||
[x84_64-glibc-next-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/build-output/linux_amd64/conduit.deb?job=docker:next
|
|
||||||
[armv7-glibc-next-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/build-output/linux_arm_v7/conduit.deb?job=docker:next
|
**Latest versions:**
|
||||||
[armv8-glibc-next-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/build-output/linux_arm64/conduit.deb?job=docker:next
|
|
||||||
|
| Target | Type | Download |
|
||||||
|
|-|-|-|
|
||||||
|
| `x86_64-unknown-linux-gnu` | Dynamically linked Debian package | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/conduit.deb?job=debian:x86_64-unknown-linux-gnu) |
|
||||||
|
| `x86_64-unknown-linux-musl` | Statically linked binary | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/conduit?job=static:x86_64-unknown-linux-musl) |
|
||||||
|
| `aarch64-unknown-linux-musl` | Statically linked binary | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/conduit?job=static:aarch64-unknown-linux-musl) |
|
||||||
|
| `x86_64-unknown-linux-musl` | OCI image | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/oci-image-amd64.tar.gz?job=oci-image:x86_64-unknown-linux-musl) |
|
||||||
|
| `aarch64-unknown-linux-musl` | OCI image | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/oci-image-arm64v8.tar.gz?job=oci-image:aarch64-unknown-linux-musl) |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ sudo wget -O /usr/local/bin/matrix-conduit <url>
|
$ sudo wget -O /usr/local/bin/matrix-conduit <url>
|
||||||
|
@ -273,7 +279,7 @@ server {
|
||||||
client_max_body_size 20M;
|
client_max_body_size 20M;
|
||||||
|
|
||||||
location /_matrix/ {
|
location /_matrix/ {
|
||||||
proxy_pass http://127.0.0.1:6167$request_uri;
|
proxy_pass http://127.0.0.1:6167;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_read_timeout 5m;
|
proxy_read_timeout 5m;
|
||||||
|
|
132
Dockerfile
132
Dockerfile
|
@ -1,132 +0,0 @@
|
||||||
# syntax=docker/dockerfile:1
|
|
||||||
FROM docker.io/rust:1.70-bullseye AS base
|
|
||||||
|
|
||||||
FROM base AS builder
|
|
||||||
WORKDIR /usr/src/conduit
|
|
||||||
|
|
||||||
# Install required packages to build Conduit and it's dependencies
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get -y --no-install-recommends install libclang-dev=1:11.0-51+nmu5
|
|
||||||
|
|
||||||
# == Build dependencies without our own code separately for caching ==
|
|
||||||
#
|
|
||||||
# Need a fake main.rs since Cargo refuses to build anything otherwise.
|
|
||||||
#
|
|
||||||
# See https://github.com/rust-lang/cargo/issues/2644 for a Cargo feature
|
|
||||||
# request that would allow just dependencies to be compiled, presumably
|
|
||||||
# regardless of whether source files are available.
|
|
||||||
RUN mkdir src && touch src/lib.rs && echo 'fn main() {}' > src/main.rs
|
|
||||||
COPY Cargo.toml Cargo.lock ./
|
|
||||||
RUN cargo build --release && rm -r src
|
|
||||||
|
|
||||||
# Copy over actual Conduit sources
|
|
||||||
COPY src src
|
|
||||||
|
|
||||||
# main.rs and lib.rs need their timestamp updated for this to work correctly since
|
|
||||||
# otherwise the build with the fake main.rs from above is newer than the
|
|
||||||
# source files (COPY preserves timestamps).
|
|
||||||
#
|
|
||||||
# Builds conduit and places the binary at /usr/src/conduit/target/release/conduit
|
|
||||||
RUN touch src/main.rs && touch src/lib.rs && cargo build --release
|
|
||||||
|
|
||||||
|
|
||||||
# ONLY USEFUL FOR CI: target stage to extract build artifacts
|
|
||||||
FROM scratch AS builder-result
|
|
||||||
COPY --from=builder /usr/src/conduit/target/release/conduit /conduit
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
|
||||||
# Build cargo-deb, a tool to package up rust binaries into .deb packages for Debian/Ubuntu based systems:
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
|
||||||
FROM base AS build-cargo-deb
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
dpkg \
|
|
||||||
dpkg-dev \
|
|
||||||
liblzma-dev
|
|
||||||
|
|
||||||
RUN cargo install cargo-deb
|
|
||||||
# => binary is in /usr/local/cargo/bin/cargo-deb
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
|
||||||
# Package conduit build-result into a .deb package:
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
|
||||||
FROM builder AS packager
|
|
||||||
WORKDIR /usr/src/conduit
|
|
||||||
|
|
||||||
COPY ./LICENSE ./LICENSE
|
|
||||||
COPY ./README.md ./README.md
|
|
||||||
COPY debian ./debian
|
|
||||||
COPY --from=build-cargo-deb /usr/local/cargo/bin/cargo-deb /usr/local/cargo/bin/cargo-deb
|
|
||||||
|
|
||||||
# --no-build makes cargo-deb reuse already compiled project
|
|
||||||
RUN cargo deb --no-build
|
|
||||||
# => Package is in /usr/src/conduit/target/debian/<project_name>_<version>_<arch>.deb
|
|
||||||
|
|
||||||
|
|
||||||
# ONLY USEFUL FOR CI: target stage to extract build artifacts
|
|
||||||
FROM scratch AS packager-result
|
|
||||||
COPY --from=packager /usr/src/conduit/target/debian/*.deb /conduit.deb
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
|
||||||
# Stuff below this line actually ends up in the resulting docker image
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
|
||||||
FROM docker.io/debian:bullseye-slim AS runner
|
|
||||||
|
|
||||||
# Standard port on which Conduit launches.
|
|
||||||
# You still need to map the port when using the docker command or docker-compose.
|
|
||||||
EXPOSE 6167
|
|
||||||
|
|
||||||
ARG DEFAULT_DB_PATH=/var/lib/matrix-conduit
|
|
||||||
|
|
||||||
ENV CONDUIT_PORT=6167 \
|
|
||||||
CONDUIT_ADDRESS="0.0.0.0" \
|
|
||||||
CONDUIT_DATABASE_PATH=${DEFAULT_DB_PATH} \
|
|
||||||
CONDUIT_CONFIG=''
|
|
||||||
# └─> Set no config file to do all configuration with env vars
|
|
||||||
|
|
||||||
# Conduit needs:
|
|
||||||
# dpkg: to install conduit.deb
|
|
||||||
# ca-certificates: for https
|
|
||||||
# iproute2 & wget: for the healthcheck script
|
|
||||||
RUN apt-get update && apt-get -y --no-install-recommends install \
|
|
||||||
dpkg \
|
|
||||||
ca-certificates \
|
|
||||||
iproute2 \
|
|
||||||
wget \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Test if Conduit is still alive, uses the same endpoint as Element
|
|
||||||
COPY ./docker/healthcheck.sh /srv/conduit/healthcheck.sh
|
|
||||||
HEALTHCHECK --start-period=5s --interval=5s CMD ./healthcheck.sh
|
|
||||||
|
|
||||||
# Install conduit.deb:
|
|
||||||
COPY --from=packager /usr/src/conduit/target/debian/*.deb /srv/conduit/
|
|
||||||
RUN dpkg -i /srv/conduit/*.deb
|
|
||||||
|
|
||||||
# Improve security: Don't run stuff as root, that does not need to run as root
|
|
||||||
# Most distros also use 1000:1000 for the first real user, so this should resolve volume mounting problems.
|
|
||||||
ARG USER_ID=1000
|
|
||||||
ARG GROUP_ID=1000
|
|
||||||
RUN set -x ; \
|
|
||||||
groupadd -r -g ${GROUP_ID} conduit ; \
|
|
||||||
useradd -l -r -M -d /srv/conduit -o -u ${USER_ID} -g conduit conduit && exit 0 ; exit 1
|
|
||||||
|
|
||||||
# Create database directory, change ownership of Conduit files to conduit user and group and make the healthcheck executable:
|
|
||||||
RUN chown -cR conduit:conduit /srv/conduit && \
|
|
||||||
chmod +x /srv/conduit/healthcheck.sh && \
|
|
||||||
mkdir -p ${DEFAULT_DB_PATH} && \
|
|
||||||
chown -cR conduit:conduit ${DEFAULT_DB_PATH}
|
|
||||||
|
|
||||||
# Change user to conduit, no root permissions afterwards:
|
|
||||||
USER conduit
|
|
||||||
# Set container home directory
|
|
||||||
WORKDIR /srv/conduit
|
|
||||||
|
|
||||||
# Run Conduit and print backtraces on panics
|
|
||||||
ENV RUST_BACKTRACE=1
|
|
||||||
ENTRYPOINT [ "/usr/sbin/matrix-conduit" ]
|
|
37
bin/complement
Executable file
37
bin/complement
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Path to Complement's source code
|
||||||
|
COMPLEMENT_SRC="$1"
|
||||||
|
|
||||||
|
# A `.jsonl` file to write test logs to
|
||||||
|
LOG_FILE="$2"
|
||||||
|
|
||||||
|
# A `.jsonl` file to write test results to
|
||||||
|
RESULTS_FILE="$3"
|
||||||
|
|
||||||
|
OCI_IMAGE="complement-conduit:dev"
|
||||||
|
|
||||||
|
env \
|
||||||
|
-C "$(git rev-parse --show-toplevel)" \
|
||||||
|
docker build \
|
||||||
|
--tag "$OCI_IMAGE" \
|
||||||
|
--file complement/Dockerfile \
|
||||||
|
.
|
||||||
|
|
||||||
|
# It's okay (likely, even) that `go test` exits nonzero
|
||||||
|
set +o pipefail
|
||||||
|
env \
|
||||||
|
-C "$COMPLEMENT_SRC" \
|
||||||
|
COMPLEMENT_BASE_IMAGE="$OCI_IMAGE" \
|
||||||
|
go test -json ./tests | tee "$LOG_FILE"
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# Post-process the results into an easy-to-compare format
|
||||||
|
cat "$LOG_FILE" | jq -c '
|
||||||
|
select(
|
||||||
|
(.Action == "pass" or .Action == "fail" or .Action == "skip")
|
||||||
|
and .Test != null
|
||||||
|
) | {Action: .Action, Test: .Test}
|
||||||
|
' | sort > "$RESULTS_FILE"
|
31
bin/nix-build-and-cache
Executable file
31
bin/nix-build-and-cache
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# The first argument must be the desired installable
|
||||||
|
INSTALLABLE="$1"
|
||||||
|
|
||||||
|
# Build the installable and forward any other arguments too
|
||||||
|
nix build "$@"
|
||||||
|
|
||||||
|
if [ ! -z ${ATTIC_TOKEN+x} ]; then
|
||||||
|
|
||||||
|
nix run --inputs-from . attic -- login \
|
||||||
|
conduit \
|
||||||
|
https://nix.computer.surgery/conduit \
|
||||||
|
"$ATTIC_TOKEN"
|
||||||
|
|
||||||
|
push_args=(
|
||||||
|
# Attic and its build dependencies
|
||||||
|
"$(nix path-info --inputs-from . attic)"
|
||||||
|
"$(nix path-info --inputs-from . attic --derivation)"
|
||||||
|
|
||||||
|
# The target installable and its build dependencies
|
||||||
|
"$(nix path-info "$INSTALLABLE" --derivation)"
|
||||||
|
"$(nix path-info "$INSTALLABLE")"
|
||||||
|
)
|
||||||
|
|
||||||
|
nix run --inputs-from . attic -- push conduit "${push_args[@]}"
|
||||||
|
else
|
||||||
|
echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache"
|
||||||
|
fi
|
|
@ -1,26 +1,30 @@
|
||||||
# For use in our CI only. This requires a build artifact created by a previous run pipline stage to be placed in cached_target/release/conduit
|
FROM rust:1.75.0
|
||||||
FROM registry.gitlab.com/jfowl/conduit-containers/rust-with-tools:commit-16a08e9b as builder
|
|
||||||
#FROM rust:latest as builder
|
|
||||||
|
|
||||||
WORKDIR /workdir
|
WORKDIR /workdir
|
||||||
|
|
||||||
ARG RUSTC_WRAPPER
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
ARG AWS_ACCESS_KEY_ID
|
libclang-dev
|
||||||
ARG AWS_SECRET_ACCESS_KEY
|
|
||||||
ARG SCCACHE_BUCKET
|
|
||||||
ARG SCCACHE_ENDPOINT
|
|
||||||
ARG SCCACHE_S3_USE_SSL
|
|
||||||
|
|
||||||
COPY . .
|
COPY Cargo.toml Cargo.toml
|
||||||
RUN mkdir -p target/release
|
COPY Cargo.lock Cargo.lock
|
||||||
RUN test -e cached_target/release/conduit && cp cached_target/release/conduit target/release/conduit || cargo build --release
|
COPY src src
|
||||||
|
RUN cargo build --release \
|
||||||
## Actual image
|
&& mv target/release/conduit conduit \
|
||||||
FROM debian:bullseye
|
&& rm -rf target
|
||||||
WORKDIR /workdir
|
|
||||||
|
|
||||||
# Install caddy
|
# Install caddy
|
||||||
RUN apt-get update && apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-testing-archive-keyring.gpg && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-testing.list && apt-get update && apt-get install -y caddy
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
debian-keyring \
|
||||||
|
debian-archive-keyring \
|
||||||
|
apt-transport-https \
|
||||||
|
curl \
|
||||||
|
&& curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/gpg.key' \
|
||||||
|
| gpg --dearmor -o /usr/share/keyrings/caddy-testing-archive-keyring.gpg \
|
||||||
|
&& curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/debian.deb.txt' \
|
||||||
|
| tee /etc/apt/sources.list.d/caddy-testing.list \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y caddy
|
||||||
|
|
||||||
COPY conduit-example.toml conduit.toml
|
COPY conduit-example.toml conduit.toml
|
||||||
COPY complement/caddy.json caddy.json
|
COPY complement/caddy.json caddy.json
|
||||||
|
@ -29,16 +33,9 @@ ENV SERVER_NAME=localhost
|
||||||
ENV CONDUIT_CONFIG=/workdir/conduit.toml
|
ENV CONDUIT_CONFIG=/workdir/conduit.toml
|
||||||
|
|
||||||
RUN sed -i "s/port = 6167/port = 8008/g" conduit.toml
|
RUN sed -i "s/port = 6167/port = 8008/g" conduit.toml
|
||||||
RUN echo "allow_federation = true" >> conduit.toml
|
|
||||||
RUN echo "allow_check_for_updates = true" >> conduit.toml
|
|
||||||
RUN echo "allow_encryption = true" >> conduit.toml
|
|
||||||
RUN echo "allow_registration = true" >> conduit.toml
|
|
||||||
RUN echo "log = \"warn,_=off,sled=off\"" >> conduit.toml
|
RUN echo "log = \"warn,_=off,sled=off\"" >> conduit.toml
|
||||||
RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml
|
RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml
|
||||||
|
|
||||||
COPY --from=builder /workdir/target/release/conduit /workdir/conduit
|
|
||||||
RUN chmod +x /workdir/conduit
|
|
||||||
|
|
||||||
EXPOSE 8008 8448
|
EXPOSE 8008 8448
|
||||||
|
|
||||||
CMD uname -a && \
|
CMD uname -a && \
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
# Running Conduit on Complement
|
# Complement
|
||||||
|
|
||||||
This assumes that you're familiar with complement, if not, please readme
|
## What's that?
|
||||||
[their readme](https://github.com/matrix-org/complement#running).
|
|
||||||
|
|
||||||
Complement works with "base images", this directory (and Dockerfile) helps build the conduit complement-ready docker
|
Have a look at [its repository](https://github.com/matrix-org/complement).
|
||||||
image.
|
|
||||||
|
|
||||||
To build, `cd` to the base directory of the workspace, and run this:
|
## How do I use it with Conduit?
|
||||||
|
|
||||||
`docker build -t complement-conduit:dev -f complement/Dockerfile .`
|
The script at [`../bin/complement`](../bin/complement) has automation for this.
|
||||||
|
It takes a few command line arguments, you can read the script to find out what
|
||||||
Then use `complement-conduit:dev` as a base image for running complement tests.
|
those are.
|
||||||
|
|
10
default.nix
Normal file
10
default.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
(import
|
||||||
|
(
|
||||||
|
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||||
|
fetchTarball {
|
||||||
|
url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{ src = ./.; }
|
||||||
|
).defaultNix
|
|
@ -76,7 +76,7 @@ to pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible
|
||||||
|
|
||||||
If you just want to test Conduit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
|
If you just want to test Conduit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
|
||||||
|
|
||||||
## Docker-compose
|
### Docker-compose
|
||||||
|
|
||||||
If the `docker run` command is not for you or your setup, you can also use one of the provided `docker-compose` files.
|
If the `docker run` command is not for you or your setup, you can also use one of the provided `docker-compose` files.
|
||||||
|
|
||||||
|
@ -161,3 +161,58 @@ So...step by step:
|
||||||
|
|
||||||
6. Run `docker-compose up -d`
|
6. Run `docker-compose up -d`
|
||||||
7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin.
|
7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Voice communication
|
||||||
|
|
||||||
|
In order to make or receive calls, a TURN server is required. Conduit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Create a configuration file called `coturn.conf` containing:
|
||||||
|
|
||||||
|
```conf
|
||||||
|
use-auth-secret
|
||||||
|
static-auth-secret=<a secret key>
|
||||||
|
realm=<your server domain>
|
||||||
|
```
|
||||||
|
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
|
||||||
|
|
||||||
|
These same values need to be set in conduit. You can either modify conduit.toml to include these lines:
|
||||||
|
```
|
||||||
|
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
|
||||||
|
turn_secret = "<secret key from coturn configuration>"
|
||||||
|
```
|
||||||
|
or append the following to the docker environment variables dependig on which configuration method you used earlier:
|
||||||
|
```yml
|
||||||
|
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
|
||||||
|
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
|
||||||
|
```
|
||||||
|
Restart Conduit to apply these changes.
|
||||||
|
|
||||||
|
### Run
|
||||||
|
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||||
|
```bash
|
||||||
|
docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
|
||||||
|
```
|
||||||
|
|
||||||
|
or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml`
|
||||||
|
and run `docker-compose up -d` in the same directory.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: 3
|
||||||
|
services:
|
||||||
|
turn:
|
||||||
|
container_name: coturn-server
|
||||||
|
image: docker.io/coturn/coturn
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: "host"
|
||||||
|
volumes:
|
||||||
|
- ./coturn.conf:/etc/coturn/turnserver.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
To understand why the host networking mode is used and explore alternative configuration options, please visit the following link: https://github.com/coturn/coturn/blob/master/docker/coturn/README.md.
|
||||||
|
For security recommendations see Synapse's [Coturn documentation](https://github.com/matrix-org/synapse/blob/develop/docs/setup/turn/coturn.md#configuration).
|
||||||
|
|
||||||
|
|
64
engage.toml
Normal file
64
engage.toml
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
interpreter = ["bash", "-euo", "pipefail", "-c"]
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "engage"
|
||||||
|
group = "versions"
|
||||||
|
script = "engage --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "rustc"
|
||||||
|
group = "versions"
|
||||||
|
script = "rustc --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo"
|
||||||
|
group = "versions"
|
||||||
|
script = "cargo --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo-fmt"
|
||||||
|
group = "versions"
|
||||||
|
script = "cargo fmt --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "rustdoc"
|
||||||
|
group = "versions"
|
||||||
|
script = "rustdoc --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo-clippy"
|
||||||
|
group = "versions"
|
||||||
|
script = "cargo clippy -- --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo-fmt"
|
||||||
|
group = "lints"
|
||||||
|
script = "cargo fmt --check -- --color=always"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo-doc"
|
||||||
|
group = "lints"
|
||||||
|
script = """
|
||||||
|
RUSTDOCFLAGS="-D warnings" cargo doc \
|
||||||
|
--workspace \
|
||||||
|
--no-deps \
|
||||||
|
--document-private-items \
|
||||||
|
--color always
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo-clippy"
|
||||||
|
group = "lints"
|
||||||
|
script = "cargo clippy --workspace --all-targets --color=always -- -D warnings"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "cargo"
|
||||||
|
group = "tests"
|
||||||
|
script = """
|
||||||
|
cargo test \
|
||||||
|
--workspace \
|
||||||
|
--all-targets \
|
||||||
|
--color=always \
|
||||||
|
-- \
|
||||||
|
--color=always
|
||||||
|
"""
|
206
flake.lock
206
flake.lock
|
@ -1,22 +1,41 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"crane": {
|
"attic": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"crane": "crane",
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": [
|
"flake-utils": "flake-utils",
|
||||||
"flake-utils"
|
"nixpkgs": "nixpkgs",
|
||||||
],
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-overlay": "rust-overlay"
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688772518,
|
"lastModified": 1705617092,
|
||||||
"narHash": "sha256-ol7gZxwvgLnxNSZwFTDJJ49xVY5teaSvF7lzlo3YQfM=",
|
"narHash": "sha256-n9PK4O4X4S1JkwpkMuYm1wHZYJzRqif8g3RuVIPD+rY=",
|
||||||
|
"owner": "zhaofengli",
|
||||||
|
"repo": "attic",
|
||||||
|
"rev": "fbe252a5c21febbe920c025560cbd63b20e24f3b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "zhaofengli",
|
||||||
|
"ref": "main",
|
||||||
|
"repo": "attic",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"crane": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"attic",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1702918879,
|
||||||
|
"narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "8b08e96c9af8c6e3a2b69af5a7fa168750fcf88e",
|
"rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -25,6 +44,27 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"crane_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1706473964,
|
||||||
|
"narHash": "sha256-Fq6xleee/TsX6NbtoRuI96bBuDHMU57PrcK9z1QEKbk=",
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"rev": "c798790eabec3e3da48190ae3698ac227aab770c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "crane",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fenix": {
|
"fenix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -33,11 +73,11 @@
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689488573,
|
"lastModified": 1705559032,
|
||||||
"narHash": "sha256-diVASflKCCryTYv0djvMnP2444mFsIG0ge5pa7ahauQ=",
|
"narHash": "sha256-Cb+Jd1+Gz4Wi+8elPnUIHnqQmE1qjDRZ+PsJaPaAffY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "39096fe3f379036ff4a5fa198950b8e79defe939",
|
"rev": "e132ea0eb0c799a2109a91688e499d7bf4962801",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -62,16 +102,29 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils": {
|
"flake-compat_2": {
|
||||||
"inputs": {
|
"flake": false,
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689068808,
|
"lastModified": 1696426674,
|
||||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667395993,
|
||||||
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -80,13 +133,78 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705309234,
|
||||||
|
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-filter": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705332318,
|
||||||
|
"narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-filter",
|
||||||
|
"rev": "3449dc925982ad46246cfc36469baf66e1b64f17",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-filter",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689444953,
|
"lastModified": 1702539185,
|
||||||
"narHash": "sha256-0o56bfb2LC38wrinPdCGLDScd77LVcr7CrH1zK7qvDg=",
|
"narHash": "sha256-KnIRG5NMdLIpEkZTnN5zovNYc0hhXjAgv6pfd5Z4c7U=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8acef304efe70152463a6399f73e636bcc363813",
|
"rev": "aa9d4729cbc99dabacb50e3994dcefb3ea0f7447",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1702780907,
|
||||||
|
"narHash": "sha256-blbrBBXjjZt6OKTcYX1jpe9SRof2P9ZYWPzq22tzXAA=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1e2e384c5b7c50dbf8e9c441a9e58d85f408b01f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-23.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705496572,
|
||||||
|
"narHash": "sha256-rPIe9G5EBLXdBdn9ilGc0nq082lzQd0xGGe092R/5QE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "842d9d80cfd4560648c785f8a4e6f3b096790e19",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -98,20 +216,23 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"crane": "crane",
|
"attic": "attic",
|
||||||
|
"crane": "crane_2",
|
||||||
"fenix": "fenix",
|
"fenix": "fenix",
|
||||||
"flake-utils": "flake-utils",
|
"flake-compat": "flake-compat_2",
|
||||||
"nixpkgs": "nixpkgs"
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nix-filter": "nix-filter",
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689441253,
|
"lastModified": 1705523001,
|
||||||
"narHash": "sha256-4MSDZaFI4DOfsLIZYPMBl0snzWhX1/OqR/QHir382CY=",
|
"narHash": "sha256-TWq5vJ6m+9HGSDMsQAmz1TMegMi79R3TTyKjnPWsQp8=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "996e054f1eb1dbfc8455ecabff0f6ff22ba7f7c8",
|
"rev": "9d9b34354d2f13e33568c9c55b226dd014a146a0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -121,31 +242,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-overlay": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": [
|
|
||||||
"crane",
|
|
||||||
"flake-utils"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"crane",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1688351637,
|
|
||||||
"narHash": "sha256-CLTufJ29VxNOIZ8UTg0lepsn3X03AmopmaLTTeHDCL4=",
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"rev": "f9b92316727af9e6c7fee4a761242f7f46880329",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
|
|
260
flake.nix
260
flake.nix
|
@ -2,92 +2,258 @@
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
nix-filter.url = "github:numtide/nix-filter";
|
||||||
|
flake-compat = {
|
||||||
|
url = "github:edolstra/flake-compat";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
fenix = {
|
fenix = {
|
||||||
url = "github:nix-community/fenix";
|
url = "github:nix-community/fenix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
crane = {
|
crane = {
|
||||||
url = "github:ipetkov/crane";
|
url = "github:ipetkov/crane?ref=master";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
inputs.flake-utils.follows = "flake-utils";
|
|
||||||
};
|
};
|
||||||
|
attic.url = "github:zhaofengli/attic?ref=main";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
{ self
|
{ self
|
||||||
, nixpkgs
|
, nixpkgs
|
||||||
, flake-utils
|
, flake-utils
|
||||||
|
, nix-filter
|
||||||
|
|
||||||
, fenix
|
, fenix
|
||||||
, crane
|
, crane
|
||||||
|
, ...
|
||||||
}: flake-utils.lib.eachDefaultSystem (system:
|
}: flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgsHost = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
# Use mold on Linux
|
|
||||||
stdenv = if pkgs.stdenv.isLinux then
|
|
||||||
pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv
|
|
||||||
else
|
|
||||||
pkgs.stdenv;
|
|
||||||
|
|
||||||
# Nix-accessible `Cargo.toml`
|
# Nix-accessible `Cargo.toml`
|
||||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||||
|
|
||||||
# The Rust toolchain to use
|
# The Rust toolchain to use
|
||||||
toolchain = fenix.packages.${system}.toolchainOf {
|
toolchain = fenix.packages.${system}.fromToolchainFile {
|
||||||
# Use the Rust version defined in `Cargo.toml`
|
file = ./rust-toolchain.toml;
|
||||||
channel = cargoToml.package.rust-version;
|
|
||||||
|
|
||||||
# THE rust-version HASH
|
# See also `rust-toolchain.toml`
|
||||||
sha256 = "sha256-gdYqng0y9iHYzYPAdkC/ka3DRny3La/S5G8ASj0Ayyc=";
|
sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8=";
|
||||||
};
|
};
|
||||||
|
|
||||||
# The system's RocksDB
|
builder = pkgs:
|
||||||
|
((crane.mkLib pkgs).overrideToolchain toolchain).buildPackage;
|
||||||
|
|
||||||
|
nativeBuildInputs = pkgs: [
|
||||||
|
# bindgen needs the build platform's libclang. Apparently due to
|
||||||
|
# "splicing weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't
|
||||||
|
# quite do the right thing here.
|
||||||
|
pkgs.buildPackages.rustPlatform.bindgenHook
|
||||||
|
];
|
||||||
|
|
||||||
|
env = pkgs: {
|
||||||
ROCKSDB_INCLUDE_DIR = "${pkgs.rocksdb}/include";
|
ROCKSDB_INCLUDE_DIR = "${pkgs.rocksdb}/include";
|
||||||
ROCKSDB_LIB_DIR = "${pkgs.rocksdb}/lib";
|
ROCKSDB_LIB_DIR = "${pkgs.rocksdb}/lib";
|
||||||
|
}
|
||||||
|
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isStatic {
|
||||||
|
ROCKSDB_STATIC = "";
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
CARGO_BUILD_RUSTFLAGS = let inherit (pkgs) lib stdenv; in
|
||||||
|
lib.concatStringsSep " " ([]
|
||||||
|
++ lib.optionals
|
||||||
|
# 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
|
||||||
|
# leaving PIE enabled.
|
||||||
|
stdenv.hostPlatform.isStatic
|
||||||
|
["-C" "relocation-model=static"]
|
||||||
|
++ lib.optionals
|
||||||
|
(stdenv.buildPlatform.config != stdenv.hostPlatform.config)
|
||||||
|
["-l" "c"]
|
||||||
|
++ lib.optionals
|
||||||
|
# This check has to match the one [here][0]. We only need to set
|
||||||
|
# these flags when using a different linker. Don't ask me why,
|
||||||
|
# though, because I don't know. All I know is it breaks otherwise.
|
||||||
|
#
|
||||||
|
# [0]: https://github.com/NixOS/nixpkgs/blob/612f97239e2cc474c13c9dafa0df378058c5ad8d/pkgs/build-support/rust/lib/default.nix#L36-L39
|
||||||
|
(
|
||||||
|
# Nixpkgs doesn't check for x86_64 here but we do, because I
|
||||||
|
# observed a failure building statically for x86_64 without
|
||||||
|
# including it here. Linkers are weird.
|
||||||
|
(stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64)
|
||||||
|
&& stdenv.hostPlatform.isStatic
|
||||||
|
&& !stdenv.isDarwin
|
||||||
|
&& !stdenv.cc.bintools.isLLVM
|
||||||
|
)
|
||||||
|
[
|
||||||
|
"-l"
|
||||||
|
"stdc++"
|
||||||
|
"-L"
|
||||||
|
"${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
# Shared between the package and the devShell
|
# What follows is stolen from [here][0]. Its purpose is to properly
|
||||||
nativeBuildInputs = (with pkgs.rustPlatform; [
|
# configure compilers and linkers for various stages of the build, and
|
||||||
bindgenHook
|
# even covers the case of build scripts that need native code compiled and
|
||||||
]);
|
# run on the build platform (I think).
|
||||||
|
#
|
||||||
builder =
|
# [0]: https://github.com/NixOS/nixpkgs/blob/612f97239e2cc474c13c9dafa0df378058c5ad8d/pkgs/build-support/rust/lib/default.nix#L64-L78
|
||||||
((crane.mkLib pkgs).overrideToolchain toolchain.toolchain).buildPackage;
|
// (
|
||||||
|
let
|
||||||
|
inherit (pkgs.rust.lib) envVars;
|
||||||
|
in
|
||||||
|
pkgs.lib.optionalAttrs
|
||||||
|
(pkgs.stdenv.targetPlatform.rust.rustcTarget
|
||||||
|
!= pkgs.stdenv.hostPlatform.rust.rustcTarget)
|
||||||
|
(
|
||||||
|
let
|
||||||
|
inherit (pkgs.stdenv.targetPlatform.rust) cargoEnvVarTarget;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages.default = builder {
|
"CC_${cargoEnvVarTarget}" = envVars.ccForTarget;
|
||||||
src = ./.;
|
"CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget;
|
||||||
|
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" =
|
||||||
|
envVars.linkerForTarget;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// (
|
||||||
|
let
|
||||||
|
inherit (pkgs.stdenv.hostPlatform.rust) cargoEnvVarTarget rustcTarget;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"CC_${cargoEnvVarTarget}" = envVars.ccForHost;
|
||||||
|
"CXX_${cargoEnvVarTarget}" = envVars.cxxForHost;
|
||||||
|
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost;
|
||||||
|
CARGO_BUILD_TARGET = rustcTarget;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// (
|
||||||
|
let
|
||||||
|
inherit (pkgs.stdenv.buildPlatform.rust) cargoEnvVarTarget;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"CC_${cargoEnvVarTarget}" = envVars.ccForBuild;
|
||||||
|
"CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild;
|
||||||
|
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild;
|
||||||
|
HOST_CC = "${pkgs.buildPackages.stdenv.cc}/bin/cc";
|
||||||
|
HOST_CXX = "${pkgs.buildPackages.stdenv.cc}/bin/c++";
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
inherit
|
package = pkgs: builder pkgs {
|
||||||
stdenv
|
src = nix-filter {
|
||||||
nativeBuildInputs
|
root = ./.;
|
||||||
ROCKSDB_INCLUDE_DIR
|
include = [
|
||||||
ROCKSDB_LIB_DIR;
|
"src"
|
||||||
|
"Cargo.toml"
|
||||||
|
"Cargo.lock"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.default = (pkgs.mkShell.override { inherit stdenv; }) {
|
# This is redundant with CI
|
||||||
# Rust Analyzer needs to be able to find the path to default crate
|
doCheck = false;
|
||||||
# sources, and it can read this environment variable to do so
|
|
||||||
RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/library";
|
|
||||||
|
|
||||||
inherit
|
env = env pkgs;
|
||||||
ROCKSDB_INCLUDE_DIR
|
nativeBuildInputs = nativeBuildInputs pkgs;
|
||||||
ROCKSDB_LIB_DIR;
|
|
||||||
|
meta.mainProgram = cargoToml.package.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkOciImage = pkgs: package:
|
||||||
|
pkgs.dockerTools.buildImage {
|
||||||
|
name = package.pname;
|
||||||
|
tag = "next";
|
||||||
|
copyToRoot = [
|
||||||
|
pkgs.dockerTools.caCertificates
|
||||||
|
];
|
||||||
|
config = {
|
||||||
|
# Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT)
|
||||||
|
# are handled as expected
|
||||||
|
Entrypoint = [
|
||||||
|
"${pkgs.lib.getExe' pkgs.tini "tini"}"
|
||||||
|
"--"
|
||||||
|
];
|
||||||
|
Cmd = [
|
||||||
|
"${pkgs.lib.getExe package}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = {
|
||||||
|
default = package pkgsHost;
|
||||||
|
oci-image = mkOciImage pkgsHost self.packages.${system}.default;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
builtins.listToAttrs
|
||||||
|
(builtins.concatLists
|
||||||
|
(builtins.map
|
||||||
|
(crossSystem:
|
||||||
|
let
|
||||||
|
binaryName = "static-${crossSystem}";
|
||||||
|
pkgsCrossStatic =
|
||||||
|
(import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
crossSystem = {
|
||||||
|
config = crossSystem;
|
||||||
|
};
|
||||||
|
}).pkgsStatic;
|
||||||
|
in
|
||||||
|
[
|
||||||
|
# An output for a statically-linked binary
|
||||||
|
{
|
||||||
|
name = binaryName;
|
||||||
|
value = package pkgsCrossStatic;
|
||||||
|
}
|
||||||
|
|
||||||
|
# An output for an OCI image based on that binary
|
||||||
|
{
|
||||||
|
name = "oci-image-${crossSystem}";
|
||||||
|
value = mkOciImage
|
||||||
|
pkgsCrossStatic
|
||||||
|
self.packages.${system}.${binaryName};
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
[
|
||||||
|
"x86_64-unknown-linux-musl"
|
||||||
|
"aarch64-unknown-linux-musl"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
devShells.default = pkgsHost.mkShell {
|
||||||
|
env = env pkgsHost // {
|
||||||
|
# Rust Analyzer needs to be able to find the path to default crate
|
||||||
|
# sources, and it can read this environment variable to do so. The
|
||||||
|
# `rust-src` component is required in order for this to work.
|
||||||
|
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
|
||||||
|
};
|
||||||
|
|
||||||
# Development tools
|
# Development tools
|
||||||
nativeBuildInputs = nativeBuildInputs ++ (with toolchain; [
|
nativeBuildInputs = nativeBuildInputs pkgsHost ++ [
|
||||||
cargo
|
# Always use nightly rustfmt because most of its options are unstable
|
||||||
clippy
|
#
|
||||||
rust-src
|
# This needs to come before `toolchain` in this list, otherwise
|
||||||
rustc
|
# `$PATH` will have stable rustfmt instead.
|
||||||
rustfmt
|
fenix.packages.${system}.latest.rustfmt
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
checks = {
|
toolchain
|
||||||
packagesDefault = self.packages.${system}.default;
|
] ++ (with pkgsHost; [
|
||||||
devShellsDefault = self.devShells.${system}.default;
|
engage
|
||||||
|
|
||||||
|
# Needed for Complement
|
||||||
|
go
|
||||||
|
olm
|
||||||
|
|
||||||
|
# Needed for our script for Complement
|
||||||
|
jq
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
22
rust-toolchain.toml
Normal file
22
rust-toolchain.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# This is the authoritiative configuration of this project's Rust toolchain.
|
||||||
|
#
|
||||||
|
# Other files that need upkeep when this changes:
|
||||||
|
#
|
||||||
|
# * `.gitlab-ci.yml`
|
||||||
|
# * `Cargo.toml`
|
||||||
|
# * `flake.nix`
|
||||||
|
#
|
||||||
|
# Search in those files for `rust-toolchain.toml` to find the relevant places.
|
||||||
|
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||||
|
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.75.0"
|
||||||
|
components = [
|
||||||
|
# For rust-analyzer
|
||||||
|
"rust-src",
|
||||||
|
]
|
||||||
|
targets = [
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"x86_64-unknown-linux-musl",
|
||||||
|
"aarch64-unknown-linux-musl",
|
||||||
|
]
|
|
@ -17,7 +17,11 @@ use ruma::{
|
||||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::{
|
||||||
|
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/keys/upload`
|
/// # `POST /_matrix/client/r0/keys/upload`
|
||||||
///
|
///
|
||||||
|
@ -335,31 +339,68 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
|
|
||||||
let mut failures = BTreeMap::new();
|
let mut failures = BTreeMap::new();
|
||||||
|
|
||||||
|
let back_off = |id| match services()
|
||||||
|
.globals
|
||||||
|
.bad_query_ratelimiter
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.entry(id)
|
||||||
|
{
|
||||||
|
hash_map::Entry::Vacant(e) => {
|
||||||
|
e.insert((Instant::now(), 1));
|
||||||
|
}
|
||||||
|
hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
||||||
|
};
|
||||||
|
|
||||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
let mut futures: FuturesUnordered<_> = get_over_federation
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(server, vec)| async move {
|
.map(|(server, vec)| async move {
|
||||||
|
if let Some((time, tries)) = services()
|
||||||
|
.globals
|
||||||
|
.bad_query_ratelimiter
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(server)
|
||||||
|
{
|
||||||
|
// Exponential backoff
|
||||||
|
let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries);
|
||||||
|
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||||
|
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.elapsed() < min_elapsed_duration {
|
||||||
|
debug!("Backing off query from {:?}", server);
|
||||||
|
return (
|
||||||
|
server,
|
||||||
|
Err(Error::BadServerResponse("bad query, still backing off")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut device_keys_input_fed = BTreeMap::new();
|
let mut device_keys_input_fed = BTreeMap::new();
|
||||||
for (user_id, keys) in vec {
|
for (user_id, keys) in vec {
|
||||||
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
server,
|
server,
|
||||||
services()
|
tokio::time::timeout(
|
||||||
.sending
|
Duration::from_secs(25),
|
||||||
.send_federation_request(
|
services().sending.send_federation_request(
|
||||||
server,
|
server,
|
||||||
federation::keys::get_keys::v1::Request {
|
federation::keys::get_keys::v1::Request {
|
||||||
device_keys: device_keys_input_fed,
|
device_keys: device_keys_input_fed,
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.await,
|
.await
|
||||||
|
.map_err(|_e| Error::BadServerResponse("Query took too long")),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
while let Some((server, response)) = futures.next().await {
|
while let Some((server, response)) = futures.next().await {
|
||||||
match response {
|
match response {
|
||||||
Ok(response) => {
|
Ok(Ok(response)) => {
|
||||||
for (user, masterkey) in response.master_keys {
|
for (user, masterkey) in response.master_keys {
|
||||||
let (master_key_id, mut master_key) =
|
let (master_key_id, mut master_key) =
|
||||||
services().users.parse_master_key(&user, &masterkey)?;
|
services().users.parse_master_key(&user, &masterkey)?;
|
||||||
|
@ -386,7 +427,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
self_signing_keys.extend(response.self_signing_keys);
|
self_signing_keys.extend(response.self_signing_keys);
|
||||||
device_keys.extend(response.device_keys);
|
device_keys.extend(response.device_keys);
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
_ => {
|
||||||
|
back_off(server.to_owned());
|
||||||
failures.insert(server.to_string(), json!({}));
|
failures.insert(server.to_string(), json!({}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub async fn create_content_route(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(create_content::v3::Response {
|
Ok(create_content::v3::Response {
|
||||||
content_uri: mxc.try_into().expect("Invalid mxc:// URI"),
|
content_uri: mxc.into(),
|
||||||
blurhash: None,
|
blurhash: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,12 @@ pub async fn join_room_by_id_route(
|
||||||
.map(|user| user.server_name().to_owned()),
|
.map(|user| user.server_name().to_owned()),
|
||||||
);
|
);
|
||||||
|
|
||||||
servers.push(body.room_id.server_name().to_owned());
|
servers.push(
|
||||||
|
body.room_id
|
||||||
|
.server_name()
|
||||||
|
.expect("Room IDs should always have a server name")
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
|
||||||
join_room_by_id_helper(
|
join_room_by_id_helper(
|
||||||
body.sender_user.as_deref(),
|
body.sender_user.as_deref(),
|
||||||
|
@ -105,7 +110,12 @@ pub async fn join_room_by_id_or_alias_route(
|
||||||
.map(|user| user.server_name().to_owned()),
|
.map(|user| user.server_name().to_owned()),
|
||||||
);
|
);
|
||||||
|
|
||||||
servers.push(room_id.server_name().to_owned());
|
servers.push(
|
||||||
|
room_id
|
||||||
|
.server_name()
|
||||||
|
.expect("Room IDs should always have a server name")
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
|
||||||
(servers, room_id)
|
(servers, room_id)
|
||||||
}
|
}
|
||||||
|
@ -400,7 +410,7 @@ pub async fn get_member_events_route(
|
||||||
if !services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(&sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -435,7 +445,7 @@ pub async fn joined_members_route(
|
||||||
if !services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(&sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -619,7 +629,7 @@ async fn join_room_by_id_helper(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(signature) = signed_value["signatures"]
|
match signed_value["signatures"]
|
||||||
.as_object()
|
.as_object()
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
|
@ -630,20 +640,22 @@ async fn join_room_by_id_helper(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Server did not send its signature",
|
"Server did not send its signature",
|
||||||
))
|
))
|
||||||
})
|
}) {
|
||||||
{
|
Ok(signature) => {
|
||||||
join_event
|
join_event
|
||||||
.get_mut("signatures")
|
.get_mut("signatures")
|
||||||
.expect("we created a valid pdu")
|
.expect("we created a valid pdu")
|
||||||
.as_object_mut()
|
.as_object_mut()
|
||||||
.expect("we created a valid pdu")
|
.expect("we created a valid pdu")
|
||||||
.insert(remote_server.to_string(), signature.clone());
|
.insert(remote_server.to_string(), signature.clone());
|
||||||
} else {
|
}
|
||||||
|
Err(e) => {
|
||||||
warn!(
|
warn!(
|
||||||
"Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}",
|
"Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
services().rooms.short.get_or_create_shortroomid(room_id)?;
|
services().rooms.short.get_or_create_shortroomid(room_id)?;
|
||||||
|
|
||||||
|
@ -710,7 +722,7 @@ async fn join_room_by_id_helper(
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Running send_join auth check");
|
info!("Running send_join auth check");
|
||||||
if !state_res::event_auth::auth_check(
|
let authenticated = state_res::event_auth::auth_check(
|
||||||
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
|
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
|
||||||
&parsed_join_pdu,
|
&parsed_join_pdu,
|
||||||
None::<PduEvent>, // TODO: third party invite
|
None::<PduEvent>, // TODO: third party invite
|
||||||
|
@ -733,7 +745,9 @@ async fn join_room_by_id_helper(
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
warn!("Auth check failed: {e}");
|
warn!("Auth check failed: {e}");
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed")
|
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed")
|
||||||
})? {
|
})?;
|
||||||
|
|
||||||
|
if !authenticated {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Auth check failed",
|
"Auth check failed",
|
||||||
|
@ -1362,7 +1376,7 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||||
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
||||||
// Ask a remote server if we don't have this room
|
// Ask a remote server if we don't have this room
|
||||||
if !services().rooms.metadata.exists(room_id)?
|
if !services().rooms.metadata.exists(room_id)?
|
||||||
&& room_id.server_name() != services().globals.server_name()
|
&& room_id.server_name() != Some(services().globals.server_name())
|
||||||
{
|
{
|
||||||
if let Err(e) = remote_leave_room(user_id, room_id).await {
|
if let Err(e) = remote_leave_room(user_id, room_id).await {
|
||||||
warn!("Failed to leave room {} remotely: {}", user_id, e);
|
warn!("Failed to leave room {} remotely: {}", user_id, e);
|
||||||
|
|
|
@ -124,7 +124,7 @@ pub async fn get_message_events_route(
|
||||||
let to = body
|
let to = body
|
||||||
.to
|
.to
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|t| PduCount::try_from_string(&t).ok());
|
.and_then(|t| PduCount::try_from_string(t).ok());
|
||||||
|
|
||||||
services().rooms.lazy_loading.lazy_load_confirm_delivery(
|
services().rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||||
sender_user,
|
sender_user,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use crate::{services, utils, Result, Ruma};
|
use crate::{services, utils, Error, Result, Ruma};
|
||||||
use ruma::api::client::presence::{get_presence, set_presence};
|
use ruma::api::client::{
|
||||||
|
error::ErrorKind,
|
||||||
|
presence::{get_presence, set_presence},
|
||||||
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
||||||
|
@ -79,6 +82,9 @@ pub async fn get_presence_route(
|
||||||
presence: presence.content.presence,
|
presence: presence.content.presence,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
todo!();
|
Err(Error::BadRequest(
|
||||||
|
ErrorKind::NotFound,
|
||||||
|
"Presence state for this user was not found",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub async fn redact_event_route(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomRedaction,
|
event_type: TimelineEventType::RoomRedaction,
|
||||||
content: to_raw_value(&RoomRedactionEventContent {
|
content: to_raw_value(&RoomRedactionEventContent {
|
||||||
|
redacts: Some(body.event_id.clone()),
|
||||||
reason: body.reason.clone(),
|
reason: body.reason.clone(),
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||||
let to = body
|
let to = body
|
||||||
.to
|
.to
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|t| PduCount::try_from_string(&t).ok());
|
.and_then(|t| PduCount::try_from_string(t).ok());
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
// Use limit or else 10, with maximum 100
|
||||||
let limit = body
|
let limit = body
|
||||||
|
@ -73,7 +73,7 @@ pub async fn get_relating_events_with_rel_type_route(
|
||||||
let to = body
|
let to = body
|
||||||
.to
|
.to
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|t| PduCount::try_from_string(&t).ok());
|
.and_then(|t| PduCount::try_from_string(t).ok());
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
// Use limit or else 10, with maximum 100
|
||||||
let limit = body
|
let limit = body
|
||||||
|
@ -121,7 +121,7 @@ pub async fn get_relating_events_route(
|
||||||
let to = body
|
let to = body
|
||||||
.to
|
.to
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|t| PduCount::try_from_string(&t).ok());
|
.and_then(|t| PduCount::try_from_string(t).ok());
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
// Use limit or else 10, with maximum 100
|
||||||
let limit = body
|
let limit = body
|
||||||
|
|
|
@ -142,8 +142,9 @@ pub async fn create_room_route(
|
||||||
content
|
content
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
// TODO: Add correct value for v11
|
||||||
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
||||||
to_raw_value(&RoomCreateEventContent::new(sender_user.clone()))
|
to_raw_value(&RoomCreateEventContent::new_v1(sender_user.clone()))
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
||||||
.get(),
|
.get(),
|
||||||
)
|
)
|
||||||
|
@ -365,7 +366,7 @@ pub async fn create_room_route(
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomName,
|
event_type: TimelineEventType::RoomName,
|
||||||
content: to_raw_value(&RoomNameEventContent::new(Some(name.clone())))
|
content: to_raw_value(&RoomNameEventContent::new(name.clone()))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
|
|
|
@ -42,24 +42,31 @@ pub async fn get_login_types_route(
|
||||||
/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
||||||
/// supported login types.
|
/// supported login types.
|
||||||
pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
|
pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
|
||||||
|
// To allow deprecated login methods
|
||||||
|
#![allow(deprecated)]
|
||||||
// Validate login method
|
// Validate login method
|
||||||
// TODO: Other login methods
|
// TODO: Other login methods
|
||||||
let user_id = match &body.login_info {
|
let user_id = match &body.login_info {
|
||||||
login::v3::LoginInfo::Password(login::v3::Password {
|
login::v3::LoginInfo::Password(login::v3::Password {
|
||||||
identifier,
|
identifier,
|
||||||
password,
|
password,
|
||||||
|
user,
|
||||||
|
address: _,
|
||||||
|
medium: _,
|
||||||
}) => {
|
}) => {
|
||||||
let username = if let UserIdentifier::UserIdOrLocalpart(user_id) = identifier {
|
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||||
user_id.to_lowercase()
|
UserId::parse_with_server_name(
|
||||||
|
user_id.to_lowercase(),
|
||||||
|
services().globals.server_name(),
|
||||||
|
)
|
||||||
|
} else if let Some(user) = user {
|
||||||
|
UserId::parse(user)
|
||||||
} else {
|
} else {
|
||||||
warn!("Bad login type: {:?}", &body.login_info);
|
warn!("Bad login type: {:?}", &body.login_info);
|
||||||
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
||||||
};
|
}
|
||||||
let user_id =
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||||
UserId::parse_with_server_name(username, services().globals.server_name())
|
|
||||||
.map_err(|_| {
|
|
||||||
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
|
||||||
})?;
|
|
||||||
let hash = services()
|
let hash = services()
|
||||||
.users
|
.users
|
||||||
.password_hash(&user_id)?
|
.password_hash(&user_id)?
|
||||||
|
@ -105,24 +112,28 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService { identifier }) => {
|
login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
||||||
|
identifier,
|
||||||
|
user,
|
||||||
|
}) => {
|
||||||
if !body.from_appservice {
|
if !body.from_appservice {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"Forbidden login type.",
|
"Forbidden login type.",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let username = if let UserIdentifier::UserIdOrLocalpart(user_id) = identifier {
|
if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||||
user_id.to_lowercase()
|
UserId::parse_with_server_name(
|
||||||
|
user_id.to_lowercase(),
|
||||||
|
services().globals.server_name(),
|
||||||
|
)
|
||||||
|
} else if let Some(user) = user {
|
||||||
|
UserId::parse(user)
|
||||||
} else {
|
} else {
|
||||||
|
warn!("Bad login type: {:?}", &body.login_info);
|
||||||
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
||||||
};
|
}
|
||||||
let user_id =
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?
|
||||||
UserId::parse_with_server_name(username, services().globals.server_name())
|
|
||||||
.map_err(|_| {
|
|
||||||
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
|
||||||
})?;
|
|
||||||
user_id
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Unsupported or unknown login type: {:?}", &body.login_info);
|
warn!("Unsupported or unknown login type: {:?}", &body.login_info);
|
||||||
|
@ -163,6 +174,8 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
|
|
||||||
info!("{} logged in", user_id);
|
info!("{} logged in", user_id);
|
||||||
|
|
||||||
|
// Homeservers are still required to send the `home_server` field
|
||||||
|
#[allow(deprecated)]
|
||||||
Ok(login::v3::Response {
|
Ok(login::v3::Response {
|
||||||
user_id,
|
user_id,
|
||||||
access_token: token,
|
access_token: token,
|
||||||
|
|
|
@ -85,7 +85,7 @@ pub async fn get_state_events_route(
|
||||||
if !services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(&sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -118,7 +118,7 @@ pub async fn get_state_events_for_key_route(
|
||||||
if !services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(&sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -157,7 +157,7 @@ pub async fn get_state_events_for_empty_key_route(
|
||||||
if !services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(&sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
|
|
@ -20,7 +20,7 @@ use ruma::{
|
||||||
StateEventType, TimelineEventType,
|
StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
uint, DeviceId, OwnedDeviceId, OwnedUserId, RoomId, UInt, UserId,
|
uint, DeviceId, JsOption, OwnedDeviceId, OwnedUserId, RoomId, UInt, UserId,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet},
|
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet},
|
||||||
|
@ -554,6 +554,7 @@ async fn sync_helper(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn load_joined_room(
|
async fn load_joined_room(
|
||||||
sender_user: &UserId,
|
sender_user: &UserId,
|
||||||
sender_device: &DeviceId,
|
sender_device: &DeviceId,
|
||||||
|
@ -590,7 +591,7 @@ async fn load_joined_room(
|
||||||
|| services()
|
|| services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.last_notification_read(&sender_user, &room_id)?
|
.last_notification_read(sender_user, room_id)?
|
||||||
> since;
|
> since;
|
||||||
|
|
||||||
let mut timeline_users = HashSet::new();
|
let mut timeline_users = HashSet::new();
|
||||||
|
@ -599,16 +600,16 @@ async fn load_joined_room(
|
||||||
}
|
}
|
||||||
|
|
||||||
services().rooms.lazy_loading.lazy_load_confirm_delivery(
|
services().rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||||
&sender_user,
|
sender_user,
|
||||||
&sender_device,
|
sender_device,
|
||||||
&room_id,
|
room_id,
|
||||||
sincecount,
|
sincecount,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Database queries:
|
// Database queries:
|
||||||
|
|
||||||
let current_shortstatehash =
|
let current_shortstatehash =
|
||||||
if let Some(s) = services().rooms.state.get_room_shortstatehash(&room_id)? {
|
if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? {
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
error!("Room {} has no state", room_id);
|
error!("Room {} has no state", room_id);
|
||||||
|
@ -618,7 +619,7 @@ async fn load_joined_room(
|
||||||
let since_shortstatehash = services()
|
let since_shortstatehash = services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_token_shortstatehash(&room_id, since)?;
|
.get_token_shortstatehash(room_id, since)?;
|
||||||
|
|
||||||
let (heroes, joined_member_count, invited_member_count, joined_since_last_sync, state_events) =
|
let (heroes, joined_member_count, invited_member_count, joined_since_last_sync, state_events) =
|
||||||
if timeline_pdus.is_empty() && since_shortstatehash == Some(current_shortstatehash) {
|
if timeline_pdus.is_empty() && since_shortstatehash == Some(current_shortstatehash) {
|
||||||
|
@ -630,12 +631,12 @@ async fn load_joined_room(
|
||||||
let joined_member_count = services()
|
let joined_member_count = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(&room_id)?
|
.room_joined_count(room_id)?
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let invited_member_count = services()
|
let invited_member_count = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_invited_count(&room_id)?
|
.room_invited_count(room_id)?
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
// Recalculate heroes (first 5 members)
|
// Recalculate heroes (first 5 members)
|
||||||
|
@ -648,7 +649,7 @@ async fn load_joined_room(
|
||||||
for hero in services()
|
for hero in services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.all_pdus(&sender_user, &room_id)?
|
.all_pdus(sender_user, room_id)?
|
||||||
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
||||||
.filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember)
|
.filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember)
|
||||||
.map(|(_, pdu)| {
|
.map(|(_, pdu)| {
|
||||||
|
@ -669,11 +670,11 @@ async fn load_joined_room(
|
||||||
) && (services()
|
) && (services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.is_joined(&user_id, &room_id)?
|
.is_joined(&user_id, room_id)?
|
||||||
|| services()
|
|| services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.is_invited(&user_id, &room_id)?)
|
.is_invited(&user_id, room_id)?)
|
||||||
{
|
{
|
||||||
Ok::<_, Error>(Some(state_key.clone()))
|
Ok::<_, Error>(Some(state_key.clone()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -789,17 +790,17 @@ async fn load_joined_room(
|
||||||
|
|
||||||
// Reset lazy loading because this is an initial sync
|
// Reset lazy loading because this is an initial sync
|
||||||
services().rooms.lazy_loading.lazy_load_reset(
|
services().rooms.lazy_loading.lazy_load_reset(
|
||||||
&sender_user,
|
sender_user,
|
||||||
&sender_device,
|
sender_device,
|
||||||
&room_id,
|
room_id,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// The state_events above should contain all timeline_users, let's mark them as lazy
|
// The state_events above should contain all timeline_users, let's mark them as lazy
|
||||||
// loaded.
|
// loaded.
|
||||||
services().rooms.lazy_loading.lazy_load_mark_sent(
|
services().rooms.lazy_loading.lazy_load_mark_sent(
|
||||||
&sender_user,
|
sender_user,
|
||||||
&sender_device,
|
sender_device,
|
||||||
&room_id,
|
room_id,
|
||||||
lazy_loaded,
|
lazy_loaded,
|
||||||
next_batchcount,
|
next_batchcount,
|
||||||
);
|
);
|
||||||
|
@ -866,14 +867,14 @@ async fn load_joined_room(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
&sender_user,
|
sender_user,
|
||||||
&sender_device,
|
sender_device,
|
||||||
&room_id,
|
room_id,
|
||||||
&event.sender,
|
&event.sender,
|
||||||
)? || lazy_load_send_redundant
|
)? || 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,
|
room_id,
|
||||||
&StateEventType::RoomMember,
|
&StateEventType::RoomMember,
|
||||||
event.sender.as_str(),
|
event.sender.as_str(),
|
||||||
)? {
|
)? {
|
||||||
|
@ -884,9 +885,9 @@ async fn load_joined_room(
|
||||||
}
|
}
|
||||||
|
|
||||||
services().rooms.lazy_loading.lazy_load_mark_sent(
|
services().rooms.lazy_loading.lazy_load_mark_sent(
|
||||||
&sender_user,
|
sender_user,
|
||||||
&sender_device,
|
sender_device,
|
||||||
&room_id,
|
room_id,
|
||||||
lazy_loaded,
|
lazy_loaded,
|
||||||
next_batchcount,
|
next_batchcount,
|
||||||
);
|
);
|
||||||
|
@ -934,7 +935,7 @@ async fn load_joined_room(
|
||||||
match new_membership {
|
match new_membership {
|
||||||
MembershipState::Join => {
|
MembershipState::Join => {
|
||||||
// A new user joined an encrypted room
|
// A new user joined an encrypted room
|
||||||
if !share_encrypted_room(&sender_user, &user_id, &room_id)? {
|
if !share_encrypted_room(sender_user, &user_id, room_id)? {
|
||||||
device_list_updates.insert(user_id);
|
device_list_updates.insert(user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -954,15 +955,15 @@ async fn load_joined_room(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(room_id)
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Don't send key updates from the sender to the sender
|
// Don't send key updates from the sender to the sender
|
||||||
&sender_user != user_id
|
sender_user != user_id
|
||||||
})
|
})
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Only send keys if the sender doesn't share an encrypted room with the target already
|
// Only send keys if the sender doesn't share an encrypted room with the target already
|
||||||
!share_encrypted_room(&sender_user, user_id, &room_id)
|
!share_encrypted_room(sender_user, user_id, room_id)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -997,7 +998,7 @@ async fn load_joined_room(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.notification_count(&sender_user, &room_id)?
|
.notification_count(sender_user, room_id)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("notification count can't go that high"),
|
.expect("notification count can't go that high"),
|
||||||
)
|
)
|
||||||
|
@ -1010,7 +1011,7 @@ async fn load_joined_room(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.highlight_count(&sender_user, &room_id)?
|
.highlight_count(sender_user, room_id)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("highlight count can't go that high"),
|
.expect("highlight count can't go that high"),
|
||||||
)
|
)
|
||||||
|
@ -1039,15 +1040,15 @@ async fn load_joined_room(
|
||||||
.rooms
|
.rooms
|
||||||
.edus
|
.edus
|
||||||
.read_receipt
|
.read_receipt
|
||||||
.readreceipts_since(&room_id, since)
|
.readreceipts_since(room_id, since)
|
||||||
.filter_map(|r| r.ok()) // Filter out buggy events
|
.filter_map(|r| r.ok()) // Filter out buggy events
|
||||||
.map(|(_, _, v)| v)
|
.map(|(_, _, v)| v)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if services().rooms.edus.typing.last_typing_update(&room_id)? > since {
|
if services().rooms.edus.typing.last_typing_update(room_id)? > since {
|
||||||
edus.push(
|
edus.push(
|
||||||
serde_json::from_str(
|
serde_json::from_str(
|
||||||
&serde_json::to_string(&services().rooms.edus.typing.typings_all(&room_id)?)
|
&serde_json::to_string(&services().rooms.edus.typing.typings_all(room_id)?)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
)
|
)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
|
@ -1056,7 +1057,7 @@ async fn load_joined_room(
|
||||||
|
|
||||||
// Save the state after this sync so we can send the correct state diff next sync
|
// Save the state after this sync so we can send the correct state diff next sync
|
||||||
services().rooms.user.associate_token_shortstatehash(
|
services().rooms.user.associate_token_shortstatehash(
|
||||||
&room_id,
|
room_id,
|
||||||
next_batch,
|
next_batch,
|
||||||
current_shortstatehash,
|
current_shortstatehash,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1065,7 +1066,7 @@ async fn load_joined_room(
|
||||||
account_data: RoomAccountData {
|
account_data: RoomAccountData {
|
||||||
events: services()
|
events: services()
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(Some(&room_id), &sender_user, since)?
|
.changes_since(Some(room_id), sender_user, since)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_, v)| {
|
.filter_map(|(_, v)| {
|
||||||
serde_json::from_str(v.json().get())
|
serde_json::from_str(v.json().get())
|
||||||
|
@ -1102,7 +1103,7 @@ async fn load_joined_room(
|
||||||
fn load_timeline(
|
fn load_timeline(
|
||||||
sender_user: &UserId,
|
sender_user: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
sincecount: PduCount,
|
roomsincecount: PduCount,
|
||||||
limit: u64,
|
limit: u64,
|
||||||
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
||||||
let timeline_pdus;
|
let timeline_pdus;
|
||||||
|
@ -1110,13 +1111,13 @@ fn load_timeline(
|
||||||
if services()
|
if services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.last_timeline_count(&sender_user, &room_id)?
|
.last_timeline_count(sender_user, room_id)?
|
||||||
> sincecount
|
> roomsincecount
|
||||||
{
|
{
|
||||||
let mut non_timeline_pdus = services()
|
let mut non_timeline_pdus = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(&sender_user, &room_id, PduCount::max())?
|
.pdus_until(sender_user, room_id, PduCount::max())?
|
||||||
.filter_map(|r| {
|
.filter_map(|r| {
|
||||||
// Filter out buggy events
|
// Filter out buggy events
|
||||||
if r.is_err() {
|
if r.is_err() {
|
||||||
|
@ -1124,7 +1125,7 @@ fn load_timeline(
|
||||||
}
|
}
|
||||||
r.ok()
|
r.ok()
|
||||||
})
|
})
|
||||||
.take_while(|(pducount, _)| pducount > &sincecount);
|
.take_while(|(pducount, _)| pducount > &roomsincecount);
|
||||||
|
|
||||||
// Take the last events for the timeline
|
// Take the last events for the timeline
|
||||||
timeline_pdus = non_timeline_pdus
|
timeline_pdus = non_timeline_pdus
|
||||||
|
@ -1178,16 +1179,15 @@ pub async fn sync_events_v4_route(
|
||||||
// Setup watchers, so if there's no response, we can wait for them
|
// Setup watchers, so if there's no response, we can wait for them
|
||||||
let watcher = 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.next_count()?;
|
||||||
|
|
||||||
let since = body
|
let globalsince = body
|
||||||
.pos
|
.pos
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|string| string.parse().ok())
|
.and_then(|string| string.parse().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let sincecount = PduCount::Normal(since);
|
|
||||||
|
|
||||||
if since == 0 {
|
if globalsince == 0 {
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services().users.forget_sync_request_connection(
|
services().users.forget_sync_request_connection(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
|
@ -1214,7 +1214,7 @@ pub async fn sync_events_v4_route(
|
||||||
if body.extensions.to_device.enabled.unwrap_or(false) {
|
if body.extensions.to_device.enabled.unwrap_or(false) {
|
||||||
services()
|
services()
|
||||||
.users
|
.users
|
||||||
.remove_to_device_events(&sender_user, &sender_device, since)?;
|
.remove_to_device_events(&sender_user, &sender_device, globalsince)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut left_encrypted_users = HashSet::new(); // Users that have left any encrypted rooms the sender was in
|
let mut left_encrypted_users = HashSet::new(); // Users that have left any encrypted rooms the sender was in
|
||||||
|
@ -1226,13 +1226,13 @@ pub async fn sync_events_v4_route(
|
||||||
device_list_changes.extend(
|
device_list_changes.extend(
|
||||||
services()
|
services()
|
||||||
.users
|
.users
|
||||||
.keys_changed(sender_user.as_ref(), since, None)
|
.keys_changed(sender_user.as_ref(), globalsince, None)
|
||||||
.filter_map(|r| r.ok()),
|
.filter_map(|r| r.ok()),
|
||||||
);
|
);
|
||||||
|
|
||||||
for room_id in &all_joined_rooms {
|
for room_id in &all_joined_rooms {
|
||||||
let current_shortstatehash =
|
let current_shortstatehash =
|
||||||
if let Some(s) = services().rooms.state.get_room_shortstatehash(&room_id)? {
|
if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? {
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
error!("Room {} has no state", room_id);
|
error!("Room {} has no state", room_id);
|
||||||
|
@ -1242,7 +1242,7 @@ pub async fn sync_events_v4_route(
|
||||||
let since_shortstatehash = services()
|
let since_shortstatehash = services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_token_shortstatehash(&room_id, since)?;
|
.get_token_shortstatehash(room_id, globalsince)?;
|
||||||
|
|
||||||
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
||||||
.and_then(|shortstatehash| {
|
.and_then(|shortstatehash| {
|
||||||
|
@ -1331,7 +1331,7 @@ pub async fn sync_events_v4_route(
|
||||||
if !share_encrypted_room(
|
if !share_encrypted_room(
|
||||||
&sender_user,
|
&sender_user,
|
||||||
&user_id,
|
&user_id,
|
||||||
&room_id,
|
room_id,
|
||||||
)? {
|
)? {
|
||||||
device_list_changes.insert(user_id);
|
device_list_changes.insert(user_id);
|
||||||
}
|
}
|
||||||
|
@ -1352,7 +1352,7 @@ pub async fn sync_events_v4_route(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(room_id)
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Don't send key updates from the sender to the sender
|
// Don't send key updates from the sender to the sender
|
||||||
|
@ -1360,7 +1360,7 @@ pub async fn sync_events_v4_route(
|
||||||
})
|
})
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Only send keys if the sender doesn't share an encrypted room with the target already
|
// Only send keys if the sender doesn't share an encrypted room with the target already
|
||||||
!share_encrypted_room(&sender_user, user_id, &room_id)
|
!share_encrypted_room(&sender_user, user_id, room_id)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -1371,7 +1371,7 @@ pub async fn sync_events_v4_route(
|
||||||
device_list_changes.extend(
|
device_list_changes.extend(
|
||||||
services()
|
services()
|
||||||
.users
|
.users
|
||||||
.keys_changed(room_id.as_ref(), since, None)
|
.keys_changed(room_id.as_ref(), globalsince, None)
|
||||||
.filter_map(|r| r.ok()),
|
.filter_map(|r| r.ok()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1408,7 +1408,7 @@ pub async fn sync_events_v4_route(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_known_rooms = BTreeMap::new();
|
let mut new_known_rooms = BTreeSet::new();
|
||||||
|
|
||||||
lists.insert(
|
lists.insert(
|
||||||
list_id.clone(),
|
list_id.clone(),
|
||||||
|
@ -1424,12 +1424,12 @@ pub async fn sync_events_v4_route(
|
||||||
let room_ids = all_joined_rooms
|
let room_ids = all_joined_rooms
|
||||||
[(u64::from(r.0) as usize)..=(u64::from(r.1) as usize)]
|
[(u64::from(r.0) as usize)..=(u64::from(r.1) as usize)]
|
||||||
.to_vec();
|
.to_vec();
|
||||||
new_known_rooms.extend(room_ids.iter().cloned().map(|r| (r, true)));
|
new_known_rooms.extend(room_ids.iter().cloned());
|
||||||
for room_id in &room_ids {
|
for room_id in &room_ids {
|
||||||
let todo_room = todo_rooms.entry(room_id.clone()).or_insert((
|
let todo_room = todo_rooms.entry(room_id.clone()).or_insert((
|
||||||
BTreeSet::new(),
|
BTreeSet::new(),
|
||||||
0,
|
0,
|
||||||
true,
|
u64::MAX,
|
||||||
));
|
));
|
||||||
let limit = list
|
let limit = list
|
||||||
.room_details
|
.room_details
|
||||||
|
@ -1440,14 +1440,18 @@ pub async fn sync_events_v4_route(
|
||||||
.0
|
.0
|
||||||
.extend(list.room_details.required_state.iter().cloned());
|
.extend(list.room_details.required_state.iter().cloned());
|
||||||
todo_room.1 = todo_room.1.max(limit);
|
todo_room.1 = todo_room.1.max(limit);
|
||||||
if known_rooms.get(&list_id).and_then(|k| k.get(room_id)) != Some(&true)
|
// 0 means unknown because it got out of date
|
||||||
{
|
todo_room.2 = todo_room.2.min(
|
||||||
todo_room.2 = false;
|
known_rooms
|
||||||
}
|
.get(&list_id)
|
||||||
|
.and_then(|k| k.get(room_id))
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
sync_events::v4::SyncOp {
|
sync_events::v4::SyncOp {
|
||||||
op: SlidingOp::Sync,
|
op: SlidingOp::Sync,
|
||||||
range: Some(r.clone()),
|
range: Some(r),
|
||||||
index: None,
|
index: None,
|
||||||
room_ids,
|
room_ids,
|
||||||
room_id: None,
|
room_id: None,
|
||||||
|
@ -1465,26 +1469,31 @@ pub async fn sync_events_v4_route(
|
||||||
conn_id.clone(),
|
conn_id.clone(),
|
||||||
list_id,
|
list_id,
|
||||||
new_known_rooms,
|
new_known_rooms,
|
||||||
|
globalsince,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut known_subscription_rooms = BTreeMap::new();
|
let mut known_subscription_rooms = BTreeSet::new();
|
||||||
for (room_id, room) in &body.room_subscriptions {
|
for (room_id, room) in &body.room_subscriptions {
|
||||||
|
if !services().rooms.metadata.exists(room_id)? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let todo_room = todo_rooms
|
let todo_room = todo_rooms
|
||||||
.entry(room_id.clone())
|
.entry(room_id.clone())
|
||||||
.or_insert((BTreeSet::new(), 0, true));
|
.or_insert((BTreeSet::new(), 0, u64::MAX));
|
||||||
let limit = room.timeline_limit.map_or(10, u64::from).min(100);
|
let limit = room.timeline_limit.map_or(10, u64::from).min(100);
|
||||||
todo_room.0.extend(room.required_state.iter().cloned());
|
todo_room.0.extend(room.required_state.iter().cloned());
|
||||||
todo_room.1 = todo_room.1.max(limit);
|
todo_room.1 = todo_room.1.max(limit);
|
||||||
if known_rooms
|
// 0 means unknown because it got out of date
|
||||||
|
todo_room.2 = todo_room.2.min(
|
||||||
|
known_rooms
|
||||||
.get("subscriptions")
|
.get("subscriptions")
|
||||||
.and_then(|k| k.get(room_id))
|
.and_then(|k| k.get(room_id))
|
||||||
!= Some(&true)
|
.copied()
|
||||||
{
|
.unwrap_or(0),
|
||||||
todo_room.2 = false;
|
);
|
||||||
}
|
known_subscription_rooms.insert(room_id.clone());
|
||||||
known_subscription_rooms.insert(room_id.clone(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for r in body.unsubscribe_rooms {
|
for r in body.unsubscribe_rooms {
|
||||||
|
@ -1499,6 +1508,7 @@ pub async fn sync_events_v4_route(
|
||||||
conn_id.clone(),
|
conn_id.clone(),
|
||||||
"subscriptions".to_owned(),
|
"subscriptions".to_owned(),
|
||||||
known_subscription_rooms,
|
known_subscription_rooms,
|
||||||
|
globalsince,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1512,12 +1522,13 @@ pub async fn sync_events_v4_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rooms = BTreeMap::new();
|
let mut rooms = BTreeMap::new();
|
||||||
for (room_id, (required_state_request, timeline_limit, known)) in &todo_rooms {
|
for (room_id, (required_state_request, timeline_limit, roomsince)) in &todo_rooms {
|
||||||
// TODO: per-room sync tokens
|
let roomsincecount = PduCount::Normal(*roomsince);
|
||||||
let (timeline_pdus, limited) =
|
|
||||||
load_timeline(&sender_user, &room_id, sincecount, *timeline_limit)?;
|
|
||||||
|
|
||||||
if *known && timeline_pdus.is_empty() {
|
let (timeline_pdus, limited) =
|
||||||
|
load_timeline(&sender_user, room_id, roomsincecount, *timeline_limit)?;
|
||||||
|
|
||||||
|
if roomsince != &0 && timeline_pdus.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,8 +1544,8 @@ pub async fn sync_events_v4_route(
|
||||||
}))
|
}))
|
||||||
})?
|
})?
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if since != 0 {
|
if roomsince != &0 {
|
||||||
Some(since.to_string())
|
Some(roomsince.to_string())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1547,30 +1558,31 @@ pub async fn sync_events_v4_route(
|
||||||
|
|
||||||
let required_state = required_state_request
|
let required_state = required_state_request
|
||||||
.iter()
|
.iter()
|
||||||
.map(|state| {
|
.flat_map(|state| {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &state.0, &state.1)
|
.room_state_get(room_id, &state.0, &state.1)
|
||||||
})
|
.ok()
|
||||||
.filter_map(|r| r.ok())
|
.flatten()
|
||||||
.filter_map(|o| o)
|
|
||||||
.map(|state| state.to_sync_state_event())
|
.map(|state| state.to_sync_state_event())
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Heroes
|
// Heroes
|
||||||
let heroes = services()
|
let heroes = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(room_id)
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.filter(|member| member != &sender_user)
|
.filter(|member| member != &sender_user)
|
||||||
.map(|member| {
|
.flat_map(|member| {
|
||||||
Ok::<_, Error>(
|
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_member(&room_id, &member)?
|
.get_member(room_id, &member)
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
.map(|memberevent| {
|
.map(|memberevent| {
|
||||||
(
|
(
|
||||||
memberevent
|
memberevent
|
||||||
|
@ -1578,32 +1590,26 @@ pub async fn sync_events_v4_route(
|
||||||
.unwrap_or_else(|| member.to_string()),
|
.unwrap_or_else(|| member.to_string()),
|
||||||
memberevent.avatar_url,
|
memberevent.avatar_url,
|
||||||
)
|
)
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.filter_map(|r| r.ok())
|
})
|
||||||
.filter_map(|o| o)
|
|
||||||
.take(5)
|
.take(5)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let name = if heroes.len() > 1 {
|
let name = match &heroes[..] {
|
||||||
let last = heroes[0].0.clone();
|
[] => None,
|
||||||
Some(
|
[only] => Some(only.0.clone()),
|
||||||
heroes[1..]
|
[firsts @ .., last] => Some(
|
||||||
|
firsts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| h.0.clone())
|
.map(|h| h.0.clone())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
+ " and "
|
+ " and "
|
||||||
+ &last,
|
+ &last.0,
|
||||||
)
|
),
|
||||||
} else if heroes.len() == 1 {
|
|
||||||
Some(heroes[0].0.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let avatar = if heroes.len() == 1 {
|
let avatar = if let [only] = &heroes[..] {
|
||||||
heroes[0].1.clone()
|
only.1.clone()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1611,17 +1617,17 @@ pub async fn sync_events_v4_route(
|
||||||
rooms.insert(
|
rooms.insert(
|
||||||
room_id.clone(),
|
room_id.clone(),
|
||||||
sync_events::v4::SlidingSyncRoom {
|
sync_events::v4::SlidingSyncRoom {
|
||||||
name: services()
|
name: services().rooms.state_accessor.get_name(room_id)?.or(name),
|
||||||
.rooms
|
avatar: if let Some(avatar) = avatar {
|
||||||
.state_accessor
|
JsOption::Some(avatar)
|
||||||
.get_name(&room_id)?
|
} else {
|
||||||
.or_else(|| name),
|
match services().rooms.state_accessor.get_avatar(room_id)? {
|
||||||
avatar: services()
|
JsOption::Some(avatar) => JsOption::from_option(avatar.url),
|
||||||
.rooms
|
JsOption::Null => JsOption::Null,
|
||||||
.state_accessor
|
JsOption::Undefined => JsOption::Undefined,
|
||||||
.get_avatar(&room_id)?
|
}
|
||||||
.map_or(avatar, |a| a.url),
|
},
|
||||||
initial: Some(!known),
|
initial: Some(roomsince == &0),
|
||||||
is_dm: None,
|
is_dm: None,
|
||||||
invite_state: None,
|
invite_state: None,
|
||||||
unread_notifications: UnreadNotificationsCount {
|
unread_notifications: UnreadNotificationsCount {
|
||||||
|
@ -1629,7 +1635,7 @@ pub async fn sync_events_v4_route(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.highlight_count(&sender_user, &room_id)?
|
.highlight_count(&sender_user, room_id)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("notification count can't go that high"),
|
.expect("notification count can't go that high"),
|
||||||
),
|
),
|
||||||
|
@ -1637,7 +1643,7 @@ pub async fn sync_events_v4_route(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.notification_count(&sender_user, &room_id)?
|
.notification_count(&sender_user, room_id)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("notification count can't go that high"),
|
.expect("notification count can't go that high"),
|
||||||
),
|
),
|
||||||
|
@ -1650,7 +1656,7 @@ pub async fn sync_events_v4_route(
|
||||||
(services()
|
(services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(&room_id)?
|
.room_joined_count(room_id)?
|
||||||
.unwrap_or(0) as u32)
|
.unwrap_or(0) as u32)
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
|
@ -1658,11 +1664,12 @@ pub async fn sync_events_v4_route(
|
||||||
(services()
|
(services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_invited_count(&room_id)?
|
.room_invited_count(room_id)?
|
||||||
.unwrap_or(0) as u32)
|
.unwrap_or(0) as u32)
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
num_live: None, // Count events in timeline greater than global sync counter
|
num_live: None, // Count events in timeline greater than global sync counter
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1681,7 +1688,7 @@ pub async fn sync_events_v4_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(sync_events::v4::Response {
|
Ok(sync_events::v4::Response {
|
||||||
initial: since == 0,
|
initial: globalsince == 0,
|
||||||
txn_id: body.txn_id.clone(),
|
txn_id: body.txn_id.clone(),
|
||||||
pos: next_batch.to_string(),
|
pos: next_batch.to_string(),
|
||||||
lists,
|
lists,
|
||||||
|
@ -1712,7 +1719,7 @@ pub async fn sync_events_v4_route(
|
||||||
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
||||||
services()
|
services()
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(None, &sender_user, since)?
|
.changes_since(None, &sender_user, globalsince)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_, v)| {
|
.filter_map(|(_, v)| {
|
||||||
serde_json::from_str(v.json().get())
|
serde_json::from_str(v.json().get())
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub async fn get_supported_versions_route(
|
||||||
"v1.2".to_owned(),
|
"v1.2".to_owned(),
|
||||||
"v1.3".to_owned(),
|
"v1.3".to_owned(),
|
||||||
"v1.4".to_owned(),
|
"v1.4".to_owned(),
|
||||||
|
"v1.5".to_owned(),
|
||||||
],
|
],
|
||||||
unstable_features: BTreeMap::from_iter([("org.matrix.e2e_cross_signing".to_owned(), true)]),
|
unstable_features: BTreeMap::from_iter([("org.matrix.e2e_cross_signing".to_owned(), true)]),
|
||||||
};
|
};
|
||||||
|
|
|
@ -341,7 +341,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns: actual_destination, host header
|
/// Returns: actual_destination, host header
|
||||||
/// Implemented according to the specification at https://matrix.org/docs/spec/server_server/r0.1.4#resolving-server-names
|
/// Implemented according to the specification at <https://matrix.org/docs/spec/server_server/r0.1.4#resolving-server-names>
|
||||||
/// Numbers in comments below refer to bullet points in linked section of specification
|
/// Numbers in comments below refer to bullet points in linked section of specification
|
||||||
async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) {
|
async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) {
|
||||||
debug!("Finding actual destination for {destination}");
|
debug!("Finding actual destination for {destination}");
|
||||||
|
@ -666,7 +666,7 @@ pub fn parse_incoming_pdu(
|
||||||
|
|
||||||
let room_version_id = services().rooms.state.get_room_version(&room_id)?;
|
let room_version_id = services().rooms.state.get_room_version(&room_id)?;
|
||||||
|
|
||||||
let (event_id, value) = match gen_event_id_canonical_json(&pdu, &room_version_id) {
|
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Event could not be converted to canonical json
|
// Event could not be converted to canonical json
|
||||||
|
@ -707,7 +707,24 @@ pub async fn send_transaction_message_route(
|
||||||
// let mut auth_cache = EventMap::new();
|
// let mut auth_cache = EventMap::new();
|
||||||
|
|
||||||
for pdu in &body.pdus {
|
for pdu in &body.pdus {
|
||||||
let r = parse_incoming_pdu(&pdu);
|
let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
|
||||||
|
warn!("Error parsing incoming event {:?}: {:?}", pdu, e);
|
||||||
|
Error::BadServerResponse("Invalid PDU in server response")
|
||||||
|
})?;
|
||||||
|
let room_id: OwnedRoomId = value
|
||||||
|
.get("room_id")
|
||||||
|
.and_then(|id| RoomId::parse(id.as_str()?).ok())
|
||||||
|
.ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Invalid room id in pdu",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
if services().rooms.state.get_room_version(&room_id).is_err() {
|
||||||
|
debug!("Server is not in room {room_id}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = parse_incoming_pdu(pdu);
|
||||||
let (event_id, value, room_id) = match r {
|
let (event_id, value, room_id) = match r {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -718,11 +735,6 @@ pub async fn send_transaction_message_route(
|
||||||
};
|
};
|
||||||
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
||||||
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.event_handler
|
|
||||||
.acl_check(sender_servername, &room_id)?;
|
|
||||||
|
|
||||||
let mutex = Arc::clone(
|
let mutex = Arc::clone(
|
||||||
services()
|
services()
|
||||||
.globals
|
.globals
|
||||||
|
@ -980,7 +992,7 @@ pub async fn get_event_route(
|
||||||
|
|
||||||
if !services().rooms.state_accessor.server_can_see_event(
|
if !services().rooms.state_accessor.server_can_see_event(
|
||||||
sender_servername,
|
sender_servername,
|
||||||
&room_id,
|
room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
)? {
|
)? {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -1046,7 +1058,7 @@ pub async fn get_backfill_route(
|
||||||
let all_events = services()
|
let all_events = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(&user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)?
|
.pdus_until(user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)?
|
||||||
.take(limit.try_into().unwrap());
|
.take(limit.try_into().unwrap());
|
||||||
|
|
||||||
let events = all_events
|
let events = all_events
|
||||||
|
@ -1063,7 +1075,7 @@ pub async fn get_backfill_route(
|
||||||
})
|
})
|
||||||
.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())
|
.filter_map(|r| r.ok().flatten())
|
||||||
.map(|pdu| PduEvent::convert_to_outgoing_federation_event(pdu))
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(get_backfill::v1::Response {
|
Ok(get_backfill::v1::Response {
|
||||||
|
@ -1787,6 +1799,13 @@ pub async fn get_devices_route(
|
||||||
return Err(Error::bad_config("Federation is disabled."));
|
return Err(Error::bad_config("Federation is disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if body.user_id.server_name() != services().globals.server_name() {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Tried to access user from other server.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let sender_servername = body
|
let sender_servername = body
|
||||||
.sender_servername
|
.sender_servername
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -1861,6 +1880,13 @@ pub async fn get_profile_information_route(
|
||||||
return Err(Error::bad_config("Federation is disabled."));
|
return Err(Error::bad_config("Federation is disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if body.user_id.server_name() != services().globals.server_name() {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Tried to access user from other server.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let mut displayname = None;
|
let mut displayname = None;
|
||||||
let mut avatar_url = None;
|
let mut avatar_url = None;
|
||||||
let mut blurhash = None;
|
let mut blurhash = None;
|
||||||
|
@ -1897,6 +1923,17 @@ pub async fn get_keys_route(body: Ruma<get_keys::v1::Request>) -> Result<get_key
|
||||||
return Err(Error::bad_config("Federation is disabled."));
|
return Err(Error::bad_config("Federation is disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if body
|
||||||
|
.device_keys
|
||||||
|
.iter()
|
||||||
|
.any(|(u, _)| u.server_name() != services().globals.server_name())
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Tried to access user from other server.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let result = get_keys_helper(None, &body.device_keys, |u| {
|
let result = get_keys_helper(None, &body.device_keys, |u| {
|
||||||
Some(u.server_name()) == body.sender_servername.as_deref()
|
Some(u.server_name()) == body.sender_servername.as_deref()
|
||||||
})
|
})
|
||||||
|
@ -1919,6 +1956,17 @@ pub async fn claim_keys_route(
|
||||||
return Err(Error::bad_config("Federation is disabled."));
|
return Err(Error::bad_config("Federation is disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if body
|
||||||
|
.one_time_keys
|
||||||
|
.iter()
|
||||||
|
.any(|(u, _)| u.server_name() != services().globals.server_name())
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Tried to access user from other server.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let result = claim_keys_helper(&body.one_time_keys).await?;
|
let result = claim_keys_helper(&body.one_time_keys).await?;
|
||||||
|
|
||||||
Ok(claim_keys::v1::Response {
|
Ok(claim_keys::v1::Response {
|
||||||
|
|
|
@ -29,7 +29,9 @@ use crate::Result;
|
||||||
/// would be used for `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`.
|
/// would be used for `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`.
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[derive(Default)]
|
||||||
pub enum ProxyConfig {
|
pub enum ProxyConfig {
|
||||||
|
#[default]
|
||||||
None,
|
None,
|
||||||
Global {
|
Global {
|
||||||
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
||||||
|
@ -48,11 +50,6 @@ impl ProxyConfig {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Default for ProxyConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
ProxyConfig::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct PartialProxyConfig {
|
pub struct PartialProxyConfig {
|
||||||
|
|
|
@ -116,7 +116,7 @@ impl KvTree for PersyTree {
|
||||||
match iter {
|
match iter {
|
||||||
Ok(iter) => Box::new(iter.filter_map(|(k, v)| {
|
Ok(iter) => Box::new(iter.filter_map(|(k, v)| {
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
|
.map(|val| ((*k).to_owned(), (*val).to_owned()))
|
||||||
.next()
|
.next()
|
||||||
})),
|
})),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -142,7 +142,7 @@ impl KvTree for PersyTree {
|
||||||
Ok(iter) => {
|
Ok(iter) => {
|
||||||
let map = iter.filter_map(|(k, v)| {
|
let map = iter.filter_map(|(k, v)| {
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
|
.map(|val| ((*k).to_owned(), (*val).to_owned()))
|
||||||
.next()
|
.next()
|
||||||
});
|
});
|
||||||
if backwards {
|
if backwards {
|
||||||
|
@ -179,7 +179,7 @@ impl KvTree for PersyTree {
|
||||||
iter.take_while(move |(k, _)| (*k).starts_with(&owned_prefix))
|
iter.take_while(move |(k, _)| (*k).starts_with(&owned_prefix))
|
||||||
.filter_map(|(k, v)| {
|
.filter_map(|(k, v)| {
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
|
.map(|val| ((*k).to_owned(), (*val).to_owned()))
|
||||||
.next()
|
.next()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl Iterator for PreparedStatementIterator<'_> {
|
||||||
struct NonAliasingBox<T>(*mut T);
|
struct NonAliasingBox<T>(*mut T);
|
||||||
impl<T> Drop for NonAliasingBox<T> {
|
impl<T> Drop for NonAliasingBox<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { Box::from_raw(self.0) };
|
drop(unsafe { Box::from_raw(self.0) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use tokio::sync::watch;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(super) struct Watchers {
|
pub(super) struct Watchers {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
watchers: RwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>,
|
watchers: RwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,13 +123,12 @@ impl service::account_data::Data for KeyValueDatabase {
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
Ok::<_, Error>((
|
Ok::<_, Error>((
|
||||||
RoomAccountDataEventType::try_from(
|
RoomAccountDataEventType::from(
|
||||||
utils::string_from_bytes(k.rsplit(|&b| b == 0xff).next().ok_or_else(
|
utils::string_from_bytes(k.rsplit(|&b| b == 0xff).next().ok_or_else(
|
||||||
|| Error::bad_database("RoomUserData ID in db is invalid."),
|
|| Error::bad_database("RoomUserData ID in db is invalid."),
|
||||||
)?)
|
)?)
|
||||||
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
||||||
)
|
),
|
||||||
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
|
||||||
serde_json::from_slice::<Raw<AnyEphemeralRoomEvent>>(&v).map_err(|_| {
|
serde_json::from_slice::<Raw<AnyEphemeralRoomEvent>>(&v).map_err(|_| {
|
||||||
Error::bad_database("Database contains invalid account data.")
|
Error::bad_database("Database contains invalid account data.")
|
||||||
})?,
|
})?,
|
||||||
|
|
|
@ -256,8 +256,8 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
..
|
..
|
||||||
} = new_keys;
|
} = new_keys;
|
||||||
|
|
||||||
keys.verify_keys.extend(verify_keys.into_iter());
|
keys.verify_keys.extend(verify_keys);
|
||||||
keys.old_verify_keys.extend(old_verify_keys.into_iter());
|
keys.old_verify_keys.extend(old_verify_keys);
|
||||||
|
|
||||||
self.server_signingkeys.insert(
|
self.server_signingkeys.insert(
|
||||||
origin.as_bytes(),
|
origin.as_bytes(),
|
||||||
|
|
|
@ -157,10 +157,9 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
.ok_or_else(|| Error::bad_database("Invalid statekey in shortstatekey_statekey."))?;
|
.ok_or_else(|| Error::bad_database("Invalid statekey in shortstatekey_statekey."))?;
|
||||||
|
|
||||||
let event_type =
|
let event_type =
|
||||||
StateEventType::try_from(utils::string_from_bytes(eventtype_bytes).map_err(|_| {
|
StateEventType::from(utils::string_from_bytes(eventtype_bytes).map_err(|_| {
|
||||||
Error::bad_database("Event type in shortstatekey_statekey is invalid unicode.")
|
Error::bad_database("Event type in shortstatekey_statekey is invalid unicode.")
|
||||||
})?)
|
})?);
|
||||||
.map_err(|_| Error::bad_database("Event type in shortstatekey_statekey is invalid."))?;
|
|
||||||
|
|
||||||
let state_key = utils::string_from_bytes(statekey_bytes).map_err(|_| {
|
let state_key = utils::string_from_bytes(statekey_bytes).map_err(|_| {
|
||||||
Error::bad_database("Statekey in shortstatekey_statekey is invalid unicode.")
|
Error::bad_database("Statekey in shortstatekey_statekey is invalid unicode.")
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
let parsed = services()
|
let parsed = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
.parse_compressed_state_event(&compressed)?;
|
.parse_compressed_state_event(compressed)?;
|
||||||
result.insert(parsed.0, parsed.1);
|
result.insert(parsed.0, parsed.1);
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
|
@ -49,7 +49,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
let (_, eventid) = services()
|
let (_, eventid) = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
.parse_compressed_state_event(&compressed)?;
|
.parse_compressed_state_event(compressed)?;
|
||||||
if let Some(pdu) = services().rooms.timeline.get_pdu(&eventid)? {
|
if let Some(pdu) = services().rooms.timeline.get_pdu(&eventid)? {
|
||||||
result.insert(
|
result.insert(
|
||||||
(
|
(
|
||||||
|
@ -101,7 +101,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
.parse_compressed_state_event(&compressed)
|
.parse_compressed_state_event(compressed)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(_, id)| id)
|
.map(|(_, id)| id)
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -471,6 +471,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user was invited to.
|
/// Returns an iterator over all rooms a user was invited to.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
fn rooms_invited<'a>(
|
fn rooms_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -549,6 +550,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user left.
|
/// Returns an iterator over all rooms a user left.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
fn rooms_left<'a>(
|
fn rooms_left<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
|
|
@ -10,7 +10,7 @@ impl service::rooms::threads::Data for KeyValueDatabase {
|
||||||
user_id: &'a UserId,
|
user_id: &'a UserId,
|
||||||
room_id: &'a RoomId,
|
room_id: &'a RoomId,
|
||||||
until: u64,
|
until: u64,
|
||||||
include: &'a IncludeThreads,
|
_include: &'a IncludeThreads,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(u64, PduEvent)>> + 'a>> {
|
) -> Result<Box<dyn Iterator<Item = Result<(u64, PduEvent)>> + 'a>> {
|
||||||
let prefix = services()
|
let prefix = services()
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -27,7 +27,7 @@ impl service::rooms::threads::Data for KeyValueDatabase {
|
||||||
self.threadid_userids
|
self.threadid_userids
|
||||||
.iter_from(¤t, true)
|
.iter_from(¤t, true)
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(move |(pduid, users)| {
|
.map(move |(pduid, _users)| {
|
||||||
let count = utils::u64_from_bytes(&pduid[(mem::size_of::<u64>())..])
|
let count = utils::u64_from_bytes(&pduid[(mem::size_of::<u64>())..])
|
||||||
.map_err(|_| Error::bad_database("Invalid pduid in threadid_userids."))?;
|
.map_err(|_| Error::bad_database("Invalid pduid in threadid_userids."))?;
|
||||||
let mut pdu = services()
|
let mut pdu = services()
|
||||||
|
@ -52,13 +52,13 @@ impl service::rooms::threads::Data for KeyValueDatabase {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(&[0xff][..]);
|
.join(&[0xff][..]);
|
||||||
|
|
||||||
self.threadid_userids.insert(&root_id, &users)?;
|
self.threadid_userids.insert(root_id, &users)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_participants(&self, root_id: &[u8]) -> Result<Option<Vec<OwnedUserId>>> {
|
fn get_participants(&self, root_id: &[u8]) -> Result<Option<Vec<OwnedUserId>>> {
|
||||||
if let Some(users) = self.threadid_userids.get(&root_id)? {
|
if let Some(users) = self.threadid_userids.get(root_id)? {
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
users
|
users
|
||||||
.split(|b| *b == 0xff)
|
.split(|b| *b == 0xff)
|
||||||
|
|
|
@ -39,11 +39,10 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
|
|
||||||
/// Returns the `count` of this pdu's id.
|
/// Returns the `count` of this pdu's id.
|
||||||
fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<PduCount>> {
|
fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<PduCount>> {
|
||||||
Ok(self
|
self.eventid_pduid
|
||||||
.eventid_pduid
|
|
||||||
.get(event_id.as_bytes())?
|
.get(event_id.as_bytes())?
|
||||||
.map(|pdu_id| pdu_count(&pdu_id))
|
.map(|pdu_id| pdu_count(&pdu_id))
|
||||||
.transpose()?)
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the json of a pdu.
|
/// Returns the json of a pdu.
|
||||||
|
@ -80,12 +79,10 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
|
|
||||||
/// Returns the pdu's id.
|
/// Returns the pdu's id.
|
||||||
fn get_pdu_id(&self, event_id: &EventId) -> Result<Option<Vec<u8>>> {
|
fn get_pdu_id(&self, event_id: &EventId) -> Result<Option<Vec<u8>>> {
|
||||||
Ok(self.eventid_pduid.get(event_id.as_bytes())?)
|
self.eventid_pduid.get(event_id.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pdu.
|
/// Returns the pdu.
|
||||||
///
|
|
||||||
/// Checks the `eventid_outlierpdu` Tree if not found in the timeline.
|
|
||||||
fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>> {
|
fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>> {
|
||||||
self.eventid_pduid
|
self.eventid_pduid
|
||||||
.get(event_id.as_bytes())?
|
.get(event_id.as_bytes())?
|
||||||
|
@ -232,7 +229,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
until: PduCount,
|
until: PduCount,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
||||||
let (prefix, current) = count_to_id(&room_id, until, 1, true)?;
|
let (prefix, current) = count_to_id(room_id, until, 1, true)?;
|
||||||
|
|
||||||
let user_id = user_id.to_owned();
|
let user_id = user_id.to_owned();
|
||||||
|
|
||||||
|
@ -259,7 +256,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
from: PduCount,
|
from: PduCount,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
||||||
let (prefix, current) = count_to_id(&room_id, from, 1, false)?;
|
let (prefix, current) = count_to_id(room_id, from, 1, false)?;
|
||||||
|
|
||||||
let user_id = user_id.to_owned();
|
let user_id = user_id.to_owned();
|
||||||
|
|
||||||
|
@ -334,7 +331,7 @@ fn count_to_id(
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
.get_shortroomid(room_id)?
|
.get_shortroomid(room_id)?
|
||||||
.expect("room exists")
|
.ok_or_else(|| Error::bad_database("Looked for bad shortroomid in timeline"))?
|
||||||
.to_be_bytes()
|
.to_be_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
let mut pdu_id = prefix.clone();
|
let mut pdu_id = prefix.clone();
|
||||||
|
|
|
@ -146,10 +146,9 @@ impl service::users::Data for KeyValueDatabase {
|
||||||
self.userid_avatarurl
|
self.userid_avatarurl
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.as_bytes())?
|
||||||
.map(|bytes| {
|
.map(|bytes| {
|
||||||
let s = utils::string_from_bytes(&bytes)
|
utils::string_from_bytes(&bytes)
|
||||||
.map_err(|_| Error::bad_database("Avatar URL in db is invalid."))?;
|
|
||||||
s.try_into()
|
|
||||||
.map_err(|_| Error::bad_database("Avatar URL in db is invalid."))
|
.map_err(|_| Error::bad_database("Avatar URL in db is invalid."))
|
||||||
|
.map(Into::into)
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
|
@ -852,7 +852,9 @@ impl KeyValueDatabase {
|
||||||
if rule.is_some() {
|
if rule.is_some() {
|
||||||
let mut rule = rule.unwrap().clone();
|
let mut rule = rule.unwrap().clone();
|
||||||
rule.rule_id = content_rule_transformation[1].to_owned();
|
rule.rule_id = content_rule_transformation[1].to_owned();
|
||||||
rules_list.content.remove(content_rule_transformation[0]);
|
rules_list
|
||||||
|
.content
|
||||||
|
.shift_remove(content_rule_transformation[0]);
|
||||||
rules_list.content.insert(rule);
|
rules_list.content.insert(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -875,7 +877,7 @@ impl KeyValueDatabase {
|
||||||
if let Some(rule) = rule {
|
if let Some(rule) = rule {
|
||||||
let mut rule = rule.clone();
|
let mut rule = rule.clone();
|
||||||
rule.rule_id = transformation[1].to_owned();
|
rule.rule_id = transformation[1].to_owned();
|
||||||
rules_list.underride.remove(transformation[0]);
|
rules_list.underride.shift_remove(transformation[0]);
|
||||||
rules_list.underride.insert(rule);
|
rules_list.underride.insert(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
#![warn(
|
|
||||||
rust_2018_idioms,
|
|
||||||
unused_qualifications,
|
|
||||||
clippy::cloned_instead_of_copied,
|
|
||||||
clippy::str_to_string
|
|
||||||
)]
|
|
||||||
#![allow(clippy::suspicious_else_formatting)]
|
|
||||||
#![deny(clippy::dbg_macro)]
|
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
mod config;
|
mod config;
|
||||||
mod database;
|
mod database;
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -1,13 +1,3 @@
|
||||||
#![warn(
|
|
||||||
rust_2018_idioms,
|
|
||||||
unused_qualifications,
|
|
||||||
clippy::cloned_instead_of_copied,
|
|
||||||
clippy::str_to_string,
|
|
||||||
clippy::future_not_send
|
|
||||||
)]
|
|
||||||
#![allow(clippy::suspicious_else_formatting)]
|
|
||||||
#![deny(clippy::dbg_macro)]
|
|
||||||
|
|
||||||
use std::{future::Future, io, net::SocketAddr, sync::atomic, time::Duration};
|
use std::{future::Future, io, net::SocketAddr, sync::atomic, time::Duration};
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
@ -238,7 +228,7 @@ async fn spawn_task<B: Send + 'static>(
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn unrecognized_method<B>(
|
async fn unrecognized_method<B: Send>(
|
||||||
req: axum::http::Request<B>,
|
req: axum::http::Request<B>,
|
||||||
next: axum::middleware::Next<B>,
|
next: axum::middleware::Next<B>,
|
||||||
) -> std::result::Result<axum::response::Response, StatusCode> {
|
) -> std::result::Result<axum::response::Response, StatusCode> {
|
||||||
|
|
|
@ -50,7 +50,7 @@ enum AdminCommand {
|
||||||
/// Registering a new bridge using the ID of an existing bridge will replace
|
/// Registering a new bridge using the ID of an existing bridge will replace
|
||||||
/// the old one.
|
/// the old one.
|
||||||
///
|
///
|
||||||
/// [commandbody]
|
/// [commandbody]()
|
||||||
/// # ```
|
/// # ```
|
||||||
/// # yaml content here
|
/// # yaml content here
|
||||||
/// # ```
|
/// # ```
|
||||||
|
@ -96,7 +96,7 @@ enum AdminCommand {
|
||||||
/// Removing a mass amount of users from a room may cause a significant amount of leave events.
|
/// Removing a mass amount of users from a room may cause a significant amount of leave events.
|
||||||
/// The time to leave rooms may depend significantly on joined rooms and servers.
|
/// The time to leave rooms may depend significantly on joined rooms and servers.
|
||||||
///
|
///
|
||||||
/// [commandbody]
|
/// [commandbody]()
|
||||||
/// # ```
|
/// # ```
|
||||||
/// # User list here
|
/// # User list here
|
||||||
/// # ```
|
/// # ```
|
||||||
|
@ -121,7 +121,7 @@ enum AdminCommand {
|
||||||
/// The PDU event is only checked for validity and is not added to the
|
/// The PDU event is only checked for validity and is not added to the
|
||||||
/// database.
|
/// database.
|
||||||
///
|
///
|
||||||
/// [commandbody]
|
/// [commandbody]()
|
||||||
/// # ```
|
/// # ```
|
||||||
/// # PDU json content here
|
/// # PDU json content here
|
||||||
/// # ```
|
/// # ```
|
||||||
|
@ -165,14 +165,14 @@ enum AdminCommand {
|
||||||
EnableRoom { room_id: Box<RoomId> },
|
EnableRoom { room_id: Box<RoomId> },
|
||||||
|
|
||||||
/// Verify json signatures
|
/// Verify json signatures
|
||||||
/// [commandbody]
|
/// [commandbody]()
|
||||||
/// # ```
|
/// # ```
|
||||||
/// # json here
|
/// # json here
|
||||||
/// # ```
|
/// # ```
|
||||||
SignJson,
|
SignJson,
|
||||||
|
|
||||||
/// Verify json signatures
|
/// Verify json signatures
|
||||||
/// [commandbody]
|
/// [commandbody]()
|
||||||
/// # ```
|
/// # ```
|
||||||
/// # json here
|
/// # json here
|
||||||
/// # ```
|
/// # ```
|
||||||
|
@ -858,12 +858,15 @@ impl Service {
|
||||||
.expect("Regex compilation should not fail");
|
.expect("Regex compilation should not fail");
|
||||||
let text = re.replace_all(&text, "<code>$1</code>: $4");
|
let text = re.replace_all(&text, "<code>$1</code>: $4");
|
||||||
|
|
||||||
// Look for a `[commandbody]` tag. If it exists, use all lines below it that
|
// Look for a `[commandbody]()` tag. If it exists, use all lines below it that
|
||||||
// start with a `#` in the USAGE section.
|
// start with a `#` in the USAGE section.
|
||||||
let mut text_lines: Vec<&str> = text.lines().collect();
|
let mut text_lines: Vec<&str> = text.lines().collect();
|
||||||
let mut command_body = String::new();
|
let mut command_body = String::new();
|
||||||
|
|
||||||
if let Some(line_index) = text_lines.iter().position(|line| *line == "[commandbody]") {
|
if let Some(line_index) = text_lines
|
||||||
|
.iter()
|
||||||
|
.position(|line| *line == "[commandbody]()")
|
||||||
|
{
|
||||||
text_lines.remove(line_index);
|
text_lines.remove(line_index);
|
||||||
|
|
||||||
while text_lines
|
while text_lines
|
||||||
|
@ -932,7 +935,7 @@ impl Service {
|
||||||
|
|
||||||
services().users.create(&conduit_user, None)?;
|
services().users.create(&conduit_user, None)?;
|
||||||
|
|
||||||
let mut content = RoomCreateEventContent::new(conduit_user.clone());
|
let mut content = RoomCreateEventContent::new_v1(conduit_user.clone());
|
||||||
content.federate = true;
|
content.federate = true;
|
||||||
content.predecessor = None;
|
content.predecessor = None;
|
||||||
content.room_version = services().globals.default_room_version();
|
content.room_version = services().globals.default_room_version();
|
||||||
|
@ -1048,7 +1051,7 @@ impl Service {
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomName,
|
event_type: TimelineEventType::RoomName,
|
||||||
content: to_raw_value(&RoomNameEventContent::new(Some(room_name)))
|
content: to_raw_value(&RoomNameEventContent::new(room_name))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
|
|
|
@ -8,6 +8,12 @@ use ruma::{
|
||||||
use crate::api::server_server::FedDest;
|
use crate::api::server_server::FedDest;
|
||||||
|
|
||||||
use crate::{services, Config, Error, Result};
|
use crate::{services, Config, Error, Result};
|
||||||
|
use futures_util::FutureExt;
|
||||||
|
use hyper::{
|
||||||
|
client::connect::dns::{GaiResolver, Name},
|
||||||
|
service::Service as HyperService,
|
||||||
|
};
|
||||||
|
use reqwest::dns::{Addrs, Resolve, Resolving};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::sync::sync_events,
|
client::sync::sync_events,
|
||||||
|
@ -17,8 +23,10 @@ use ruma::{
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
|
error::Error as StdError,
|
||||||
fs,
|
fs,
|
||||||
future::Future,
|
future::{self, Future},
|
||||||
|
iter,
|
||||||
net::{IpAddr, SocketAddr},
|
net::{IpAddr, SocketAddr},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -56,6 +64,7 @@ pub struct Service {
|
||||||
pub unstable_room_versions: Vec<RoomVersionId>,
|
pub unstable_room_versions: Vec<RoomVersionId>,
|
||||||
pub bad_event_ratelimiter: Arc<RwLock<HashMap<OwnedEventId, RateLimitState>>>,
|
pub bad_event_ratelimiter: Arc<RwLock<HashMap<OwnedEventId, RateLimitState>>>,
|
||||||
pub bad_signature_ratelimiter: Arc<RwLock<HashMap<Vec<String>, RateLimitState>>>,
|
pub bad_signature_ratelimiter: Arc<RwLock<HashMap<Vec<String>, RateLimitState>>>,
|
||||||
|
pub bad_query_ratelimiter: Arc<RwLock<HashMap<OwnedServerName, RateLimitState>>>,
|
||||||
pub servername_ratelimiter: Arc<RwLock<HashMap<OwnedServerName, Arc<Semaphore>>>>,
|
pub servername_ratelimiter: Arc<RwLock<HashMap<OwnedServerName, Arc<Semaphore>>>>,
|
||||||
pub sync_receivers: RwLock<HashMap<(OwnedUserId, OwnedDeviceId), SyncHandle>>,
|
pub sync_receivers: RwLock<HashMap<(OwnedUserId, OwnedDeviceId), SyncHandle>>,
|
||||||
pub roomid_mutex_insert: RwLock<HashMap<OwnedRoomId, Arc<Mutex<()>>>>,
|
pub roomid_mutex_insert: RwLock<HashMap<OwnedRoomId, Arc<Mutex<()>>>>,
|
||||||
|
@ -98,6 +107,45 @@ impl Default for RotationHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Resolver {
|
||||||
|
inner: GaiResolver,
|
||||||
|
overrides: Arc<RwLock<TlsNameMap>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver {
|
||||||
|
pub fn new(overrides: Arc<RwLock<TlsNameMap>>) -> Self {
|
||||||
|
Resolver {
|
||||||
|
inner: GaiResolver::new(),
|
||||||
|
overrides,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolve for Resolver {
|
||||||
|
fn resolve(&self, name: Name) -> Resolving {
|
||||||
|
self.overrides
|
||||||
|
.read()
|
||||||
|
.expect("lock should not be poisoned")
|
||||||
|
.get(name.as_str())
|
||||||
|
.and_then(|(override_name, port)| {
|
||||||
|
override_name.first().map(|first_name| {
|
||||||
|
let x: Box<dyn Iterator<Item = SocketAddr> + Send> =
|
||||||
|
Box::new(iter::once(SocketAddr::new(*first_name, *port)));
|
||||||
|
let x: Resolving = Box::pin(future::ready(Ok(x)));
|
||||||
|
x
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let this = &mut self.inner.clone();
|
||||||
|
Box::pin(HyperService::<Name>::call(this, name).map(|result| {
|
||||||
|
result
|
||||||
|
.map(|addrs| -> Addrs { Box::new(addrs) })
|
||||||
|
.map_err(|err| -> Box<dyn StdError + Send + Sync> { Box::new(err) })
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
pub fn load(db: &'static dyn Data, config: Config) -> Result<Self> {
|
pub fn load(db: &'static dyn Data, config: Config) -> Result<Self> {
|
||||||
let keypair = db.load_keypair();
|
let keypair = db.load_keypair();
|
||||||
|
@ -119,14 +167,8 @@ impl Service {
|
||||||
.map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()));
|
.map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()));
|
||||||
|
|
||||||
let default_client = reqwest_client_builder(&config)?.build()?;
|
let default_client = reqwest_client_builder(&config)?.build()?;
|
||||||
let name_override = Arc::clone(&tls_name_override);
|
|
||||||
let federation_client = reqwest_client_builder(&config)?
|
let federation_client = reqwest_client_builder(&config)?
|
||||||
.resolve_fn(move |domain| {
|
.dns_resolver(Arc::new(Resolver::new(tls_name_override.clone())))
|
||||||
let read_guard = name_override.read().unwrap();
|
|
||||||
let (override_name, port) = read_guard.get(&domain)?;
|
|
||||||
let first_name = override_name.get(0)?;
|
|
||||||
Some(SocketAddr::new(*first_name, *port))
|
|
||||||
})
|
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// Supported and stable room versions
|
// Supported and stable room versions
|
||||||
|
@ -160,6 +202,7 @@ impl Service {
|
||||||
unstable_room_versions,
|
unstable_room_versions,
|
||||||
bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
||||||
bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
bad_query_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
||||||
servername_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
servername_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
||||||
roomid_mutex_state: RwLock::new(HashMap::new()),
|
roomid_mutex_state: RwLock::new(HashMap::new()),
|
||||||
roomid_mutex_insert: RwLock::new(HashMap::new()),
|
roomid_mutex_insert: RwLock::new(HashMap::new()),
|
||||||
|
|
|
@ -385,7 +385,7 @@ impl PartialEq for PduEvent {
|
||||||
}
|
}
|
||||||
impl PartialOrd for PduEvent {
|
impl PartialOrd for PduEvent {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
self.event_id.partial_cmp(&other.event_id)
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Ord for PduEvent {
|
impl Ord for PduEvent {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod data;
|
mod data;
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
use ruma::events::AnySyncTimelineEvent;
|
use ruma::{events::AnySyncTimelineEvent, push::PushConditionPowerLevelsCtx};
|
||||||
|
|
||||||
use crate::{services, Error, PduEvent, Result};
|
use crate::{services, Error, PduEvent, Result};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
|
@ -193,6 +193,12 @@ impl Service {
|
||||||
pdu: &Raw<AnySyncTimelineEvent>,
|
pdu: &Raw<AnySyncTimelineEvent>,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<&'a [Action]> {
|
) -> Result<&'a [Action]> {
|
||||||
|
let power_levels = PushConditionPowerLevelsCtx {
|
||||||
|
users: power_levels.users.clone(),
|
||||||
|
users_default: power_levels.users_default,
|
||||||
|
notifications: power_levels.notifications.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
let ctx = PushConditionRoomCtx {
|
let ctx = PushConditionRoomCtx {
|
||||||
room_id: room_id.to_owned(),
|
room_id: room_id.to_owned(),
|
||||||
member_count: 10_u32.into(), // TODO: get member count efficiently
|
member_count: 10_u32.into(), // TODO: get member count efficiently
|
||||||
|
@ -201,9 +207,7 @@ impl Service {
|
||||||
.users
|
.users
|
||||||
.displayname(user)?
|
.displayname(user)?
|
||||||
.unwrap_or_else(|| user.localpart().to_owned()),
|
.unwrap_or_else(|| user.localpart().to_owned()),
|
||||||
users_power_levels: power_levels.users.clone(),
|
power_levels: Some(power_levels),
|
||||||
default_power_level: power_levels.users_default,
|
|
||||||
notification_power_levels: power_levels.notifications.clone(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ruleset.get_actions(pdu, &ctx))
|
Ok(ruleset.get_actions(pdu, &ctx))
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub trait Data: Send + Sync {
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
/// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
|
/// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn readreceipts_since<'a>(
|
fn readreceipts_since<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
|
|
|
@ -92,6 +92,8 @@ impl Service {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
services().rooms.event_handler.acl_check(origin, room_id)?;
|
||||||
|
|
||||||
// 1. Skip the PDU if we already have it as a timeline event
|
// 1. Skip the PDU if we already have it as a timeline event
|
||||||
if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? {
|
if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? {
|
||||||
return Ok(Some(pdu_id.to_vec()));
|
return Ok(Some(pdu_id.to_vec()));
|
||||||
|
@ -117,7 +119,15 @@ impl Service {
|
||||||
.ok_or_else(|| Error::bad_database("Failed to find first pdu in db."))?;
|
.ok_or_else(|| Error::bad_database("Failed to find first pdu in db."))?;
|
||||||
|
|
||||||
let (incoming_pdu, val) = self
|
let (incoming_pdu, val) = self
|
||||||
.handle_outlier_pdu(origin, &create_event, event_id, room_id, value, pub_key_map)
|
.handle_outlier_pdu(
|
||||||
|
origin,
|
||||||
|
&create_event,
|
||||||
|
event_id,
|
||||||
|
room_id,
|
||||||
|
value,
|
||||||
|
false,
|
||||||
|
pub_key_map,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
self.check_room_id(room_id, &incoming_pdu)?;
|
self.check_room_id(room_id, &incoming_pdu)?;
|
||||||
|
|
||||||
|
@ -174,7 +184,22 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors >= 5 {
|
if errors >= 5 {
|
||||||
break;
|
// Timeout other events
|
||||||
|
match services()
|
||||||
|
.globals
|
||||||
|
.bad_event_ratelimiter
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.entry((*prev_id).to_owned())
|
||||||
|
{
|
||||||
|
hash_map::Entry::Vacant(e) => {
|
||||||
|
e.insert((Instant::now(), 1));
|
||||||
|
}
|
||||||
|
hash_map::Entry::Occupied(mut e) => {
|
||||||
|
*e.get_mut() = (Instant::now(), e.get().1 + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((pdu, json)) = eventid_info.remove(&*prev_id) {
|
if let Some((pdu, json)) = eventid_info.remove(&*prev_id) {
|
||||||
|
@ -226,7 +251,7 @@ impl Service {
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.remove(&room_id.to_owned());
|
.remove(&room_id.to_owned());
|
||||||
warn!(
|
debug!(
|
||||||
"Handling prev event {} took {}m{}s",
|
"Handling prev event {} took {}m{}s",
|
||||||
prev_id,
|
prev_id,
|
||||||
elapsed.as_secs() / 60,
|
elapsed.as_secs() / 60,
|
||||||
|
@ -266,6 +291,7 @@ impl Service {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
|
||||||
#[tracing::instrument(skip(self, create_event, value, pub_key_map))]
|
#[tracing::instrument(skip(self, create_event, value, pub_key_map))]
|
||||||
fn handle_outlier_pdu<'a>(
|
fn handle_outlier_pdu<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -274,6 +300,7 @@ impl Service {
|
||||||
event_id: &'a EventId,
|
event_id: &'a EventId,
|
||||||
room_id: &'a RoomId,
|
room_id: &'a RoomId,
|
||||||
mut value: BTreeMap<String, CanonicalJsonValue>,
|
mut value: BTreeMap<String, CanonicalJsonValue>,
|
||||||
|
auth_events_known: bool,
|
||||||
pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
|
pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
|
||||||
) -> AsyncRecursiveType<'a, Result<(Arc<PduEvent>, BTreeMap<String, CanonicalJsonValue>)>> {
|
) -> AsyncRecursiveType<'a, Result<(Arc<PduEvent>, BTreeMap<String, CanonicalJsonValue>)>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
@ -315,7 +342,7 @@ impl Service {
|
||||||
Ok(ruma::signatures::Verified::Signatures) => {
|
Ok(ruma::signatures::Verified::Signatures) => {
|
||||||
// Redact
|
// Redact
|
||||||
warn!("Calculated hash does not match: {}", event_id);
|
warn!("Calculated hash does not match: {}", event_id);
|
||||||
match ruma::canonical_json::redact(value, room_version_id, None) {
|
let obj = match ruma::canonical_json::redact(value, room_version_id, None) {
|
||||||
Ok(obj) => obj,
|
Ok(obj) => obj,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -323,7 +350,17 @@ impl Service {
|
||||||
"Redaction failed",
|
"Redaction failed",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip the PDU if it is redacted and we already have it as an outlier event
|
||||||
|
if services().rooms.timeline.get_pdu_json(event_id)?.is_some() {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Event was redacted and we already knew about it",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj
|
||||||
}
|
}
|
||||||
Ok(ruma::signatures::Verified::All) => value,
|
Ok(ruma::signatures::Verified::All) => value,
|
||||||
};
|
};
|
||||||
|
@ -341,6 +378,7 @@ impl Service {
|
||||||
|
|
||||||
self.check_room_id(room_id, &incoming_pdu)?;
|
self.check_room_id(room_id, &incoming_pdu)?;
|
||||||
|
|
||||||
|
if !auth_events_known {
|
||||||
// 4. fetch any missing auth events doing all checks listed here starting at 1. These are not timeline events
|
// 4. fetch any missing auth events doing all checks listed here starting at 1. These are not timeline events
|
||||||
// 5. Reject "due to auth events" if can't get all the auth events or some of the auth events are also rejected "due to auth events"
|
// 5. Reject "due to auth events" if can't get all the auth events or some of the auth events are also rejected "due to auth events"
|
||||||
// NOTE: Step 5 is not applied anymore because it failed too often
|
// NOTE: Step 5 is not applied anymore because it failed too often
|
||||||
|
@ -358,6 +396,7 @@ impl Service {
|
||||||
pub_key_map,
|
pub_key_map,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
// 6. Reject "due to auth events" if the event doesn't pass auth based on the auth events
|
// 6. Reject "due to auth events" if the event doesn't pass auth based on the auth events
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -942,14 +981,21 @@ impl Service {
|
||||||
|
|
||||||
debug!("Resolving state");
|
debug!("Resolving state");
|
||||||
|
|
||||||
let lock = services().globals.stateres_mutex.lock();
|
let fetch_event = |id: &_| {
|
||||||
let state = match state_res::resolve(room_version_id, &fork_states, auth_chain_sets, |id| {
|
|
||||||
let res = services().rooms.timeline.get_pdu(id);
|
let res = services().rooms.timeline.get_pdu(id);
|
||||||
if let Err(e) = &res {
|
if let Err(e) = &res {
|
||||||
error!("LOOK AT ME Failed to fetch event: {}", e);
|
error!("LOOK AT ME Failed to fetch event: {}", e);
|
||||||
}
|
}
|
||||||
res.ok().flatten()
|
res.ok().flatten()
|
||||||
}) {
|
};
|
||||||
|
|
||||||
|
let lock = services().globals.stateres_mutex.lock();
|
||||||
|
let state = match state_res::resolve(
|
||||||
|
room_version_id,
|
||||||
|
&fork_states,
|
||||||
|
auth_chain_sets,
|
||||||
|
fetch_event,
|
||||||
|
) {
|
||||||
Ok(new_state) => new_state,
|
Ok(new_state) => new_state,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(Error::bad_database("State resolution failed, either an event could not be found or deserialization"));
|
return Err(Error::bad_database("State resolution failed, either an event could not be found or deserialization"));
|
||||||
|
@ -986,6 +1032,7 @@ impl Service {
|
||||||
/// b. Look at outlier pdu tree
|
/// b. Look at outlier pdu tree
|
||||||
/// c. Ask origin server over federation
|
/// c. Ask origin server over federation
|
||||||
/// d. TODO: Ask other servers over federation?
|
/// d. TODO: Ask other servers over federation?
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) fn fetch_and_handle_outliers<'a>(
|
pub(crate) fn fetch_and_handle_outliers<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -1013,26 +1060,6 @@ impl Service {
|
||||||
|
|
||||||
let mut pdus = vec![];
|
let mut pdus = vec![];
|
||||||
for id in events {
|
for id in events {
|
||||||
if let Some((time, tries)) = services()
|
|
||||||
.globals
|
|
||||||
.bad_event_ratelimiter
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&**id)
|
|
||||||
{
|
|
||||||
// Exponential backoff
|
|
||||||
let mut min_elapsed_duration =
|
|
||||||
Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
|
||||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
|
||||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
if time.elapsed() < min_elapsed_duration {
|
|
||||||
info!("Backing off from {}", id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a. Look in the main timeline (pduid_pdu tree)
|
// a. Look in the main timeline (pduid_pdu tree)
|
||||||
// b. Look at outlier pdu tree
|
// b. Look at outlier pdu tree
|
||||||
// (get_pdu_json checks both)
|
// (get_pdu_json checks both)
|
||||||
|
@ -1050,6 +1077,26 @@ impl Service {
|
||||||
let mut events_all = HashSet::new();
|
let mut events_all = HashSet::new();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while let Some(next_id) = todo_auth_events.pop() {
|
while let Some(next_id) = todo_auth_events.pop() {
|
||||||
|
if let Some((time, tries)) = services()
|
||||||
|
.globals
|
||||||
|
.bad_event_ratelimiter
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&*next_id)
|
||||||
|
{
|
||||||
|
// Exponential backoff
|
||||||
|
let mut min_elapsed_duration =
|
||||||
|
Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
||||||
|
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||||
|
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.elapsed() < min_elapsed_duration {
|
||||||
|
info!("Backing off from {}", next_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if events_all.contains(&next_id) {
|
if events_all.contains(&next_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1060,7 +1107,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Some(_)) = services().rooms.timeline.get_pdu(&next_id) {
|
if let Ok(Some(_)) = services().rooms.timeline.get_pdu(&next_id) {
|
||||||
trace!("Found {} in db", id);
|
trace!("Found {} in db", next_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1119,6 +1166,26 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (next_id, value) in events_in_reverse_order.iter().rev() {
|
for (next_id, value) in events_in_reverse_order.iter().rev() {
|
||||||
|
if let Some((time, tries)) = services()
|
||||||
|
.globals
|
||||||
|
.bad_event_ratelimiter
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&**next_id)
|
||||||
|
{
|
||||||
|
// Exponential backoff
|
||||||
|
let mut min_elapsed_duration =
|
||||||
|
Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
||||||
|
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||||
|
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.elapsed() < min_elapsed_duration {
|
||||||
|
info!("Backing off from {}", next_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.handle_outlier_pdu(
|
.handle_outlier_pdu(
|
||||||
origin,
|
origin,
|
||||||
|
@ -1126,6 +1193,7 @@ impl Service {
|
||||||
next_id,
|
next_id,
|
||||||
room_id,
|
room_id,
|
||||||
value.clone(),
|
value.clone(),
|
||||||
|
true,
|
||||||
pub_key_map,
|
pub_key_map,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1530,6 +1598,11 @@ impl Service {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if acl_event_content.allow.is_empty() {
|
||||||
|
// Ignore broken acl events
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if acl_event_content.is_allowed(server_name) {
|
if acl_event_content.is_allowed(server_name) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,6 +14,7 @@ use super::timeline::PduCount;
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
pub db: &'static dyn Data,
|
pub db: &'static dyn Data,
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub lazy_load_waiting:
|
pub lazy_load_waiting:
|
||||||
Mutex<HashMap<(OwnedUserId, OwnedDeviceId, OwnedRoomId, PduCount), HashSet<OwnedUserId>>>,
|
Mutex<HashMap<(OwnedUserId, OwnedDeviceId, OwnedRoomId, PduCount), HashSet<OwnedUserId>>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use ruma::{EventId, RoomId, UserId};
|
||||||
|
|
||||||
pub trait Data: Send + Sync {
|
pub trait Data: Send + Sync {
|
||||||
fn add_relation(&self, from: u64, to: u64) -> Result<()>;
|
fn add_relation(&self, from: u64, to: u64) -> Result<()>;
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn relations_until<'a>(
|
fn relations_until<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &'a UserId,
|
user_id: &'a UserId,
|
||||||
|
|
|
@ -40,6 +40,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn paginate_relations_with_filter(
|
pub fn paginate_relations_with_filter(
|
||||||
&self,
|
&self,
|
||||||
sender_user: &UserId,
|
sender_user: &UserId,
|
||||||
|
@ -82,7 +83,7 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
.user_can_see_event(sender_user, room_id, &pdu.event_id)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||||
|
@ -106,7 +107,7 @@ impl Service {
|
||||||
let events_before: Vec<_> = services()
|
let events_before: Vec<_> = services()
|
||||||
.rooms
|
.rooms
|
||||||
.pdu_metadata
|
.pdu_metadata
|
||||||
.relations_until(sender_user, &room_id, target, from)?
|
.relations_until(sender_user, room_id, target, from)?
|
||||||
.filter(|r| {
|
.filter(|r| {
|
||||||
r.as_ref().map_or(true, |(_, pdu)| {
|
r.as_ref().map_or(true, |(_, pdu)| {
|
||||||
filter_event_type.as_ref().map_or(true, |t| &pdu.kind == t)
|
filter_event_type.as_ref().map_or(true, |t| &pdu.kind == t)
|
||||||
|
@ -129,7 +130,7 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
.user_can_see_event(sender_user, room_id, &pdu.event_id)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||||
|
|
|
@ -4,6 +4,7 @@ use ruma::RoomId;
|
||||||
pub trait Data: Send + Sync {
|
pub trait Data: Send + Sync {
|
||||||
fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()>;
|
fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()>;
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn search_pdus<'a>(
|
fn search_pdus<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
|
|
|
@ -134,7 +134,7 @@ impl Service {
|
||||||
|
|
||||||
if serde_json::from_str::<SpaceChildEventContent>(pdu.content.get())
|
if serde_json::from_str::<SpaceChildEventContent>(pdu.content.get())
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|c| c.via)
|
.map(|c| c.via)
|
||||||
.map_or(true, |v| v.is_empty())
|
.map_or(true, |v| v.is_empty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -185,7 +185,9 @@ impl Service {
|
||||||
stack.push(children_ids);
|
stack.push(children_ids);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let server = current_room.server_name();
|
let server = current_room
|
||||||
|
.server_name()
|
||||||
|
.expect("Room IDs should always have a server name");
|
||||||
if server == services().globals.server_name() {
|
if server == services().globals.server_name() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -193,11 +195,11 @@ impl Service {
|
||||||
// Early return so the client can see some data already
|
// Early return so the client can see some data already
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
warn!("Asking {server} for /hierarchy");
|
debug!("Asking {server} for /hierarchy");
|
||||||
if let Ok(response) = services()
|
if let Ok(response) = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
&server,
|
server,
|
||||||
federation::space::get_hierarchy::v1::Request {
|
federation::space::get_hierarchy::v1::Request {
|
||||||
room_id: current_room.to_owned(),
|
room_id: current_room.to_owned(),
|
||||||
suggested_only,
|
suggested_only,
|
||||||
|
@ -235,7 +237,7 @@ impl Service {
|
||||||
.room
|
.room
|
||||||
.allowed_room_ids
|
.allowed_room_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|room| AllowRule::room_membership(room))
|
.map(AllowRule::room_membership)
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -245,7 +247,7 @@ impl Service {
|
||||||
.room
|
.room
|
||||||
.allowed_room_ids
|
.allowed_room_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|room| AllowRule::room_membership(room))
|
.map(AllowRule::room_membership)
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -313,7 +315,7 @@ impl Service {
|
||||||
canonical_alias: services()
|
canonical_alias: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomCanonicalAlias, "")?
|
.room_state_get(room_id, &StateEventType::RoomCanonicalAlias, "")?
|
||||||
.map_or(Ok(None), |s| {
|
.map_or(Ok(None), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomCanonicalAliasEventContent| c.alias)
|
.map(|c: RoomCanonicalAliasEventContent| c.alias)
|
||||||
|
@ -321,11 +323,11 @@ impl Service {
|
||||||
Error::bad_database("Invalid canonical alias event in database.")
|
Error::bad_database("Invalid canonical alias event in database.")
|
||||||
})
|
})
|
||||||
})?,
|
})?,
|
||||||
name: services().rooms.state_accessor.get_name(&room_id)?,
|
name: services().rooms.state_accessor.get_name(room_id)?,
|
||||||
num_joined_members: services()
|
num_joined_members: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(&room_id)?
|
.room_joined_count(room_id)?
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
warn!("Room {} has no member count", room_id);
|
warn!("Room {} has no member count", room_id);
|
||||||
0
|
0
|
||||||
|
@ -336,7 +338,7 @@ impl Service {
|
||||||
topic: services()
|
topic: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomTopic, "")?
|
.room_state_get(room_id, &StateEventType::RoomTopic, "")?
|
||||||
.map_or(Ok(None), |s| {
|
.map_or(Ok(None), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomTopicEventContent| Some(c.topic))
|
.map(|c: RoomTopicEventContent| Some(c.topic))
|
||||||
|
@ -348,7 +350,7 @@ impl Service {
|
||||||
world_readable: services()
|
world_readable: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomHistoryVisibility, "")?
|
.room_state_get(room_id, &StateEventType::RoomHistoryVisibility, "")?
|
||||||
.map_or(Ok(false), |s| {
|
.map_or(Ok(false), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomHistoryVisibilityEventContent| {
|
.map(|c: RoomHistoryVisibilityEventContent| {
|
||||||
|
@ -363,7 +365,7 @@ impl Service {
|
||||||
guest_can_join: services()
|
guest_can_join: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomGuestAccess, "")?
|
.room_state_get(room_id, &StateEventType::RoomGuestAccess, "")?
|
||||||
.map_or(Ok(false), |s| {
|
.map_or(Ok(false), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomGuestAccessEventContent| {
|
.map(|c: RoomGuestAccessEventContent| {
|
||||||
|
@ -376,7 +378,7 @@ impl Service {
|
||||||
avatar_url: services()
|
avatar_url: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomAvatar, "")?
|
.room_state_get(room_id, &StateEventType::RoomAvatar, "")?
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomAvatarEventContent| c.url)
|
.map(|c: RoomAvatarEventContent| c.url)
|
||||||
|
@ -389,7 +391,7 @@ impl Service {
|
||||||
let join_rule = services()
|
let join_rule = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomJoinRules, "")?
|
.room_state_get(room_id, &StateEventType::RoomJoinRules, "")?
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomJoinRulesEventContent| c.join_rule)
|
.map(|c: RoomJoinRulesEventContent| c.join_rule)
|
||||||
|
@ -415,7 +417,7 @@ impl Service {
|
||||||
room_type: services()
|
room_type: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomCreate, "")?
|
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
serde_json::from_str::<RoomCreateEventContent>(s.content.get()).map_err(|e| {
|
serde_json::from_str::<RoomCreateEventContent>(s.content.get()).map_err(|e| {
|
||||||
error!("Invalid room create event in database: {}", e);
|
error!("Invalid room create event in database: {}", e);
|
||||||
|
@ -455,7 +457,7 @@ impl Service {
|
||||||
SpaceRoomJoinRule::Invite => services()
|
SpaceRoomJoinRule::Invite => services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.is_joined(sender_user, &room_id)?,
|
.is_joined(sender_user, room_id)?,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -479,8 +481,7 @@ impl Service {
|
||||||
match join_rule {
|
match join_rule {
|
||||||
JoinRule::Restricted(r) => {
|
JoinRule::Restricted(r) => {
|
||||||
for rule in &r.allow {
|
for rule in &r.allow {
|
||||||
match rule {
|
if let join_rules::AllowRule::RoomMembership(rm) = rule {
|
||||||
join_rules::AllowRule::RoomMembership(rm) => {
|
|
||||||
if let Ok(true) = services()
|
if let Ok(true) = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
|
@ -489,8 +490,6 @@ impl Service {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
|
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
api::client::error::ErrorKind,
|
||||||
events::{
|
events::{
|
||||||
room::{create::RoomCreateEventContent, member::MembershipState},
|
room::{create::RoomCreateEventContent, member::MembershipState},
|
||||||
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
||||||
|
@ -40,7 +41,7 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
.parse_compressed_state_event(&new)
|
.parse_compressed_state_event(new)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(_, id)| id)
|
.map(|(_, id)| id)
|
||||||
}) {
|
}) {
|
||||||
|
@ -331,7 +332,7 @@ impl Service {
|
||||||
"",
|
"",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let create_event_content: Option<RoomCreateEventContent> = create_event
|
let create_event_content: RoomCreateEventContent = create_event
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|create_event| {
|
.map(|create_event| {
|
||||||
serde_json::from_str(create_event.content.get()).map_err(|e| {
|
serde_json::from_str(create_event.content.get()).map_err(|e| {
|
||||||
|
@ -339,14 +340,10 @@ impl Service {
|
||||||
Error::bad_database("Invalid create event in db.")
|
Error::bad_database("Invalid create event in db.")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?
|
||||||
let room_version = create_event_content
|
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "No create event found"))?;
|
||||||
.map(|create_event| create_event.room_version)
|
|
||||||
.ok_or_else(|| {
|
Ok(create_event_content.room_version)
|
||||||
warn!("Invalid room version for room {room_id}");
|
|
||||||
Error::BadDatabase("Invalid room version")
|
|
||||||
})?;
|
|
||||||
Ok(room_version)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<Option<u64>> {
|
pub fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<Option<u64>> {
|
||||||
|
@ -415,7 +412,7 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
.parse_compressed_state_event(&compressed)
|
.parse_compressed_state_event(compressed)
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
.filter_map(|(shortstatekey, event_id)| {
|
.filter_map(|(shortstatekey, event_id)| {
|
||||||
|
|
|
@ -16,7 +16,7 @@ use ruma::{
|
||||||
},
|
},
|
||||||
StateEventType,
|
StateEventType,
|
||||||
},
|
},
|
||||||
EventId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ impl Service {
|
||||||
return Ok(*visibility);
|
return Ok(*visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
let currently_member = services().rooms.state_cache.is_joined(&user_id, &room_id)?;
|
let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?;
|
||||||
|
|
||||||
let history_visibility = self
|
let history_visibility = self
|
||||||
.state_get(shortstatehash, &StateEventType::RoomHistoryVisibility, "")?
|
.state_get(shortstatehash, &StateEventType::RoomHistoryVisibility, "")?
|
||||||
|
@ -197,11 +197,11 @@ impl Service {
|
||||||
HistoryVisibility::Shared => currently_member,
|
HistoryVisibility::Shared => currently_member,
|
||||||
HistoryVisibility::Invited => {
|
HistoryVisibility::Invited => {
|
||||||
// Allow if any member on requesting server was AT LEAST invited, else deny
|
// Allow if any member on requesting server was AT LEAST invited, else deny
|
||||||
self.user_was_invited(shortstatehash, &user_id)
|
self.user_was_invited(shortstatehash, user_id)
|
||||||
}
|
}
|
||||||
HistoryVisibility::Joined => {
|
HistoryVisibility::Joined => {
|
||||||
// Allow if any member on requested server was joined, else deny
|
// Allow if any member on requested server was joined, else deny
|
||||||
self.user_was_joined(shortstatehash, &user_id)
|
self.user_was_joined(shortstatehash, user_id)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Unknown history visibility {history_visibility}");
|
error!("Unknown history visibility {history_visibility}");
|
||||||
|
@ -221,10 +221,10 @@ impl Service {
|
||||||
/// the room's history_visibility at that event's state.
|
/// the room's history_visibility at that event's state.
|
||||||
#[tracing::instrument(skip(self, user_id, room_id))]
|
#[tracing::instrument(skip(self, user_id, room_id))]
|
||||||
pub fn user_can_see_state_events(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
pub fn user_can_see_state_events(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||||
let currently_member = services().rooms.state_cache.is_joined(&user_id, &room_id)?;
|
let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?;
|
||||||
|
|
||||||
let history_visibility = self
|
let history_visibility = self
|
||||||
.room_state_get(&room_id, &StateEventType::RoomHistoryVisibility, "")?
|
.room_state_get(room_id, &StateEventType::RoomHistoryVisibility, "")?
|
||||||
.map_or(Ok(HistoryVisibility::Shared), |s| {
|
.map_or(Ok(HistoryVisibility::Shared), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomHistoryVisibilityEventContent| c.history_visibility)
|
.map(|c: RoomHistoryVisibilityEventContent| c.history_visibility)
|
||||||
|
@ -276,20 +276,26 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomName, "")?
|
.room_state_get(room_id, &StateEventType::RoomName, "")?
|
||||||
.map_or(Ok(None), |s| {
|
.map_or(Ok(None), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomNameEventContent| c.name)
|
.map(|c: RoomNameEventContent| Some(c.name))
|
||||||
.map_err(|_| Error::bad_database("Invalid room name event in database."))
|
.map_err(|e| {
|
||||||
|
error!(
|
||||||
|
"Invalid room name event in database for room {}. {}",
|
||||||
|
room_id, e
|
||||||
|
);
|
||||||
|
Error::bad_database("Invalid room name event in database.")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_avatar(&self, room_id: &RoomId) -> Result<Option<RoomAvatarEventContent>> {
|
pub fn get_avatar(&self, room_id: &RoomId) -> Result<JsOption<RoomAvatarEventContent>> {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomAvatar, "")?
|
.room_state_get(room_id, &StateEventType::RoomAvatar, "")?
|
||||||
.map_or(Ok(None), |s| {
|
.map_or(Ok(JsOption::Undefined), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid room avatar event in database."))
|
.map_err(|_| Error::bad_database("Invalid room avatar event in database."))
|
||||||
})
|
})
|
||||||
|
@ -303,7 +309,7 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomMember, user_id.as_str())?
|
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
||||||
.map_or(Ok(None), |s| {
|
.map_or(Ok(None), |s| {
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid room member event in database."))
|
.map_err(|_| Error::bad_database("Invalid room member event in database."))
|
||||||
|
|
|
@ -78,6 +78,7 @@ pub trait Data: Send + Sync {
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a>;
|
) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a>;
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user was invited to.
|
/// Returns an iterator over all rooms a user was invited to.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn rooms_invited<'a>(
|
fn rooms_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
@ -96,6 +97,7 @@ pub trait Data: Send + Sync {
|
||||||
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>>;
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>>;
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user left.
|
/// Returns an iterator over all rooms a user left.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn rooms_left<'a>(
|
fn rooms_left<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|
|
@ -16,6 +16,7 @@ use self::data::StateDiff;
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
pub db: &'static dyn Data,
|
pub db: &'static dyn Data,
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub stateinfo_cache: Mutex<
|
pub stateinfo_cache: Mutex<
|
||||||
LruCache<
|
LruCache<
|
||||||
u64,
|
u64,
|
||||||
|
@ -33,6 +34,7 @@ pub type CompressedStateEvent = [u8; 2 * size_of::<u64>()];
|
||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
/// Returns a stack with info on shortstatehash, full state, added diff and removed diff for the selected shortstatehash and each parent layer.
|
/// Returns a stack with info on shortstatehash, full state, added diff and removed diff for the selected shortstatehash and each parent layer.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn load_shortstatehash_info(
|
pub fn load_shortstatehash_info(
|
||||||
&self,
|
&self,
|
||||||
|
@ -131,6 +133,7 @@ impl Service {
|
||||||
/// * `statediffremoved` - Removed from base. Each vec is shortstatekey+shorteventid
|
/// * `statediffremoved` - Removed from base. Each vec is shortstatekey+shorteventid
|
||||||
/// * `diff_to_sibling` - Approximately how much the diff grows each time for this layer
|
/// * `diff_to_sibling` - Approximately how much the diff grows each time for this layer
|
||||||
/// * `parent_states` - A stack with info on shortstatehash, full state, added diff and removed diff for each parent layer
|
/// * `parent_states` - A stack with info on shortstatehash, full state, added diff and removed diff for each parent layer
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
#[tracing::instrument(skip(
|
#[tracing::instrument(skip(
|
||||||
self,
|
self,
|
||||||
statediffnew,
|
statediffnew,
|
||||||
|
@ -164,7 +167,7 @@ impl Service {
|
||||||
for removed in statediffremoved.iter() {
|
for removed in statediffremoved.iter() {
|
||||||
if !parent_new.remove(removed) {
|
if !parent_new.remove(removed) {
|
||||||
// It was not added in the parent and we removed it
|
// It was not added in the parent and we removed it
|
||||||
parent_removed.insert(removed.clone());
|
parent_removed.insert(*removed);
|
||||||
}
|
}
|
||||||
// Else it was added in the parent and we removed it again. We can forget this change
|
// Else it was added in the parent and we removed it again. We can forget this change
|
||||||
}
|
}
|
||||||
|
@ -172,7 +175,7 @@ impl Service {
|
||||||
for new in statediffnew.iter() {
|
for new in statediffnew.iter() {
|
||||||
if !parent_removed.remove(new) {
|
if !parent_removed.remove(new) {
|
||||||
// It was not touched in the parent and we added it
|
// It was not touched in the parent and we added it
|
||||||
parent_new.insert(new.clone());
|
parent_new.insert(*new);
|
||||||
}
|
}
|
||||||
// Else it was removed in the parent and we added it again. We can forget this change
|
// Else it was removed in the parent and we added it again. We can forget this change
|
||||||
}
|
}
|
||||||
|
@ -217,7 +220,7 @@ impl Service {
|
||||||
for removed in statediffremoved.iter() {
|
for removed in statediffremoved.iter() {
|
||||||
if !parent_new.remove(removed) {
|
if !parent_new.remove(removed) {
|
||||||
// It was not added in the parent and we removed it
|
// It was not added in the parent and we removed it
|
||||||
parent_removed.insert(removed.clone());
|
parent_removed.insert(*removed);
|
||||||
}
|
}
|
||||||
// Else it was added in the parent and we removed it again. We can forget this change
|
// Else it was added in the parent and we removed it again. We can forget this change
|
||||||
}
|
}
|
||||||
|
@ -225,7 +228,7 @@ impl Service {
|
||||||
for new in statediffnew.iter() {
|
for new in statediffnew.iter() {
|
||||||
if !parent_removed.remove(new) {
|
if !parent_removed.remove(new) {
|
||||||
// It was not touched in the parent and we added it
|
// It was not touched in the parent and we added it
|
||||||
parent_new.insert(new.clone());
|
parent_new.insert(*new);
|
||||||
}
|
}
|
||||||
// Else it was removed in the parent and we added it again. We can forget this change
|
// Else it was removed in the parent and we added it again. We can forget this change
|
||||||
}
|
}
|
||||||
|
@ -253,6 +256,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the new shortstatehash, and the state diff from the previous room state
|
/// Returns the new shortstatehash, and the state diff from the previous room state
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn save_state(
|
pub fn save_state(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::{PduEvent, Result};
|
||||||
use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId};
|
use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId};
|
||||||
|
|
||||||
pub trait Data: Send + Sync {
|
pub trait Data: Send + Sync {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn threads_until<'a>(
|
fn threads_until<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &'a UserId,
|
user_id: &'a UserId,
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl Service {
|
||||||
self.db.threads_until(user_id, room_id, until, include)
|
self.db.threads_until(user_id, room_id, until, include)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_to_thread<'a>(&'a self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> {
|
pub fn add_to_thread(&self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> {
|
||||||
let root_id = &services()
|
let root_id = &services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
|
@ -103,7 +103,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut users = Vec::new();
|
let mut users = Vec::new();
|
||||||
if let Some(userids) = self.db.get_participants(&root_id)? {
|
if let Some(userids) = self.db.get_participants(root_id)? {
|
||||||
users.extend_from_slice(&userids);
|
users.extend_from_slice(&userids);
|
||||||
users.push(pdu.sender.clone());
|
users.push(pdu.sender.clone());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -66,6 +66,7 @@ pub trait Data: Send + Sync {
|
||||||
|
|
||||||
/// Returns an iterator over all events and their tokens in a room that happened before the
|
/// Returns an iterator over all events and their tokens in a room that happened before the
|
||||||
/// event with id `until` in reverse-chronological order.
|
/// event with id `until` in reverse-chronological order.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn pdus_until<'a>(
|
fn pdus_until<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
@ -75,6 +76,7 @@ pub trait Data: Send + Sync {
|
||||||
|
|
||||||
/// Returns an iterator over all events in a room that happened after the event with id `from`
|
/// Returns an iterator over all events in a room that happened after the event with id `from`
|
||||||
/// in chronological order.
|
/// in chronological order.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn pdus_after<'a>(
|
fn pdus_after<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|
|
@ -58,8 +58,8 @@ impl PduCount {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_from_string(token: &str) -> Result<Self> {
|
pub fn try_from_string(token: &str) -> Result<Self> {
|
||||||
if token.starts_with('-') {
|
if let Some(stripped) = token.strip_prefix('-') {
|
||||||
token[1..].parse().map(PduCount::Backfilled)
|
stripped.parse().map(PduCount::Backfilled)
|
||||||
} else {
|
} else {
|
||||||
token.parse().map(PduCount::Normal)
|
token.parse().map(PduCount::Normal)
|
||||||
}
|
}
|
||||||
|
@ -90,18 +90,6 @@ impl Ord for PduCount {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn comparisons() {
|
|
||||||
assert!(PduCount::Normal(1) < PduCount::Normal(2));
|
|
||||||
assert!(PduCount::Backfilled(2) < PduCount::Backfilled(1));
|
|
||||||
assert!(PduCount::Normal(1) > PduCount::Backfilled(1));
|
|
||||||
assert!(PduCount::Backfilled(1) < PduCount::Normal(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
pub db: &'static dyn Data,
|
pub db: &'static dyn Data,
|
||||||
|
@ -112,7 +100,7 @@ pub struct Service {
|
||||||
impl Service {
|
impl Service {
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<Option<Arc<PduEvent>>> {
|
pub fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<Option<Arc<PduEvent>>> {
|
||||||
self.all_pdus(&user_id!("@doesntmatter:conduit.rs"), &room_id)?
|
self.all_pdus(user_id!("@doesntmatter:conduit.rs"), room_id)?
|
||||||
.next()
|
.next()
|
||||||
.map(|o| o.map(|(_, p)| Arc::new(p)))
|
.map(|o| o.map(|(_, p)| Arc::new(p)))
|
||||||
.transpose()
|
.transpose()
|
||||||
|
@ -320,12 +308,25 @@ impl Service {
|
||||||
let mut notifies = Vec::new();
|
let mut notifies = Vec::new();
|
||||||
let mut highlights = Vec::new();
|
let mut highlights = Vec::new();
|
||||||
|
|
||||||
for user in services()
|
let mut push_target = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.get_our_real_users(&pdu.room_id)?
|
.get_our_real_users(&pdu.room_id)?;
|
||||||
.iter()
|
|
||||||
{
|
if pdu.kind == TimelineEventType::RoomMember {
|
||||||
|
if let Some(state_key) = &pdu.state_key {
|
||||||
|
let target_user_id = UserId::parse(state_key.clone())
|
||||||
|
.expect("This state_key was previously validated");
|
||||||
|
|
||||||
|
if !push_target.contains(&target_user_id) {
|
||||||
|
let mut target = push_target.as_ref().clone();
|
||||||
|
target.insert(target_user_id);
|
||||||
|
push_target = Arc::new(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for user in push_target.iter() {
|
||||||
// Don't notify the user of their own events
|
// Don't notify the user of their own events
|
||||||
if user == &pdu.sender {
|
if user == &pdu.sender {
|
||||||
continue;
|
continue;
|
||||||
|
@ -458,7 +459,7 @@ impl Service {
|
||||||
let to_conduit = body.starts_with(&format!("{server_user}: "))
|
let to_conduit = body.starts_with(&format!("{server_user}: "))
|
||||||
|| body.starts_with(&format!("{server_user} "))
|
|| body.starts_with(&format!("{server_user} "))
|
||||||
|| body == format!("{server_user}:")
|
|| body == format!("{server_user}:")
|
||||||
|| body == format!("{server_user}");
|
|| body == server_user;
|
||||||
|
|
||||||
// This will evaluate to false if the emergency password is set up so that
|
// This will evaluate to false if the emergency password is set up so that
|
||||||
// the administrator can execute commands as conduit
|
// the administrator can execute commands as conduit
|
||||||
|
@ -842,7 +843,7 @@ impl Service {
|
||||||
|
|
||||||
let target = pdu
|
let target = pdu
|
||||||
.state_key()
|
.state_key()
|
||||||
.filter(|v| v.starts_with("@"))
|
.filter(|v| v.starts_with('@'))
|
||||||
.unwrap_or(sender.as_str());
|
.unwrap_or(sender.as_str());
|
||||||
let server_name = services().globals.server_name();
|
let server_name = services().globals.server_name();
|
||||||
let server_user = format!("@conduit:{}", server_name);
|
let server_user = format!("@conduit:{}", server_name);
|
||||||
|
@ -850,7 +851,7 @@ impl Service {
|
||||||
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
||||||
|
|
||||||
if content.membership == MembershipState::Leave {
|
if content.membership == MembershipState::Leave {
|
||||||
if target == &server_user {
|
if target == server_user {
|
||||||
warn!("Conduit user cannot leave from admins room");
|
warn!("Conduit user cannot leave from admins room");
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -876,7 +877,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
if content.membership == MembershipState::Ban && pdu.state_key().is_some() {
|
if content.membership == MembershipState::Ban && pdu.state_key().is_some() {
|
||||||
if target == &server_user {
|
if target == server_user {
|
||||||
warn!("Conduit user cannot be banned in admins room");
|
warn!("Conduit user cannot be banned in admins room");
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -1048,7 +1049,7 @@ impl Service {
|
||||||
#[tracing::instrument(skip(self, room_id))]
|
#[tracing::instrument(skip(self, room_id))]
|
||||||
pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result<()> {
|
pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result<()> {
|
||||||
let first_pdu = self
|
let first_pdu = self
|
||||||
.all_pdus(&user_id!("@doesntmatter:conduit.rs"), &room_id)?
|
.all_pdus(user_id!("@doesntmatter:conduit.rs"), room_id)?
|
||||||
.next()
|
.next()
|
||||||
.expect("Room is not empty")?;
|
.expect("Room is not empty")?;
|
||||||
|
|
||||||
|
@ -1060,7 +1061,7 @@ impl Service {
|
||||||
let power_levels: RoomPowerLevelsEventContent = services()
|
let power_levels: RoomPowerLevelsEventContent = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomPowerLevels, "")?
|
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||||
.map(|ev| {
|
.map(|ev| {
|
||||||
serde_json::from_str(ev.content.get())
|
serde_json::from_str(ev.content.get())
|
||||||
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
|
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
|
||||||
|
@ -1091,11 +1092,9 @@ impl Service {
|
||||||
.await;
|
.await;
|
||||||
match response {
|
match response {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let mut pub_key_map = RwLock::new(BTreeMap::new());
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
for pdu in response.pdus {
|
for pdu in response.pdus {
|
||||||
if let Err(e) = self
|
if let Err(e) = self.backfill_pdu(backfill_server, pdu, &pub_key_map).await
|
||||||
.backfill_pdu(backfill_server, pdu, &mut pub_key_map)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
warn!("Failed to add backfilled pdu: {e}");
|
warn!("Failed to add backfilled pdu: {e}");
|
||||||
}
|
}
|
||||||
|
@ -1142,7 +1141,7 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.handle_incoming_pdu(origin, &event_id, &room_id, value, false, &pub_key_map)
|
.handle_incoming_pdu(origin, &event_id, &room_id, value, false, pub_key_map)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let value = self.get_pdu_json(&event_id)?.expect("We just created it");
|
let value = self.get_pdu_json(&event_id)?.expect("We just created it");
|
||||||
|
@ -1175,8 +1174,7 @@ impl Service {
|
||||||
|
|
||||||
drop(insert_lock);
|
drop(insert_lock);
|
||||||
|
|
||||||
match pdu.kind {
|
if pdu.kind == TimelineEventType::RoomMessage {
|
||||||
TimelineEventType::RoomMessage => {
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct ExtractBody {
|
struct ExtractBody {
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
|
@ -1192,11 +1190,22 @@ impl Service {
|
||||||
.index_pdu(shortroomid, &pdu_id, &body)?;
|
.index_pdu(shortroomid, &pdu_id, &body)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
drop(mutex_lock);
|
drop(mutex_lock);
|
||||||
|
|
||||||
info!("Prepended backfill pdu");
|
info!("Prepended backfill pdu");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn comparisons() {
|
||||||
|
assert!(PduCount::Normal(1) < PduCount::Normal(2));
|
||||||
|
assert!(PduCount::Backfilled(2) < PduCount::Backfilled(1));
|
||||||
|
assert!(PduCount::Normal(1) > PduCount::Backfilled(1));
|
||||||
|
assert!(PduCount::Backfilled(1) < PduCount::Normal(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::Result;
|
||||||
use super::{OutgoingKind, SendingEventType};
|
use super::{OutgoingKind, SendingEventType};
|
||||||
|
|
||||||
pub trait Data: Send + Sync {
|
pub trait Data: Send + Sync {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn active_requests<'a>(
|
fn active_requests<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> Box<dyn Iterator<Item = Result<(Vec<u8>, OutgoingKind, SendingEventType)>> + 'a>;
|
) -> Box<dyn Iterator<Item = Result<(Vec<u8>, OutgoingKind, SendingEventType)>> + 'a>;
|
||||||
|
|
|
@ -131,7 +131,7 @@ impl Service {
|
||||||
for (key, outgoing_kind, event) in self.db.active_requests().filter_map(|r| r.ok()) {
|
for (key, outgoing_kind, event) in self.db.active_requests().filter_map(|r| r.ok()) {
|
||||||
let entry = initial_transactions
|
let entry = initial_transactions
|
||||||
.entry(outgoing_kind.clone())
|
.entry(outgoing_kind.clone())
|
||||||
.or_insert_with(Vec::new);
|
.or_default();
|
||||||
|
|
||||||
if entry.len() > 30 {
|
if entry.len() > 30 {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod data;
|
mod data;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::{BTreeMap, BTreeSet},
|
||||||
mem,
|
mem,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
@ -28,12 +28,13 @@ use crate::{services, Error, Result};
|
||||||
pub struct SlidingSyncCache {
|
pub struct SlidingSyncCache {
|
||||||
lists: BTreeMap<String, SyncRequestList>,
|
lists: BTreeMap<String, SyncRequestList>,
|
||||||
subscriptions: BTreeMap<OwnedRoomId, sync_events::v4::RoomSubscription>,
|
subscriptions: BTreeMap<OwnedRoomId, sync_events::v4::RoomSubscription>,
|
||||||
known_rooms: BTreeMap<String, BTreeMap<OwnedRoomId, bool>>,
|
known_rooms: BTreeMap<String, BTreeMap<OwnedRoomId, u64>>, // For every room, the roomsince number
|
||||||
extensions: ExtensionsConfig,
|
extensions: ExtensionsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
pub db: &'static dyn Data,
|
pub db: &'static dyn Data,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub connections:
|
pub connections:
|
||||||
Mutex<BTreeMap<(OwnedUserId, OwnedDeviceId, String), Arc<Mutex<SlidingSyncCache>>>>,
|
Mutex<BTreeMap<(OwnedUserId, OwnedDeviceId, String), Arc<Mutex<SlidingSyncCache>>>>,
|
||||||
}
|
}
|
||||||
|
@ -61,7 +62,7 @@ impl Service {
|
||||||
user_id: OwnedUserId,
|
user_id: OwnedUserId,
|
||||||
device_id: OwnedDeviceId,
|
device_id: OwnedDeviceId,
|
||||||
request: &mut sync_events::v4::Request,
|
request: &mut sync_events::v4::Request,
|
||||||
) -> BTreeMap<String, BTreeMap<OwnedRoomId, bool>> {
|
) -> BTreeMap<String, BTreeMap<OwnedRoomId, u64>> {
|
||||||
let Some(conn_id) = request.conn_id.clone() else {
|
let Some(conn_id) = request.conn_id.clone() else {
|
||||||
return BTreeMap::new();
|
return BTreeMap::new();
|
||||||
};
|
};
|
||||||
|
@ -127,6 +128,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, Some(cached_filters)) => list.filters = Some(cached_filters),
|
(_, Some(cached_filters)) => list.filters = Some(cached_filters),
|
||||||
|
(Some(list_filters), _) => list.filters = Some(list_filters.clone()),
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
}
|
}
|
||||||
if list.bump_event_types.is_empty() {
|
if list.bump_event_types.is_empty() {
|
||||||
|
@ -136,12 +138,18 @@ impl Service {
|
||||||
cached.lists.insert(list_id.clone(), list.clone());
|
cached.lists.insert(list_id.clone(), list.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached.subscriptions.extend(
|
||||||
.subscriptions
|
|
||||||
.extend(request.room_subscriptions.clone().into_iter());
|
|
||||||
request
|
request
|
||||||
.room_subscriptions
|
.room_subscriptions
|
||||||
.extend(cached.subscriptions.clone().into_iter());
|
.iter()
|
||||||
|
.map(|(k, v)| (k.clone(), v.clone())),
|
||||||
|
);
|
||||||
|
request.room_subscriptions.extend(
|
||||||
|
cached
|
||||||
|
.subscriptions
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (k.clone(), v.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
request.extensions.e2ee.enabled = request
|
request.extensions.e2ee.enabled = request
|
||||||
.extensions
|
.extensions
|
||||||
|
@ -210,7 +218,8 @@ impl Service {
|
||||||
device_id: OwnedDeviceId,
|
device_id: OwnedDeviceId,
|
||||||
conn_id: String,
|
conn_id: String,
|
||||||
list_id: String,
|
list_id: String,
|
||||||
new_cached_rooms: BTreeMap<OwnedRoomId, bool>,
|
new_cached_rooms: BTreeSet<OwnedRoomId>,
|
||||||
|
globalsince: u64,
|
||||||
) {
|
) {
|
||||||
let mut cache = self.connections.lock().unwrap();
|
let mut cache = self.connections.lock().unwrap();
|
||||||
let cached = Arc::clone(
|
let cached = Arc::clone(
|
||||||
|
@ -228,7 +237,20 @@ impl Service {
|
||||||
let cached = &mut cached.lock().unwrap();
|
let cached = &mut cached.lock().unwrap();
|
||||||
drop(cache);
|
drop(cache);
|
||||||
|
|
||||||
cached.known_rooms.insert(list_id, new_cached_rooms);
|
for (roomid, lastsince) in cached
|
||||||
|
.known_rooms
|
||||||
|
.entry(list_id.clone())
|
||||||
|
.or_default()
|
||||||
|
.iter_mut()
|
||||||
|
{
|
||||||
|
if !new_cached_rooms.contains(roomid) {
|
||||||
|
*lastsince = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let list = cached.known_rooms.entry(list_id).or_default();
|
||||||
|
for roomid in new_cached_rooms {
|
||||||
|
list.insert(roomid, globalsince);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if account is deactivated
|
/// Check if account is deactivated
|
||||||
|
|
|
@ -116,9 +116,11 @@ impl Error {
|
||||||
Self::BadRequest(kind, _) => (
|
Self::BadRequest(kind, _) => (
|
||||||
kind.clone(),
|
kind.clone(),
|
||||||
match kind {
|
match kind {
|
||||||
Forbidden | GuestAccessForbidden | ThreepidAuthFailed | ThreepidDenied => {
|
WrongRoomKeysVersion { .. }
|
||||||
StatusCode::FORBIDDEN
|
| Forbidden
|
||||||
}
|
| GuestAccessForbidden
|
||||||
|
| ThreepidAuthFailed
|
||||||
|
| ThreepidDenied => StatusCode::FORBIDDEN,
|
||||||
Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED,
|
Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED,
|
||||||
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
||||||
LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
|
LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
|
||||||
|
|
Loading…
Reference in a new issue