Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c29197b3f4 | |||
| 739eab46d5 | |||
| 923a98eb66 | |||
| 4430e4dee0 | |||
| d67f19a55d | |||
| b903b46d16 | |||
| 167559bb27 | |||
| 838e4b9d8d | |||
| 038b71fc9d | |||
| 720fbd09c2 | |||
| c42cb90dd3 | |||
| 5950355348 | |||
| f79bd2ac72 | |||
| 80ec0e31b1 | |||
| bda44b16b1 | |||
| e2280aa1a5 | |||
| bdf2de076a | |||
| 1797fec3c9 | |||
| 188fa5a073 | |||
| f0c63c539b | |||
| 649e9da1f8 | |||
| df28359a19 | |||
| 9370e93a8d | |||
| bdd5845490 | |||
| bacffd6174 | |||
| a1bfd7a018 | |||
| 7009f56a7a | |||
| 2c0bfac43e | |||
| fcb6c8a113 | |||
| 1ab77aeb91 |
@@ -38,8 +38,11 @@ env:
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
# Get error output from nix that we can actually use
|
||||
NIX_CONFIG: show-trace = true
|
||||
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
@@ -57,7 +60,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Tag comparison check
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') }}
|
||||
run: |
|
||||
# Tag mismatch with latest repo tag check to prevent potential downgrades
|
||||
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
@@ -115,7 +118,7 @@ jobs:
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop .#all-features --command true
|
||||
|
||||
@@ -237,7 +240,7 @@ jobs:
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop .#all-features --command true
|
||||
|
||||
@@ -293,15 +296,15 @@ jobs:
|
||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
||||
|
||||
@@ -21,8 +21,11 @@ env:
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
# Get error output from nix that we can actually use
|
||||
NIX_CONFIG: show-trace = true
|
||||
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
@@ -98,7 +101,7 @@ jobs:
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop --command true
|
||||
|
||||
|
||||
Generated
+13
-22
@@ -769,7 +769,7 @@ dependencies = [
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ruma",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0)",
|
||||
"ruma-identifiers-validation",
|
||||
"rustyline-async",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2988,7 +2988,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"regex",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=c51ccb2c68d2e3557eb12b1a49036531711ec0e5)",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_html_form",
|
||||
@@ -3015,7 +3015,7 @@ dependencies = [
|
||||
"pulldown-cmark",
|
||||
"regex",
|
||||
"ruma-common",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=c51ccb2c68d2e3557eb12b1a49036531711ec0e5)",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -3046,15 +3046,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.9.0"
|
||||
@@ -3074,7 +3065,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=c51ccb2c68d2e3557eb12b1a49036531711ec0e5)",
|
||||
"ruma-identifiers-validation",
|
||||
"serde",
|
||||
"syn 2.0.71",
|
||||
"toml",
|
||||
@@ -3138,8 +3129,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-librocksdb-sys"
|
||||
version = "0.23.1+9.3.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=db1ba33c2b78ad228e2525e8902d059c24fc81a1#db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
version = "0.24.0+9.4.0"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=4056a3b0f823013fec49f6d0b3e5698856e6476a#4056a3b0f823013fec49f6d0b3e5698856e6476a"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bzip2-sys",
|
||||
@@ -3155,8 +3146,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-rocksdb"
|
||||
version = "0.27.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=db1ba33c2b78ad228e2525e8902d059c24fc81a1#db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
version = "0.28.0"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=4056a3b0f823013fec49f6d0b3e5698856e6476a#4056a3b0f823013fec49f6d0b3e5698856e6476a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rust-librocksdb-sys",
|
||||
@@ -4250,7 +4241,7 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390261f60711c2af573c#b348dca742af641c47bc390261f60711c2af573c"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=4d78a14a5e03f539b8c6b475aefa08bb14e4de91#4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
@@ -4261,7 +4252,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390261f60711c2af573c#b348dca742af641c47bc390261f60711c2af573c"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=4d78a14a5e03f539b8c6b475aefa08bb14e4de91#4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4271,7 +4262,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390261f60711c2af573c#b348dca742af641c47bc390261f60711c2af573c"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=4d78a14a5e03f539b8c6b475aefa08bb14e4de91#4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -4291,7 +4282,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390261f60711c2af573c#b348dca742af641c47bc390261f60711c2af573c"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=4d78a14a5e03f539b8c6b475aefa08bb14e4de91#4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -4319,7 +4310,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.18"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390261f60711c2af573c#b348dca742af641c47bc390261f60711c2af573c"
|
||||
source = "git+https://github.com/girlbossceo/tracing?rev=4d78a14a5e03f539b8c6b475aefa08bb14e4de91#4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
|
||||
+16
-10
@@ -309,7 +309,7 @@ features = [
|
||||
|
||||
[workspace.dependencies.ruma-identifiers-validation]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
rev = "c51ccb2c68d2e3557eb12b1a49036531711ec0e5"
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
path = "deps/rust-rocksdb"
|
||||
@@ -333,7 +333,7 @@ version = "0.10.8"
|
||||
|
||||
# optional opentelemetry, performance measurements, flamegraphs, etc for performance measurements and monitoring
|
||||
[workspace.dependencies.opentelemetry]
|
||||
version = "0.24.0"
|
||||
version = "0.21.0"
|
||||
|
||||
[workspace.dependencies.tracing-flame]
|
||||
version = "0.2.0"
|
||||
@@ -342,11 +342,11 @@ version = "0.2.0"
|
||||
version = "0.22.0"
|
||||
|
||||
[workspace.dependencies.opentelemetry_sdk]
|
||||
version = "0.24.0"
|
||||
version = "0.21.2"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
[workspace.dependencies.opentelemetry-jaeger]
|
||||
version = "0.22.0"
|
||||
version = "0.20.0"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
# optional sentry metrics for crash/panic reporting
|
||||
@@ -405,8 +405,8 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustyline-async]
|
||||
git = "https://github.com/girlbossceo/rustyline-async"
|
||||
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||
version = "0.4.2"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.termimad]
|
||||
version = "0.29.4"
|
||||
@@ -425,16 +425,16 @@ version = "0.1"
|
||||
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
||||
[patch.crates-io.tracing-subscriber]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
[patch.crates-io.tracing]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
[patch.crates-io.tracing-core]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
[patch.crates-io.tracing-log]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
|
||||
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
|
||||
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
|
||||
@@ -442,6 +442,12 @@ rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
git = "https://github.com/girlbossceo/axum-server"
|
||||
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
|
||||
|
||||
# adds a tab completion callback: https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
||||
# adds event for CTRL+\: https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||
[patch.crates-io.rustyline-async]
|
||||
git = "https://github.com/girlbossceo/rustyline-async"
|
||||
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||
|
||||
#
|
||||
# Our crates
|
||||
#
|
||||
|
||||
@@ -421,8 +421,11 @@ allow_profile_lookup_federation_requests = true
|
||||
|
||||
# Set this to any float value to multiply conduwuit's in-memory LRU caches with.
|
||||
# May be useful if you have significant memory to spare to increase performance.
|
||||
#
|
||||
# This was previously called `conduit_cache_capacity_modifier`
|
||||
#
|
||||
# Defaults to 1.0.
|
||||
#conduit_cache_capacity_modifier = 1.0
|
||||
#cache_capacity_modifier = 1.0
|
||||
|
||||
# Set this to any float value in megabytes for conduwuit to tell the database engine that this much memory is available for database-related caches.
|
||||
# May be useful if you have significant memory to spare to increase performance.
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||
rev = "db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
rev = "4056a3b0f823013fec49f6d0b3e5698856e6476a"
|
||||
#branch = "master"
|
||||
default-features = false
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Otherwise, follow standard Rust project build guides (installing git and cloning
|
||||
While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
|
||||
you to make sure that the file permissions are correctly set up.
|
||||
|
||||
In Debian or RHEL, you can use this command to create a conduwuit user:
|
||||
In Debian or Fedora/RHEL, you can use this command to create a conduwuit user:
|
||||
|
||||
```bash
|
||||
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
||||
@@ -53,13 +53,11 @@ RocksDB is the only supported database backend.
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
|
||||
|
||||
Debian or RHEL:
|
||||
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this:
|
||||
|
||||
```bash
|
||||
sudo chown -R root:root /etc/conduwuit
|
||||
sudo chmod 755 /etc/conduwuit
|
||||
sudo chmod -R 755 /etc/conduwuit
|
||||
```
|
||||
|
||||
If you use the default database path you also need to run this:
|
||||
|
||||
+6
-2
@@ -184,6 +184,10 @@ cargo test \
|
||||
name = "nix-default"
|
||||
group = "tests"
|
||||
script = """
|
||||
bin/nix-build-and-cache just .#default
|
||||
nix run -L .#default -- --help
|
||||
env DIRENV_DEVSHELL=dynamic \
|
||||
direnv exec . \
|
||||
bin/nix-build-and-cache just .#default
|
||||
env DIRENV_DEVSHELL=dynamic \
|
||||
direnv exec . \
|
||||
nix run -L .#default -- --help
|
||||
"""
|
||||
|
||||
Generated
+25
-25
@@ -9,11 +9,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1717279440,
|
||||
"narHash": "sha256-kH04ReTjxOpQumgWnqy40vvQLSnLGxWP6RF3nq5Esrk=",
|
||||
"lastModified": 1720542474,
|
||||
"narHash": "sha256-aKjJ/4l2I9+wNGTaOGRsuS3M1+IoTibqgEMPDikXm04=",
|
||||
"owner": "zhaofengli",
|
||||
"repo": "attic",
|
||||
"rev": "717cc95983cdc357bc347d70be20ced21f935843",
|
||||
"rev": "6139576a3ce6bb992e0f6c3022528ec233e45f00",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -81,11 +81,11 @@
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1719903368,
|
||||
"narHash": "sha256-PPzgxM4Bir+Zh9FUV/v+RBxEYeJxYVmi/BYo3uqt268=",
|
||||
"lastModified": 1720637557,
|
||||
"narHash": "sha256-oZz6nCmFmdJZpC+K1iOG2KkzTI6rlAmndxANPDVU7X0=",
|
||||
"owner": "matrix-org",
|
||||
"repo": "complement",
|
||||
"rev": "bc97f1ddc1cd7485faf80c8935ee2641f3e1b57c",
|
||||
"rev": "0d14432e010482ea9e13a6f7c47c1533c0c9d62f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -123,11 +123,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720226507,
|
||||
"narHash": "sha256-yHVvNsgrpyNTXZBEokL8uyB2J6gB1wEx0KOJzoeZi1A=",
|
||||
"lastModified": 1720546058,
|
||||
"narHash": "sha256-iU2yVaPIZm5vMGdlT0+57vdB/aPq/V5oZFBRwYw+HBM=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "0aed560c5c0a61c9385bddff471a13036203e11c",
|
||||
"rev": "2d83156f23c43598cf44e152c33a59d3892f8b29",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -209,11 +209,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720420198,
|
||||
"narHash": "sha256-OIuDb6pHDyGpo7YMFyuRzMLcHm7mRvlYOz0Ht7ps2sU=",
|
||||
"lastModified": 1720852044,
|
||||
"narHash": "sha256-3NBYz8VuXuKU+8ONd9NFafCNjPEGHIZQ2Mdoam1a4mY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "abc0549e3560189462a7d394cc9d50af4608d103",
|
||||
"rev": "5087b12a595ee73131a944d922f24d81dae05725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -381,11 +381,11 @@
|
||||
"liburing": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1719025212,
|
||||
"narHash": "sha256-kD0yhjNStqC6uFqC1AxBwUpc/HlSFtiKrV+gwDyroDc=",
|
||||
"lastModified": 1720798442,
|
||||
"narHash": "sha256-gtPppAoksMLW4GuruQ36nf4EAqIA1Bs6V9Xcx8dBxrQ=",
|
||||
"owner": "axboe",
|
||||
"repo": "liburing",
|
||||
"rev": "7b3245583069bd481190c9da18f22e9fc8c3a805",
|
||||
"rev": "1d674f83b7d0f07553ac44d99a401b05853d9dbe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -606,11 +606,11 @@
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1720418205,
|
||||
"narHash": "sha256-cPJoFPXU44GlhWg4pUk9oUPqurPlCFZ11ZQPk21GTPU=",
|
||||
"lastModified": 1720768451,
|
||||
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "655a58a72a6601292512670343087c2d75d859c1",
|
||||
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -673,16 +673,16 @@
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1719949653,
|
||||
"narHash": "sha256-DYx7XHH2GEh17GukKhXs6laM6l+eugCmRkF0adpi9wk=",
|
||||
"lastModified": 1720900786,
|
||||
"narHash": "sha256-Vta9Um/RRuWwZ46BjXftV06iWLm/j/9MX39emXUvSAY=",
|
||||
"owner": "girlbossceo",
|
||||
"repo": "rocksdb",
|
||||
"rev": "a935c0273e1ba44eacf88ce3685a9b9831486155",
|
||||
"rev": "911f4243e69c2e320a7a209bf1f5f3ff5f825495",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "girlbossceo",
|
||||
"ref": "v9.3.1",
|
||||
"ref": "v9.4.0",
|
||||
"repo": "rocksdb",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -705,11 +705,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1720344064,
|
||||
"narHash": "sha256-STmaV9Zu74QtkGGrbr9uMhskwagfCjJqOAYapXabiuk=",
|
||||
"lastModified": 1720717809,
|
||||
"narHash": "sha256-6I+fm+nTLF/iaj7ffiFGlSY7POmubwUaPA/Wq0Bm53M=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "a5b21ea0aa644dffd7cf958b43f11f221d53404e",
|
||||
"rev": "ffbc5ad993d5cd2f3b8bcf9a511165470944ab91",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
flake-utils.url = "github:numtide/flake-utils?ref=main";
|
||||
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.3.1"; flake = false; };
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.4.0"; flake = false; };
|
||||
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
|
||||
};
|
||||
|
||||
@@ -45,10 +44,34 @@
|
||||
# we have this already at https://github.com/girlbossceo/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||
# unsetting this so i don't have to revert it and make this nix exclusive
|
||||
patches = [];
|
||||
# no real reason to have snappy, no one uses this
|
||||
cmakeFlags = pkgs.lib.subtractLists
|
||||
[ "-DWITH_SNAPPY=1" ]
|
||||
old.cmakeFlags;
|
||||
[
|
||||
# no real reason to have snappy, no one uses this
|
||||
"-DWITH_SNAPPY=1"
|
||||
# we dont need to use ldb or sst_dump (core_tools)
|
||||
"-DWITH_CORE_TOOLS=1"
|
||||
# we dont need to build rocksdb tests
|
||||
"-DWITH_TESTS=1"
|
||||
# we use rust-rocksdb via C interface and dont need C++ RTTI
|
||||
"-DUSE_RTTI=1"
|
||||
]
|
||||
old.cmakeFlags
|
||||
++ [
|
||||
# we dont need to use ldb or sst_dump (core_tools)
|
||||
"-DWITH_CORE_TOOLS=0"
|
||||
# we dont need trace tools
|
||||
"-DWITH_TRACE_TOOLS=0"
|
||||
# we dont need to build rocksdb tests
|
||||
"-DWITH_TESTS=0"
|
||||
# we use rust-rocksdb via C interface and dont need C++ RTTI
|
||||
"-DUSE_RTTI=0"
|
||||
];
|
||||
|
||||
# outputs has "tools" which we dont need or use
|
||||
outputs = [ "out" ];
|
||||
|
||||
# preInstall hooks has stuff for messing with ldb/sst_dump which we dont need or use
|
||||
preInstall = "";
|
||||
});
|
||||
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
||||
liburing = pkgs.liburing.overrideAttrs (old: {
|
||||
@@ -123,8 +146,12 @@
|
||||
default = scopeHost.main;
|
||||
all-features = scopeHost.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
||||
|
||||
@@ -132,8 +159,12 @@
|
||||
oci-image-all-features = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
};
|
||||
oci-image-hmalloc = scopeHost.oci-image.override {
|
||||
@@ -175,8 +206,12 @@
|
||||
name = "${binaryName}-all-features";
|
||||
value = scopeCrossStatic.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -200,8 +235,12 @@
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -229,8 +268,12 @@
|
||||
(scopeHostStatic.overrideScope (final: prev: {
|
||||
main = prev.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
}));
|
||||
devShells.no-features = mkDevShell
|
||||
|
||||
@@ -13,6 +13,12 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic {
|
||||
lib.concatStringsSep
|
||||
" "
|
||||
([]
|
||||
++ lib.optionals
|
||||
stdenv.targetPlatform.isx86_64
|
||||
[ "-C" "target-cpu=x86-64-v2" ]
|
||||
++ lib.optionals
|
||||
stdenv.targetPlatform.isAarch64
|
||||
[ "-C" "target-cpu=cortex-a55" ] # cortex-a55 == ARMv8.2-a
|
||||
# This disables PIE for static builds, which isn't great in terms
|
||||
# of security. Unfortunately, my hand is forced because nixpkgs'
|
||||
# `libstdc++.a` is built without `-fPIE`, which precludes us from
|
||||
|
||||
@@ -71,10 +71,29 @@ buildDepsOnlyEnv =
|
||||
# which breaks Darwin entirely
|
||||
enableLiburing = enableLiburing;
|
||||
}).overrideAttrs (old: {
|
||||
# TODO: static rocksdb fails to build on darwin
|
||||
# TODO: static rocksdb fails to build on darwin, also see <https://github.com/NixOS/nixpkgs/issues/320448>
|
||||
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
|
||||
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
|
||||
|
||||
enableLiburing = enableLiburing;
|
||||
|
||||
sse42Support = stdenv.targetPlatform.isx86_64;
|
||||
|
||||
cmakeFlags = if stdenv.targetPlatform.isx86_64
|
||||
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||
++ lib.optionals stdenv.targetPlatform.isx86_64 [
|
||||
"-DPORTABLE=x86-64-v2"
|
||||
"-DUSE_SSE=1"
|
||||
"-DHAVE_SSE=1"
|
||||
"-DHAVE_SSE42=1"
|
||||
]
|
||||
else if stdenv.targetPlatform.isAarch64
|
||||
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||
++ lib.optionals stdenv.targetPlatform.isAarch64 [
|
||||
# cortex-a55 == ARMv8.2-a
|
||||
"-DPORTABLE=armv8.2-a"
|
||||
]
|
||||
else old.cmakeFlags;
|
||||
});
|
||||
in
|
||||
{
|
||||
@@ -101,7 +120,11 @@ buildPackageEnv = {
|
||||
# Only needed in static stdenv because these are transitive dependencies of rocksdb
|
||||
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
|
||||
+ lib.optionalString (enableLiburing && stdenv.hostPlatform.isStatic)
|
||||
" -L${lib.getLib liburing}/lib -luring";
|
||||
" -L${lib.getLib liburing}/lib -luring"
|
||||
+ lib.optionalString stdenv.targetPlatform.isx86_64
|
||||
" -Ctarget-cpu=x86-64-v2"
|
||||
+ lib.optionalString stdenv.targetPlatform.isAarch64
|
||||
" -Ctarget-cpu=cortex-a55"; # cortex-a55 == ARMv8.2-a
|
||||
};
|
||||
|
||||
|
||||
@@ -126,6 +149,8 @@ commonAttrs = {
|
||||
];
|
||||
};
|
||||
|
||||
dontStrip = profile == "dev";
|
||||
|
||||
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
||||
@@ -117,7 +117,12 @@ pub(crate) async fn set_room_visibility_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
||||
}
|
||||
|
||||
user_can_publish_room(sender_user, &body.room_id)?;
|
||||
if !user_can_publish_room(sender_user, &body.room_id)? {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"User is not allowed to publish this room",
|
||||
));
|
||||
}
|
||||
|
||||
match &body.visibility {
|
||||
room::Visibility::Public => {
|
||||
@@ -377,8 +382,8 @@ fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||
Ok(event.sender == user_id)
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Unauthorized,
|
||||
"You are not allowed to publish this room to the room directory",
|
||||
ErrorKind::forbidden(),
|
||||
"User is not allowed to publish this room",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,12 +200,13 @@ pub(crate) async fn get_content_route(
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services().media.get(&mxc).await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
||||
let file = content.expect("content");
|
||||
|
||||
Ok(get_content::v3::Response {
|
||||
file,
|
||||
@@ -282,8 +283,8 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services().media.get(&mxc).await?
|
||||
{
|
||||
@@ -293,6 +294,7 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
Some(body.filename.clone()),
|
||||
));
|
||||
|
||||
let file = content.expect("content");
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
@@ -371,8 +373,8 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services()
|
||||
.media
|
||||
@@ -388,6 +390,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
.await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
||||
let file = content.expect("content");
|
||||
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file,
|
||||
|
||||
+5
-5
@@ -1,15 +1,15 @@
|
||||
pub mod client;
|
||||
mod router;
|
||||
pub mod routes;
|
||||
pub mod router;
|
||||
pub mod server;
|
||||
|
||||
extern crate conduit_core as conduit;
|
||||
extern crate conduit_service as service;
|
||||
|
||||
pub(crate) use conduit::{debug_info, debug_warn, utils, Error, Result};
|
||||
pub(crate) use service::{pdu::PduEvent, services, user_is_local};
|
||||
pub(crate) use conduit::{debug_info, debug_warn, pdu::PduEvent, utils, Error, Result};
|
||||
pub(crate) use service::{services, user_is_local};
|
||||
|
||||
pub(crate) use self::router::{Ruma, RumaResponse};
|
||||
pub use crate::router::State;
|
||||
pub(crate) use crate::router::{Ruma, RumaResponse};
|
||||
|
||||
conduit::mod_ctor! {}
|
||||
conduit::mod_dtor! {}
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
mod args;
|
||||
mod auth;
|
||||
mod handler;
|
||||
mod request;
|
||||
mod response;
|
||||
|
||||
use axum::{
|
||||
response::IntoResponse,
|
||||
routing::{any, get, post},
|
||||
Router,
|
||||
};
|
||||
use conduit::{err, Error, Server};
|
||||
use conduit::{err, Server};
|
||||
use http::Uri;
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
|
||||
use crate::{client, router::RouterExt, server};
|
||||
use self::handler::RouterExt;
|
||||
pub(super) use self::{args::Args as Ruma, response::RumaResponse};
|
||||
use crate::{client, server};
|
||||
|
||||
pub fn build(router: Router, server: &Server) -> Router {
|
||||
pub type State = &'static service::Services;
|
||||
|
||||
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
let config = &server.config;
|
||||
let router = router
|
||||
.ruma_route(client::get_supported_versions_route)
|
||||
@@ -95,7 +104,7 @@ pub fn build(router: Router, server: &Server) -> Router {
|
||||
.ruma_route(client::get_member_events_route)
|
||||
.ruma_route(client::get_protocols_route)
|
||||
.route("/_matrix/client/unstable/thirdparty/protocols",
|
||||
get(client::get_protocols_route_unstable))
|
||||
get(client::get_protocols_route_unstable))
|
||||
.ruma_route(client::send_message_event_route)
|
||||
.ruma_route(client::send_state_event_for_key_route)
|
||||
.ruma_route(client::get_state_events_route)
|
||||
@@ -180,15 +189,15 @@ pub fn build(router: Router, server: &Server) -> Router {
|
||||
.ruma_route(client::get_relating_events_with_rel_type_route)
|
||||
.ruma_route(client::get_relating_events_route)
|
||||
.ruma_route(client::get_hierarchy_route)
|
||||
.ruma_route(client::get_mutual_rooms_route)
|
||||
.ruma_route(client::get_room_summary)
|
||||
.route(
|
||||
"/_matrix/client/unstable/im.nheko.summary/rooms/:room_id_or_alias/summary",
|
||||
get(client::get_room_summary_legacy)
|
||||
)
|
||||
.ruma_route(client::well_known_support)
|
||||
.ruma_route(client::well_known_client)
|
||||
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
||||
.ruma_route(client::get_mutual_rooms_route)
|
||||
.ruma_route(client::get_room_summary)
|
||||
.route(
|
||||
"/_matrix/client/unstable/im.nheko.summary/rooms/:room_id_or_alias/summary",
|
||||
get(client::get_room_summary_legacy)
|
||||
)
|
||||
.ruma_route(client::well_known_support)
|
||||
.ruma_route(client::well_known_client)
|
||||
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
||||
.route("/_matrix/client/r0/rooms/:room_id/initialSync", get(initial_sync))
|
||||
.route("/_matrix/client/v3/rooms/:room_id/initialSync", get(initial_sync))
|
||||
.route("/client/server.json", get(client::syncv3_client_server_json));
|
||||
@@ -233,7 +242,7 @@ pub fn build(router: Router, server: &Server) -> Router {
|
||||
}
|
||||
|
||||
async fn initial_sync(_uri: Uri) -> impl IntoResponse {
|
||||
Error::BadRequest(ErrorKind::GuestAccessForbidden, "Guest access not implemented")
|
||||
err!(Request(GuestAccessForbidden("Guest access not implemented")))
|
||||
}
|
||||
|
||||
async fn federation_disabled() -> impl IntoResponse { err!(Config("allow_federation", "Federation is disabled.")) }
|
||||
@@ -1,24 +1,15 @@
|
||||
mod auth;
|
||||
mod handler;
|
||||
mod request;
|
||||
mod response;
|
||||
|
||||
use std::{mem, ops::Deref};
|
||||
|
||||
use axum::{async_trait, body::Body, extract::FromRequest};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use conduit::{debug, debug_warn, trace, warn};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, IncomingRequest},
|
||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||
};
|
||||
use conduit::{debug, err, trace, Error, Result};
|
||||
use ruma::{api::IncomingRequest, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId};
|
||||
|
||||
use self::{auth::Auth, request::Request};
|
||||
pub(super) use self::{handler::RouterExt, response::RumaResponse};
|
||||
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
|
||||
use super::{auth, auth::Auth, request, request::Request};
|
||||
use crate::{service::appservice::RegistrationInfo, services};
|
||||
|
||||
/// Extractor for Ruma request structs
|
||||
pub(crate) struct Ruma<T> {
|
||||
pub(crate) struct Args<T> {
|
||||
/// Request struct body
|
||||
pub(crate) body: T,
|
||||
|
||||
@@ -44,7 +35,7 @@ pub(crate) struct Ruma<T> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T, S> FromRequest<S, Body> for Ruma<T>
|
||||
impl<T, S> FromRequest<S, Body> for Args<T>
|
||||
where
|
||||
T: IncomingRequest,
|
||||
{
|
||||
@@ -65,7 +56,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Ruma<T> {
|
||||
impl<T> Deref for Args<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.body }
|
||||
@@ -109,21 +100,14 @@ where
|
||||
let mut http_request = hyper::Request::builder()
|
||||
.uri(request.parts.uri.clone())
|
||||
.method(request.parts.method.clone());
|
||||
*http_request.headers_mut().unwrap() = request.parts.headers.clone();
|
||||
let http_request = http_request.body(body).unwrap();
|
||||
debug!(
|
||||
"{:?} {:?} {:?}",
|
||||
http_request.method(),
|
||||
http_request.uri(),
|
||||
http_request.headers()
|
||||
);
|
||||
*http_request.headers_mut().expect("mutable http headers") = request.parts.headers.clone();
|
||||
let http_request = http_request.body(body).expect("http request body");
|
||||
|
||||
trace!("{:?} {:?} {:?}", http_request.method(), http_request.uri(), json_body);
|
||||
let body = T::try_from_http_request(http_request, &request.path).map_err(|e| {
|
||||
warn!("try_from_http_request failed: {e:?}",);
|
||||
debug_warn!("JSON body: {:?}", json_body);
|
||||
Error::BadRequest(ErrorKind::BadJson, "Failed to deserialize request.")
|
||||
})?;
|
||||
let headers = http_request.headers();
|
||||
let method = http_request.method();
|
||||
let uri = http_request.uri();
|
||||
debug!("{method:?} {uri:?} {headers:?}");
|
||||
trace!("{method:?} {uri:?} {json_body:?}");
|
||||
|
||||
Ok(body)
|
||||
T::try_from_http_request(http_request, &request.path).map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use conduit::Result;
|
||||
use http::Method;
|
||||
use ruma::api::IncomingRequest;
|
||||
|
||||
use super::{Ruma, RumaResponse};
|
||||
use super::{Ruma, RumaResponse, State};
|
||||
|
||||
pub(in super::super) trait RouterExt {
|
||||
fn ruma_route<H, T>(self, handler: H) -> Self
|
||||
@@ -18,7 +18,7 @@ pub(in super::super) trait RouterExt {
|
||||
H: RumaHandler<T>;
|
||||
}
|
||||
|
||||
impl RouterExt for Router {
|
||||
impl RouterExt for Router<State> {
|
||||
fn ruma_route<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: RumaHandler<T>,
|
||||
@@ -28,9 +28,9 @@ impl RouterExt for Router {
|
||||
}
|
||||
|
||||
pub(in super::super) trait RumaHandler<T> {
|
||||
fn add_routes(&self, router: Router) -> Router;
|
||||
fn add_routes(&self, router: Router<State>) -> Router<State>;
|
||||
|
||||
fn add_route(&self, router: Router, path: &str) -> Router;
|
||||
fn add_route(&self, router: Router<State>, path: &str) -> Router<State>;
|
||||
}
|
||||
|
||||
macro_rules! ruma_handler {
|
||||
@@ -41,17 +41,17 @@ macro_rules! ruma_handler {
|
||||
Req: IncomingRequest + Send + 'static,
|
||||
Ret: IntoResponse,
|
||||
Fut: Future<Output = Result<Req::OutgoingResponse, Ret>> + Send,
|
||||
Fun: FnOnce($($tx,)* Ruma<Req>) -> Fut + Clone + Send + Sync + 'static,
|
||||
$( $tx: FromRequestParts<()> + Send + 'static, )*
|
||||
Fun: FnOnce($($tx,)* Ruma<Req>,) -> Fut + Clone + Send + Sync + 'static,
|
||||
$( $tx: FromRequestParts<State> + Send + 'static, )*
|
||||
{
|
||||
fn add_routes(&self, router: Router) -> Router {
|
||||
fn add_routes(&self, router: Router<State>) -> Router<State> {
|
||||
Req::METADATA
|
||||
.history
|
||||
.all_paths()
|
||||
.fold(router, |router, path| self.add_route(router, path))
|
||||
}
|
||||
|
||||
fn add_route(&self, router: Router, path: &str) -> Router {
|
||||
fn add_route(&self, router: Router<State>, path: &str) -> Router<State> {
|
||||
let handle = self.clone();
|
||||
let method = method_to_filter(&Req::METADATA.method);
|
||||
let action = |$($tx,)* req| async { handle($($tx,)* req).await.map(RumaResponse) };
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::str;
|
||||
|
||||
use axum::{extract::Path, RequestExt, RequestPartsExt};
|
||||
use bytes::Bytes;
|
||||
use conduit::err;
|
||||
use http::request::Parts;
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{services, Error, Result};
|
||||
use crate::{services, Result};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(super) struct QueryParams {
|
||||
@@ -26,14 +26,15 @@ pub(super) async fn from(request: hyper::Request<axum::body::Body>) -> Result<Re
|
||||
let (mut parts, body) = limited.into_parts();
|
||||
|
||||
let path: Path<Vec<String>> = parts.extract().await?;
|
||||
let query = serde_html_form::from_str(parts.uri.query().unwrap_or_default())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to read query parameters"))?;
|
||||
let query = parts.uri.query().unwrap_or_default();
|
||||
let query =
|
||||
serde_html_form::from_str(query).map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
||||
|
||||
let max_body_size = services().globals.config.max_request_size;
|
||||
|
||||
let body = axum::body::to_bytes(body, max_body_size)
|
||||
.await
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::TooLarge, "Request body too large"))?;
|
||||
.map_err(|e| err!(Request(TooLarge("Request body too large: {e}"))))?;
|
||||
|
||||
Ok(Request {
|
||||
path,
|
||||
|
||||
+59
-59
@@ -1,7 +1,8 @@
|
||||
use std::{collections::BTreeMap, net::IpAddr, time::Instant};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::debug_warn;
|
||||
use conduit::{debug, debug_warn, err, trace, warn, Err};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::error::ErrorKind,
|
||||
@@ -18,11 +19,10 @@ use ruma::{
|
||||
OwnedEventId, ServerName,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{
|
||||
service::rooms::event_handler::parse_incoming_pdu,
|
||||
services,
|
||||
services::Services,
|
||||
utils::{self},
|
||||
Error, Result, Ruma,
|
||||
};
|
||||
@@ -34,29 +34,23 @@ type ResolvedMap = BTreeMap<OwnedEventId, Result<(), Error>>;
|
||||
/// Push EDUs and PDUs to this server.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "send")]
|
||||
pub(crate) async fn send_transaction_message_route(
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<send_transaction_message::v1::Request>,
|
||||
State(services): State<&Services>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<send_transaction_message::v1::Request>,
|
||||
) -> Result<send_transaction_message::v1::Response> {
|
||||
let origin = body.origin.as_ref().expect("server is authenticated");
|
||||
|
||||
if *origin != body.body.origin {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Not allowed to send transactions on behalf of other servers",
|
||||
));
|
||||
return Err!(Request(Forbidden(
|
||||
"Not allowed to send transactions on behalf of other servers"
|
||||
)));
|
||||
}
|
||||
|
||||
if body.pdus.len() > 50_usize {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Not allowed to send more than 50 PDUs in one transaction",
|
||||
));
|
||||
return Err!(Request(Forbidden("Not allowed to send more than 50 PDUs in one transaction")));
|
||||
}
|
||||
|
||||
if body.edus.len() > 100_usize {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Not allowed to send more than 100 EDUs in one transaction",
|
||||
));
|
||||
return Err!(Request(Forbidden("Not allowed to send more than 100 EDUs in one transaction")));
|
||||
}
|
||||
|
||||
let txn_start_time = Instant::now();
|
||||
@@ -69,8 +63,8 @@ pub(crate) async fn send_transaction_message_route(
|
||||
"Starting txn",
|
||||
);
|
||||
|
||||
let resolved_map = handle_pdus(&client, &body, origin, &txn_start_time).await?;
|
||||
handle_edus(&client, &body, origin).await?;
|
||||
let resolved_map = handle_pdus(services, &client, &body, origin, &txn_start_time).await?;
|
||||
handle_edus(services, &client, &body, origin).await?;
|
||||
|
||||
debug!(
|
||||
pdus = ?body.pdus.len(),
|
||||
@@ -90,7 +84,8 @@ pub(crate) async fn send_transaction_message_route(
|
||||
}
|
||||
|
||||
async fn handle_pdus(
|
||||
_client: &IpAddr, body: &Ruma<send_transaction_message::v1::Request>, origin: &ServerName, txn_start_time: &Instant,
|
||||
services: &Services, _client: &IpAddr, body: &Ruma<send_transaction_message::v1::Request>, origin: &ServerName,
|
||||
txn_start_time: &Instant,
|
||||
) -> Result<ResolvedMap> {
|
||||
let mut parsed_pdus = Vec::with_capacity(body.pdus.len());
|
||||
for pdu in &body.pdus {
|
||||
@@ -110,7 +105,7 @@ async fn handle_pdus(
|
||||
// corresponding signing keys
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
if !parsed_pdus.is_empty() {
|
||||
services()
|
||||
services
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_required_signing_keys(parsed_pdus.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
||||
@@ -126,7 +121,7 @@ async fn handle_pdus(
|
||||
let mut resolved_map = BTreeMap::new();
|
||||
for (event_id, value, room_id) in parsed_pdus {
|
||||
let pdu_start_time = Instant::now();
|
||||
let mutex_lock = services()
|
||||
let mutex_lock = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.mutex_federation
|
||||
@@ -134,7 +129,7 @@ async fn handle_pdus(
|
||||
.await;
|
||||
resolved_map.insert(
|
||||
event_id.clone(),
|
||||
services()
|
||||
services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(origin, &room_id, &event_id, value, true, &pub_key_map)
|
||||
@@ -162,7 +157,7 @@ async fn handle_pdus(
|
||||
}
|
||||
|
||||
async fn handle_edus(
|
||||
client: &IpAddr, body: &Ruma<send_transaction_message::v1::Request>, origin: &ServerName,
|
||||
services: &Services, client: &IpAddr, body: &Ruma<send_transaction_message::v1::Request>, origin: &ServerName,
|
||||
) -> Result<()> {
|
||||
for edu in body
|
||||
.edus
|
||||
@@ -170,12 +165,12 @@ async fn handle_edus(
|
||||
.filter_map(|edu| serde_json::from_str::<Edu>(edu.json().get()).ok())
|
||||
{
|
||||
match edu {
|
||||
Edu::Presence(presence) => handle_edu_presence(client, origin, presence).await?,
|
||||
Edu::Receipt(receipt) => handle_edu_receipt(client, origin, receipt).await?,
|
||||
Edu::Typing(typing) => handle_edu_typing(client, origin, typing).await?,
|
||||
Edu::DeviceListUpdate(content) => handle_edu_device_list_update(client, origin, content).await?,
|
||||
Edu::DirectToDevice(content) => handle_edu_direct_to_device(client, origin, content).await?,
|
||||
Edu::SigningKeyUpdate(content) => handle_edu_signing_key_update(client, origin, content).await?,
|
||||
Edu::Presence(presence) => handle_edu_presence(services, client, origin, presence).await?,
|
||||
Edu::Receipt(receipt) => handle_edu_receipt(services, client, origin, receipt).await?,
|
||||
Edu::Typing(typing) => handle_edu_typing(services, client, origin, typing).await?,
|
||||
Edu::DeviceListUpdate(content) => handle_edu_device_list_update(services, client, origin, content).await?,
|
||||
Edu::DirectToDevice(content) => handle_edu_direct_to_device(services, client, origin, content).await?,
|
||||
Edu::SigningKeyUpdate(content) => handle_edu_signing_key_update(services, client, origin, content).await?,
|
||||
Edu::_Custom(ref _custom) => {
|
||||
debug_warn!(?body.edus, "received custom/unknown EDU");
|
||||
},
|
||||
@@ -185,8 +180,10 @@ async fn handle_edus(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_presence(_client: &IpAddr, origin: &ServerName, presence: PresenceContent) -> Result<()> {
|
||||
if !services().globals.allow_incoming_presence() {
|
||||
async fn handle_edu_presence(
|
||||
services: &Services, _client: &IpAddr, origin: &ServerName, presence: PresenceContent,
|
||||
) -> Result<()> {
|
||||
if !services.globals.allow_incoming_presence() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -199,7 +196,7 @@ async fn handle_edu_presence(_client: &IpAddr, origin: &ServerName, presence: Pr
|
||||
continue;
|
||||
}
|
||||
|
||||
services().presence.set_presence(
|
||||
services.presence.set_presence(
|
||||
&update.user_id,
|
||||
&update.presence,
|
||||
Some(update.currently_active),
|
||||
@@ -211,13 +208,15 @@ async fn handle_edu_presence(_client: &IpAddr, origin: &ServerName, presence: Pr
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_receipt(_client: &IpAddr, origin: &ServerName, receipt: ReceiptContent) -> Result<()> {
|
||||
if !services().globals.allow_incoming_read_receipts() {
|
||||
async fn handle_edu_receipt(
|
||||
services: &Services, _client: &IpAddr, origin: &ServerName, receipt: ReceiptContent,
|
||||
) -> Result<()> {
|
||||
if !services.globals.allow_incoming_read_receipts() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (room_id, room_updates) in receipt.receipts {
|
||||
if services()
|
||||
if services
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(origin, &room_id)
|
||||
@@ -239,7 +238,7 @@ async fn handle_edu_receipt(_client: &IpAddr, origin: &ServerName, receipt: Rece
|
||||
continue;
|
||||
}
|
||||
|
||||
if services()
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
@@ -255,7 +254,7 @@ async fn handle_edu_receipt(_client: &IpAddr, origin: &ServerName, receipt: Rece
|
||||
room_id: room_id.clone(),
|
||||
};
|
||||
|
||||
services()
|
||||
services
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipt_update(&user_id, &room_id, &event)?;
|
||||
@@ -273,8 +272,10 @@ async fn handle_edu_receipt(_client: &IpAddr, origin: &ServerName, receipt: Rece
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_typing(_client: &IpAddr, origin: &ServerName, typing: TypingContent) -> Result<()> {
|
||||
if !services().globals.config.allow_incoming_typing {
|
||||
async fn handle_edu_typing(
|
||||
services: &Services, _client: &IpAddr, origin: &ServerName, typing: TypingContent,
|
||||
) -> Result<()> {
|
||||
if !services.globals.config.allow_incoming_typing {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ async fn handle_edu_typing(_client: &IpAddr, origin: &ServerName, typing: Typing
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if services()
|
||||
if services
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(typing.user_id.server_name(), &typing.room_id)
|
||||
@@ -299,26 +300,26 @@ async fn handle_edu_typing(_client: &IpAddr, origin: &ServerName, typing: Typing
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if services()
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(&typing.user_id, &typing.room_id)?
|
||||
{
|
||||
if typing.typing {
|
||||
let timeout = utils::millis_since_unix_epoch().saturating_add(
|
||||
services()
|
||||
services
|
||||
.globals
|
||||
.config
|
||||
.typing_federation_timeout_s
|
||||
.saturating_mul(1000),
|
||||
);
|
||||
services()
|
||||
services
|
||||
.rooms
|
||||
.typing
|
||||
.typing_add(&typing.user_id, &typing.room_id, timeout)
|
||||
.await?;
|
||||
} else {
|
||||
services()
|
||||
services
|
||||
.rooms
|
||||
.typing
|
||||
.typing_remove(&typing.user_id, &typing.room_id)
|
||||
@@ -336,7 +337,7 @@ async fn handle_edu_typing(_client: &IpAddr, origin: &ServerName, typing: Typing
|
||||
}
|
||||
|
||||
async fn handle_edu_device_list_update(
|
||||
_client: &IpAddr, origin: &ServerName, content: DeviceListUpdateContent,
|
||||
services: &Services, _client: &IpAddr, origin: &ServerName, content: DeviceListUpdateContent,
|
||||
) -> Result<()> {
|
||||
let DeviceListUpdateContent {
|
||||
user_id,
|
||||
@@ -351,13 +352,13 @@ async fn handle_edu_device_list_update(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
services().users.mark_device_key_update(&user_id)?;
|
||||
services.users.mark_device_key_update(&user_id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_direct_to_device(
|
||||
_client: &IpAddr, origin: &ServerName, content: DirectDeviceContent,
|
||||
services: &Services, _client: &IpAddr, origin: &ServerName, content: DirectDeviceContent,
|
||||
) -> Result<()> {
|
||||
let DirectDeviceContent {
|
||||
sender,
|
||||
@@ -375,7 +376,7 @@ async fn handle_edu_direct_to_device(
|
||||
}
|
||||
|
||||
// Check if this is a new transaction id
|
||||
if services()
|
||||
if services
|
||||
.transaction_ids
|
||||
.existing_txnid(&sender, None, &message_id)?
|
||||
.is_some()
|
||||
@@ -387,28 +388,27 @@ async fn handle_edu_direct_to_device(
|
||||
for (target_device_id_maybe, event) in map {
|
||||
match target_device_id_maybe {
|
||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||
services().users.add_to_device_event(
|
||||
services.users.add_to_device_event(
|
||||
&sender,
|
||||
target_user_id,
|
||||
target_device_id,
|
||||
&ev_type.to_string(),
|
||||
event.deserialize_as().map_err(|e| {
|
||||
error!("To-Device event is invalid: {event:?} {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
event
|
||||
.deserialize_as()
|
||||
.map_err(|e| err!(Request(InvalidParam(error!("To-Device event is invalid: {e}")))))?,
|
||||
)?;
|
||||
},
|
||||
|
||||
DeviceIdOrAllDevices::AllDevices => {
|
||||
for target_device_id in services().users.all_device_ids(target_user_id) {
|
||||
services().users.add_to_device_event(
|
||||
for target_device_id in services.users.all_device_ids(target_user_id) {
|
||||
services.users.add_to_device_event(
|
||||
&sender,
|
||||
target_user_id,
|
||||
&target_device_id?,
|
||||
&ev_type.to_string(),
|
||||
event
|
||||
.deserialize_as()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?,
|
||||
.map_err(|e| err!(Request(InvalidParam("Event is invalid: {e}"))))?,
|
||||
)?;
|
||||
}
|
||||
},
|
||||
@@ -417,7 +417,7 @@ async fn handle_edu_direct_to_device(
|
||||
}
|
||||
|
||||
// Save transaction id with empty data
|
||||
services()
|
||||
services
|
||||
.transaction_ids
|
||||
.add_txnid(&sender, None, &message_id, &[])?;
|
||||
|
||||
@@ -425,7 +425,7 @@ async fn handle_edu_direct_to_device(
|
||||
}
|
||||
|
||||
async fn handle_edu_signing_key_update(
|
||||
_client: &IpAddr, origin: &ServerName, content: SigningKeyUpdateContent,
|
||||
services: &Services, _client: &IpAddr, origin: &ServerName, content: SigningKeyUpdateContent,
|
||||
) -> Result<()> {
|
||||
let SigningKeyUpdateContent {
|
||||
user_id,
|
||||
@@ -442,7 +442,7 @@ async fn handle_edu_signing_key_update(
|
||||
}
|
||||
|
||||
if let Some(master_key) = master_key {
|
||||
services()
|
||||
services
|
||||
.users
|
||||
.add_cross_signing_keys(&user_id, &master_key, &self_signing_key, &None, true)?;
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ pub struct Config {
|
||||
|
||||
#[serde(default = "default_pdu_cache_capacity")]
|
||||
pub pdu_cache_capacity: u32,
|
||||
#[serde(default = "default_conduit_cache_capacity_modifier")]
|
||||
pub conduit_cache_capacity_modifier: f64,
|
||||
#[serde(default = "default_cache_capacity_modifier", alias = "conduit_cache_capacity_modifier")]
|
||||
pub cache_capacity_modifier: f64,
|
||||
#[serde(default = "default_auth_chain_cache_capacity")]
|
||||
pub auth_chain_cache_capacity: u32,
|
||||
#[serde(default = "default_shorteventid_cache_capacity")]
|
||||
@@ -391,8 +391,9 @@ struct ListeningAddr {
|
||||
addrs: Either<IpAddr, Vec<IpAddr>>,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str] = &[
|
||||
const DEPRECATED_KEYS: &[&str; 9] = &[
|
||||
"cache_capacity",
|
||||
"conduit_cache_capacity_modifier",
|
||||
"max_concurrent_requests",
|
||||
"well_known_client",
|
||||
"well_known_server",
|
||||
@@ -484,7 +485,7 @@ impl fmt::Display for Config {
|
||||
);
|
||||
line("Database backups to keep", &self.database_backups_to_keep.to_string());
|
||||
line("Database cache capacity (MB)", &self.db_cache_capacity_mb.to_string());
|
||||
line("Cache capacity modifier", &self.conduit_cache_capacity_modifier.to_string());
|
||||
line("Cache capacity modifier", &self.cache_capacity_modifier.to_string());
|
||||
line("PDU cache capacity", &self.pdu_cache_capacity.to_string());
|
||||
line("Auth chain cache capacity", &self.auth_chain_cache_capacity.to_string());
|
||||
line("Short eventid cache capacity", &self.shorteventid_cache_capacity.to_string());
|
||||
@@ -847,7 +848,7 @@ fn default_db_cache_capacity_mb() -> f64 { 256.0 }
|
||||
|
||||
fn default_pdu_cache_capacity() -> u32 { 150_000 }
|
||||
|
||||
fn default_conduit_cache_capacity_modifier() -> f64 { 1.0 }
|
||||
fn default_cache_capacity_modifier() -> f64 { 1.0 }
|
||||
|
||||
fn default_auth_chain_cache_capacity() -> u32 { 100_000 }
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
//! Err(Database(error!("problem with db: {msg}")))` logs the error at the
|
||||
//! callsite and then returns the error with the same string. Caller has the
|
||||
//! option of replacing `error!` with `debug_error!`.
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! Err {
|
||||
($($args:tt)*) => {
|
||||
@@ -49,14 +50,16 @@ macro_rules! err {
|
||||
$crate::$level!($($args),*);
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::forbidden(),
|
||||
$crate::format_maybe!($($args),*)
|
||||
$crate::format_maybe!($($args),*),
|
||||
::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
}};
|
||||
|
||||
(Request(Forbidden($($args:expr),*))) => {
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::forbidden(),
|
||||
$crate::format_maybe!($($args),*)
|
||||
$crate::format_maybe!($($args),*),
|
||||
::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
};
|
||||
|
||||
@@ -64,14 +67,16 @@ macro_rules! err {
|
||||
$crate::$level!($($args),*);
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::format_maybe!($($args),*)
|
||||
$crate::format_maybe!($($args),*),
|
||||
::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
}};
|
||||
|
||||
(Request($variant:ident($($args:expr),*))) => {
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::format_maybe!($($args),*)
|
||||
$crate::format_maybe!($($args),*),
|
||||
::http::StatusCode::BAD_REQUEST
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ pub enum Error {
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
|
||||
#[error("{0}: {1}")]
|
||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>),
|
||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
@@ -78,7 +78,7 @@ pub enum Error {
|
||||
|
||||
// conduwuit
|
||||
#[error("Arithmetic operation failed: {0}")]
|
||||
Arithmetic(&'static str),
|
||||
Arithmetic(Cow<'static, str>),
|
||||
#[error("There was a problem with the '{0}' directive in your configuration: {1}")]
|
||||
Config(&'static str, Cow<'static, str>),
|
||||
#[error("{0}")]
|
||||
@@ -120,7 +120,7 @@ impl Error {
|
||||
|
||||
match self {
|
||||
Self::Federation(_, error) => response::ruma_error_kind(error).clone(),
|
||||
Self::BadRequest(kind, _) | Self::Request(kind, _) => kind.clone(),
|
||||
Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
|
||||
_ => Unknown,
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,8 @@ impl Error {
|
||||
pub fn status_code(&self) -> http::StatusCode {
|
||||
match self {
|
||||
Self::Federation(_, ref error) | Self::RumaError(ref error) => error.status_code,
|
||||
Self::BadRequest(ref kind, _) | Self::Request(ref kind, _) => response::bad_request_code(kind),
|
||||
Self::Request(ref kind, _, code) => response::status_code(kind, *code),
|
||||
Self::BadRequest(ref kind, ..) => response::bad_request_code(kind),
|
||||
Self::Conflict(_) => http::StatusCode::CONFLICT,
|
||||
_ => http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
|
||||
+38
-16
@@ -1,7 +1,13 @@
|
||||
use bytes::BytesMut;
|
||||
use http::StatusCode;
|
||||
use http_body_util::Full;
|
||||
use ruma::api::{client::uiaa::UiaaResponse, OutgoingResponse};
|
||||
use ruma::api::{
|
||||
client::{
|
||||
error::{ErrorBody, ErrorKind},
|
||||
uiaa::UiaaResponse,
|
||||
},
|
||||
OutgoingResponse,
|
||||
};
|
||||
|
||||
use super::Error;
|
||||
use crate::error;
|
||||
@@ -25,7 +31,7 @@ impl From<Error> for UiaaResponse {
|
||||
return Self::AuthResponse(uiaainfo);
|
||||
}
|
||||
|
||||
let body = ruma::api::client::error::ErrorBody::Standard {
|
||||
let body = ErrorBody::Standard {
|
||||
kind: error.kind(),
|
||||
message: error.message(),
|
||||
};
|
||||
@@ -37,10 +43,33 @@ impl From<Error> for UiaaResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn bad_request_code(kind: &ruma::api::client::error::ErrorKind) -> StatusCode {
|
||||
use ruma::api::client::error::ErrorKind::*;
|
||||
pub(super) fn status_code(kind: &ErrorKind, hint: StatusCode) -> StatusCode {
|
||||
if hint == StatusCode::BAD_REQUEST {
|
||||
bad_request_code(kind)
|
||||
} else {
|
||||
hint
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
use ErrorKind::*;
|
||||
|
||||
match kind {
|
||||
// 429
|
||||
LimitExceeded {
|
||||
..
|
||||
} => StatusCode::TOO_MANY_REQUESTS,
|
||||
|
||||
// 413
|
||||
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
|
||||
// 405
|
||||
Unrecognized => StatusCode::METHOD_NOT_ALLOWED,
|
||||
|
||||
// 404
|
||||
NotFound => StatusCode::NOT_FOUND,
|
||||
|
||||
// 403
|
||||
GuestAccessForbidden
|
||||
| ThreepidAuthFailed
|
||||
| UserDeactivated
|
||||
@@ -52,26 +81,20 @@ pub(super) fn bad_request_code(kind: &ruma::api::client::error::ErrorKind) -> St
|
||||
..
|
||||
} => StatusCode::FORBIDDEN,
|
||||
|
||||
// 401
|
||||
UnknownToken {
|
||||
..
|
||||
}
|
||||
| MissingToken
|
||||
| Unauthorized => StatusCode::UNAUTHORIZED,
|
||||
|
||||
LimitExceeded {
|
||||
..
|
||||
} => StatusCode::TOO_MANY_REQUESTS,
|
||||
|
||||
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
|
||||
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
||||
|
||||
// 400
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> String {
|
||||
if let ruma::api::client::error::ErrorBody::Standard {
|
||||
if let ErrorBody::Standard {
|
||||
message,
|
||||
..
|
||||
} = &error.body
|
||||
@@ -82,7 +105,6 @@ pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> Str
|
||||
format!("{error}")
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_kind(e: &ruma::api::client::error::Error) -> &ruma::api::client::error::ErrorKind {
|
||||
e.error_kind()
|
||||
.unwrap_or(&ruma::api::client::error::ErrorKind::Unknown)
|
||||
pub(super) fn ruma_error_kind(e: &ruma::api::client::error::Error) -> &ErrorKind {
|
||||
e.error_kind().unwrap_or(&ErrorKind::Unknown)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ use std::{cmp, time::Duration};
|
||||
|
||||
pub use checked_ops::checked_ops;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::{Err, Error, Result};
|
||||
|
||||
/// Checked arithmetic expression. Returns a Result<R, Error::Arithmetic>
|
||||
#[macro_export]
|
||||
macro_rules! checked {
|
||||
($($input:tt)*) => {
|
||||
$crate::utils::math::checked_ops!($($input)*)
|
||||
.ok_or_else(|| $crate::Error::Arithmetic("operation overflowed or result invalid"))
|
||||
.ok_or_else(|| $crate::err!(Arithmetic("operation overflowed or result invalid")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ macro_rules! validated {
|
||||
($($input:tt)*) => {
|
||||
//#[allow(clippy::arithmetic_side_effects)] {
|
||||
//Some($($input)*)
|
||||
// .ok_or_else(|| $crate::Error::Arithmetic("this error should never been seen"))
|
||||
// .ok_or_else(|| $crate::err!(Arithmetic("this error should never been seen")))
|
||||
//}
|
||||
|
||||
//NOTE: remove me when stmt_expr_attributes is stable
|
||||
@@ -57,7 +57,7 @@ pub fn continue_exponential_backoff(min: Duration, max: Duration, elapsed: Durat
|
||||
#[allow(clippy::as_conversions)]
|
||||
pub fn usize_from_f64(val: f64) -> Result<usize, Error> {
|
||||
if val < 0.0 {
|
||||
return Err(Error::Arithmetic("Converting negative float to unsigned integer"));
|
||||
return Err!(Arithmetic("Converting negative float to unsigned integer"));
|
||||
}
|
||||
|
||||
//SAFETY: <https://doc.rust-lang.org/std/primitive.f64.html#method.to_int_unchecked>
|
||||
|
||||
@@ -310,7 +310,7 @@ fn set_table_with_shared_cache(
|
||||
}
|
||||
|
||||
fn cache_size(config: &Config, base_size: u32, entity_size: usize) -> usize {
|
||||
let ents = f64::from(base_size) * config.conduit_cache_capacity_modifier;
|
||||
let ents = f64::from(base_size) * config.cache_capacity_modifier;
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
(ents as usize)
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::{
|
||||
Router,
|
||||
};
|
||||
use axum_client_ip::SecureClientIpSource;
|
||||
use conduit::{Result, Server};
|
||||
use conduit::{error, Result, Server};
|
||||
use http::{
|
||||
header::{self, HeaderName},
|
||||
HeaderValue, Method, StatusCode,
|
||||
@@ -149,7 +149,7 @@ fn cors_layer(_server: &Server) -> CorsLayer {
|
||||
fn body_limit_layer(server: &Server) -> DefaultBodyLimit { DefaultBodyLimit::max(server.config.max_request_size) }
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip_all, name = "panic")]
|
||||
fn catch_panic(err: Box<dyn Any + Send + 'static>) -> http::Response<http_body_util::Full<bytes::Bytes>> {
|
||||
conduit_service::services()
|
||||
.server
|
||||
@@ -165,17 +165,17 @@ fn catch_panic(err: Box<dyn Any + Send + 'static>) -> http::Response<http_body_u
|
||||
"Unknown internal server error occurred.".to_owned()
|
||||
};
|
||||
|
||||
error!("{details:#}");
|
||||
let body = serde_json::json!({
|
||||
"errcode": "M_UNKNOWN",
|
||||
"error": "M_UNKNOWN: Internal server error occurred",
|
||||
"details": details,
|
||||
})
|
||||
.to_string();
|
||||
});
|
||||
|
||||
http::Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(http_body_util::Full::from(body))
|
||||
.body(http_body_util::Full::from(body.to_string()))
|
||||
.expect("Failed to create response for our panic catcher?")
|
||||
}
|
||||
|
||||
|
||||
+8
-23
@@ -4,9 +4,8 @@ use axum::{
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use conduit::{debug, debug_error, debug_warn, defer, error, trace, Error, Result, Server};
|
||||
use conduit::{debug, debug_error, debug_warn, defer, err, error, trace, Result, Server};
|
||||
use http::{Method, StatusCode, Uri};
|
||||
use ruma::api::client::error::{Error as RumaError, ErrorBody, ErrorKind};
|
||||
|
||||
#[tracing::instrument(skip_all, level = "debug")]
|
||||
pub(crate) async fn spawn(
|
||||
@@ -58,33 +57,13 @@ pub(crate) async fn handle(
|
||||
trace!(active, finished, "leave");
|
||||
}};
|
||||
|
||||
let method = req.method().clone();
|
||||
let uri = req.uri().clone();
|
||||
let method = req.method().clone();
|
||||
let result = next.run(req).await;
|
||||
handle_result(&method, &uri, result)
|
||||
}
|
||||
|
||||
fn handle_result(method: &Method, uri: &Uri, result: Response) -> Result<Response, StatusCode> {
|
||||
handle_result_log(method, uri, &result);
|
||||
match result.status() {
|
||||
StatusCode::METHOD_NOT_ALLOWED => handle_result_405(method, uri, &result),
|
||||
_ => Ok(result),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_result_405(_method: &Method, _uri: &Uri, result: &Response) -> Result<Response, StatusCode> {
|
||||
let error = Error::RumaError(RumaError {
|
||||
status_code: result.status(),
|
||||
body: ErrorBody::Standard {
|
||||
kind: ErrorKind::Unrecognized,
|
||||
message: "M_UNRECOGNIZED: Method not allowed for endpoint".to_owned(),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(error.into_response())
|
||||
}
|
||||
|
||||
fn handle_result_log(method: &Method, uri: &Uri, result: &Response) {
|
||||
let status = result.status();
|
||||
let reason = status.canonical_reason().unwrap_or("Unknown Reason");
|
||||
let code = status.as_u16();
|
||||
@@ -97,4 +76,10 @@ fn handle_result_log(method: &Method, uri: &Uri, result: &Response) {
|
||||
} else {
|
||||
trace!(method = ?method, uri = ?uri, "{code} {reason}");
|
||||
}
|
||||
|
||||
if status == StatusCode::METHOD_NOT_ALLOWED {
|
||||
return Ok(err!(Request(Unrecognized("Method Not Allowed"))).into_response());
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -2,24 +2,23 @@ use std::sync::Arc;
|
||||
|
||||
use axum::{response::IntoResponse, routing::get, Router};
|
||||
use conduit::{Error, Server};
|
||||
use conduit_service as service;
|
||||
use http::Uri;
|
||||
use http::{StatusCode, Uri};
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
|
||||
extern crate conduit_api as api;
|
||||
extern crate conduit_service as service;
|
||||
|
||||
pub(crate) fn build(server: &Arc<Server>) -> Router {
|
||||
let state = service::services();
|
||||
let router = Router::new()
|
||||
let router = Router::<api::State>::new();
|
||||
|
||||
api::router::build(router, server)
|
||||
.route("/", get(it_works))
|
||||
.fallback(not_found)
|
||||
.with_state(state);
|
||||
|
||||
api::routes::build(router, server)
|
||||
.with_state(service::services())
|
||||
}
|
||||
|
||||
async fn not_found(_uri: Uri) -> impl IntoResponse {
|
||||
Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request")
|
||||
Error::Request(ErrorKind::Unrecognized, "Not Found".into(), StatusCode::NOT_FOUND)
|
||||
}
|
||||
|
||||
async fn it_works() -> &'static str { "hewwo from conduwuit woof!" }
|
||||
|
||||
@@ -127,8 +127,8 @@ async fn fini(server: &Arc<Server>, listener: UnixListener, mut tasks: JoinSet<(
|
||||
debug!("Waiting for requests to finish...");
|
||||
while server.metrics.requests_spawn_active.load(Ordering::Relaxed) > 0 {
|
||||
tokio::select! {
|
||||
_ = tasks.join_next() => {}
|
||||
() = sleep(FINI_POLL_INTERVAL) => {}
|
||||
task = tasks.join_next() => if task.is_none() { break; },
|
||||
() = sleep(FINI_POLL_INTERVAL) => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduit::{debug, error, Error, Result};
|
||||
use conduit::{debug, error, error::default_log, Error, Result};
|
||||
pub use create::create_admin_room;
|
||||
pub use grant::make_user_admin;
|
||||
use loole::{Receiver, Sender};
|
||||
@@ -239,9 +239,9 @@ async fn respond_to_room(content: RoomMessageEventContent, room_id: &RoomId, use
|
||||
.build_and_append_pdu(response_pdu, user_id, room_id, &state_lock)
|
||||
.await
|
||||
{
|
||||
if let Err(e) = handle_response_error(e, room_id, user_id, &state_lock).await {
|
||||
error!("{e}");
|
||||
}
|
||||
handle_response_error(e, room_id, user_id, &state_lock)
|
||||
.await
|
||||
.unwrap_or_else(default_log);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,13 @@ pub(crate) struct Data {
|
||||
url_previews: Arc<Map>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Metadata {
|
||||
pub(super) content_disposition: Option<String>,
|
||||
pub(super) content_type: Option<String>,
|
||||
pub(super) key: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub(super) fn new(db: &Arc<Database>) -> Self {
|
||||
Self {
|
||||
@@ -104,9 +111,7 @@ impl Data {
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
pub(super) fn search_file_metadata(
|
||||
&self, mxc: &str, width: u32, height: u32,
|
||||
) -> Result<(Option<String>, Option<String>, Vec<u8>)> {
|
||||
pub(super) fn search_file_metadata(&self, mxc: &str, width: u32, height: u32) -> Result<Metadata> {
|
||||
let mut prefix = mxc.as_bytes().to_vec();
|
||||
prefix.push(0xFF);
|
||||
prefix.extend_from_slice(&width.to_be_bytes());
|
||||
@@ -141,7 +146,12 @@ impl Data {
|
||||
.map_err(|_| Error::bad_database("Content Disposition in mediaid_file is invalid unicode."))?,
|
||||
)
|
||||
};
|
||||
Ok((content_disposition, content_type, key))
|
||||
|
||||
Ok(Metadata {
|
||||
content_disposition,
|
||||
content_type,
|
||||
key,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets all the media keys in our database (this includes all the metadata
|
||||
|
||||
+33
-183
@@ -1,13 +1,13 @@
|
||||
mod data;
|
||||
mod tests;
|
||||
mod thumbnail;
|
||||
|
||||
use std::{collections::HashMap, io::Cursor, num::Saturating as Sat, path::PathBuf, sync::Arc, time::SystemTime};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc, time::SystemTime};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use conduit::{checked, debug, debug_error, error, utils, Error, Result, Server};
|
||||
use data::Data;
|
||||
use image::imageops::FilterType;
|
||||
use conduit::{debug, debug_error, err, error, utils, Err, Result, Server};
|
||||
use data::{Data, Metadata};
|
||||
use ruma::{OwnedMxcUri, OwnedUserId};
|
||||
use serde::Serialize;
|
||||
use tokio::{
|
||||
@@ -20,10 +20,9 @@ use crate::services;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileMeta {
|
||||
#[allow(dead_code)]
|
||||
pub content_disposition: Option<String>,
|
||||
pub content: Option<Vec<u8>>,
|
||||
pub content_type: Option<String>,
|
||||
pub file: Vec<u8>,
|
||||
pub content_disposition: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Default)]
|
||||
@@ -101,47 +100,30 @@ impl Service {
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
error!("Failed to find any media keys for MXC \"{mxc}\" in our database (MXC does not exist)");
|
||||
Err(Error::bad_database(
|
||||
"Failed to find any media keys for the provided MXC in our database (MXC does not exist)",
|
||||
))
|
||||
Err!(Database(error!(
|
||||
"Failed to find any media keys for MXC {mxc:?} in our database."
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Uploads or replaces a file thumbnail.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn upload_thumbnail(
|
||||
&self, sender_user: Option<OwnedUserId>, mxc: &str, content_disposition: Option<&str>,
|
||||
content_type: Option<&str>, width: u32, height: u32, file: &[u8],
|
||||
) -> Result<()> {
|
||||
let key = if let Some(user) = sender_user {
|
||||
self.db
|
||||
.create_file_metadata(Some(user.as_str()), mxc, width, height, content_disposition, content_type)?
|
||||
} else {
|
||||
self.db
|
||||
.create_file_metadata(None, mxc, width, height, content_disposition, content_type)?
|
||||
};
|
||||
|
||||
//TODO: Dangling metadata in database if creation fails
|
||||
let mut f = self.create_media_file(&key).await?;
|
||||
f.write_all(file).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Downloads a file.
|
||||
pub async fn get(&self, mxc: &str) -> Result<Option<FileMeta>> {
|
||||
if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc, 0, 0) {
|
||||
let mut file = Vec::new();
|
||||
if let Ok(Metadata {
|
||||
content_disposition,
|
||||
content_type,
|
||||
key,
|
||||
}) = self.db.search_file_metadata(mxc, 0, 0)
|
||||
{
|
||||
let mut content = Vec::new();
|
||||
let path = self.get_media_file(&key);
|
||||
BufReader::new(fs::File::open(path).await?)
|
||||
.read_to_end(&mut file)
|
||||
.read_to_end(&mut content)
|
||||
.await?;
|
||||
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
content: Some(content),
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -154,23 +136,16 @@ impl Service {
|
||||
let all_keys = self.db.get_all_media_keys();
|
||||
|
||||
let user_duration: SystemTime = match cyborgtime::parse_duration(&time) {
|
||||
Ok(duration) => {
|
||||
debug!("Parsed duration: {:?}", duration);
|
||||
debug!("System time now: {:?}", SystemTime::now());
|
||||
SystemTime::now().checked_sub(duration).ok_or_else(|| {
|
||||
Error::bad_database("Duration specified is not valid against the current system time")
|
||||
})?
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to parse user-specified time duration: {}", e);
|
||||
return Err(Error::bad_database("Failed to parse user-specified time duration."));
|
||||
},
|
||||
Err(e) => return Err!(Database(error!("Failed to parse specified time duration: {e}"))),
|
||||
Ok(duration) => SystemTime::now()
|
||||
.checked_sub(duration)
|
||||
.ok_or(err!(Arithmetic("Duration {duration:?} is too large")))?,
|
||||
};
|
||||
|
||||
let mut remote_mxcs: Vec<String> = vec![];
|
||||
|
||||
for key in all_keys {
|
||||
debug!("Full MXC key from database: {:?}", key);
|
||||
debug!("Full MXC key from database: {key:?}");
|
||||
|
||||
// we need to get the MXC URL from the first part of the key (the first 0xff /
|
||||
// 255 push). this is all necessary because of conduit using magic keys for
|
||||
@@ -179,24 +154,19 @@ impl Service {
|
||||
let mxc = parts
|
||||
.next()
|
||||
.map(|bytes| {
|
||||
utils::string_from_bytes(bytes).map_err(|e| {
|
||||
error!("Failed to parse MXC unicode bytes from our database: {}", e);
|
||||
Error::bad_database("Failed to parse MXC unicode bytes from our database")
|
||||
})
|
||||
utils::string_from_bytes(bytes)
|
||||
.map_err(|e| err!(Database(error!("Failed to parse MXC unicode bytes from our database: {e}"))))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let Some(mxc_s) = mxc else {
|
||||
return Err(Error::bad_database(
|
||||
"Parsed MXC URL unicode bytes from database but still is None",
|
||||
));
|
||||
return Err!(Database("Parsed MXC URL unicode bytes from database but still is None"));
|
||||
};
|
||||
|
||||
debug!("Parsed MXC key to URL: {}", mxc_s);
|
||||
|
||||
debug!("Parsed MXC key to URL: {mxc_s}");
|
||||
let mxc = OwnedMxcUri::from(mxc_s);
|
||||
if mxc.server_name() == Ok(services().globals.server_name()) {
|
||||
debug!("Ignoring local media MXC: {}", mxc);
|
||||
debug!("Ignoring local media MXC: {mxc}");
|
||||
// ignore our own MXC URLs as this would be local media.
|
||||
continue;
|
||||
}
|
||||
@@ -215,14 +185,14 @@ impl Service {
|
||||
},
|
||||
Err(err) => {
|
||||
if force {
|
||||
error!("Could not delete MXC path {:?}: {:?}. Skipping...", path, err);
|
||||
error!("Could not delete MXC path {path:?}: {err:?}. Skipping...");
|
||||
continue;
|
||||
}
|
||||
return Err(err.into());
|
||||
},
|
||||
};
|
||||
debug!("File created at: {:?}", file_created_at);
|
||||
|
||||
debug!("File created at: {file_created_at:?}");
|
||||
if file_created_at <= user_duration {
|
||||
debug!("File is within user duration, pushing to list of file paths and keys to delete.");
|
||||
remote_mxcs.push(mxc.to_string());
|
||||
@@ -232,15 +202,12 @@ impl Service {
|
||||
debug!(
|
||||
"Finished going through all our media in database for eligible keys to delete, checking if these are empty"
|
||||
);
|
||||
|
||||
if remote_mxcs.is_empty() {
|
||||
return Err(Error::bad_database("Did not found any eligible MXCs to delete."));
|
||||
return Err!(Database("Did not found any eligible MXCs to delete."));
|
||||
}
|
||||
|
||||
debug!("Deleting media now in the past \"{:?}\".", user_duration);
|
||||
|
||||
debug!("Deleting media now in the past {user_duration:?}.");
|
||||
let mut deletion_count: usize = 0;
|
||||
|
||||
for mxc in remote_mxcs {
|
||||
debug!("Deleting MXC {mxc} from database and filesystem");
|
||||
self.delete(&mxc).await?;
|
||||
@@ -250,123 +217,6 @@ impl Service {
|
||||
Ok(deletion_count)
|
||||
}
|
||||
|
||||
/// Returns width, height of the thumbnail and whether it should be cropped.
|
||||
/// Returns None when the server should send the original file.
|
||||
pub fn thumbnail_properties(&self, width: u32, height: u32) -> Option<(u32, u32, bool)> {
|
||||
match (width, height) {
|
||||
(0..=32, 0..=32) => Some((32, 32, true)),
|
||||
(0..=96, 0..=96) => Some((96, 96, true)),
|
||||
(0..=320, 0..=240) => Some((320, 240, false)),
|
||||
(0..=640, 0..=480) => Some((640, 480, false)),
|
||||
(0..=800, 0..=600) => Some((800, 600, false)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Downloads a file's thumbnail.
|
||||
///
|
||||
/// Here's an example on how it works:
|
||||
///
|
||||
/// - Client requests an image with width=567, height=567
|
||||
/// - Server rounds that up to (800, 600), so it doesn't have to save too
|
||||
/// many thumbnails
|
||||
/// - Server rounds that up again to (958, 600) to fix the aspect ratio
|
||||
/// (only for width,height>96)
|
||||
/// - Server creates the thumbnail and sends it to the user
|
||||
///
|
||||
/// For width,height <= 96 the server uses another thumbnailing algorithm
|
||||
/// which crops the image afterwards.
|
||||
pub async fn get_thumbnail(&self, mxc: &str, width: u32, height: u32) -> Result<Option<FileMeta>> {
|
||||
let (width, height, crop) = self
|
||||
.thumbnail_properties(width, height)
|
||||
.unwrap_or((0, 0, false)); // 0, 0 because that's the original file
|
||||
|
||||
if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc, width, height) {
|
||||
// Using saved thumbnail
|
||||
let mut file = Vec::new();
|
||||
let path = self.get_media_file(&key);
|
||||
fs::File::open(path).await?.read_to_end(&mut file).await?;
|
||||
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file: file.clone(),
|
||||
}))
|
||||
} else if let Ok((content_disposition, content_type, key)) = self.db.search_file_metadata(mxc, 0, 0) {
|
||||
// Generate a thumbnail
|
||||
let mut file = Vec::new();
|
||||
let path = self.get_media_file(&key);
|
||||
fs::File::open(path).await?.read_to_end(&mut file).await?;
|
||||
|
||||
if let Ok(image) = image::load_from_memory(&file) {
|
||||
let original_width = image.width();
|
||||
let original_height = image.height();
|
||||
if width > original_width || height > original_height {
|
||||
return Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file: file.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
let thumbnail = if crop {
|
||||
image.resize_to_fill(width, height, FilterType::CatmullRom)
|
||||
} else {
|
||||
let (exact_width, exact_height) = {
|
||||
let ratio = Sat(original_width) * Sat(height);
|
||||
let nratio = Sat(width) * Sat(original_height);
|
||||
|
||||
let use_width = nratio <= ratio;
|
||||
let intermediate = if use_width {
|
||||
Sat(original_height) * Sat(checked!(width / original_width)?)
|
||||
} else {
|
||||
Sat(original_width) * Sat(checked!(height / original_height)?)
|
||||
};
|
||||
|
||||
if use_width {
|
||||
(width, intermediate.0)
|
||||
} else {
|
||||
(intermediate.0, height)
|
||||
}
|
||||
};
|
||||
|
||||
image.thumbnail_exact(exact_width, exact_height)
|
||||
};
|
||||
|
||||
let mut thumbnail_bytes = Vec::new();
|
||||
thumbnail.write_to(&mut Cursor::new(&mut thumbnail_bytes), image::ImageFormat::Png)?;
|
||||
|
||||
// Save thumbnail in database so we don't have to generate it again next time
|
||||
let thumbnail_key = self.db.create_file_metadata(
|
||||
None,
|
||||
mxc,
|
||||
width,
|
||||
height,
|
||||
content_disposition.as_deref(),
|
||||
content_type.as_deref(),
|
||||
)?;
|
||||
|
||||
let mut f = self.create_media_file(&thumbnail_key).await?;
|
||||
f.write_all(&thumbnail_bytes).await?;
|
||||
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file: thumbnail_bytes.clone(),
|
||||
}))
|
||||
} else {
|
||||
// Couldn't parse file to generate thumbnail, send original
|
||||
Ok(Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file: file.clone(),
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_url_preview(&self, url: &str) -> Option<UrlPreviewData> { self.db.get_url_preview(url) }
|
||||
|
||||
/// TODO: use this?
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
use std::{cmp, io::Cursor, num::Saturating as Sat};
|
||||
|
||||
use conduit::{checked, Result};
|
||||
use image::{imageops::FilterType, DynamicImage};
|
||||
use ruma::OwnedUserId;
|
||||
use tokio::{
|
||||
fs,
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
};
|
||||
|
||||
use super::{data::Metadata, FileMeta};
|
||||
|
||||
impl super::Service {
|
||||
/// Uploads or replaces a file thumbnail.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn upload_thumbnail(
|
||||
&self, sender_user: Option<OwnedUserId>, mxc: &str, content_disposition: Option<&str>,
|
||||
content_type: Option<&str>, width: u32, height: u32, file: &[u8],
|
||||
) -> Result<()> {
|
||||
let key = if let Some(user) = sender_user {
|
||||
self.db
|
||||
.create_file_metadata(Some(user.as_str()), mxc, width, height, content_disposition, content_type)?
|
||||
} else {
|
||||
self.db
|
||||
.create_file_metadata(None, mxc, width, height, content_disposition, content_type)?
|
||||
};
|
||||
|
||||
//TODO: Dangling metadata in database if creation fails
|
||||
let mut f = self.create_media_file(&key).await?;
|
||||
f.write_all(file).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Downloads a file's thumbnail.
|
||||
///
|
||||
/// Here's an example on how it works:
|
||||
///
|
||||
/// - Client requests an image with width=567, height=567
|
||||
/// - Server rounds that up to (800, 600), so it doesn't have to save too
|
||||
/// many thumbnails
|
||||
/// - Server rounds that up again to (958, 600) to fix the aspect ratio
|
||||
/// (only for width,height>96)
|
||||
/// - Server creates the thumbnail and sends it to the user
|
||||
///
|
||||
/// For width,height <= 96 the server uses another thumbnailing algorithm
|
||||
/// which crops the image afterwards.
|
||||
#[tracing::instrument(skip(self), name = "thumbnail", level = "debug")]
|
||||
pub async fn get_thumbnail(&self, mxc: &str, width: u32, height: u32) -> Result<Option<FileMeta>> {
|
||||
// 0, 0 because that's the original file
|
||||
let (width, height, crop) = thumbnail_properties(width, height).unwrap_or((0, 0, false));
|
||||
|
||||
if let Ok(metadata) = self.db.search_file_metadata(mxc, width, height) {
|
||||
self.get_thumbnail_saved(metadata).await
|
||||
} else if let Ok(metadata) = self.db.search_file_metadata(mxc, 0, 0) {
|
||||
self.get_thumbnail_generate(mxc, width, height, crop, metadata)
|
||||
.await
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Using saved thumbnail
|
||||
#[tracing::instrument(skip(self), name = "saved", level = "debug")]
|
||||
async fn get_thumbnail_saved(&self, data: Metadata) -> Result<Option<FileMeta>> {
|
||||
let mut content = Vec::new();
|
||||
let path = self.get_media_file(&data.key);
|
||||
fs::File::open(path)
|
||||
.await?
|
||||
.read_to_end(&mut content)
|
||||
.await?;
|
||||
|
||||
Ok(Some(into_filemeta(data, content)))
|
||||
}
|
||||
|
||||
/// Generate a thumbnail
|
||||
#[tracing::instrument(skip(self), name = "generate", level = "debug")]
|
||||
async fn get_thumbnail_generate(
|
||||
&self, mxc: &str, width: u32, height: u32, crop: bool, data: Metadata,
|
||||
) -> Result<Option<FileMeta>> {
|
||||
let mut content = Vec::new();
|
||||
let path = self.get_media_file(&data.key);
|
||||
fs::File::open(path)
|
||||
.await?
|
||||
.read_to_end(&mut content)
|
||||
.await?;
|
||||
|
||||
let Ok(image) = image::load_from_memory(&content) else {
|
||||
// Couldn't parse file to generate thumbnail, send original
|
||||
return Ok(Some(into_filemeta(data, content)));
|
||||
};
|
||||
|
||||
if width > image.width() || height > image.height() {
|
||||
return Ok(Some(into_filemeta(data, content)));
|
||||
}
|
||||
|
||||
let mut thumbnail_bytes = Vec::new();
|
||||
let thumbnail = thumbnail_generate(&image, width, height, crop)?;
|
||||
thumbnail.write_to(&mut Cursor::new(&mut thumbnail_bytes), image::ImageFormat::Png)?;
|
||||
|
||||
// Save thumbnail in database so we don't have to generate it again next time
|
||||
let thumbnail_key = self.db.create_file_metadata(
|
||||
None,
|
||||
mxc,
|
||||
width,
|
||||
height,
|
||||
data.content_disposition.as_deref(),
|
||||
data.content_type.as_deref(),
|
||||
)?;
|
||||
|
||||
let mut f = self.create_media_file(&thumbnail_key).await?;
|
||||
f.write_all(&thumbnail_bytes).await?;
|
||||
|
||||
Ok(Some(into_filemeta(data, thumbnail_bytes)))
|
||||
}
|
||||
}
|
||||
|
||||
fn thumbnail_generate(image: &DynamicImage, width: u32, height: u32, crop: bool) -> Result<DynamicImage> {
|
||||
let thumbnail = if crop {
|
||||
image.resize_to_fill(width, height, FilterType::CatmullRom)
|
||||
} else {
|
||||
let (exact_width, exact_height) = thumbnail_dimension(image, width, height)?;
|
||||
image.thumbnail_exact(exact_width, exact_height)
|
||||
};
|
||||
|
||||
Ok(thumbnail)
|
||||
}
|
||||
|
||||
fn thumbnail_dimension(image: &DynamicImage, width: u32, height: u32) -> Result<(u32, u32)> {
|
||||
let image_width = image.width();
|
||||
let image_height = image.height();
|
||||
|
||||
let width = cmp::min(width, image_width);
|
||||
let height = cmp::min(height, image_height);
|
||||
|
||||
let use_width = Sat(width) * Sat(image_height) < Sat(height) * Sat(image_width);
|
||||
|
||||
let x = if use_width {
|
||||
let dividend = (Sat(height) * Sat(image_width)).0;
|
||||
checked!(dividend / image_height)?
|
||||
} else {
|
||||
width
|
||||
};
|
||||
|
||||
let y = if !use_width {
|
||||
let dividend = (Sat(width) * Sat(image_height)).0;
|
||||
checked!(dividend / image_width)?
|
||||
} else {
|
||||
height
|
||||
};
|
||||
|
||||
Ok((x, y))
|
||||
}
|
||||
|
||||
/// Returns width, height of the thumbnail and whether it should be cropped.
|
||||
/// Returns None when the server should send the original file.
|
||||
fn thumbnail_properties(width: u32, height: u32) -> Option<(u32, u32, bool)> {
|
||||
match (width, height) {
|
||||
(0..=32, 0..=32) => Some((32, 32, true)),
|
||||
(0..=96, 0..=96) => Some((96, 96, true)),
|
||||
(0..=320, 0..=240) => Some((320, 240, false)),
|
||||
(0..=640, 0..=480) => Some((640, 480, false)),
|
||||
(0..=800, 0..=600) => Some((800, 600, false)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filemeta(data: Metadata, content: Vec<u8>) -> FileMeta {
|
||||
FileMeta {
|
||||
content: Some(content),
|
||||
content_type: data.content_type,
|
||||
content_disposition: data.content_disposition,
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ impl Data {
|
||||
pub(super) fn new(server: &Arc<Server>, db: &Arc<Database>) -> Self {
|
||||
let config = &server.config;
|
||||
let cache_size = f64::from(config.auth_chain_cache_capacity);
|
||||
let cache_size = usize_from_f64(cache_size * config.conduit_cache_capacity_modifier).expect("valid cache size");
|
||||
let cache_size = usize_from_f64(cache_size * config.cache_capacity_modifier).expect("valid cache size");
|
||||
Self {
|
||||
shorteventid_authchain: db["shorteventid_authchain"].clone(),
|
||||
auth_chain_cache: Mutex::new(LruCache::new(cache_size)),
|
||||
|
||||
@@ -161,7 +161,7 @@ impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
let config = &args.server.config;
|
||||
let cache_size = f64::from(config.roomid_spacehierarchy_cache_capacity);
|
||||
let cache_size = cache_size * config.conduit_cache_capacity_modifier;
|
||||
let cache_size = cache_size * config.cache_capacity_modifier;
|
||||
Ok(Arc::new(Self {
|
||||
roomid_spacehierarchy_cache: Mutex::new(LruCache::new(usize_from_f64(cache_size)?)),
|
||||
}))
|
||||
|
||||
@@ -45,9 +45,9 @@ impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
let config = &args.server.config;
|
||||
let server_visibility_cache_capacity =
|
||||
f64::from(config.server_visibility_cache_capacity) * config.conduit_cache_capacity_modifier;
|
||||
f64::from(config.server_visibility_cache_capacity) * config.cache_capacity_modifier;
|
||||
let user_visibility_cache_capacity =
|
||||
f64::from(config.user_visibility_cache_capacity) * config.conduit_cache_capacity_modifier;
|
||||
f64::from(config.user_visibility_cache_capacity) * config.cache_capacity_modifier;
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
db: Data::new(args.db),
|
||||
|
||||
@@ -55,7 +55,7 @@ pub struct Service {
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
let config = &args.server.config;
|
||||
let cache_capacity = f64::from(config.stateinfo_cache_capacity) * config.conduit_cache_capacity_modifier;
|
||||
let cache_capacity = f64::from(config.stateinfo_cache_capacity) * config.cache_capacity_modifier;
|
||||
Ok(Arc::new(Self {
|
||||
db: Data::new(args.db),
|
||||
stateinfo_cache: StdMutex::new(LruCache::new(usize_from_f64(cache_capacity)?)),
|
||||
|
||||
@@ -443,7 +443,9 @@ impl crate::globals::resolver::Resolver {
|
||||
impl CachedDest {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn valid(&self) -> bool { self.expire > SystemTime::now() }
|
||||
pub fn valid(&self) -> bool { true }
|
||||
|
||||
//pub fn valid(&self) -> bool { self.expire > SystemTime::now() }
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn default_expire() -> SystemTime { rand::timepoint_secs(60 * 60 * 18..60 * 60 * 36) }
|
||||
@@ -452,7 +454,9 @@ impl CachedDest {
|
||||
impl CachedOverride {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn valid(&self) -> bool { self.expire > SystemTime::now() }
|
||||
pub fn valid(&self) -> bool { true }
|
||||
|
||||
//pub fn valid(&self) -> bool { self.expire > SystemTime::now() }
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn default_expire() -> SystemTime { rand::timepoint_secs(60 * 60 * 6..60 * 60 * 12) }
|
||||
|
||||
+42
-57
@@ -1,6 +1,6 @@
|
||||
use std::{collections::BTreeMap, mem::size_of, sync::Arc};
|
||||
|
||||
use conduit::{debug_info, utils, warn, Error, Result};
|
||||
use conduit::{debug_info, err, utils, warn, Err, Error, Result};
|
||||
use database::{Database, Map};
|
||||
use ruma::{
|
||||
api::client::{device::Device, error::ErrorKind, filter::FilterDefinition},
|
||||
@@ -87,19 +87,19 @@ impl Data {
|
||||
let mut parts = bytes.split(|&b| b == 0xFF);
|
||||
let user_bytes = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("User ID in token_userdeviceid is invalid."))?;
|
||||
.ok_or_else(|| err!(Database("User ID in token_userdeviceid is invalid.")))?;
|
||||
let device_bytes = parts
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("Device ID in token_userdeviceid is invalid."))?;
|
||||
.ok_or_else(|| err!(Database("Device ID in token_userdeviceid is invalid.")))?;
|
||||
|
||||
Ok(Some((
|
||||
UserId::parse(
|
||||
utils::string_from_bytes(user_bytes)
|
||||
.map_err(|_| Error::bad_database("User ID in token_userdeviceid is invalid unicode."))?,
|
||||
.map_err(|e| err!(Database("User ID in token_userdeviceid is invalid unicode. {e}")))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("User ID in token_userdeviceid is invalid."))?,
|
||||
.map_err(|e| err!(Database("User ID in token_userdeviceid is invalid. {e}")))?,
|
||||
utils::string_from_bytes(device_bytes)
|
||||
.map_err(|_| Error::bad_database("Device ID in token_userdeviceid is invalid."))?,
|
||||
.map_err(|e| err!(Database("Device ID in token_userdeviceid is invalid. {e}")))?,
|
||||
)))
|
||||
})
|
||||
}
|
||||
@@ -109,9 +109,9 @@ impl Data {
|
||||
Box::new(self.userid_password.iter().map(|(bytes, _)| {
|
||||
UserId::parse(
|
||||
utils::string_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("User ID in userid_password is invalid unicode."))?,
|
||||
.map_err(|e| err!(Database("User ID in userid_password is invalid unicode. {e}")))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("User ID in userid_password is invalid."))
|
||||
.map_err(|e| err!(Database("User ID in userid_password is invalid. {e}")))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ impl Data {
|
||||
.map_or(Ok(None), |bytes| {
|
||||
Ok(Some(
|
||||
utils::string_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Displayname in db is invalid."))?,
|
||||
.map_err(|e| err!(Database("Displayname in db is invalid. {e}")))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
@@ -188,10 +188,8 @@ impl Data {
|
||||
self.userid_avatarurl
|
||||
.get(user_id.as_bytes())?
|
||||
.map(|bytes| {
|
||||
let s_bytes = utils::string_from_bytes(&bytes).map_err(|e| {
|
||||
warn!("Avatar URL in db is invalid: {}", e);
|
||||
Error::bad_database("Avatar URL in db is invalid.")
|
||||
})?;
|
||||
let s_bytes = utils::string_from_bytes(&bytes)
|
||||
.map_err(|e| err!(Database(warn!("Avatar URL in db is invalid: {e}"))))?;
|
||||
let mxc_uri: OwnedMxcUri = s_bytes.into();
|
||||
Ok(mxc_uri)
|
||||
})
|
||||
@@ -215,10 +213,7 @@ impl Data {
|
||||
self.userid_blurhash
|
||||
.get(user_id.as_bytes())?
|
||||
.map(|bytes| {
|
||||
let s = utils::string_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Avatar URL in db is invalid."))?;
|
||||
|
||||
Ok(s)
|
||||
utils::string_from_bytes(&bytes).map_err(|e| err!(Database("Avatar URL in db is invalid. {e}")))
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
@@ -314,9 +309,9 @@ impl Data {
|
||||
bytes
|
||||
.rsplit(|&b| b == 0xFF)
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("UserDevice ID in db is invalid."))?,
|
||||
.ok_or_else(|| err!(Database("UserDevice ID in db is invalid.")))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("Device ID in userdeviceid_metadata is invalid."))?
|
||||
.map_err(|e| err!(Database("Device ID in userdeviceid_metadata is invalid. {e}")))?
|
||||
.into())
|
||||
}),
|
||||
)
|
||||
@@ -330,13 +325,9 @@ impl Data {
|
||||
|
||||
// should not be None, but we shouldn't assert either lol...
|
||||
if self.userdeviceid_metadata.get(&userdeviceid)?.is_none() {
|
||||
warn!(
|
||||
"Called set_token for a non-existent user \"{}\" and/or device ID \"{}\" with no metadata in database",
|
||||
user_id, device_id
|
||||
);
|
||||
return Err(Error::bad_database(
|
||||
"User does not exist or device ID has no metadata in database.",
|
||||
));
|
||||
return Err!(Database(error!(
|
||||
"User {user_id:?} does not exist or device ID {device_id:?} has no metadata."
|
||||
)));
|
||||
}
|
||||
|
||||
// Remove old token
|
||||
@@ -366,14 +357,9 @@ impl Data {
|
||||
// Only existing devices should be able to call this, but we shouldn't assert
|
||||
// either...
|
||||
if self.userdeviceid_metadata.get(&key)?.is_none() {
|
||||
warn!(
|
||||
"Called add_one_time_key for a non-existent user \"{}\" and/or device ID \"{}\" with no metadata in \
|
||||
database",
|
||||
user_id, device_id
|
||||
);
|
||||
return Err(Error::bad_database(
|
||||
"User does not exist or device ID has no metadata in database.",
|
||||
));
|
||||
return Err!(Database(error!(
|
||||
"User {user_id:?} does not exist or device ID {device_id:?} has no metadata."
|
||||
)));
|
||||
}
|
||||
|
||||
key.push(0xFF);
|
||||
@@ -401,7 +387,7 @@ impl Data {
|
||||
.get(user_id.as_bytes())?
|
||||
.map_or(Ok(0), |bytes| {
|
||||
utils::u64_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Count in roomid_lastroomactiveupdate is invalid."))
|
||||
.map_err(|e| err!(Database("Count in roomid_lastroomactiveupdate is invalid. {e}")))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -429,11 +415,10 @@ impl Data {
|
||||
serde_json::from_slice(
|
||||
key.rsplit(|&b| b == 0xFF)
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("OneTimeKeyId in db is invalid."))?,
|
||||
.ok_or_else(|| err!(Database("OneTimeKeyId in db is invalid.")))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("OneTimeKeyId in db is invalid."))?,
|
||||
serde_json::from_slice(&value)
|
||||
.map_err(|_| Error::bad_database("OneTimeKeys in db are invalid."))?,
|
||||
.map_err(|e| err!(Database("OneTimeKeyId in db is invalid. {e}")))?,
|
||||
serde_json::from_slice(&value).map_err(|e| err!(Database("OneTimeKeys in db are invalid. {e}")))?,
|
||||
))
|
||||
})
|
||||
.transpose()
|
||||
@@ -457,9 +442,9 @@ impl Data {
|
||||
bytes
|
||||
.rsplit(|&b| b == 0xFF)
|
||||
.next()
|
||||
.ok_or_else(|| Error::bad_database("OneTimeKey ID in db is invalid."))?,
|
||||
.ok_or_else(|| err!(Database("OneTimeKey ID in db is invalid.")))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("DeviceKeyId in db is invalid."))?
|
||||
.map_err(|e| err!(Database("DeviceKeyId in db is invalid. {e}")))?
|
||||
.algorithm(),
|
||||
)
|
||||
}) {
|
||||
@@ -581,19 +566,19 @@ impl Data {
|
||||
.get(&key)?
|
||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Tried to sign nonexistent key."))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("key in keyid_key is invalid."))?;
|
||||
.map_err(|e| err!(Database("key in keyid_key is invalid. {e}")))?;
|
||||
|
||||
let signatures = cross_signing_key
|
||||
.get_mut("signatures")
|
||||
.ok_or_else(|| Error::bad_database("key in keyid_key has no signatures field."))?
|
||||
.ok_or_else(|| err!(Database("key in keyid_key has no signatures field.")))?
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| Error::bad_database("key in keyid_key has invalid signatures field."))?
|
||||
.ok_or_else(|| err!(Database("key in keyid_key has invalid signatures field.")))?
|
||||
.entry(sender_id.to_string())
|
||||
.or_insert_with(|| serde_json::Map::new().into());
|
||||
|
||||
signatures
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| Error::bad_database("signatures in keyid_key for a user is invalid."))?
|
||||
.ok_or_else(|| err!(Database("signatures in keyid_key for a user is invalid.")))?
|
||||
.insert(signature.0, signature.1.into());
|
||||
|
||||
self.keyid_key.insert(
|
||||
@@ -640,7 +625,7 @@ impl Data {
|
||||
Error::bad_database("User ID in devicekeychangeid_userid is invalid unicode.")
|
||||
})?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("User ID in devicekeychangeid_userid is invalid."))
|
||||
.map_err(|e| err!(Database("User ID in devicekeychangeid_userid is invalid. {e}")))
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -685,7 +670,7 @@ impl Data {
|
||||
|
||||
self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| {
|
||||
Ok(Some(
|
||||
serde_json::from_slice(&bytes).map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?,
|
||||
serde_json::from_slice(&bytes).map_err(|e| err!(Database("DeviceKeys in db are invalid. {e}")))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
@@ -719,7 +704,7 @@ impl Data {
|
||||
) -> Result<Option<Raw<CrossSigningKey>>> {
|
||||
self.keyid_key.get(key)?.map_or(Ok(None), |bytes| {
|
||||
let mut cross_signing_key = serde_json::from_slice::<serde_json::Value>(&bytes)
|
||||
.map_err(|_| Error::bad_database("CrossSigningKey in db is invalid."))?;
|
||||
.map_err(|e| err!(Database("CrossSigningKey in db is invalid. {e}")))?;
|
||||
clean_signatures(&mut cross_signing_key, sender_user, user_id, allowed_signatures)?;
|
||||
|
||||
Ok(Some(Raw::from_json(
|
||||
@@ -751,7 +736,7 @@ impl Data {
|
||||
self.keyid_key.get(&key)?.map_or(Ok(None), |bytes| {
|
||||
Ok(Some(
|
||||
serde_json::from_slice(&bytes)
|
||||
.map_err(|_| Error::bad_database("CrossSigningKey in db is invalid."))?,
|
||||
.map_err(|e| err!(Database("CrossSigningKey in db is invalid. {e}")))?,
|
||||
))
|
||||
})
|
||||
})
|
||||
@@ -792,7 +777,7 @@ impl Data {
|
||||
for (_, value) in self.todeviceid_events.scan_prefix(prefix) {
|
||||
events.push(
|
||||
serde_json::from_slice(&value)
|
||||
.map_err(|_| Error::bad_database("Event in todeviceid_events is invalid."))?,
|
||||
.map_err(|e| err!(Database("Event in todeviceid_events is invalid. {e}")))?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -816,7 +801,7 @@ impl Data {
|
||||
Ok::<_, Error>((
|
||||
key.clone(),
|
||||
utils::u64_from_bytes(&key[key.len().saturating_sub(size_of::<u64>())..key.len()])
|
||||
.map_err(|_| Error::bad_database("ToDeviceId has invalid count bytes."))?,
|
||||
.map_err(|e| err!(Database("ToDeviceId has invalid count bytes. {e}")))?,
|
||||
))
|
||||
})
|
||||
.filter_map(Result::ok)
|
||||
@@ -877,7 +862,7 @@ impl Data {
|
||||
.get(user_id.as_bytes())?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
utils::u64_from_bytes(&bytes)
|
||||
.map_err(|_| Error::bad_database("Invalid devicelistversion in db."))
|
||||
.map_err(|e| err!(Database("Invalid devicelistversion in db. {e}")))
|
||||
.map(Some)
|
||||
})
|
||||
}
|
||||
@@ -893,7 +878,7 @@ impl Data {
|
||||
.scan_prefix(key)
|
||||
.map(|(_, bytes)| {
|
||||
serde_json::from_slice::<Device>(&bytes)
|
||||
.map_err(|_| Error::bad_database("Device in userdeviceid_metadata is invalid."))
|
||||
.map_err(|e| err!(Database("Device in userdeviceid_metadata is invalid. {e}")))
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -920,7 +905,7 @@ impl Data {
|
||||
let raw = self.userfilterid_filter.get(&key)?;
|
||||
|
||||
if let Some(raw) = raw {
|
||||
serde_json::from_slice(&raw).map_err(|_| Error::bad_database("Invalid filter event in db."))
|
||||
serde_json::from_slice(&raw).map_err(|e| err!(Database("Invalid filter event in db. {e}")))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@@ -954,7 +939,7 @@ impl Data {
|
||||
let expires_at = u64::from_be_bytes(
|
||||
expires_at_bytes
|
||||
.try_into()
|
||||
.map_err(|_| Error::bad_database("expires_at in openid_userid is invalid u64."))?,
|
||||
.map_err(|e| err!(Database("expires_at in openid_userid is invalid u64. {e}")))?,
|
||||
);
|
||||
|
||||
if expires_at < utils::millis_since_unix_epoch() {
|
||||
@@ -966,9 +951,9 @@ impl Data {
|
||||
|
||||
UserId::parse(
|
||||
utils::string_from_bytes(user_bytes)
|
||||
.map_err(|_| Error::bad_database("User ID in openid_userid is invalid unicode."))?,
|
||||
.map_err(|e| err!(Database("User ID in openid_userid is invalid unicode. {e}")))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("User ID in openid_userid is invalid."))
|
||||
.map_err(|e| err!(Database("User ID in openid_userid is invalid. {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user