mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-04-11 10:15:43 +00:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe637f481d | ||
|
|
18e43e1d35 | ||
|
|
09fca89ac5 | ||
|
|
9f19a2025d | ||
|
|
6b918966d4 | ||
|
|
328502c1cd | ||
|
|
d15e461303 | ||
|
|
6946eead28 | ||
|
|
09d3240365 | ||
|
|
653ec3799e | ||
|
|
6de9f52d5a | ||
|
|
484e7d1d2a | ||
|
|
dfa01541b3 | ||
|
|
adbe9268ce | ||
|
|
3504e6e724 | ||
|
|
154b2ab490 | ||
|
|
2231ccf118 | ||
|
|
d4d9f92ade | ||
|
|
e4e1636da8 | ||
|
|
e99aac9550 | ||
|
|
ddb87168ed | ||
|
|
245c34e659 | ||
|
|
43b07be3fc | ||
|
|
99d98efeb1 | ||
|
|
7b25ef2e6c | ||
|
|
1f8a7a707c | ||
|
|
86ec20e787 | ||
|
|
8c21388f01 | ||
|
|
d657fa32e9 | ||
|
|
321e197d8c | ||
|
|
16a98b0683 | ||
|
|
9e1bbc1650 | ||
|
|
91ff6a36a4 | ||
|
|
56f1d8be1f | ||
|
|
ed60f189cc | ||
|
|
cabf4362be | ||
|
|
2472c7c47a | ||
|
|
136cb038cf | ||
|
|
8f89be0fbd | ||
|
|
bbdced9c90 | ||
|
|
a6f4dc2b74 | ||
|
|
df203fa244 | ||
|
|
c6e6eb0af3 | ||
|
|
29babebc4d | ||
|
|
2f3194840c | ||
|
|
0ebb323490 | ||
|
|
f8e1255994 | ||
|
|
b5c0c30a5e | ||
|
|
ac4590952b | ||
|
|
67569cb9c8 | ||
|
|
11ec0dff4f | ||
|
|
a198f0481a | ||
|
|
6266e0ab5e | ||
|
|
9ee1485960 | ||
|
|
05314ec46c | ||
|
|
b66d2d44d0 | ||
|
|
3b2db9027a | ||
|
|
97e81885db | ||
|
|
706c1c993b | ||
|
|
cb70d51e2b | ||
|
|
bfb827a418 | ||
|
|
e2fb588a8c | ||
|
|
43c4dfc5df |
45
.github/workflows/ci.yml
vendored
45
.github/workflows/ci.yml
vendored
@@ -14,10 +14,10 @@ on:
|
||||
- 'docs/**'
|
||||
- 'debian/**'
|
||||
- 'docker/**'
|
||||
- 'test_results/**'
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
tags:
|
||||
- '*'
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -48,6 +48,18 @@ jobs:
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Tag comparison check
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
# Tag mismatch with latest repo tag check to prevent potential downgrades
|
||||
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
|
||||
if [ $LATEST_TAG != ${{ github.ref_name }} ]; then
|
||||
echo '# WARNING: Attempting to run this workflow for a tag that is not the latest repo tag. Aborting.'
|
||||
echo '# WARNING: Attempting to run this workflow for a tag that is not the latest repo tag. Aborting.' >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
|
||||
@@ -138,7 +150,7 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -223,20 +235,21 @@ jobs:
|
||||
name: Docker publish
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') && github.event_name != 'pull_request'
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '')
|
||||
env:
|
||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
|
||||
DOCKER_TAG: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
|
||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
|
||||
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
|
||||
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
|
||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
|
||||
GLCR_ARM64: registry.gitlab.com/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GLCR_AMD64: registry.gitlab.com/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
|
||||
GLCR_TAG: registry.gitlab.com/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
|
||||
GLCR_BRANCH: registry.gitlab.com/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
|
||||
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 }}
|
||||
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 }}
|
||||
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 }}
|
||||
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
||||
steps:
|
||||
|
||||
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
8
.github/workflows/trivy.yml
vendored
8
.github/workflows/trivy.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
schedule:
|
||||
- cron: '00 12 * * *'
|
||||
|
||||
@@ -24,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Trivy code and vulnerability scanner on repo
|
||||
uses: aquasecurity/trivy-action@0.19.0
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
scan-type: repo
|
||||
format: sarif
|
||||
@@ -32,9 +34,9 @@ jobs:
|
||||
severity: CRITICAL,HIGH,MEDIUM,LOW
|
||||
|
||||
- name: Run Trivy code and vulnerability scanner on filesystem
|
||||
uses: aquasecurity/trivy-action@0.19.0
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
scan-type: fs
|
||||
format: sarif
|
||||
output: trivy-results.sarif
|
||||
severity: CRITICAL,HIGH,MEDIUM,LOW
|
||||
severity: CRITICAL,HIGH,MEDIUM,LOW
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# Local environment overrides
|
||||
/.env
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
|
||||
92
CONTRIBUTING.md
Normal file
92
CONTRIBUTING.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Contributing guide
|
||||
|
||||
This page is for about contributing to conduwuit. The [development](docs/development.md) page may be of interest for you as well.
|
||||
|
||||
If you would like to work on an [issue][issues] that is not assigned, preferably ask in the Matrix room first at [#conduwuit:puppygock.gay][conduwuit-matrix], and comment on it.
|
||||
|
||||
### Linting and Formatting
|
||||
|
||||
It is mandatory all your changes satisfy the lints (clippy, rustc, rustdoc, etc) and your code is formatted via the **nightly** `cargo fmt`. A lot of the `rustfmt.toml` features depend on nightly toolchain. It would be ideal if they weren't nightly-exclusive features, but they currently still are. CI's rustfmt uses nightly.
|
||||
|
||||
If you need to allow a lint, please make sure it's either obvious as to why (e.g. clippy saying redundant clone but it's actually required) or it has a comment saying why. Do not write inefficient code for the sake of satisfying lints. If a lint is wrong and provides a more inefficient solution or suggestion, allow the lint and mention that in a comment.
|
||||
|
||||
### Running CI tests locally
|
||||
|
||||
conduwuit's CI for tests, linting, formatting, audit, etc use [`engage`][engage]. engage can be installed from nixpkgs or `cargo install engage`. conduwuit's Nix flake devshell has the nixpkgs engage with `direnv`. Use `engage --help` for more usage details.
|
||||
|
||||
To test, format, lint, etc that CI would do, install engage, allow the `.envrc` file using `direnv allow`, and run `engage`.
|
||||
|
||||
All of the tasks are defined at the [engage.toml][engage.toml] file. You can view all of them neatly by running `engage list`
|
||||
|
||||
If you would like to run only a specific engage task group, use `just`:
|
||||
- `engage just <group>`
|
||||
- Example: `engage just lints`
|
||||
|
||||
If you would like to run a specific engage task in a specific group, use `just <GROUP> [TASK]`: `engage just lints cargo-fmt`
|
||||
|
||||
The following binaries are used in [`engage.toml`][engage.toml]:
|
||||
|
||||
- [`engage`][engage]
|
||||
- `nix`
|
||||
- [`direnv`][direnv]
|
||||
- `rustc`
|
||||
- `cargo`
|
||||
- `cargo-fmt`
|
||||
- `rustdoc`
|
||||
- `cargo-clippy`
|
||||
- [`cargo-audit`][cargo-audit]
|
||||
- [`cargo-deb`][cargo-deb]
|
||||
- [`lychee`][lychee]
|
||||
|
||||
### Matrix tests
|
||||
|
||||
CI runs [Complement][complement], but currently does not fail if results from the checked-in results differ with the new results. If your changes are done to fix Matrix tests, note that in your pull request. If more Complement tests start failing from your changes, please review the logs (they are uploaded as artifacts) and determine if they're intended or not.
|
||||
|
||||
If you'd like to run Complement locally using Nix, see the [testing](docs/development/testing.md) page.
|
||||
|
||||
[Sytest][sytest] support will come soon.
|
||||
|
||||
### Writing documentation
|
||||
|
||||
conduwuit's website uses [`mdbook`][mdbook] and deployed via CI using GitHub Pages in the [`documentation.yml`][documentation.yml] workflow file with Nix's mdbook in the devshell. All documentation is in the `docs/` directory at the top level. The compiled mdbook website is also uploaded as an artifact.
|
||||
|
||||
To build the documentation using Nix, run: `bin/nix-build-and-cache just .#book`
|
||||
|
||||
The output of the mdbook generation is in `result/`. mdbooks can be opened in your browser from the individual HTML files without any web server needed.
|
||||
|
||||
### Inclusivity and Diversity
|
||||
|
||||
All **MUST** code and write with inclusivity and diversity in mind. See the [following page by Google on writing inclusive code and documentation](https://developers.google.com/style/inclusive-documentation).
|
||||
|
||||
This **EXPLICITLY** forbids usage of terms like "blacklist"/"whitelist" and "master"/"slave", [forbids gender-specific words and phrases](https://developers.google.com/style/pronouns#gender-neutral-pronouns), forbids ableist language like "sanity-check", "cripple", or "insane", and forbids culture-specific language (e.g. US-only holidays or cultures).
|
||||
|
||||
No exceptions are allowed. Dependencies that may use these terms are allowed but [do not replicate the name in your functions or variables](https://developers.google.com/style/inclusive-documentation#write-around).
|
||||
|
||||
In addition to language, write and code with the user experience in mind. This is software that intends to be used by everyone, so make it easy and comfortable for everyone to use. 🏳️⚧️
|
||||
|
||||
### Variable, comment, function, etc standards
|
||||
|
||||
Rust's default style and standards with regards to [function names, variable names, comments](https://rust-lang.github.io/api-guidelines/naming.html), etc applies here.
|
||||
|
||||
### Creating pull requests
|
||||
|
||||
Please try to keep contributions to the GitHub. While the mirrors of conduwuit allow for pull/merge requests, there is no guarantee I will see them in a timely manner. Additionally, please mark WIP or unfinished or incomplete PRs as drafts. This prevents me from having to ping once in a while to double check the status of it, especially when the CI completed successfully and everything so it *looks* done.
|
||||
|
||||
If you open a pull request on one of the mirrors, it is your responsibility to inform me about its existence. In the future I may try to solve this with more repo bots in the conduwuit Matrix room. There is no mailing list or email-patch support on the sr.ht mirror, but if you'd like to email me a git patch you can do so at `strawberry@puppygock.gay`.
|
||||
|
||||
Direct all PRs/MRs to the `main` branch.
|
||||
|
||||
By sending a pull request or patch, you are agreeing that your changes are allowed to be licenced under the Apache-2.0 licence and all of your conduct is in line with the Contributor's Covenant.
|
||||
|
||||
[issues]: https://github.com/girlbossceo/conduwuit/issues
|
||||
[conduwuit-matrix]: https://matrix.to/#/#conduwuit:puppygock.gay
|
||||
[complement]: https://github.com/matrix-org/complement/
|
||||
[engage.toml]: https://github.com/girlbossceo/conduwuit/blob/main/engage.toml
|
||||
[engage]: https://charles.page.computer.surgery/engage/
|
||||
[sytest]: https://github.com/matrix-org/sytest/
|
||||
[cargo-deb]: https://github.com/kornelski/cargo-deb
|
||||
[lychee]: https://github.com/lycheeverse/lychee
|
||||
[cargo-audit]: https://github.com/RustSec/rustsec/tree/main/cargo-audit
|
||||
[direnv]: https://direnv.net/
|
||||
[mdbook]: https://rust-lang.github.io/mdBook/
|
||||
[documentation.yml]: https://github.com/girlbossceo/conduwuit/blob/main/.github/workflows/documentation.yml
|
||||
312
Cargo.lock
generated
312
Cargo.lock
generated
@@ -61,15 +61,15 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -103,9 +103,9 @@ checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e9eabd7a98fe442131a17c316bd9349c43695e49e730c3c8e12cfb5f4da2693"
|
||||
checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"flate2",
|
||||
@@ -136,7 +136,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -147,7 +147,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -161,9 +161,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
@@ -375,7 +375,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -410,9 +410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "5.0.0"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19483b140a7ac7174d34b5a581b406c64f84da5409d3e09cf4fff604f9270e67"
|
||||
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
@@ -472,9 +472,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.96"
|
||||
version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -551,7 +551,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -568,7 +568,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "conduit"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"async-trait",
|
||||
@@ -594,6 +594,7 @@ dependencies = [
|
||||
"hyper 1.3.1",
|
||||
"hyper-util",
|
||||
"image",
|
||||
"infer",
|
||||
"ipaddress",
|
||||
"itertools",
|
||||
"jsonwebtoken",
|
||||
@@ -614,6 +615,7 @@ dependencies = [
|
||||
"ruma-identifiers-validation",
|
||||
"rusqlite",
|
||||
"rust-rocksdb",
|
||||
"sanitize-filename",
|
||||
"sd-notify",
|
||||
"sentry",
|
||||
"sentry-tower",
|
||||
@@ -774,7 +776,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -878,7 +880,7 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1046,7 +1048,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1090,9 +1092,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -1357,16 +1359,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.26.0"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
|
||||
checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1605,6 +1607,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199"
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.15"
|
||||
@@ -1885,9 +1893,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
||||
checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf",
|
||||
@@ -1899,9 +1907,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever_rcdom"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2"
|
||||
checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18"
|
||||
dependencies = [
|
||||
"html5ever",
|
||||
"markup5ever",
|
||||
@@ -2043,9 +2051,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
@@ -2057,20 +2065,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -2092,9 +2099,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.44"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
@@ -2103,11 +2110,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
@@ -2115,9 +2121,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -2287,9 +2293,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
@@ -2311,7 +2317,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2332,21 +2338,21 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.1"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2355,7 +2361,17 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"phf_shared 0.10.0",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
@@ -2368,6 +2384,15 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
@@ -2385,7 +2410,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2464,9 +2489,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.81"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2479,7 +2504,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
@@ -2496,15 +2521,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48"
|
||||
checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2697,8 +2722,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.9.4"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
@@ -2717,8 +2742,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -2729,8 +2754,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.17.4"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
@@ -2751,8 +2776,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.12.1"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
@@ -2781,8 +2806,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.27.11"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.28.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap 2.2.6",
|
||||
@@ -2803,8 +2828,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -2815,8 +2840,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.3"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.9.5"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror",
|
||||
@@ -2824,8 +2849,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -2834,8 +2859,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate",
|
||||
@@ -2843,14 +2868,14 @@ dependencies = [
|
||||
"quote",
|
||||
"ruma-identifiers-validation",
|
||||
"serde",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -2861,8 +2886,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
@@ -2877,8 +2902,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#1c291e18efd0559c2dd76b27d555c86471cbb0fd"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/girlbossceo/ruma?branch=conduwuit-changes#9e29e07ae1561fa7e6ed1897192f9c43c111b026"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"js_int",
|
||||
@@ -2906,7 +2931,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rust-librocksdb-sys"
|
||||
version = "0.21.0+9.1.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?branch=master#c5cd6bd25152ef1f8a488761351da0c3d29ed93a"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=c5cd6bd25152ef1f8a488761351da0c3d29ed93a#c5cd6bd25152ef1f8a488761351da0c3d29ed93a"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bzip2-sys",
|
||||
@@ -2923,7 +2948,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rust-rocksdb"
|
||||
version = "0.25.0"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?branch=master#c5cd6bd25152ef1f8a488761351da0c3d29ed93a"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=c5cd6bd25152ef1f8a488761351da0c3d29ed93a#c5cd6bd25152ef1f8a488761351da0c3d29ed93a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rust-librocksdb-sys",
|
||||
@@ -2931,9 +2956,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -3001,9 +3026,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.5.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54"
|
||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
@@ -3028,15 +3053,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
|
||||
checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@@ -3047,6 +3072,16 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sanitize-filename"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
@@ -3080,11 +3115,11 @@ checksum = "621e3680f3e07db4c9c2c3fb07c6223ab2fab2e54bd3c04c3ae037990f428c32"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
|
||||
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.5.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -3093,9 +3128,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
|
||||
checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -3103,9 +3138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "sentry"
|
||||
@@ -3244,22 +3279,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.200"
|
||||
version = "1.0.201"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
|
||||
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.200"
|
||||
version = "1.0.201"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
|
||||
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3277,9 +3312,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.116"
|
||||
version = "1.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -3480,7 +3515,7 @@ dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"phf_shared",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
]
|
||||
@@ -3491,8 +3526,8 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
@@ -3525,9 +3560,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.60"
|
||||
version = "2.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
||||
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3559,22 +3594,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.59"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
|
||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.59"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
|
||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3723,7 +3758,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3772,16 +3807,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3826,7 +3860,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.7",
|
||||
"winnow 0.6.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3929,7 +3963,7 @@ source = "git+https://github.com/girlbossceo/tracing?branch=tracing-subscriber/e
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4200,7 +4234,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4234,7 +4268,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4277,9 +4311,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpage"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb86b12e58d490a99867f561ce8466ffa7b73e24d015a8e7f5bc111d4424ba2"
|
||||
checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac"
|
||||
dependencies = [
|
||||
"html5ever",
|
||||
"markup5ever_rcdom",
|
||||
@@ -4520,9 +4554,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
|
||||
checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -4549,9 +4583,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.17.0"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650"
|
||||
checksum = "7c376f76ed09df711203e20c3ef5ce556f0166fa03d39590016c0fd625437fad"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
@@ -4566,22 +4600,22 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.32"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"syn 2.0.61",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
57
Cargo.toml
57
Cargo.toml
@@ -10,15 +10,17 @@ authors = [
|
||||
homepage = "https://conduwuit.puppyirl.gay/"
|
||||
repository = "https://github.com/girlbossceo/conduwuit"
|
||||
readme = "README.md"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
edition = "2021"
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
rust-version = "1.76.0"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[dependencies]
|
||||
console-subscriber = { version = "0.2", optional = true }
|
||||
|
||||
infer = { version = "0.15", default-features = false }
|
||||
|
||||
# for hot lib reload
|
||||
hot-lib-reloader = { version = "^0.7", optional = true }
|
||||
|
||||
@@ -26,7 +28,7 @@ hot-lib-reloader = { version = "^0.7", optional = true }
|
||||
rand = "0.8.5"
|
||||
|
||||
# Used for conduit::Error type
|
||||
thiserror = "1.0.59"
|
||||
thiserror = "1.0.60"
|
||||
|
||||
# Used to encode server public key
|
||||
base64 = "0.22.1"
|
||||
@@ -77,6 +79,7 @@ url = { version = "2.5.0", features = ["serde"] }
|
||||
async-trait = "0.1.80"
|
||||
|
||||
lru-cache = "0.1.2"
|
||||
sanitize-filename = "0.5.0"
|
||||
|
||||
# standard date and time tools
|
||||
[dependencies.chrono]
|
||||
@@ -109,6 +112,7 @@ features = [
|
||||
"add-extension",
|
||||
"cors",
|
||||
"sensitive-headers",
|
||||
"set-header",
|
||||
"trace",
|
||||
"util",
|
||||
"catch-panic",
|
||||
@@ -129,14 +133,14 @@ features = ["rustls-tls-native-roots", "socks", "hickory-dns"]
|
||||
# all the serde stuff
|
||||
# Used for pdu definition
|
||||
[dependencies.serde]
|
||||
version = "1.0.200"
|
||||
version = "1.0.201"
|
||||
features = ["rc"]
|
||||
# Used for appservice registration files
|
||||
[dependencies.serde_yaml]
|
||||
version = "0.9.34"
|
||||
# Used for ruma wrapper
|
||||
[dependencies.serde_json]
|
||||
version = "1.0.116"
|
||||
version = "1.0.117"
|
||||
features = ["raw_value"]
|
||||
|
||||
|
||||
@@ -230,7 +234,7 @@ features = ["use_std"]
|
||||
|
||||
# for URL previews
|
||||
[dependencies.webpage]
|
||||
version = "2.0"
|
||||
version = "2.0.1"
|
||||
default-features = false
|
||||
|
||||
# to support multiple variations of setting a config option
|
||||
@@ -295,7 +299,8 @@ default-features = false
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||
branch = "master"
|
||||
rev = "c5cd6bd25152ef1f8a488761351da0c3d29ed93a"
|
||||
#branch = "master"
|
||||
optional = true
|
||||
default-features = true
|
||||
features = ["multi-threaded-cf", "zstd"]
|
||||
@@ -362,44 +367,45 @@ default = [
|
||||
"brotli_compression",
|
||||
"zstd_compression",
|
||||
"release_max_log_level",
|
||||
"io_uring",
|
||||
]
|
||||
backend_sqlite = ["sqlite"]
|
||||
backend_rocksdb = ["rocksdb"]
|
||||
rocksdb = ["rust-rocksdb"]
|
||||
rocksdb = ["dep:rust-rocksdb"]
|
||||
jemalloc = [
|
||||
"tikv-jemalloc-sys",
|
||||
"tikv-jemalloc-ctl",
|
||||
"tikv-jemallocator",
|
||||
"dep:tikv-jemalloc-sys",
|
||||
"dep:tikv-jemalloc-ctl",
|
||||
"dep:tikv-jemallocator",
|
||||
"rust-rocksdb/jemalloc",
|
||||
]
|
||||
jemalloc_prof = ["tikv-jemalloc-sys/profiling"]
|
||||
sqlite = ["rusqlite", "parking_lot", "thread_local"]
|
||||
systemd = ["sd-notify"]
|
||||
sentry_telemetry = ["sentry", "sentry-tracing", "sentry-tower"]
|
||||
sqlite = ["dep:rusqlite", "dep:parking_lot", "dep:thread_local"]
|
||||
systemd = ["dep:sd-notify"]
|
||||
sentry_telemetry = ["dep:sentry", "dep:sentry-tracing", "dep:sentry-tower"]
|
||||
|
||||
gzip_compression = ["tower-http/compression-gzip", "reqwest/gzip"]
|
||||
zstd_compression = ["tower-http/compression-zstd"]
|
||||
brotli_compression = ["tower-http/compression-br", "reqwest/brotli"]
|
||||
|
||||
sha256_media = ["sha2"]
|
||||
sha256_media = ["dep:sha2"]
|
||||
io_uring = ["rust-rocksdb/io-uring"]
|
||||
axum_dual_protocol = ["axum-server-dual-protocol"]
|
||||
axum_dual_protocol = ["dep:axum-server-dual-protocol"]
|
||||
|
||||
perf_measurements = [
|
||||
"opentelemetry",
|
||||
"tracing-flame",
|
||||
"tracing-opentelemetry",
|
||||
"opentelemetry_sdk",
|
||||
"opentelemetry-jaeger",
|
||||
"dep:opentelemetry",
|
||||
"dep:tracing-flame",
|
||||
"dep:tracing-opentelemetry",
|
||||
"dep:opentelemetry_sdk",
|
||||
"dep:opentelemetry-jaeger",
|
||||
]
|
||||
|
||||
# enable the tokio_console server
|
||||
# incompatible with release_max_log_level
|
||||
tokio_console = ["console-subscriber", "tokio/tracing"]
|
||||
tokio_console = ["dep:console-subscriber", "tokio/tracing"]
|
||||
|
||||
hot_reload = ["dep:hot-lib-reloader"]
|
||||
|
||||
hardened_malloc = ["hardened_malloc-rs"]
|
||||
hardened_malloc = ["dep:hardened_malloc-rs"]
|
||||
|
||||
# increases performance, reduces build times, and reduces binary size by not compiling or
|
||||
# genreating code for log level filters that users will generally not use (debug and trace) only in release builds
|
||||
@@ -459,9 +465,11 @@ systemd-units = { unit-name = "conduwuit" }
|
||||
|
||||
|
||||
[profile.dev]
|
||||
#debug = 0
|
||||
lto = 'off'
|
||||
codegen-units = 512
|
||||
incremental = true
|
||||
overflow-checks = true
|
||||
#panic = "abort"
|
||||
|
||||
# seems to speed up continuous debug compilations
|
||||
@@ -622,6 +630,8 @@ manual_let_else = "warn"
|
||||
trivially_copy_pass_by_ref = "warn"
|
||||
wildcard_imports = "warn"
|
||||
checked_conversions = "warn"
|
||||
#integer_arithmetic = "warn"
|
||||
#as_conversions = "warn"
|
||||
|
||||
# some sadness
|
||||
missing_errors_doc = "allow"
|
||||
@@ -642,7 +652,6 @@ map_err_ignore = "allow"
|
||||
missing_docs_in_private_items = "allow"
|
||||
multiple_inherent_impl = "allow"
|
||||
error_impl_error = "allow"
|
||||
as_conversions = "allow"
|
||||
string_add = "allow"
|
||||
string_slice = "allow"
|
||||
ref_patterns = "allow"
|
||||
|
||||
17
README.md
17
README.md
@@ -2,8 +2,6 @@ # conduwuit
|
||||
|
||||
`main` / stable: [](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
|
||||
|
||||
`dev`: [](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
|
||||
|
||||
<!-- ANCHOR: catchphrase -->
|
||||
### a very cool, featureful fork of [Conduit](https://conduit.rs/)
|
||||
<!-- ANCHOR_END: catchphrase -->
|
||||
@@ -28,6 +26,10 @@ #### Can I try it out?
|
||||
|
||||
An official conduwuit server ran by me is available at transfem.dev ([element.transfem.dev](https://element.transfem.dev) / [cinny.transfem.dev](https://cinny.transfem.dev))
|
||||
|
||||
transfem.dev is a public homeserver that can be used, it is not a "test only homeserver". This means there are rules, so please read the rules: [https://transfem.dev/homeserver_rules.txt](https://transfem.dev/homeserver_rules.txt)
|
||||
|
||||
transfem.dev is also listed at [servers.joinmatrix.org](https://servers.joinmatrix.org/)
|
||||
|
||||
#### What is the current status?
|
||||
|
||||
conduwuit is a hard fork of Conduit which is in beta, meaning you can join and participate in most
|
||||
@@ -37,14 +39,6 @@ #### What is the current status?
|
||||
<!-- ANCHOR_END: body -->
|
||||
|
||||
<!-- ANCHOR: footer -->
|
||||
#### How can I contribute?
|
||||
|
||||
1. Look for an issue you would like to work on and make sure it's not assigned
|
||||
to other users
|
||||
2. Ask someone to assign the issue to you (comment on the issue or chat in
|
||||
[#conduwuit:puppygock.gay](https://matrix.to/#/#conduwuit:puppygock.gay))
|
||||
3. Fork the repo and work on the issue.
|
||||
4. Submit a PR (please keep contributions to the GitHub repo, main development is done here, not the GitLab repo which exists just as a mirror. If you are avoiding GitHub, feel free to join our Matrix chat to get your patch in.)
|
||||
|
||||
#### Contact
|
||||
|
||||
@@ -69,7 +63,8 @@ #### Is it conduwuit or Conduwuit?
|
||||
#### Mirrors of conduwuit
|
||||
|
||||
- GitHub: <https://github.com/girlbossceo/conduwuit>
|
||||
- GitLab: <https://gitlab.com/girlbossceo/conduwuit>
|
||||
- GitLab: <https://gitlab.com/conduwuit/conduwuit>
|
||||
- git.girlcock.ceo: <https://git.girlcock.ceo/strawberry/conduwuit>
|
||||
- git.gay: <https://git.gay/june/conduwuit>
|
||||
- Codeberg: <https://codeberg.org/girlbossceo/conduwuit>
|
||||
- sourcehut: <https://git.sr.ht/~girlbossceo/conduwuit>
|
||||
|
||||
@@ -20,12 +20,9 @@ OCI_IMAGE="complement-conduit:dev"
|
||||
toplevel="$(git rev-parse --show-toplevel)"
|
||||
|
||||
pushd "$toplevel" > /dev/null
|
||||
# uses nix-output-monitor (nom) if available
|
||||
if command -v nom &> /dev/null; then
|
||||
nom build .#complement
|
||||
else
|
||||
nix build -L .#complement
|
||||
fi
|
||||
|
||||
bin/nix-build-and-cache just .#complement
|
||||
|
||||
docker load < result
|
||||
popd > /dev/null
|
||||
|
||||
|
||||
@@ -13,41 +13,55 @@ just() {
|
||||
nix build -L "$@"
|
||||
fi
|
||||
|
||||
if [ ! -z "$ATTIC_TOKEN" ]; then
|
||||
# historical "conduit" store for compatibility purposes, same as conduwuit
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
login \
|
||||
conduit \
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduit}" \
|
||||
"$ATTIC_TOKEN"
|
||||
|
||||
readarray -t outputs < <(nix path-info "$@")
|
||||
readarray -t derivations < <(nix path-info "$@" --derivation)
|
||||
|
||||
# Push the target installable and its build dependencies
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
push \
|
||||
conduit \
|
||||
"${outputs[@]}" \
|
||||
"${derivations[@]}"
|
||||
|
||||
# main "conduwuit" store
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
login \
|
||||
conduwuit \
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduwuit}" \
|
||||
"$ATTIC_TOKEN"
|
||||
|
||||
# Push the target installable and its build dependencies
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
push \
|
||||
conduwuit \
|
||||
"${outputs[@]}" \
|
||||
"${derivations[@]}"
|
||||
else
|
||||
if [ -z "$ATTIC_TOKEN" ]; then
|
||||
echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache"
|
||||
return
|
||||
fi
|
||||
|
||||
# historical "conduit" store for compatibility purposes, same as conduwuit
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
login \
|
||||
conduit \
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduit}" \
|
||||
"$ATTIC_TOKEN"
|
||||
|
||||
# Find all output paths of the installables and their build dependencies
|
||||
readarray -t derivations < <(nix path-info --derivation "$@")
|
||||
cache=()
|
||||
for derivation in "${derivations[@]}"; do
|
||||
cache+=(
|
||||
"$(nix-store --query --requisites --include-outputs "$derivation")"
|
||||
)
|
||||
done
|
||||
|
||||
# Upload them to Attic (conduit store)
|
||||
#
|
||||
# Use `xargs` and a here-string because something would probably explode if
|
||||
# several thousand arguments got passed to a command at once. Hopefully no
|
||||
# store paths include a newline in them.
|
||||
(
|
||||
IFS=$'\n'
|
||||
nix shell --inputs-from "$toplevel" attic -c xargs \
|
||||
attic push conduit <<< "${cache[*]}"
|
||||
)
|
||||
|
||||
# main "conduwuit" store
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
login \
|
||||
conduwuit \
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduwuit}" \
|
||||
"$ATTIC_TOKEN"
|
||||
|
||||
# Upload them to Attic (conduwuit store)
|
||||
#
|
||||
# Use `xargs` and a here-string because something would probably explode if
|
||||
# several thousand arguments got passed to a command at once. Hopefully no
|
||||
# store paths include a newline in them.
|
||||
(
|
||||
IFS=$'\n'
|
||||
nix shell --inputs-from "$toplevel" attic -c xargs \
|
||||
attic push conduwuit <<< "${cache[*]}"
|
||||
)
|
||||
}
|
||||
|
||||
# Build and cache things needed for CI
|
||||
@@ -56,7 +70,7 @@ ci() {
|
||||
--inputs-from "$toplevel"
|
||||
|
||||
# Keep sorted
|
||||
"$toplevel#devShells.x86_64-linux.default.inputDerivation"
|
||||
"$toplevel#devShells.x86_64-linux.default"
|
||||
attic#default
|
||||
nixpkgs#direnv
|
||||
nixpkgs#jq
|
||||
|
||||
@@ -269,6 +269,19 @@ url_preview_check_root_domain = false
|
||||
# Defaults to true
|
||||
allow_profile_lookup_federation_requests = true
|
||||
|
||||
# Config option to automatically deactivate the account of any user who attempts to join a:
|
||||
# - banned room
|
||||
# - forbidden room alias
|
||||
# - room alias or ID with a forbidden server name
|
||||
#
|
||||
# This may be useful if all your banned lists consist of toxic rooms or servers that no good faith user would ever attempt to join, and
|
||||
# to automatically remediate the problem without any admin user intervention.
|
||||
#
|
||||
# This will also make the user leave all rooms. Federation (e.g. remote room invites) are ignored here.
|
||||
#
|
||||
# Defaults to false as rooms can be banned for non-moderation-related reasons
|
||||
#auto_deactivate_banned_room_attempts = false
|
||||
|
||||
|
||||
### Misc
|
||||
|
||||
|
||||
3
debian/README.md
vendored
3
debian/README.md
vendored
@@ -1,5 +1,4 @@
|
||||
conduwuit for Debian
|
||||
==================
|
||||
# conduwuit for Debian
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
14
debian/conduwuit.service
vendored
14
debian/conduwuit.service
vendored
@@ -1,13 +1,18 @@
|
||||
[Unit]
|
||||
Description=conduwuit Matrix homeserver
|
||||
Documentation=https://conduwuit.puppyirl.gay/
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
User=_conduwuit
|
||||
Group=_conduwuit
|
||||
User=conduwuit
|
||||
Group=conduwuit
|
||||
Type=notify
|
||||
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
|
||||
ExecStart=/usr/sbin/conduwuit
|
||||
|
||||
AmbientCapabilities=
|
||||
CapabilityBoundingSet=
|
||||
|
||||
@@ -39,14 +44,11 @@ SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service @resources
|
||||
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc
|
||||
SystemCallErrorNumber=EPERM
|
||||
StateDirectory=matrix-conduit
|
||||
StateDirectory=conduwuit
|
||||
|
||||
RuntimeDirectory=conduit
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Environment="CONDUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
|
||||
ExecStart=/usr/sbin/conduwuit
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
|
||||
8
debian/postinst
vendored
8
debian/postinst
vendored
@@ -7,21 +7,21 @@ CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit/
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the `_conduwuit` user if it does not exist yet.
|
||||
if ! getent passwd _conduwuit > /dev/null ; then
|
||||
# Create the `conduwuit` user if it does not exist yet.
|
||||
if ! getent passwd conduwuit > /dev/null ; then
|
||||
echo 'Adding system user for the conduwuit Matrix homeserver' 1>&2
|
||||
adduser --system --group --quiet \
|
||||
--home "$CONDUWUIT_DATABASE_PATH" \
|
||||
--disabled-login \
|
||||
--shell "/usr/sbin/nologin" \
|
||||
--force-badname \
|
||||
_conduwuit
|
||||
conduwuit
|
||||
fi
|
||||
|
||||
# Create the database path if it does not exist yet and fix up ownership
|
||||
# and permissions.
|
||||
mkdir -p "$CONDUWUIT_DATABASE_PATH"
|
||||
chown _conduwuit:_conduwuit -R "$CONDUWUIT_DATABASE_PATH"
|
||||
chown conduwuit:conduwuit -R "$CONDUWUIT_DATABASE_PATH"
|
||||
chmod 700 "$CONDUWUIT_DATABASE_PATH"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -2,14 +2,17 @@ # Summary
|
||||
|
||||
- [Introduction](introduction.md)
|
||||
- [Differences from upstream Conduit](differences.md)
|
||||
|
||||
- [Example configuration](configuration.md)
|
||||
- [Deploying](deploying.md)
|
||||
- [Generic](deploying/generic.md)
|
||||
- [Debian](deploying/debian.md)
|
||||
- [Docker](deploying/docker.md)
|
||||
- [NixOS](deploying/nixos.md)
|
||||
- [Docker](deploying/docker.md)
|
||||
- [Arch Linux](deploying/arch-linux.md)
|
||||
- [Debian](deploying/debian.md)
|
||||
- [TURN](turn.md)
|
||||
- [Appservices](appservices.md)
|
||||
- [Maintenance](maintenance.md)
|
||||
- [Troubleshooting](troubleshooting.md)
|
||||
- [Development](development.md)
|
||||
- [Contributing](contributing.md)
|
||||
- [Testing](development/testing.md)
|
||||
|
||||
1
docs/contributing.md
Normal file
1
docs/contributing.md
Normal file
@@ -0,0 +1 @@
|
||||
{{#include ../CONTRIBUTING.md}}
|
||||
8
docs/deploying/arch-linux.md
Normal file
8
docs/deploying/arch-linux.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# conduwuit for Arch Linux
|
||||
|
||||
Currently conduwuit is only on the Arch User Repository (AUR).
|
||||
|
||||
The conduwuit AUR packages are community maintained and are not maintained by conduwuit development team, but the AUR package maintainers are in the Matrix room. Please attempt to verify your AUR package's PKGBUILD file looks fine before asking for support.
|
||||
|
||||
- [conduwuit](https://aur.archlinux.org/packages/conduwuit) - latest tagged conduwuit
|
||||
- [conduwuit-git](https://aur.archlinux.org/packages/conduwuit-git) - latest git conduwuit from `main` branch
|
||||
@@ -12,22 +12,17 @@ ### Use a registry
|
||||
| Registry | Image | Size | Notes |
|
||||
| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable tagged image. |
|
||||
| GitLab Registry | [registry.gitlab.com/girlbossceo/conduwuit:latest][gl] | ![Image Size][shield-latest] | Stable tagged image. |
|
||||
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:latest][gl] | ![Image Size][shield-latest] | Stable tagged image. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable tagged image. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| GitLab Registry | [registry.gitlab.com/girlbossceo/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:dev][gh] | ![Image Size][shield-dev] | Development version/branch. |
|
||||
| GitLab Registry | [registry.gitlab.com/girlbossceo/conduwuit:dev][gl] | ![Image Size][shield-dev] | Development version/branch. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:dev][dh] | ![Image Size][shield-dev] | Development version/branch. |
|
||||
|
||||
|
||||
[dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit
|
||||
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
|
||||
[gl]: https://gitlab.com/girlbossceo/conduwuit/container_registry/6351657
|
||||
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6351657
|
||||
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
|
||||
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
|
||||
[shield-dev]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/dev
|
||||
|
||||
|
||||
Use
|
||||
@@ -56,9 +51,9 @@ ### Run
|
||||
|
||||
or you can use [docker compose](#docker-compose).
|
||||
|
||||
The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, an example can be found [here](../configuration.md).
|
||||
The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, the example config can be found [here](../configuration.md).
|
||||
You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars. For an overview of possible
|
||||
values, please take a look at the `docker-compose.yml` file.
|
||||
values, please take a look at the [`docker-compose.yml`](docker-compose.yml) file.
|
||||
|
||||
If you just want to test conduwuit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
|
||||
|
||||
@@ -112,92 +107,7 @@ ### Use Traefik as Proxy
|
||||
|
||||
With the service `well-known` we use a single `nginx` container that will serve those two files.
|
||||
|
||||
So...step by step:
|
||||
|
||||
1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
||||
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml) from the repository and remove `.for-traefik` (or `.with-traefik`) from the filename.
|
||||
2. Open both files and modify/adjust them to your needs. Meaning, change the `CONDUIT_SERVER_NAME` and the volume host mappings according to your needs.
|
||||
3. Create the `conduwuit.toml` config file, an example can be found [here](../configuration.md), or set `CONDUIT_CONFIG=""` and configure conduwuit per env vars.
|
||||
4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`.
|
||||
5. Create the files needed by the `well-known` service.
|
||||
|
||||
- `./nginx/matrix.conf` (relative to the compose file, you can change this, but then also need to change the volume mapping)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
server_name <SUBDOMAIN>.<DOMAIN>;
|
||||
listen 80 default_server;
|
||||
|
||||
location /.well-known/matrix/server {
|
||||
return 200 '{"m.server": "<SUBDOMAIN>.<DOMAIN>:443"}';
|
||||
types { } default_type "application/json; charset=utf-8";
|
||||
}
|
||||
|
||||
location /.well-known/matrix/client {
|
||||
return 200 '{"m.homeserver": {"base_url": "https://<SUBDOMAIN>.<DOMAIN>"}}';
|
||||
types { } default_type "application/json; charset=utf-8";
|
||||
add_header "Access-Control-Allow-Origin" *;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
6. Run `docker compose up -d`
|
||||
7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin.
|
||||
|
||||
|
||||
|
||||
|
||||
## Voice communication
|
||||
|
||||
In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place.
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a configuration file called `coturn.conf` containing:
|
||||
|
||||
```conf
|
||||
use-auth-secret
|
||||
static-auth-secret=<a secret key>
|
||||
realm=<your server domain>
|
||||
```
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
|
||||
|
||||
These same values need to be set in conduwuit. You can either modify conduwuit.toml to include these lines:
|
||||
```
|
||||
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
|
||||
turn_secret = "<secret key from coturn configuration>"
|
||||
```
|
||||
or append the following to the docker environment variables dependig on which configuration method you used earlier:
|
||||
```yml
|
||||
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
|
||||
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
|
||||
```
|
||||
Restart Conduit to apply these changes.
|
||||
|
||||
### Run
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
```bash
|
||||
docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
|
||||
```
|
||||
|
||||
or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml`
|
||||
and run `docker compose up -d` in the same directory.
|
||||
|
||||
```yml
|
||||
version: 3
|
||||
services:
|
||||
turn:
|
||||
container_name: coturn-server
|
||||
image: docker.io/coturn/coturn
|
||||
restart: unless-stopped
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- ./coturn.conf:/etc/coturn/turnserver.conf
|
||||
```
|
||||
|
||||
To understand why the host networking mode is used and explore alternative configuration options, please visit the following link: https://github.com/coturn/coturn/blob/master/docker/coturn/README.md.
|
||||
For security recommendations see Synapse's [Coturn documentation](https://github.com/matrix-org/synapse/blob/develop/docs/setup/turn/coturn.md#configuration).
|
||||
See the [TURN](../turn.md) page.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Generic deployment documentation
|
||||
|
||||
### Please note that this documentation is not fully representative of conduwuit at the moment. Assume majority of it is outdated.
|
||||
|
||||
> ## Getting help
|
||||
>
|
||||
> If you run into any problems while setting up conduwuit, ask us
|
||||
@@ -13,23 +11,16 @@ ## Installing conduwuit
|
||||
|
||||
Prebuilt binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest).
|
||||
|
||||
Alternatively, you may compile the binary yourself. First, install any dependencies:
|
||||
The latest tagged release also includes the Debian packages.
|
||||
|
||||
```bash
|
||||
# Debian
|
||||
$ sudo apt install libclang-dev build-essential
|
||||
Alternatively, you may compile the binary yourself. We recommend using [Lix](https://lix.systems) to build conduwuit as this has the most guaranteed
|
||||
reproducibiltiy and easiest to get a build environment and output going.
|
||||
|
||||
# RHEL
|
||||
$ sudo dnf install clang
|
||||
```
|
||||
Then, `cd` into the source tree of conduwuit and run:
|
||||
```bash
|
||||
$ cargo build --release
|
||||
```
|
||||
Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang, installing liburing for io_uring and RocksDB, etc).
|
||||
|
||||
## Adding a conduwuit user
|
||||
|
||||
While conduwuit can run as any user it is usually better to use dedicated users for different services. This also allows
|
||||
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:
|
||||
@@ -38,6 +29,12 @@ ## Adding a conduwuit user
|
||||
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
||||
```
|
||||
|
||||
For distros without `adduser`:
|
||||
|
||||
```bash
|
||||
sudo useradd -r --shell /usr/bin/nologin --no-create-home conduwuit
|
||||
```
|
||||
|
||||
## Forwarding ports in the firewall or the router
|
||||
|
||||
conduwuit uses the ports 443 and 8448 both of which need to be open in the firewall.
|
||||
@@ -46,45 +43,18 @@ ## Forwarding ports in the firewall or the router
|
||||
|
||||
## Setting up a systemd service
|
||||
|
||||
Now we'll set up a systemd service for conduwuit, so it's easy to start/stop conduwuit and set it to autostart when your
|
||||
server reboots. Simply paste the default systemd service you can find below into
|
||||
`/etc/systemd/system/conduwuit.service`.
|
||||
|
||||
```systemd
|
||||
[Unit]
|
||||
Description=conduwuit Matrix Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
User=conduwuit
|
||||
Group=conduwuit
|
||||
RuntimeDirectory=conduwuit
|
||||
RuntimeDirectoryMode=0750
|
||||
Restart=always
|
||||
ExecStart=/usr/local/bin/conduwuit
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Finally, run
|
||||
|
||||
```bash
|
||||
$ sudo systemctl daemon-reload
|
||||
```
|
||||
The systemd unit for conduwuit can be found [here](../../debian/conduwuit.service). You may need to change the `ExecStart=` path to where you placed the conduwuit binary.
|
||||
|
||||
## Creating the conduwuit configuration file
|
||||
|
||||
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. Paste this in **and take a moment
|
||||
to read it. You need to change at least the server name.**
|
||||
RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
|
||||
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. The example config can be found at [conduwuit-example.toml](../configuration.md).**Please take a moment to read it. You need to change at least the server name.**
|
||||
|
||||
See the following example config at [conduwuit-example.toml](../configuration.md)
|
||||
RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
As we are using a conduwuit specific user we need to allow it to read the config. To do that you can run this command on
|
||||
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
|
||||
|
||||
Debian or RHEL:
|
||||
|
||||
```bash
|
||||
@@ -102,7 +72,7 @@ ## Setting the correct file permissions
|
||||
|
||||
## Setting up the Reverse Proxy
|
||||
|
||||
Refer to the documentation or various guides online of your chosen reverse proxy software. A Caddy example will be provided as this is the recommended reverse proxy for new users and is very trivial.
|
||||
Refer to the documentation or various guides online of your chosen reverse proxy software. A [Caddy](https://caddyserver.com/) example will be provided as this is the recommended reverse proxy for new users and is very trivial to use (handles TLS, reverse proxy headers, etc transparently with proper defaults).
|
||||
|
||||
### Caddy
|
||||
|
||||
@@ -118,10 +88,10 @@ ### Caddy
|
||||
}
|
||||
```
|
||||
|
||||
That's it! Just start or enable the service and you're set.
|
||||
That's it! Just start and enable the service and you're set.
|
||||
|
||||
```bash
|
||||
$ sudo systemctl enable caddy
|
||||
$ sudo systemctl enable --now caddy
|
||||
```
|
||||
|
||||
## You're done!
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Development
|
||||
|
||||
Information about developing the project. If you are only interested in using
|
||||
it, you can safely ignore this section.
|
||||
it, you can safely ignore this section. If you plan on contributing, see the
|
||||
[contributor's guide](contributing.md).
|
||||
|
||||
## Debugging with `tokio-console`
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ ### list of features, bug fixes, etc that conduwuit does that Conduit does not:
|
||||
|
||||
## Performance:
|
||||
- Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently
|
||||
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
|
||||
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests (download and thumbnail) to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
|
||||
- Add feature flags and config options to enable/build with zstd, brotli, and/or gzip HTTP body compression (response and request)
|
||||
- Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results (NXDOMAINs, successful queries, etc) using hickory-dns / hickory-resolver
|
||||
- Vastly improve RocksDB default settings to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage or buggy filesystems.
|
||||
@@ -22,6 +22,7 @@ ## Performance:
|
||||
- Add config options for RocksDB compression and bottommost compression, including choosing the algorithm and compression level
|
||||
- Use [loole](https://github.com/mahdi-shojaee/loole) MPSC channels instead of tokio MPSC channels for huge performance boosts in sending channels (mainly relevant for federation) and presence channels
|
||||
- Use `tracing`/`log`'s `release_max_level_info` feature to improve performance, build speeds, binary size, and CPU usage in release builds by avoid compiling debug/trace log level macros that users will generally never use (can be disabled with a build-time feature flag)
|
||||
- Enable RocksDB async read I/O via `io_uring` by default
|
||||
|
||||
|
||||
## General Fixes:
|
||||
@@ -35,7 +36,7 @@ ## General Fixes:
|
||||
- Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing
|
||||
- Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10
|
||||
- Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string.
|
||||
- Allow HEAD HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
|
||||
- Allow HEAD and PATCH (MSC4138) HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
|
||||
- Resolve and remove some "features" from upstream that result in concurrency hazards, exponential backoff issues, or arbitrary performance limiters
|
||||
- Find more servers for outbound federation `/hierarchy` requests instead of just the room ID server name
|
||||
- Support for suggesting servers to join through at `/_matrix/client/v3/directory/room/{roomAlias}`
|
||||
@@ -55,6 +56,7 @@ ## Moderation:
|
||||
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events
|
||||
- Add support for a "global ACLs" feature (`forbidden_remote_server_names`) that blocks inbound remote room invites, room joins by room ID on server name, room joins by room alias on server name, incoming federated joins, and incoming federated room directory requests. This is very helpful for blocking servers that are purely toxic/bad and serve no value in allowing our users to suffer from things like room invite spam or such. Please note that this is not a substitute for room ACLs.
|
||||
- Add support for a config option to forbid our local users from sending federated room directory requests for (`forbidden_remote_room_directory_server_names`). Similar to above, useful for blocking servers that help prevent our users from wandering into bad areas of Matrix via room directories of those malicious servers.
|
||||
- Add config option for auto remediating/deactivating local non-admin users who attempt to join bad/forbidden rooms (`auto_deactivate_banned_room_attempts`)
|
||||
|
||||
|
||||
## Privacy/Security:
|
||||
@@ -68,6 +70,9 @@ ## Privacy/Security:
|
||||
- Config option to disable incoming and/or outgoing remote read receipts
|
||||
- Config option to disable incoming and/or outgoing remote typing indicators
|
||||
- Config option to disable incoming, outgoing, and/or local presence
|
||||
- Sanitise file names for the `Content-Disposition` header for all media requests (thumbnails, downloads, uploads)
|
||||
- Return `inline` or `attachment` based on the detected file MIME type for the `Content-Disposition` and only allow images/videos/text/audio to be `inline`
|
||||
- Send secure default HTTP headers such as a strong restrictive CSP, deny iframes, disable `X-XSS-Protection`, disable interest cohort in `Permission-Policy`, etc to mitigate any potential attack surface such as from untrusted media
|
||||
|
||||
|
||||
## Administration/Logging:
|
||||
@@ -81,7 +86,7 @@ ## Administration/Logging:
|
||||
- Warn on unknown config options specified
|
||||
- Add `/_conduwuit/server_version` route to return the version of conduwuit without relying on the federation API `/_matrix/federation/v1/version`
|
||||
- Add configurable RocksDB recovery modes to aid in recovering corrupted RocksDB databases
|
||||
- Support config options via `CONDUWUIT_` prefix
|
||||
- Support config options via `CONDUWUIT_` prefix and accessing non-global struct config options with the `__` split (e.g. `CONDUWUIT_WELL_KNOWN__SERVER`)
|
||||
- Add support for listening on multiple TCP ports
|
||||
- Disable update check by default as it's not useful for conduwuit
|
||||
- **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting
|
||||
@@ -89,7 +94,8 @@ ## Administration/Logging:
|
||||
|
||||
## Maintenance/Stability:
|
||||
- GitLab CI ported to GitHub Actions
|
||||
- Repo is mirrored to GitHub, GitLab, git.gay, sourcehut, and Codeberg (see README.md for their links)
|
||||
- Repo is mirrored to GitHub, GitLab, git.gay, git.girlcock.ceo, sourcehut, and Codeberg (see README.md for their links)
|
||||
- Docker container images published to GitLab Container Registry, GitHub Container Registry, and Dockerhub
|
||||
- Extensively revamp the example config to be extremely helpful and useful to both new users and power users
|
||||
- Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness
|
||||
- Add a **lot** of other clippy and rustc lints and a rustfmt.toml file
|
||||
@@ -146,6 +152,7 @@ ## Misc:
|
||||
- Implement running and diff'ing Complement results in CI
|
||||
- Interest in supporting other operating systems such as macOS, BSDs, and Windows, and getting them added into CI and doing builds for them
|
||||
- Add config option for disabling RocksDB Direct IO if needed
|
||||
- Add various documentation on maintaining conduwuit, using RocksDB online backups, some troubleshooting, using admin commands, etc
|
||||
- (Developers): Add support for tokio-console
|
||||
- (Developers): Add support for tracing flame graphs
|
||||
- Add `release-debuginfo` Cargo build profile
|
||||
|
||||
@@ -6,7 +6,7 @@ # Conduwuit
|
||||
|
||||
#### What's different about your fork than upstream Conduit?
|
||||
|
||||
See [differences.md](differences.md)
|
||||
See the [differences](differences.md) page
|
||||
|
||||
#### How can I deploy my own?
|
||||
|
||||
@@ -14,4 +14,8 @@ #### How can I deploy my own?
|
||||
|
||||
If you want to connect an Appservice to Conduwuit, take a look at the [appservices documentation](appservices.md).
|
||||
|
||||
#### How can I contribute?
|
||||
|
||||
See the [contributor's guide](contributing.md)
|
||||
|
||||
{{#include ../README.md:footer}}
|
||||
|
||||
63
docs/maintenance.md
Normal file
63
docs/maintenance.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Maintaining your conduwuit setup
|
||||
|
||||
## Moderation
|
||||
|
||||
conduwuit has moderation through admin room commands. "binary commands" (medium priority) and an admin API (low priority) is planned. Some moderation-related config options are available in the example config such as "global ACLs" and blocking media requests to certain servers. See the example config for the moderation config options under the "Moderation / Privacy / Security" section.
|
||||
|
||||
conduwuit has moderation admin commands for:
|
||||
- managing room aliases (`!admin rooms alias`)
|
||||
- managing room directory (`!admin rooms directory`)
|
||||
- managing room banning/blocking and user removal (`!admin rooms moderation`)
|
||||
- managing user accounts (`!admin users`)
|
||||
- fetching `/.well-known/matrix/support` from servers (`!admin federation`)
|
||||
- blocking incoming federation for certain rooms (not the same as room banning) (`!admin federation`)
|
||||
- deleting media (see [the media section](#media))
|
||||
|
||||
Any commands with `-list` in them will require a codeblock in the message with each object being newline delimited. An example of doing this is:
|
||||
|
||||
````
|
||||
!admin rooms moderation ban-list-of-rooms
|
||||
```
|
||||
!roomid1:server.name
|
||||
!roomid2:server.name
|
||||
!roomid3:server.name
|
||||
```
|
||||
````
|
||||
|
||||
## Database
|
||||
|
||||
If using RocksDB, there's very little you need to do. Compaction is ran automatically based on various defined thresholds tuned for conduwuit to be high performance with the least I/O amplifcation or overhead. Manually running compaction is not recommended, or compaction via a timer. RocksDB is built with io_uring support via liburing for async read I/O.
|
||||
|
||||
Some RocksDB settings can be adjusted such as the compression method chosen. See the RocksDB section in the [example config](configuration.md). btrfs users may benefit from disabling compression on RocksDB if CoW is in use.
|
||||
|
||||
RocksDB troubleshooting can be found [in the RocksDB section of troubleshooting](troubleshooting.md).
|
||||
|
||||
## Backups
|
||||
|
||||
Currently only RocksDB supports online backups. If you'd like to backup your database online without any downtime, see the `!admin server` command for the backup commands and the `database_backup_path` config options in the example config. Please note that the format of the database backup is not the exact same. This is unfortunately a bad design choice by Facebook as we are using the database backup engine API from RocksDB, however the data is still there and can still be joined together.
|
||||
|
||||
To restore a backup from an online RocksDB backup:
|
||||
- shutdown conduwuit
|
||||
- create a new directory for merging together the data
|
||||
- in the online backup created, copy all `.sst` files in `$DATABASE_BACKUP_PATH/shared_checksum` to your new directory
|
||||
- trim all the strings so instead of `######_sxxxxxxxxx.sst`, it reads `######.sst`. A way of doing this with sed and bash is `for file in *.sst; do mv "$file" "$(echo "$file" | sed 's/_s.*/.sst/')"; done`
|
||||
- copy all the files in `$DATABASE_BACKUP_PATH/1` to your new directory
|
||||
- set your `database_path` config option to your new directory, or replace your old one with the new one you crafted
|
||||
- start up conduwuit again and it should open as normal
|
||||
|
||||
If you'd like to do an offline backup, shutdown conduwuit and copy your `database_path` directory elsewhere. This can be restored with no modifications needed.
|
||||
|
||||
Backing up media is also just copying the `media/` directory from your database directory.
|
||||
|
||||
## Media
|
||||
|
||||
Media still needs various work, however conduwuit implements media deletion via:
|
||||
- MXC URI
|
||||
- Delete list of MXC URIs
|
||||
- Delete remote media in the past `N` seconds/minutes
|
||||
|
||||
See the `!admin media` command for further information. All media in conduwuit is stored at `$DATABASE_DIR/media`. This will be configurable soon.
|
||||
|
||||
If you are finding yourself needing extensive granular control over media, we recommend looking into [Matrix Media Repo](https://github.com/t2bot/matrix-media-repo). conduwuit intends to implement various utilities for media, but MMR is dedicated to extensive media management.
|
||||
|
||||
Built-in S3 support is also planned, but for now using a "S3 filesystem" on `media/` works. conduwuit also sends a `Cache-Control` header of 1 year and immutable for all media requests (download and thumbnail) to reduce unnecessary media requests from browsers.
|
||||
62
docs/troubleshooting.md
Normal file
62
docs/troubleshooting.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Troubleshooting conduwuit
|
||||
|
||||
> ## Docker users ⚠️
|
||||
>
|
||||
> Docker is extremely UX unfriendly. Because of this, a ton of issues or support is actually Docker support, not conduwuit support. We also cannot document the ever-growing list of Docker issues here.
|
||||
>
|
||||
> If you intend on asking for support and you are using Docker, **PLEASE** triple validate your issues are **NOT** because you have a misconfiguration in your Docker setup.
|
||||
>
|
||||
> If there are things like Compose file issues or Dockerhub image issues, those can still be mentioned as long as they're something we can fix.
|
||||
|
||||
## Rocksdb / database issues
|
||||
|
||||
#### Direct IO
|
||||
|
||||
Some filesystems may not like RocksDB using [Direct IO](https://github.com/facebook/rocksdb/wiki/Direct-IO). Direct IO is for non-buffered I/O which improves conduwuit performance, but at least FUSE is a filesystem potentially known to not like this. See the [example config](configuration.md) for disabling it if needed. Issues from Direct IO on unsupported filesystems are usually shown as startup errors.
|
||||
|
||||
#### Database corruption
|
||||
|
||||
If your database is corrupted and is failing to start (e.g. checksum mismatch), it may be recoverable but careful steps must be taken, and there is no guarantee it may be recoverable.
|
||||
|
||||
RocksDB has the following recovery modes:
|
||||
|
||||
- `TolerateCorruptedTailRecords`
|
||||
- `AbsoluteConsistency`
|
||||
- `PointInTime`
|
||||
- `SkipAnyCorruptedRecord`
|
||||
|
||||
By default, conduwuit uses `TolerateCorruptedTailRecords` as generally these may be due to bad federation and we can re-fetch the correct data over federation. The RocksDB default is `PointInTime` which will attempt to restore a "snapshot" of the data when it was last known to be good. This data can be either a few seconds old, or multiple minutes prior. `PointInTime` may not be suitable for default usage due to clients and servers possibly not being able to handle sudden "backwards time travels", and `AbsoluteConsistency` may be too strict.
|
||||
|
||||
`AbsoluteConsistency` will fail to start the database if any sign of corruption is detected. `SkipAnyCorruptedRecord` will skip all forms of corruption unless it forbids the database from opening (e.g. too severe). Usage of `SkipAnyCorruptedRecord` voids any support as this may cause more damage and/or leave your database in a permanently inconsistent state, but it may do something if `PointInTime` does not work as a last ditch effort.
|
||||
|
||||
With this in mind:
|
||||
- First start conduwuit with the `PointInTime` recovery method. See the [example config](configuration.md) for how to do this using `rocksdb_recovery_mode`
|
||||
- If your database successfully opens, clients are recommended to clear their client cache to account for the rollback
|
||||
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as much possible corruption is restored
|
||||
- If all goes will, you should be able to restore back to using `TolerateCorruptedTailRecords` and you have successfully recovered your database
|
||||
|
||||
## Media
|
||||
|
||||
#### "File name too long"
|
||||
|
||||
If you are running into the "file name is too long" OS error for media requests, your filesystem cannot handle file name lengths >=255 characters. This is unfortuntely due to Conduit (upstream) using base64 for file name keys which is very problematic for some filesystems as the base64 input is untrusted and long file names or specific inputs can cause this. If you would like to avoid this, you may build conduwuit yourself with the `sha256_media` feature. **This will lose database compatibility with upstream**.
|
||||
|
||||
## Debugging
|
||||
|
||||
Note that users should not really be debugging things. If you find yourself debugging and find the issue, please let us know and/or how we can fix it. Various debug commands can be found in `!admin debug`.
|
||||
|
||||
#### Debug/Trace log level
|
||||
|
||||
conduwuit builds without debug or trace log levels by default for at least performance reasons. This may change in the future and/or binaries providing such configurations may be provided. If you need to access debug/trace log levels, you will need to build without the `release_max_log_level` feature.
|
||||
|
||||
#### Changing log level dynamically
|
||||
|
||||
conduwuit supports changing the tracing log environment filter on-the-fly using the admin command `!admin debug change-log-level`. This accepts a string **without quotes** the same format as the `log` config option.
|
||||
|
||||
#### Pinging servers
|
||||
|
||||
conduwuit can ping other servers using `!admin debug ping`. This takes a server name and goes through the server discovery process and queries `/_matrix/federation/v1/version`. Errors are outputted.
|
||||
|
||||
#### Allocator memory stats
|
||||
|
||||
If using jemalloc (for now) and built with jemallocator's `stats` feature, you can see conduwuit's jemalloc memory stats by using `!admin debug memory-stats`
|
||||
62
docs/turn.md
62
docs/turn.md
@@ -1,25 +1,55 @@
|
||||
# Setting up TURN/STURN
|
||||
|
||||
## General instructions
|
||||
In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image.
|
||||
|
||||
* It is assumed you have a [Coturn server](https://github.com/coturn/coturn) up and running. See [Synapse reference implementation](https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md).
|
||||
### Configuration
|
||||
|
||||
## Edit/Add a few settings to your existing conduit.toml
|
||||
Create a configuration file called `coturn.conf` containing:
|
||||
|
||||
```conf
|
||||
use-auth-secret
|
||||
static-auth-secret=<a secret key>
|
||||
realm=<your server domain>
|
||||
```
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
|
||||
|
||||
These same values need to be set in conduwuit. You can either modify conduwuit.toml to include these lines:
|
||||
|
||||
```
|
||||
# Refer to your Coturn settings.
|
||||
# `your.turn.url` has to match the REALM setting of your Coturn as well as `transport`.
|
||||
turn_uris = ["turn:your.turn.url?transport=udp", "turn:your.turn.url?transport=tcp"]
|
||||
|
||||
# static-auth-secret of your turnserver
|
||||
turn_secret = "ADD SECRET HERE"
|
||||
|
||||
# If you have your TURN server configured to use a username and password
|
||||
# you can provide these information too. In this case comment out `turn_secret above`!
|
||||
#turn_username = ""
|
||||
#turn_password = ""
|
||||
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
|
||||
turn_secret = "<secret key from coturn configuration>"
|
||||
```
|
||||
|
||||
## Apply settings
|
||||
or append the following to the docker environment variables dependig on which configuration method you used earlier:
|
||||
|
||||
Restart Conduit.
|
||||
```yml
|
||||
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
|
||||
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
|
||||
```
|
||||
|
||||
Restart conduwuit to apply these changes.
|
||||
|
||||
### Run
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
```bash
|
||||
docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
|
||||
```
|
||||
|
||||
or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml`
|
||||
and run `docker compose up -d` in the same directory.
|
||||
|
||||
```yml
|
||||
version: 3
|
||||
services:
|
||||
turn:
|
||||
container_name: coturn-server
|
||||
image: docker.io/coturn/coturn
|
||||
restart: unless-stopped
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- ./coturn.conf:/etc/coturn/turnserver.conf
|
||||
```
|
||||
|
||||
To understand why the host networking mode is used and explore alternative configuration options, please visit [Coturn's Docker documentation](https://github.com/coturn/coturn/blob/master/docker/coturn/README.md).
|
||||
|
||||
For security recommendations see Synapse's [Coturn documentation](https://element-hq.github.io/synapse/latest/turn-howto.html).
|
||||
|
||||
@@ -131,7 +131,7 @@ cargo clippy \
|
||||
[[task]]
|
||||
name = "lychee"
|
||||
group = "lints"
|
||||
script = "lychee --offline docs"
|
||||
script = "lychee --verbose --offline docs *.md"
|
||||
|
||||
[[task]]
|
||||
name = "cargo"
|
||||
|
||||
10
flake.lock
generated
10
flake.lock
generated
@@ -238,15 +238,15 @@
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1713810944,
|
||||
"narHash": "sha256-/Xf0bzNJPclH9IP80QNaABfhj4IAR5LycYET18VFCXc=",
|
||||
"owner": "facebook",
|
||||
"lastModified": 1714770052,
|
||||
"narHash": "sha256-NCPYF2wYBsB9OHEkZSOYoPlxjC9BBMhJp8EM5M1o3Mc=",
|
||||
"owner": "girlbossceo",
|
||||
"repo": "rocksdb",
|
||||
"rev": "6f7cabeac80a3a6150be2c8a8369fcecb107bf43",
|
||||
"rev": "db6df0b185774778457dabfcbd822cb81760cade",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "facebook",
|
||||
"owner": "girlbossceo",
|
||||
"ref": "v9.1.1",
|
||||
"repo": "rocksdb",
|
||||
"type": "github"
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
flake-utils.url = "github:numtide/flake-utils?ref=main";
|
||||
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.1"; flake = false; };
|
||||
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.1.1"; flake = false; };
|
||||
};
|
||||
|
||||
outputs = inputs:
|
||||
@@ -21,7 +22,7 @@
|
||||
file = ./rust-toolchain.toml;
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
sha256 = "sha256-e4mlaJehWBymYxJGgnbuCObVlqMlQSilZ8FljG9zPHY=";
|
||||
sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8";
|
||||
};
|
||||
|
||||
scope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||
@@ -179,6 +180,10 @@
|
||||
# Useful for editing the book locally
|
||||
mdbook
|
||||
])
|
||||
++ (if !pkgsHost.stdenv.isDarwin then [
|
||||
# Needed for building with io_uring
|
||||
pkgsHost.liburing
|
||||
] else [])
|
||||
++
|
||||
scopeHost.main.nativeBuildInputs;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ stdenv.mkDerivation {
|
||||
include = [
|
||||
"book.toml"
|
||||
"conduwuit-example.toml"
|
||||
"CONTRIBUTING.md"
|
||||
"README.md"
|
||||
"debian/README.md"
|
||||
"docs"
|
||||
|
||||
@@ -37,7 +37,7 @@ buildDepsOnlyEnv =
|
||||
});
|
||||
|
||||
buildPackageEnv = {
|
||||
CONDUWUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev;
|
||||
CONDUWUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
|
||||
} // buildDepsOnlyEnv;
|
||||
|
||||
commonAttrs = {
|
||||
@@ -83,9 +83,15 @@ craneLib.buildPackage ( commonAttrs // {
|
||||
(features != [])
|
||||
"--features " + (builtins.concatStringsSep "," features);
|
||||
|
||||
# This is redundant with CI
|
||||
cargoTestCommand = "";
|
||||
|
||||
# This is redundant with CI
|
||||
doCheck = false;
|
||||
|
||||
# https://crane.dev/faq/rebuilds-bindgen.html
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
|
||||
|
||||
env = buildPackageEnv;
|
||||
|
||||
passthru = {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||
|
||||
[toolchain]
|
||||
channel = "1.76.0"
|
||||
channel = "1.77.0"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
"rust-src",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use register::RegistrationKind;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
@@ -238,7 +240,7 @@ pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<
|
||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||
// content is set to the user's display name with a space before it
|
||||
if !services().globals.new_user_displayname_suffix().is_empty() {
|
||||
displayname.push_str(&(" ".to_owned() + services().globals.new_user_displayname_suffix()));
|
||||
_ = write!(displayname, " {}", services().globals.config.new_user_displayname_suffix);
|
||||
}
|
||||
|
||||
services()
|
||||
@@ -531,7 +533,7 @@ pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Res
|
||||
}
|
||||
|
||||
// Make the user leave all rooms before deactivation
|
||||
client_server::leave_all_rooms(sender_user).await?;
|
||||
client_server::leave_all_rooms(sender_user).await;
|
||||
|
||||
// Remove devices and mark account as deactivated
|
||||
services().users.deactivate_account(sender_user)?;
|
||||
|
||||
@@ -269,9 +269,7 @@ async fn alias_checks(room_alias: &OwnedRoomAliasId, appservice_info: &Option<Re
|
||||
if !info.aliases.is_match(room_alias.as_str()) {
|
||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
|
||||
}
|
||||
}
|
||||
|
||||
if services().appservice.is_exclusive_alias(room_alias).await {
|
||||
} else if services().appservice.is_exclusive_alias(room_alias).await {
|
||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use ruma::api::client::{
|
||||
backup::{
|
||||
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version,
|
||||
delete_backup_keys, delete_backup_keys_for_room, delete_backup_keys_for_session, delete_backup_version,
|
||||
get_backup_info, get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session,
|
||||
get_latest_backup_info, update_backup_version,
|
||||
use ruma::{
|
||||
api::client::{
|
||||
backup::{
|
||||
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version,
|
||||
delete_backup_keys, delete_backup_keys_for_room, delete_backup_keys_for_session, delete_backup_version,
|
||||
get_backup_info, get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session,
|
||||
get_latest_backup_info, update_backup_version,
|
||||
},
|
||||
error::ErrorKind,
|
||||
},
|
||||
error::ErrorKind,
|
||||
UInt,
|
||||
};
|
||||
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
@@ -56,7 +59,8 @@ pub(crate) async fn get_latest_backup_info_route(
|
||||
|
||||
Ok(get_latest_backup_info::v3::Response {
|
||||
algorithm,
|
||||
count: (services().key_backups.count_keys(sender_user, &version)? as u32).into(),
|
||||
count: (UInt::try_from(services().key_backups.count_keys(sender_user, &version)?)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services().key_backups.get_etag(sender_user, &version)?,
|
||||
version,
|
||||
})
|
||||
@@ -76,10 +80,12 @@ pub(crate) async fn get_backup_info_route(
|
||||
|
||||
Ok(get_backup_info::v3::Response {
|
||||
algorithm,
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
@@ -139,10 +145,12 @@ pub(crate) async fn add_backup_keys_route(
|
||||
}
|
||||
|
||||
Ok(add_backup_keys::v3::Response {
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
@@ -181,10 +189,12 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
}
|
||||
|
||||
Ok(add_backup_keys_for_room::v3::Response {
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
@@ -221,10 +231,12 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
.add_key(sender_user, &body.version, &body.room_id, &body.session_id, &body.session_data)?;
|
||||
|
||||
Ok(add_backup_keys_for_session::v3::Response {
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
@@ -294,10 +306,12 @@ pub(crate) async fn delete_backup_keys_route(
|
||||
.delete_all_keys(sender_user, &body.version)?;
|
||||
|
||||
Ok(delete_backup_keys::v3::Response {
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
@@ -317,10 +331,12 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
.delete_room_keys(sender_user, &body.version, &body.room_id)?;
|
||||
|
||||
Ok(delete_backup_keys_for_room::v3::Response {
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
@@ -340,10 +356,12 @@ pub(crate) async fn delete_backup_keys_for_session_route(
|
||||
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)?;
|
||||
|
||||
Ok(delete_backup_keys_for_session::v3::Response {
|
||||
count: (services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)? as u32)
|
||||
.into(),
|
||||
count: (UInt::try_from(
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
|
||||
@@ -63,8 +63,8 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
||||
lazy_loaded.insert(base_event.sender.as_str().to_owned());
|
||||
}
|
||||
|
||||
// Use limit with maximum 100
|
||||
let limit = u64::from(body.limit).min(100) as usize;
|
||||
// Use limit or else 10, with maximum 100
|
||||
let limit = usize::try_from(body.limit).unwrap_or(10).min(100);
|
||||
|
||||
let base_event = base_event.to_room_event();
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
StateEventType,
|
||||
},
|
||||
ServerName, UInt,
|
||||
uint, ServerName, UInt,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
@@ -198,8 +198,9 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
});
|
||||
}
|
||||
|
||||
// Use limit or else 10, with maximum 100
|
||||
let limit = limit.map_or(10, u64::from);
|
||||
let mut num_since = 0_u64;
|
||||
let mut num_since: u64 = 0;
|
||||
|
||||
if let Some(s) = &since {
|
||||
let mut characters = s.chars();
|
||||
@@ -363,12 +364,16 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
|
||||
all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members));
|
||||
|
||||
let total_room_count_estimate = (all_rooms.len() as u32).into();
|
||||
let total_room_count_estimate = UInt::try_from(all_rooms.len()).unwrap_or_else(|_| uint!(0));
|
||||
|
||||
let chunk: Vec<_> = all_rooms
|
||||
.into_iter()
|
||||
.skip(num_since as usize)
|
||||
.take(limit as usize)
|
||||
.skip(
|
||||
num_since
|
||||
.try_into()
|
||||
.expect("num_since should not be this high"),
|
||||
)
|
||||
.take(limit.try_into().expect("limit should not be this high"))
|
||||
.collect();
|
||||
|
||||
let prev_batch = if num_since == 0 {
|
||||
@@ -377,10 +382,15 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
Some(format!("p{num_since}"))
|
||||
};
|
||||
|
||||
let next_batch = if chunk.len() < limit as usize {
|
||||
let next_batch = if chunk.len() < limit.try_into().unwrap() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("n{}", num_since + limit))
|
||||
Some(format!(
|
||||
"n{}",
|
||||
num_since
|
||||
.checked_add(limit)
|
||||
.expect("num_since and limit should not be that large")
|
||||
))
|
||||
};
|
||||
|
||||
Ok(get_public_rooms_filtered::v3::Response {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
@@ -338,7 +339,9 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||
hash_map::Entry::Vacant(e) => {
|
||||
e.insert((Instant::now(), 1));
|
||||
},
|
||||
hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
||||
hash_map::Entry::Occupied(mut e) => {
|
||||
*e.get_mut() = (Instant::now(), e.get().1.saturating_add(1));
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -353,10 +356,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||
.get(server)
|
||||
{
|
||||
// Exponential backoff
|
||||
let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||
}
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off query from {:?}", server);
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
debug_warn,
|
||||
service::media::{FileMeta, UrlPreviewData},
|
||||
services,
|
||||
utils::{self, server_name::server_is_ours},
|
||||
utils::{
|
||||
self,
|
||||
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
||||
server_name::server_is_ours,
|
||||
},
|
||||
Error, Result, Ruma, RumaResponse,
|
||||
};
|
||||
|
||||
@@ -25,7 +29,7 @@
|
||||
const MXC_LENGTH: usize = 32;
|
||||
|
||||
/// Cache control for immutable objects
|
||||
const CACHE_CONTROL_IMMUTABLE: &str = "public, max-age=31536000, immutable";
|
||||
const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
|
||||
|
||||
const CORP_CROSS_ORIGIN: &str = "cross-origin";
|
||||
|
||||
@@ -130,7 +134,13 @@ pub(crate) async fn create_content_route(
|
||||
mxc.clone(),
|
||||
body.filename
|
||||
.as_ref()
|
||||
.map(|filename| "inline; filename=".to_owned() + filename)
|
||||
.map(|filename| {
|
||||
format!(
|
||||
"{}; filename={}",
|
||||
content_disposition_type(&body.file, &body.content_type),
|
||||
sanitise_filename(filename.to_owned())
|
||||
)
|
||||
})
|
||||
.as_deref(),
|
||||
body.content_type.as_deref(),
|
||||
&body.file,
|
||||
@@ -173,11 +183,13 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
|
||||
|
||||
Ok(get_content::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
@@ -186,7 +198,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
||||
get_remote_content(
|
||||
let response = get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
@@ -197,6 +209,20 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
||||
.map_err(|e| {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Error::BadRequest(ErrorKind::NotFound, "Remote media error.")
|
||||
})?;
|
||||
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&response.file,
|
||||
&response.content_type,
|
||||
response.content_disposition,
|
||||
));
|
||||
|
||||
Ok(get_content::v3::Response {
|
||||
file: response.file,
|
||||
content_type: response.content_type,
|
||||
content_disposition,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
||||
})
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
@@ -237,13 +263,15 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
if let Some(FileMeta {
|
||||
content_type,
|
||||
file,
|
||||
..
|
||||
content_disposition,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
|
||||
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition: Some(format!("inline; filename={}", body.filename)),
|
||||
content_disposition,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
@@ -257,13 +285,21 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(remote_content_response) => Ok(get_content_as_filename::v3::Response {
|
||||
content_disposition: Some(format!("inline: filename={}", body.filename)),
|
||||
content_type: remote_content_response.content_type,
|
||||
file: remote_content_response.file,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
}),
|
||||
Ok(remote_content_response) => {
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&remote_content_response.file,
|
||||
&remote_content_response.content_type,
|
||||
remote_content_response.content_disposition,
|
||||
));
|
||||
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
content_disposition,
|
||||
content_type: remote_content_response.content_type,
|
||||
file: remote_content_response.file,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
||||
@@ -308,7 +344,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
if let Some(FileMeta {
|
||||
content_type,
|
||||
file,
|
||||
..
|
||||
content_disposition,
|
||||
}) = services()
|
||||
.media
|
||||
.get_thumbnail(
|
||||
@@ -322,11 +358,14 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
)
|
||||
.await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
|
||||
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
content_disposition,
|
||||
})
|
||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
||||
if services()
|
||||
@@ -371,7 +410,19 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(get_thumbnail_response)
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&get_thumbnail_response.file,
|
||||
&get_thumbnail_response.content_type,
|
||||
get_thumbnail_response.content_disposition,
|
||||
));
|
||||
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file: get_thumbnail_response.file,
|
||||
content_type: get_thumbnail_response.content_type,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
||||
content_disposition,
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
@@ -429,18 +480,30 @@ async fn get_remote_content(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&content_response.file,
|
||||
&content_response.content_type,
|
||||
content_response.content_disposition,
|
||||
));
|
||||
|
||||
services()
|
||||
.media
|
||||
.create(
|
||||
None,
|
||||
mxc.to_owned(),
|
||||
content_response.content_disposition.as_deref(),
|
||||
content_disposition.as_deref(),
|
||||
content_response.content_type.as_deref(),
|
||||
&content_response.file,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(content_response)
|
||||
Ok(get_content::v3::Response {
|
||||
file: content_response.file,
|
||||
content_type: content_response.content_type,
|
||||
content_disposition,
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
async fn download_image(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
@@ -20,12 +21,13 @@
|
||||
room::{
|
||||
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
message::RoomMessageEventContent,
|
||||
},
|
||||
StateEventType, TimelineEventType,
|
||||
},
|
||||
serde::Base64,
|
||||
state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
||||
OwnedUserId, RoomId, RoomVersionId, UserId,
|
||||
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
};
|
||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||
use tokio::sync::RwLock;
|
||||
@@ -39,6 +41,91 @@
|
||||
Error, PduEvent, Result, Ruma,
|
||||
};
|
||||
|
||||
/// Checks if the room is banned in any way possible and the sender user is not
|
||||
/// an admin.
|
||||
///
|
||||
/// Performs automatic deactivation if `auto_deactivate_banned_room_attempts` is
|
||||
/// enabled
|
||||
#[tracing::instrument]
|
||||
async fn banned_room_check(user_id: &UserId, room_id: Option<&RoomId>, server_name: Option<&ServerName>) -> Result<()> {
|
||||
if !services().users.is_admin(user_id)? {
|
||||
if let Some(room_id) = room_id {
|
||||
if services().rooms.metadata.is_banned(room_id)?
|
||||
|| services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&room_id.server_name().unwrap().to_owned())
|
||||
{
|
||||
warn!(
|
||||
"User {user_id} who is not an admin attempted to send an invite for or attempted to join a banned \
|
||||
room or banned room server name: {room_id}."
|
||||
);
|
||||
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.auto_deactivate_banned_room_attempts
|
||||
{
|
||||
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
)))
|
||||
.await;
|
||||
|
||||
// ignore errors
|
||||
leave_all_rooms(user_id).await;
|
||||
_ = services().users.deactivate_account(user_id);
|
||||
}
|
||||
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This room is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
} else if let Some(server_name) = server_name {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server_name.to_owned())
|
||||
{
|
||||
warn!(
|
||||
"User {user_id} who is not an admin tried joining a room which has the server name {server_name} \
|
||||
that is globally forbidden. Rejecting.",
|
||||
);
|
||||
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.auto_deactivate_banned_room_attempts
|
||||
{
|
||||
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
)))
|
||||
.await;
|
||||
|
||||
// ignore errors
|
||||
leave_all_rooms(user_id).await;
|
||||
_ = services().users.deactivate_account(user_id);
|
||||
}
|
||||
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
|
||||
///
|
||||
/// Tries to join the sender user into a room.
|
||||
@@ -52,32 +139,7 @@ pub(crate) async fn join_room_by_id_route(
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This room is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room ID {} which has a server name that is globally forbidden. \
|
||||
Rejecting.",
|
||||
body.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
}
|
||||
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
|
||||
|
||||
// There is no body.server_name for /roomId/join
|
||||
let mut servers = services()
|
||||
@@ -130,31 +192,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
||||
Ok(room_id) => {
|
||||
if services().rooms.metadata.is_banned(&room_id)? && !services().users.is_admin(sender_user)? {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This room is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(server) = room_id.server_name() {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room ID {room_id} which has a server name that is globally \
|
||||
forbidden. Rejecting.",
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
}
|
||||
banned_room_check(sender_user, Some(&room_id), room_id.server_name()).await?;
|
||||
|
||||
let mut servers = body.server_name.clone();
|
||||
servers.extend(
|
||||
@@ -185,69 +223,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
(servers, room_id)
|
||||
},
|
||||
Err(room_alias) => {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&room_alias.server_name().to_owned())
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room alias {room_alias} which has a server name that is \
|
||||
globally forbidden. Rejecting.",
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
|
||||
|
||||
if services().rooms.metadata.is_banned(&response.room_id)? && !services().users.is_admin(sender_user)? {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This room is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&room_alias.server_name().to_owned())
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which the alias has a \
|
||||
server name that is globally forbidden. Rejecting.",
|
||||
&response.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(server) = response.room_id.server_name() {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which has a server \
|
||||
name that is globally forbidden. Rejecting.",
|
||||
&response.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
}
|
||||
banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name())).await?;
|
||||
|
||||
let mut servers = body.server_name;
|
||||
servers.extend(response.servers);
|
||||
@@ -320,30 +298,7 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
|
||||
));
|
||||
}
|
||||
|
||||
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
|
||||
info!(
|
||||
"Local user {} who is not an admin attempted to send an invite for banned room {}.",
|
||||
&sender_user, &body.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This room is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
}
|
||||
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
|
||||
|
||||
if let invite_user::v3::InvitationRecipient::UserId {
|
||||
user_id,
|
||||
@@ -362,15 +317,6 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
|
||||
pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if let Ok(true) = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_left(sender_user, &body.room_id)
|
||||
{
|
||||
info!("{} is not in room {}", &body.user_id, &body.room_id);
|
||||
return Ok(kick_user::v3::Response {});
|
||||
}
|
||||
|
||||
let mut event: RoomMemberEventContent = serde_json::from_str(
|
||||
services()
|
||||
.rooms
|
||||
@@ -427,17 +373,6 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
|
||||
pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if let Ok(Some(membership_event)) = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, sender_user)
|
||||
{
|
||||
if membership_event.membership == MembershipState::Ban {
|
||||
info!("{} is already banned in {}", &body.user_id, &body.room_id);
|
||||
return Ok(ban_user::v3::Response {});
|
||||
}
|
||||
}
|
||||
|
||||
let event = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
@@ -445,11 +380,11 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
.map_or(
|
||||
Ok(RoomMemberEventContent {
|
||||
membership: MembershipState::Ban,
|
||||
displayname: services().users.displayname(&body.user_id)?,
|
||||
avatar_url: services().users.avatar_url(&body.user_id)?,
|
||||
displayname: None,
|
||||
avatar_url: None,
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
blurhash: services().users.blurhash(&body.user_id)?,
|
||||
blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(),
|
||||
reason: body.reason.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
}),
|
||||
@@ -457,14 +392,8 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
serde_json::from_str(event.content.get())
|
||||
.map(|event: RoomMemberEventContent| RoomMemberEventContent {
|
||||
membership: MembershipState::Ban,
|
||||
displayname: services()
|
||||
.users
|
||||
.displayname(&body.user_id)
|
||||
.unwrap_or_default(),
|
||||
avatar_url: services()
|
||||
.users
|
||||
.avatar_url(&body.user_id)
|
||||
.unwrap_or_default(),
|
||||
displayname: None,
|
||||
avatar_url: None,
|
||||
blurhash: services().users.blurhash(&body.user_id).unwrap_or_default(),
|
||||
reason: body.reason.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
@@ -513,17 +442,6 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if let Ok(Some(membership_event)) = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, sender_user)
|
||||
{
|
||||
if membership_event.membership != MembershipState::Ban {
|
||||
info!("{} is already unbanned in {}", &body.user_id, &body.room_id);
|
||||
return Ok(unban_user::v3::Response {});
|
||||
}
|
||||
}
|
||||
|
||||
let mut event: RoomMemberEventContent = serde_json::from_str(
|
||||
services()
|
||||
.rooms
|
||||
@@ -1301,8 +1219,8 @@ async fn make_join_request(
|
||||
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
|
||||
let mut make_join_response_and_server = Err(Error::BadServerResponse("No server available to assist in joining."));
|
||||
|
||||
let mut make_join_counter = 0;
|
||||
let mut incompatible_room_version_count = 0;
|
||||
let mut make_join_counter: u16 = 0;
|
||||
let mut incompatible_room_version_count: u8 = 0;
|
||||
|
||||
for remote_server in servers {
|
||||
if server_is_ours(remote_server) {
|
||||
@@ -1322,7 +1240,7 @@ async fn make_join_request(
|
||||
.await;
|
||||
|
||||
trace!("make_join response: {:?}", make_join_response);
|
||||
make_join_counter += 1;
|
||||
make_join_counter = make_join_counter.saturating_add(1);
|
||||
|
||||
if let Err(ref e) = make_join_response {
|
||||
trace!("make_join ErrorKind string: {:?}", e.error_code().to_string());
|
||||
@@ -1336,7 +1254,7 @@ async fn make_join_request(
|
||||
.to_string()
|
||||
.contains("M_UNSUPPORTED_ROOM_VERSION")
|
||||
{
|
||||
incompatible_room_version_count += 1;
|
||||
incompatible_room_version_count = incompatible_room_version_count.saturating_add(1);
|
||||
}
|
||||
|
||||
if incompatible_room_version_count > 15 {
|
||||
@@ -1393,7 +1311,9 @@ async fn validate_and_add_event_id(
|
||||
Entry::Vacant(e) => {
|
||||
e.insert((Instant::now(), 1));
|
||||
},
|
||||
Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
||||
Entry::Occupied(mut e) => {
|
||||
*e.get_mut() = (Instant::now(), e.get().1.saturating_add(1));
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1405,10 +1325,8 @@ async fn validate_and_add_event_id(
|
||||
.get(&event_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||
}
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off from {}", event_id);
|
||||
@@ -1605,8 +1523,9 @@ pub(crate) async fn invite_helper(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Make a user leave all their joined rooms
|
||||
pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||
// Make a user leave all their joined rooms, forgets all rooms, and ignores
|
||||
// errors
|
||||
pub(crate) async fn leave_all_rooms(user_id: &UserId) {
|
||||
let all_rooms = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -1626,10 +1545,9 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||
};
|
||||
|
||||
// ignore errors
|
||||
_ = services().rooms.state_cache.forget(&room_id, user_id);
|
||||
_ = leave_room(user_id, &room_id, None).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
||||
|
||||
@@ -144,7 +144,7 @@ pub(crate) async fn get_message_events_route(
|
||||
.lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)
|
||||
.await?;
|
||||
|
||||
let limit = u64::from(body.limit).min(100) as usize;
|
||||
let limit = usize::try_from(body.limit).unwrap_or(10).min(100);
|
||||
|
||||
let next_token;
|
||||
|
||||
@@ -159,8 +159,9 @@ pub(crate) async fn get_message_events_route(
|
||||
.timeline
|
||||
.pdus_after(sender_user, &body.room_id, from)?
|
||||
.filter_map(Result::ok) // Filter out buggy events
|
||||
.filter(|(_, pdu)| contains_url_filter(pdu, &body.filter))
|
||||
.filter(|(_, pdu)| visibility_filter(pdu, sender_user, &body.room_id))
|
||||
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)
|
||||
|
||||
})
|
||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||
.take(limit)
|
||||
.collect();
|
||||
@@ -205,8 +206,7 @@ pub(crate) async fn get_message_events_route(
|
||||
.timeline
|
||||
.pdus_until(sender_user, &body.room_id, from)?
|
||||
.filter_map(Result::ok) // Filter out buggy events
|
||||
.filter(|(_, pdu)| contains_url_filter(pdu, &body.filter))
|
||||
.filter(|(_, pdu)| visibility_filter(pdu, sender_user, &body.room_id))
|
||||
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)})
|
||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||
.take(limit)
|
||||
.collect();
|
||||
|
||||
@@ -49,9 +49,20 @@ pub(crate) async fn get_presence_route(body: Ruma<get_presence::v3::Request>) ->
|
||||
}
|
||||
|
||||
if let Some(presence) = presence_event {
|
||||
let status_msg = if presence
|
||||
.content
|
||||
.status_msg
|
||||
.as_ref()
|
||||
.is_some_and(String::is_empty)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
presence.content.status_msg
|
||||
};
|
||||
|
||||
Ok(get_presence::v3::Response {
|
||||
// TODO: Should ruma just use the presenceeventcontent type here?
|
||||
status_msg: presence.content.status_msg,
|
||||
status_msg,
|
||||
currently_active: presence.content.currently_active,
|
||||
last_active_ago: presence
|
||||
.content
|
||||
|
||||
@@ -91,12 +91,12 @@ fn is_report_valid(
|
||||
));
|
||||
}
|
||||
|
||||
if services()
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&pdu.room_id)
|
||||
.filter_map(Result::ok)
|
||||
.any(|user_id| user_id != *sender_user)
|
||||
.any(|user_id| user_id == *sender_user)
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::NotFound,
|
||||
|
||||
@@ -801,7 +801,15 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
||||
.map_err(|_| Error::bad_database("Invalid room event in database."))?;
|
||||
|
||||
// Setting events_default and invite to the greater of 50 and users_default + 1
|
||||
let new_level = max(int!(50), power_levels_event_content.users_default + int!(1));
|
||||
let new_level = max(
|
||||
int!(50),
|
||||
power_levels_event_content
|
||||
.users_default
|
||||
.checked_add(int!(1))
|
||||
.ok_or_else(|| {
|
||||
Error::BadRequest(ErrorKind::BadJson, "users_default power levels event content is not valid")
|
||||
})?,
|
||||
);
|
||||
power_levels_event_content.events_default = new_level;
|
||||
power_levels_event_content.invite = new_level;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
events::AnyStateEvent,
|
||||
serde::Raw,
|
||||
OwnedRoomId,
|
||||
uint, OwnedRoomId,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
@@ -39,7 +39,12 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
||||
});
|
||||
|
||||
// Use limit or else 10, with maximum 100
|
||||
let limit = filter.limit.map_or(10, u64::from).min(100) as usize;
|
||||
let limit: usize = filter
|
||||
.limit
|
||||
.unwrap_or_else(|| uint!(10))
|
||||
.try_into()
|
||||
.unwrap_or(10)
|
||||
.min(100);
|
||||
|
||||
let mut room_states: BTreeMap<OwnedRoomId, Vec<Raw<AnyStateEvent>>> = BTreeMap::new();
|
||||
|
||||
@@ -106,14 +111,16 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
||||
}
|
||||
}
|
||||
|
||||
let skip = match body.next_batch.as_ref().map(|s| s.parse()) {
|
||||
let skip: usize = match body.next_batch.as_ref().map(|s| s.parse()) {
|
||||
Some(Ok(s)) => s,
|
||||
Some(Err(_)) => return Err(Error::BadRequest(ErrorKind::InvalidParam, "Invalid next_batch token.")),
|
||||
None => 0, // Default to the start
|
||||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
for _ in 0..skip + limit {
|
||||
let next_batch: usize = skip.saturating_add(limit);
|
||||
|
||||
for _ in 0..next_batch {
|
||||
if let Some(s) = searches
|
||||
.iter_mut()
|
||||
.map(|s| (s.peek().cloned(), s))
|
||||
@@ -162,12 +169,12 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
||||
let next_batch = if results.len() < limit {
|
||||
None
|
||||
} else {
|
||||
Some((skip + limit).to_string())
|
||||
Some(next_batch.to_string())
|
||||
};
|
||||
|
||||
Ok(search_events::v3::Response::new(ResultCategories {
|
||||
room_events: ResultRoomEvents {
|
||||
count: Some((results.len() as u32).into()),
|
||||
count: Some(results.len().try_into().unwrap_or_else(|_| uint!(0))),
|
||||
groups: BTreeMap::new(), // TODO
|
||||
next_batch,
|
||||
results,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, space::get_hierarchy},
|
||||
UInt,
|
||||
uint, UInt,
|
||||
};
|
||||
|
||||
use crate::{service::rooms::spaces::PagnationToken, services, Error, Result, Ruma};
|
||||
@@ -14,10 +14,12 @@
|
||||
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let limit = body
|
||||
let limit: usize = body
|
||||
.limit
|
||||
.unwrap_or_else(|| UInt::from(10_u32))
|
||||
.min(UInt::from(100_u32));
|
||||
.unwrap_or_else(|| uint!(10))
|
||||
.try_into()
|
||||
.unwrap_or(10)
|
||||
.min(100);
|
||||
|
||||
let max_depth = body
|
||||
.max_depth
|
||||
@@ -45,9 +47,9 @@ pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>)
|
||||
.get_client_hierarchy(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
u64::from(limit) as usize,
|
||||
key.map_or(0, |token| u64::from(token.skip) as usize),
|
||||
u64::from(max_depth) as usize,
|
||||
limit,
|
||||
key.map_or(0, |token| token.skip.try_into().unwrap_or(0)),
|
||||
max_depth.try_into().unwrap_or(3),
|
||||
body.suggested_only,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -539,7 +539,7 @@ async fn handle_left_room(
|
||||
|
||||
left_state_ids.insert(leave_shortstatekey, left_event_id);
|
||||
|
||||
let mut i = 0;
|
||||
let mut i: u8 = 0;
|
||||
for (key, id) in left_state_ids {
|
||||
if full_state || since_state_ids.get(&key) != Some(&id) {
|
||||
let (event_type, state_key) = services().rooms.short.get_statekey_from_short(key)?;
|
||||
@@ -557,7 +557,7 @@ async fn handle_left_room(
|
||||
|
||||
left_state_events.push(pdu.to_sync_state_event());
|
||||
|
||||
i += 1;
|
||||
i = i.wrapping_add(1);
|
||||
if i % 100 == 0 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
@@ -705,7 +705,7 @@ async fn load_joined_room(
|
||||
// Recalculate heroes (first 5 members)
|
||||
let mut heroes = Vec::new();
|
||||
|
||||
if joined_member_count + invited_member_count <= 5 {
|
||||
if joined_member_count.saturating_add(invited_member_count) <= 5 {
|
||||
// Go through all PDUs and for each member event, check if the user is still
|
||||
// joined or invited until we have 5 or we reach the end
|
||||
|
||||
@@ -784,7 +784,7 @@ async fn load_joined_room(
|
||||
let mut state_events = Vec::new();
|
||||
let mut lazy_loaded = HashSet::new();
|
||||
|
||||
let mut i = 0;
|
||||
let mut i: u8 = 0;
|
||||
for (shortstatekey, id) in current_state_ids {
|
||||
let (event_type, state_key) = services()
|
||||
.rooms
|
||||
@@ -798,7 +798,7 @@ async fn load_joined_room(
|
||||
};
|
||||
state_events.push(pdu);
|
||||
|
||||
i += 1;
|
||||
i = i.wrapping_add(1);
|
||||
if i % 100 == 0 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
@@ -819,7 +819,7 @@ async fn load_joined_room(
|
||||
}
|
||||
state_events.push(pdu);
|
||||
|
||||
i += 1;
|
||||
i = i.wrapping_add(1);
|
||||
if i % 100 == 0 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
@@ -1416,10 +1416,14 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.ranges
|
||||
.into_iter()
|
||||
.map(|mut r| {
|
||||
r.0 =
|
||||
r.0.clamp(uint!(0), UInt::from(all_joined_rooms.len() as u32 - 1));
|
||||
r.1 =
|
||||
r.1.clamp(r.0, UInt::from(all_joined_rooms.len() as u32 - 1));
|
||||
r.0 = r.0.clamp(
|
||||
uint!(0),
|
||||
UInt::try_from(all_joined_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
|
||||
);
|
||||
r.1 = r.1.clamp(
|
||||
r.0,
|
||||
UInt::try_from(all_joined_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
|
||||
);
|
||||
let room_ids = all_joined_rooms[(u64::from(r.0) as usize)..=(u64::from(r.1) as usize)].to_vec();
|
||||
new_known_rooms.extend(room_ids.iter().cloned());
|
||||
for room_id in &room_ids {
|
||||
@@ -1592,14 +1596,13 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.collect::<Vec<_>>();
|
||||
let name = match heroes.len().cmp(&(1_usize)) {
|
||||
Ordering::Greater => {
|
||||
let firsts = heroes[1..]
|
||||
.iter()
|
||||
.map(|h| h.0.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let last = heroes[0].0.clone();
|
||||
Some(
|
||||
heroes[1..]
|
||||
.iter()
|
||||
.map(|h| h.0.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ") + " and " + &last,
|
||||
)
|
||||
Some(format!("{firsts} and {last}"))
|
||||
},
|
||||
Ordering::Equal => Some(heroes[0].0.clone()),
|
||||
Ordering::Less => None,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use ruma::api::client::{error::ErrorKind, threads::get_threads};
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, threads::get_threads},
|
||||
uint,
|
||||
};
|
||||
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
@@ -9,7 +12,8 @@ pub(crate) async fn get_threads_route(body: Ruma<get_threads::v1::Request>) -> R
|
||||
// Use limit or else 10, with maximum 100
|
||||
let limit = body
|
||||
.limit
|
||||
.and_then(|l| l.try_into().ok())
|
||||
.unwrap_or_else(|| uint!(10))
|
||||
.try_into()
|
||||
.unwrap_or(10)
|
||||
.min(100);
|
||||
|
||||
|
||||
@@ -22,14 +22,30 @@ pub(crate) async fn create_typing_event_route(
|
||||
|
||||
if let Typing::Yes(duration) = body.state {
|
||||
let duration = utils::clamp(
|
||||
duration.as_millis() as u64,
|
||||
services().globals.config.typing_client_timeout_min_s * 1000,
|
||||
services().globals.config.typing_client_timeout_max_s * 1000,
|
||||
duration.as_millis().try_into().unwrap_or(u64::MAX),
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_client_timeout_min_s
|
||||
.checked_mul(1000)
|
||||
.unwrap(),
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_client_timeout_max_s
|
||||
.checked_mul(1000)
|
||||
.unwrap(),
|
||||
);
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_add(sender_user, &body.room_id, utils::millis_since_unix_epoch() + duration)
|
||||
.typing_add(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
utils::millis_since_unix_epoch()
|
||||
.checked_add(duration)
|
||||
.expect("user typing timeout should not get this high"),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
services()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/// and don't share a room with the sender
|
||||
pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) -> Result<search_users::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let limit = u64::from(body.limit) as usize;
|
||||
let limit = usize::try_from(body.limit).unwrap_or(10); // default limit is 10
|
||||
|
||||
let mut users = services().users.iter().filter_map(|user_id| {
|
||||
// Filter out buggy users (they should not exist, but you never know...)
|
||||
|
||||
@@ -21,7 +21,9 @@ pub(crate) async fn turn_server_route(
|
||||
|
||||
let (username, password) = if !turn_secret.is_empty() {
|
||||
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
||||
SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()),
|
||||
SystemTime::now()
|
||||
.checked_add(Duration::from_secs(services().globals.turn_ttl()))
|
||||
.expect("TURN TTL should not get this high"),
|
||||
)
|
||||
.expect("time is valid");
|
||||
|
||||
|
||||
@@ -101,7 +101,9 @@ pub(crate) async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
||||
old_verify_keys: BTreeMap::new(),
|
||||
signatures: BTreeMap::new(),
|
||||
valid_until_ts: MilliSecondsSinceUnixEpoch::from_system_time(
|
||||
SystemTime::now() + Duration::from_secs(86400 * 7),
|
||||
SystemTime::now()
|
||||
.checked_add(Duration::from_secs(86400 * 7))
|
||||
.expect("valid_until_ts should not get this high"),
|
||||
)
|
||||
.expect("time is valid"),
|
||||
})
|
||||
@@ -398,8 +400,13 @@ pub(crate) async fn send_transaction_message_route(
|
||||
.is_joined(&typing.user_id, &typing.room_id)?
|
||||
{
|
||||
if typing.typing {
|
||||
let timeout = utils::millis_since_unix_epoch()
|
||||
+ services().globals.config.typing_federation_timeout_s * 1000;
|
||||
let timeout = utils::millis_since_unix_epoch().saturating_add(
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_federation_timeout_s
|
||||
.saturating_mul(1000),
|
||||
);
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
@@ -662,7 +669,7 @@ pub(crate) async fn get_missing_events_route(
|
||||
}
|
||||
|
||||
if body.earliest_events.contains(&queued_events[i]) {
|
||||
i += 1;
|
||||
i = i.saturating_add(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -671,7 +678,7 @@ pub(crate) async fn get_missing_events_route(
|
||||
&body.room_id,
|
||||
&queued_events[i],
|
||||
)? {
|
||||
i += 1;
|
||||
i = i.saturating_add(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -688,7 +695,7 @@ pub(crate) async fn get_missing_events_route(
|
||||
);
|
||||
events.push(PduEvent::convert_to_outgoing_federation_event(pdu));
|
||||
}
|
||||
i += 1;
|
||||
i = i.saturating_add(1);
|
||||
}
|
||||
|
||||
Ok(get_missing_events::v1::Response {
|
||||
|
||||
@@ -207,6 +207,8 @@ pub(crate) struct Config {
|
||||
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) auto_join_rooms: Vec<OwnedRoomId>,
|
||||
#[serde(default)]
|
||||
pub(crate) auto_deactivate_banned_room_attempts: bool,
|
||||
|
||||
#[serde(default = "default_rocksdb_log_level")]
|
||||
pub(crate) rocksdb_log_level: String,
|
||||
@@ -612,6 +614,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"Allow incoming profile lookup federation requests",
|
||||
&self.allow_profile_lookup_federation_requests.to_string(),
|
||||
),
|
||||
(
|
||||
"Auto deactivate banned room join attempts",
|
||||
&self.auto_deactivate_banned_room_attempts.to_string(),
|
||||
),
|
||||
("Notification push path", &self.notification_push_path),
|
||||
("Allow room creation", &self.allow_room_creation.to_string()),
|
||||
(
|
||||
|
||||
@@ -105,7 +105,7 @@ fn changes_since(
|
||||
|
||||
// Skip the data that's exactly at since, because we sent that last time
|
||||
let mut first_possible = prefix.clone();
|
||||
first_possible.extend_from_slice(&(since + 1).to_be_bytes());
|
||||
first_possible.extend_from_slice(&(since.saturating_add(1)).to_be_bytes());
|
||||
|
||||
for r in self
|
||||
.roomuserdataid_accountdata
|
||||
|
||||
@@ -158,18 +158,15 @@ fn memory_usage(&self) -> String {
|
||||
let max_appservice_in_room_cache = self.appservice_in_room_cache.read().unwrap().capacity();
|
||||
let max_lasttimelinecount_cache = self.lasttimelinecount_cache.lock().unwrap().capacity();
|
||||
|
||||
let mut response = format!(
|
||||
format!(
|
||||
"\
|
||||
auth_chain_cache: {auth_chain_cache} / {max_auth_chain_cache}
|
||||
our_real_users_cache: {our_real_users_cache} / {max_our_real_users_cache}
|
||||
appservice_in_room_cache: {appservice_in_room_cache} / {max_appservice_in_room_cache}
|
||||
lasttimelinecount_cache: {lasttimelinecount_cache} / {max_lasttimelinecount_cache}\n\n"
|
||||
);
|
||||
if let Ok(db_stats) = self.db.memory_usage() {
|
||||
response += &db_stats;
|
||||
}
|
||||
|
||||
response
|
||||
lasttimelinecount_cache: {lasttimelinecount_cache} / {max_lasttimelinecount_cache}\n\n
|
||||
{}",
|
||||
self.db.memory_usage().unwrap_or_default()
|
||||
)
|
||||
}
|
||||
|
||||
fn clear_caches(&self, amount: u32) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruma::{events::presence::PresenceEvent, presence::PresenceState, OwnedUserId, UInt, UserId};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
database::KeyValueDatabase,
|
||||
debug_info,
|
||||
service::{self, presence::Presence},
|
||||
services,
|
||||
utils::{self, user_id_from_bytes},
|
||||
@@ -50,13 +50,21 @@ fn set_presence(
|
||||
|
||||
// tighten for state flicker?
|
||||
if !state_changed && last_active_ts <= last_last_active_ts {
|
||||
debug!(
|
||||
debug_info!(
|
||||
"presence spam {:?} last_active_ts:{:?} <= {:?}",
|
||||
user_id, last_active_ts, last_last_active_ts
|
||||
user_id,
|
||||
last_active_ts,
|
||||
last_last_active_ts
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let status_msg = if status_msg.as_ref().is_some_and(String::is_empty) {
|
||||
None
|
||||
} else {
|
||||
status_msg
|
||||
};
|
||||
|
||||
let presence = Presence::new(
|
||||
presence_state.to_owned(),
|
||||
currently_active.unwrap_or(false),
|
||||
|
||||
@@ -23,10 +23,10 @@ fn relations_until<'a>(
|
||||
let mut current = prefix.clone();
|
||||
|
||||
let count_raw = match until {
|
||||
PduCount::Normal(x) => x - 1,
|
||||
PduCount::Normal(x) => x.saturating_sub(1),
|
||||
PduCount::Backfilled(x) => {
|
||||
current.extend_from_slice(&0_u64.to_be_bytes());
|
||||
u64::MAX - x - 1
|
||||
u64::MAX.saturating_sub(x).saturating_sub(1)
|
||||
},
|
||||
};
|
||||
current.extend_from_slice(&count_raw.to_be_bytes());
|
||||
|
||||
@@ -48,7 +48,7 @@ fn readreceipts_since<'a>(
|
||||
let prefix2 = prefix.clone();
|
||||
|
||||
let mut first_possible_edu = prefix.clone();
|
||||
first_possible_edu.extend_from_slice(&(since + 1).to_be_bytes()); // +1 so we don't send the event at since
|
||||
first_possible_edu.extend_from_slice(&(since.saturating_add(1)).to_be_bytes()); // +1 so we don't send the event at since
|
||||
|
||||
Box::new(
|
||||
self.readreceiptid_readreceipt
|
||||
|
||||
@@ -17,7 +17,7 @@ async fn state_full_ids(&self, shortstatehash: u64) -> Result<HashMap<u64, Arc<E
|
||||
.expect("there is always one layer")
|
||||
.1;
|
||||
let mut result = HashMap::new();
|
||||
let mut i = 0;
|
||||
let mut i: u8 = 0;
|
||||
for compressed in full_state.iter() {
|
||||
let parsed = services()
|
||||
.rooms
|
||||
@@ -25,7 +25,7 @@ async fn state_full_ids(&self, shortstatehash: u64) -> Result<HashMap<u64, Arc<E
|
||||
.parse_compressed_state_event(compressed)?;
|
||||
result.insert(parsed.0, parsed.1);
|
||||
|
||||
i += 1;
|
||||
i = i.wrapping_add(1);
|
||||
if i % 100 == 0 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ async fn state_full(&self, shortstatehash: u64) -> Result<HashMap<(StateEventTyp
|
||||
.1;
|
||||
|
||||
let mut result = HashMap::new();
|
||||
let mut i = 0;
|
||||
let mut i: u8 = 0;
|
||||
for compressed in full_state.iter() {
|
||||
let (_, eventid) = services()
|
||||
.rooms
|
||||
@@ -63,7 +63,7 @@ async fn state_full(&self, shortstatehash: u64) -> Result<HashMap<(StateEventTyp
|
||||
);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
i = i.wrapping_add(1);
|
||||
if i % 100 == 0 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
@@ -154,11 +154,11 @@ fn update_joined_count(&self, room_id: &RoomId) -> Result<()> {
|
||||
if user_is_local(&joined) && !services().users.is_deactivated(&joined).unwrap_or(true) {
|
||||
real_users.insert(joined);
|
||||
}
|
||||
joinedcount += 1;
|
||||
joinedcount = joinedcount.saturating_add(1);
|
||||
}
|
||||
|
||||
for _invited in self.room_members_invited(room_id).filter_map(Result::ok) {
|
||||
invitedcount += 1;
|
||||
invitedcount = invitedcount.saturating_add(1);
|
||||
}
|
||||
|
||||
self.roomid_joinedcount
|
||||
|
||||
@@ -256,7 +256,7 @@ fn pdu_count(pdu_id: &[u8]) -> Result<PduCount> {
|
||||
utils::u64_from_bytes(&pdu_id[pdu_id.len() - 2 * size_of::<u64>()..pdu_id.len() - size_of::<u64>()]);
|
||||
|
||||
if matches!(second_last_u64, Ok(0)) {
|
||||
Ok(PduCount::Backfilled(u64::MAX - last_u64))
|
||||
Ok(PduCount::Backfilled(u64::MAX.saturating_sub(last_u64)))
|
||||
} else {
|
||||
Ok(PduCount::Normal(last_u64))
|
||||
}
|
||||
@@ -275,22 +275,18 @@ fn count_to_id(room_id: &RoomId, count: PduCount, offset: u64, subtract: bool) -
|
||||
let count_raw = match count {
|
||||
PduCount::Normal(x) => {
|
||||
if subtract {
|
||||
x - offset
|
||||
x.saturating_sub(offset)
|
||||
} else {
|
||||
x + offset
|
||||
x.saturating_add(offset)
|
||||
}
|
||||
},
|
||||
PduCount::Backfilled(x) => {
|
||||
pdu_id.extend_from_slice(&0_u64.to_be_bytes());
|
||||
let num = u64::MAX - x;
|
||||
let num = u64::MAX.saturating_sub(x);
|
||||
if subtract {
|
||||
if num > 0 {
|
||||
num - offset
|
||||
} else {
|
||||
num
|
||||
}
|
||||
num.saturating_sub(offset)
|
||||
} else {
|
||||
num + offset
|
||||
num.saturating_add(offset)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -110,7 +110,8 @@ fn get_shared_rooms<'a>(
|
||||
.enumerate()
|
||||
.find(|(_, &b)| b == 0xFF)
|
||||
.ok_or_else(|| Error::bad_database("Invalid userroomid_joined in db."))?
|
||||
.0 + 1; // +1 because the room id starts AFTER the separator
|
||||
.0
|
||||
.saturating_add(1); // +1 because the room id starts AFTER the separator
|
||||
|
||||
let room_id = key[roomid_index..].to_vec();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
|
||||
events::{AnyToDeviceEvent, StateEventType},
|
||||
serde::Raw,
|
||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedDeviceKeyId,
|
||||
uint, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedDeviceKeyId,
|
||||
OwnedMxcUri, OwnedUserId, UInt, UserId,
|
||||
};
|
||||
use tracing::warn;
|
||||
@@ -414,7 +414,7 @@ fn count_one_time_keys(
|
||||
.algorithm(),
|
||||
)
|
||||
}) {
|
||||
*counts.entry(algorithm?).or_default() += UInt::from(1_u32);
|
||||
*counts.entry(algorithm?).or_default() += uint!(1);
|
||||
}
|
||||
|
||||
Ok(counts)
|
||||
@@ -561,7 +561,7 @@ fn keys_changed<'a>(
|
||||
prefix.push(0xFF);
|
||||
|
||||
let mut start = prefix.clone();
|
||||
start.extend_from_slice(&(from + 1).to_be_bytes());
|
||||
start.extend_from_slice(&(from.saturating_add(1)).to_be_bytes());
|
||||
|
||||
let to = to.unwrap_or(u64::MAX);
|
||||
|
||||
|
||||
@@ -173,13 +173,11 @@ pub(crate) async fn migrations(db: &KeyValueDatabase, config: &Config) -> Result
|
||||
let mut current_sstatehash: Option<u64> = None;
|
||||
let mut current_room = None;
|
||||
let mut current_state = HashSet::new();
|
||||
let mut counter = 0;
|
||||
|
||||
let mut handle_state = |current_sstatehash: u64,
|
||||
current_room: &RoomId,
|
||||
current_state: HashSet<_>,
|
||||
last_roomstates: &mut HashMap<_, _>| {
|
||||
counter += 1;
|
||||
let handle_state = |current_sstatehash: u64,
|
||||
current_room: &RoomId,
|
||||
current_state: HashSet<_>,
|
||||
last_roomstates: &mut HashMap<_, _>| {
|
||||
let last_roomsstatehash = last_roomstates.get(current_room);
|
||||
|
||||
let states_parents = last_roomsstatehash.map_or_else(
|
||||
|
||||
@@ -340,6 +340,7 @@ pub(crate) async fn load_or_create(config: Config, tracing_reload_handler: LogLe
|
||||
|
||||
roomid_inviteviaservers: builder.open_tree("roomid_inviteviaservers")?,
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
auth_chain_cache: Mutex::new(LruCache::new(
|
||||
(f64::from(config.auth_chain_cache_capacity) * config.conduit_cache_capacity_modifier) as usize,
|
||||
)),
|
||||
@@ -401,19 +402,8 @@ fn check_db_setup(config: &Config) -> Result<()> {
|
||||
let sqlite_exists = path.join("conduit.db").exists();
|
||||
let rocksdb_exists = path.join("IDENTITY").exists();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
if sqlite_exists {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if rocksdb_exists {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if count > 1 {
|
||||
warn!("Multiple databases at database_path detected");
|
||||
return Ok(());
|
||||
if sqlite_exists && rocksdb_exists {
|
||||
return Err(Error::bad_config("Multiple databases at database_path detected."));
|
||||
}
|
||||
|
||||
if sqlite_exists && config.database_backend != "sqlite" {
|
||||
|
||||
@@ -35,7 +35,11 @@ pub(crate) struct Engine {
|
||||
impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
fn open(config: &Config) -> Result<Self> {
|
||||
let cache_capacity_bytes = config.db_cache_capacity_mb * 1024.0 * 1024.0;
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
let row_cache_capacity_bytes = (cache_capacity_bytes * 0.50) as usize;
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
let col_cache_capacity_bytes = (cache_capacity_bytes * 0.50) as usize;
|
||||
|
||||
let mut col_cache = HashMap::new();
|
||||
@@ -128,6 +132,7 @@ fn uncork(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
fn memory_usage(&self) -> Result<String> {
|
||||
let mut res = String::new();
|
||||
let stats = rust_rocksdb::perf::get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.row_cache]))?;
|
||||
|
||||
@@ -145,7 +145,14 @@ pub(crate) fn cf_options(cfg: &Config, name: &str, mut opts: Options, cache: &mu
|
||||
cache_size(cfg, cfg.statekeyshort_cache_capacity, 1024),
|
||||
),
|
||||
|
||||
"pduid_pdu" => set_table_with_new_cache(&mut opts, cfg, cache, name, cfg.pdu_cache_capacity as usize * 1536),
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
"pduid_pdu" => set_table_with_new_cache(
|
||||
&mut opts,
|
||||
cfg,
|
||||
cache,
|
||||
name,
|
||||
(cfg.pdu_cache_capacity as usize).saturating_mul(1536),
|
||||
),
|
||||
|
||||
"eventid_outlierpdu" => set_table_with_shared_cache(&mut opts, cfg, cache, name, "pduid_pdu"),
|
||||
|
||||
@@ -309,7 +316,10 @@ 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;
|
||||
|
||||
ents as usize * entity_size
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
(ents as usize)
|
||||
.checked_mul(entity_size)
|
||||
.expect("cache capacity size is too large")
|
||||
}
|
||||
|
||||
fn table_options(_config: &Config) -> BlockBasedOptions {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
struct PreparedStatementIterator<'a> {
|
||||
iterator: Box<dyn Iterator<Item = TupleOfBytes> + 'a>,
|
||||
_statement_ref: NonAliasingBox<rusqlite::Statement<'a>>,
|
||||
_statement_ref: AliasableBox<rusqlite::Statement<'a>>,
|
||||
}
|
||||
|
||||
impl Iterator for PreparedStatementIterator<'_> {
|
||||
@@ -30,16 +30,25 @@ impl Iterator for PreparedStatementIterator<'_> {
|
||||
fn next(&mut self) -> Option<Self::Item> { self.iterator.next() }
|
||||
}
|
||||
|
||||
struct NonAliasingBox<T>(*mut T);
|
||||
impl<T> Drop for NonAliasingBox<T> {
|
||||
struct AliasableBox<T>(*mut T);
|
||||
impl<T> Drop for AliasableBox<T> {
|
||||
fn drop(&mut self) {
|
||||
// TODO: figure out why this is necessary, but also this is sqlite so dont think
|
||||
// i care that much. i tried checking commit history but couldn't find out why
|
||||
// this was done.
|
||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||
unsafe {
|
||||
_ = Box::from_raw(self.0);
|
||||
};
|
||||
// SAFETY: This is cursed and relies on non-local reasoning.
|
||||
//
|
||||
// In order for this to be safe:
|
||||
//
|
||||
// * All aliased references to this value must have been dropped first, for
|
||||
// example by coming after its referrers in struct fields, because struct
|
||||
// fields are automatically dropped in order from top to bottom in the absence
|
||||
// of an explicit Drop impl. Otherwise, the referrers may read into
|
||||
// deallocated memory.
|
||||
// * This type must not be copyable or cloneable. Otherwise, double-free can
|
||||
// occur.
|
||||
//
|
||||
// These conditions are met, but again, note that changing safe code in
|
||||
// this module can result in unsoundness if any of these constraints are
|
||||
// violated.
|
||||
unsafe { drop(Box::from_raw(self.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,8 +102,13 @@ fn open(config: &Config) -> Result<Self> {
|
||||
// 2. divide by permanent connections + permanent iter connections + write
|
||||
// connection
|
||||
// 3. round down to nearest integer
|
||||
let cache_size_per_thread: u32 =
|
||||
((config.db_cache_capacity_mb * 1024.0) / ((num_cpus::get().max(1) * 2) + 1) as f64) as u32;
|
||||
#[allow(
|
||||
clippy::as_conversions,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_sign_loss
|
||||
)]
|
||||
let cache_size_per_thread = ((config.db_cache_capacity_mb * 1024.0) / ((num_cpus::get() as f64 * 2.0) + 1.0)) as u32;
|
||||
|
||||
let writer = Mutex::new(Engine::prepare_conn(&path, cache_size_per_thread)?);
|
||||
|
||||
@@ -161,7 +175,7 @@ fn iter_with_guard<'a>(&'a self, guard: &'a Connection) -> Box<dyn Iterator<Item
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let statement_ref = NonAliasingBox(statement);
|
||||
let statement_ref = AliasableBox(statement);
|
||||
|
||||
//let name = self.name.clone();
|
||||
|
||||
@@ -250,7 +264,7 @@ fn iter_from<'a>(&'a self, from: &[u8], backwards: bool) -> Box<dyn Iterator<Ite
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let statement_ref = NonAliasingBox(statement);
|
||||
let statement_ref = AliasableBox(statement);
|
||||
|
||||
let iterator = Box::new(
|
||||
statement
|
||||
@@ -272,7 +286,7 @@ fn iter_from<'a>(&'a self, from: &[u8], backwards: bool) -> Box<dyn Iterator<Ite
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let statement_ref = NonAliasingBox(statement);
|
||||
let statement_ref = AliasableBox(statement);
|
||||
|
||||
let iterator = Box::new(
|
||||
statement
|
||||
|
||||
@@ -47,7 +47,7 @@ pub(crate) fn wake(&self, key: &[u8]) {
|
||||
let mut watchers = self.watchers.write().unwrap();
|
||||
for prefix in triggered {
|
||||
if let Some(tx) = watchers.remove(prefix) {
|
||||
_ = tx.0.send(());
|
||||
tx.0.send(()).expect("channel should still be open");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Router,
|
||||
};
|
||||
use http::{
|
||||
header::{self, HeaderName},
|
||||
header::{self, HeaderName, HeaderValue},
|
||||
Method, StatusCode, Uri,
|
||||
};
|
||||
use ruma::api::client::{
|
||||
@@ -17,6 +17,7 @@
|
||||
use tower_http::{
|
||||
catch_panic::CatchPanicLayer,
|
||||
cors::{self, CorsLayer},
|
||||
set_header::SetResponseHeaderLayer,
|
||||
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
|
||||
ServiceBuilderExt as _,
|
||||
};
|
||||
@@ -32,6 +33,9 @@ pub(crate) async fn build(server: &Server) -> io::Result<axum::routing::IntoMake
|
||||
let base_middlewares = base_middlewares.layer(sentry_tower::NewSentryLayer::<http::Request<_>>::new_from_top());
|
||||
|
||||
let x_forwarded_for = HeaderName::from_static("x-forwarded-for");
|
||||
let permissions_policy = HeaderName::from_static("permissions-policy");
|
||||
let origin_agent_cluster = HeaderName::from_static("origin-agent-cluster"); // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster
|
||||
|
||||
let middlewares = base_middlewares
|
||||
.sensitive_headers([header::AUTHORIZATION])
|
||||
.sensitive_request_headers([x_forwarded_for].into())
|
||||
@@ -44,6 +48,33 @@ pub(crate) async fn build(server: &Server) -> io::Result<axum::routing::IntoMake
|
||||
.on_response(DefaultOnResponse::new().level(Level::DEBUG)),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(request_handle))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
origin_agent_cluster,
|
||||
HeaderValue::from_static("?1"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
header::X_CONTENT_TYPE_OPTIONS,
|
||||
HeaderValue::from_static("nosniff"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
header::X_XSS_PROTECTION,
|
||||
HeaderValue::from_static("0"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
header::X_FRAME_OPTIONS,
|
||||
HeaderValue::from_static("DENY"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
permissions_policy,
|
||||
HeaderValue::from_static("interest-cohort=(),browsing-topics=()"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
header::CONTENT_SECURITY_POLICY,
|
||||
HeaderValue::from_static(
|
||||
"sandbox; default-src 'none'; font-src 'none'; script-src 'none'; plugin-types application/pdf; \
|
||||
style-src 'unsafe-inline'; object-src 'self'; frame-ancesors 'none';",
|
||||
),
|
||||
))
|
||||
.layer(cors_layer(server))
|
||||
.layer(DefaultBodyLimit::max(
|
||||
server
|
||||
@@ -130,10 +161,13 @@ fn request_result_log(method: &Method, uri: &Uri, result: &axum::response::Respo
|
||||
}
|
||||
}
|
||||
|
||||
/// Cross-Origin-Resource-Sharing header as defined by spec:
|
||||
/// <https://spec.matrix.org/latest/client-server-api/#web-browser-clients>
|
||||
fn cors_layer(_server: &Server) -> CorsLayer {
|
||||
const METHODS: [Method; 6] = [
|
||||
const METHODS: [Method; 7] = [
|
||||
Method::GET,
|
||||
Method::HEAD,
|
||||
Method::PATCH,
|
||||
Method::POST,
|
||||
Method::PUT,
|
||||
Method::DELETE,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
pub(crate) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let appservice_config = body[1..body.len() - 1].join("\n");
|
||||
let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
||||
match parsed_config {
|
||||
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
||||
|
||||
@@ -121,7 +121,7 @@ pub(crate) async fn get_remote_pdu_list(
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let list = body
|
||||
.clone()
|
||||
.drain(1..body.len() - 1)
|
||||
.drain(1..body.len().checked_sub(1).unwrap())
|
||||
.filter_map(|pdu| EventId::parse(pdu).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -381,7 +381,7 @@ pub(crate) async fn change_log_level(
|
||||
|
||||
pub(crate) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let string = body[1..body.len() - 1].join("\n");
|
||||
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(mut value) => {
|
||||
ruma::signatures::sign_json(
|
||||
@@ -404,7 +404,7 @@ pub(crate) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent
|
||||
|
||||
pub(crate) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let string = body[1..body.len() - 1].join("\n");
|
||||
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(value) => {
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
|
||||
@@ -139,14 +139,19 @@ pub(crate) async fn delete(
|
||||
|
||||
pub(crate) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let mxc_list = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
|
||||
let mxc_list = body
|
||||
.clone()
|
||||
.drain(1..body.len().checked_sub(1).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut mxc_deletion_count = 0;
|
||||
let mut mxc_deletion_count: usize = 0;
|
||||
|
||||
for mxc in mxc_list {
|
||||
debug!("Deleting MXC {mxc} in bulk");
|
||||
services().media.delete(mxc.to_owned()).await?;
|
||||
mxc_deletion_count += 1;
|
||||
mxc_deletion_count = mxc_deletion_count
|
||||
.checked_add(1)
|
||||
.expect("mxc_deletion_count should not get this high");
|
||||
}
|
||||
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
|
||||
@@ -22,7 +22,7 @@ pub(crate) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMe
|
||||
|
||||
let rooms = rooms
|
||||
.into_iter()
|
||||
.skip(page.saturating_sub(1) * PAGE_SIZE)
|
||||
.skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE))
|
||||
.take(PAGE_SIZE)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ pub(crate) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) ->
|
||||
|
||||
let rooms = rooms
|
||||
.into_iter()
|
||||
.skip(page.saturating_sub(1) * PAGE_SIZE)
|
||||
.skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE))
|
||||
.take(PAGE_SIZE)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ pub(crate) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventCon
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Services:\n{response0}\n\nDatabase:\n{response1}\n{}",
|
||||
if !response2.is_empty() {
|
||||
"Allocator:\n{response2}"
|
||||
format!("Allocator:\n {response2}")
|
||||
} else {
|
||||
""
|
||||
String::new()
|
||||
}
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -61,8 +61,13 @@ pub(crate) async fn create(
|
||||
|
||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||
// content is set to the user's display name with a space before it
|
||||
if !services().globals.new_user_displayname_suffix().is_empty() {
|
||||
displayname.push_str(&(" ".to_owned() + services().globals.new_user_displayname_suffix()));
|
||||
if !services()
|
||||
.globals
|
||||
.config
|
||||
.new_user_displayname_suffix
|
||||
.is_empty()
|
||||
{
|
||||
_ = write!(displayname, " {}", services().globals.config.new_user_displayname_suffix);
|
||||
}
|
||||
|
||||
services()
|
||||
@@ -162,7 +167,7 @@ pub(crate) async fn deactivate(
|
||||
services().users.deactivate_account(&user_id)?;
|
||||
|
||||
if leave_rooms {
|
||||
leave_all_rooms(&user_id).await?;
|
||||
leave_all_rooms(&user_id).await;
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -234,7 +239,7 @@ pub(crate) async fn deactivate_all(body: Vec<&str>, leave_rooms: bool, force: bo
|
||||
}
|
||||
}
|
||||
|
||||
let mut deactivation_count = 0;
|
||||
let mut deactivation_count: u16 = 0;
|
||||
let mut admins = Vec::new();
|
||||
|
||||
if !force {
|
||||
@@ -271,13 +276,13 @@ pub(crate) async fn deactivate_all(body: Vec<&str>, leave_rooms: bool, force: bo
|
||||
}
|
||||
|
||||
if services().users.deactivate_account(user_id).is_ok() {
|
||||
deactivation_count += 1;
|
||||
deactivation_count = deactivation_count.saturating_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
if leave_rooms {
|
||||
for &user_id in &user_ids {
|
||||
_ = leave_all_rooms(user_id).await;
|
||||
leave_all_rooms(user_id).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +316,7 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
|
||||
},
|
||||
};
|
||||
|
||||
if user_id.server_name() != services().globals.server_name() {
|
||||
if !user_is_local(&user_id) {
|
||||
return Ok(RoomMessageEventContent::text_plain("User does not belong to our server."));
|
||||
}
|
||||
|
||||
@@ -335,7 +340,8 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
|
||||
rooms.reverse();
|
||||
|
||||
let output_plain = format!(
|
||||
"Rooms {user_id} Joined:\n{}",
|
||||
"Rooms {user_id} Joined ({}):\n{}",
|
||||
rooms.len(),
|
||||
rooms
|
||||
.iter()
|
||||
.map(|(id, members, name)| format!("{id}\tMembers: {members}\tName: {name}"))
|
||||
@@ -343,8 +349,9 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
|
||||
.join("\n")
|
||||
);
|
||||
let output_html = format!(
|
||||
"<table><caption>Rooms {user_id} \
|
||||
Joined</caption>\n<tr><th>id</th>\t<th>members</th>\t<th>name</th></tr>\n{}</table>",
|
||||
"<table><caption>Rooms {user_id} Joined \
|
||||
({})</caption>\n<tr><th>id</th>\t<th>members</th>\t<th>name</th></tr>\n{}</table>",
|
||||
rooms.len(),
|
||||
rooms
|
||||
.iter()
|
||||
.fold(String::new(), |mut output, (id, members, name)| {
|
||||
|
||||
@@ -89,6 +89,8 @@ pub(crate) fn new(config: &Config, resolver: &Arc<resolver::Resolver>) -> Client
|
||||
fn base(config: &Config) -> Result<reqwest::ClientBuilder> {
|
||||
let version = conduwuit_version();
|
||||
|
||||
let user_agent = format!("Conduwuit/{version}");
|
||||
|
||||
let mut builder = reqwest::Client::builder()
|
||||
.hickory_dns(true)
|
||||
.connect_timeout(Duration::from_secs(config.request_conn_timeout))
|
||||
@@ -96,7 +98,7 @@ fn base(config: &Config) -> Result<reqwest::ClientBuilder> {
|
||||
.timeout(Duration::from_secs(config.request_total_timeout))
|
||||
.pool_idle_timeout(Duration::from_secs(config.request_idle_timeout))
|
||||
.pool_max_idle_per_host(config.request_idle_per_host.into())
|
||||
.user_agent("Conduwuit".to_owned() + "/" + &version)
|
||||
.user_agent(user_agent)
|
||||
.redirect(redirect::Policy::limited(6))
|
||||
.connection_verbose(true);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(crate) struct Hooked {
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
pub(crate) fn new(config: &Config) -> Self {
|
||||
let (sys_conf, mut opts) = hickory_resolver::system_conf::read_system_conf()
|
||||
.map_err(|e| {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FileMeta {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) content_disposition: Option<String>,
|
||||
pub(crate) content_type: Option<String>,
|
||||
pub(crate) file: Vec<u8>,
|
||||
@@ -187,7 +188,9 @@ pub(crate) async fn delete_all_remote_media_at_after_time(&self, time: String) -
|
||||
Ok(duration) => {
|
||||
debug!("Parsed duration: {:?}", duration);
|
||||
debug!("System time now: {:?}", SystemTime::now());
|
||||
SystemTime::now() - duration
|
||||
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);
|
||||
|
||||
@@ -40,6 +40,7 @@ pub(crate) struct Services<'a> {
|
||||
}
|
||||
|
||||
impl Services<'_> {
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
pub(crate) fn build<
|
||||
D: appservice::Data
|
||||
+ pusher::Data
|
||||
|
||||
@@ -188,13 +188,14 @@ pub(crate) fn get_actions<'a>(
|
||||
|
||||
let ctx = PushConditionRoomCtx {
|
||||
room_id: room_id.to_owned(),
|
||||
member_count: UInt::from(
|
||||
member_count: UInt::try_from(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(room_id)?
|
||||
.unwrap_or(1) as u32,
|
||||
),
|
||||
.unwrap_or(1),
|
||||
)
|
||||
.unwrap_or_else(|_| uint!(0)),
|
||||
user_id: user.to_owned(),
|
||||
user_display_name: services()
|
||||
.users
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
pub(crate) use data::Data;
|
||||
use ruma::{api::client::error::ErrorKind, EventId, RoomId};
|
||||
use tracing::{debug, error, warn};
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{services, Error, Result};
|
||||
use crate::{services, utils::debug_slice_truncated, Error, Result};
|
||||
|
||||
pub(crate) struct Service {
|
||||
pub(crate) db: &'static dyn Data,
|
||||
@@ -30,7 +30,7 @@ pub(crate) async fn event_ids_iter<'a>(
|
||||
.filter_map(move |sid| services().rooms.short.get_eventid_from_short(sid).ok()))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip(self), fields(starting_events = debug_slice_truncated(starting_events, 5)))]
|
||||
pub(crate) async fn get_auth_chain(&self, room_id: &RoomId, starting_events: &[&EventId]) -> Result<Vec<u64>> {
|
||||
const NUM_BUCKETS: usize = 50; //TODO: change possible w/o disrupting db?
|
||||
const BUCKET: BTreeSet<(u64, &EventId)> = BTreeSet::new();
|
||||
@@ -68,6 +68,7 @@ pub(crate) async fn get_auth_chain(&self, room_id: &RoomId, starting_events: &[&
|
||||
.auth_chain
|
||||
.get_cached_eventid_authchain(&chunk_key)?
|
||||
{
|
||||
trace!("Found cache entry for whole chunk");
|
||||
full_auth_chain.extend(cached.iter().copied());
|
||||
hits += 1;
|
||||
continue;
|
||||
@@ -82,6 +83,7 @@ pub(crate) async fn get_auth_chain(&self, room_id: &RoomId, starting_events: &[&
|
||||
.auth_chain
|
||||
.get_cached_eventid_authchain(&[sevent_id])?
|
||||
{
|
||||
trace!(?event_id, "Found cache entry for event");
|
||||
chunk_cache.extend(cached.iter().copied());
|
||||
hits2 += 1;
|
||||
} else {
|
||||
@@ -132,15 +134,18 @@ pub(crate) async fn get_auth_chain(&self, room_id: &RoomId, starting_events: &[&
|
||||
Ok(full_auth_chain)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, event_id))]
|
||||
#[tracing::instrument(skip(self, room_id))]
|
||||
fn get_auth_chain_inner(&self, room_id: &RoomId, event_id: &EventId) -> Result<HashSet<u64>> {
|
||||
let mut todo = vec![Arc::from(event_id)];
|
||||
let mut found = HashSet::new();
|
||||
|
||||
while let Some(event_id) = todo.pop() {
|
||||
trace!(?event_id, "processing auth event");
|
||||
|
||||
match services().rooms.timeline.get_pdu(&event_id) {
|
||||
Ok(Some(pdu)) => {
|
||||
if pdu.room_id != room_id {
|
||||
error!(?event_id, ?pdu, "auth event for incorrect room_id");
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Evil event in db"));
|
||||
}
|
||||
for auth_event in &pdu.auth_events {
|
||||
@@ -150,6 +155,7 @@ fn get_auth_chain_inner(&self, room_id: &RoomId, event_id: &EventId) -> Result<H
|
||||
.get_or_create_shorteventid(auth_event)?;
|
||||
|
||||
if found.insert(sauthevent) {
|
||||
trace!(?event_id, ?auth_event, "adding auth event to processing queue");
|
||||
todo.push(auth_event.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ pub(crate) async fn handle_prev_pdu<'a>(
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
let duration = time.elapsed();
|
||||
|
||||
if duration < min_duration {
|
||||
debug!(
|
||||
duration = ?duration,
|
||||
@@ -1003,7 +1004,7 @@ pub(crate) fn fetch_and_handle_outliers<'a>(
|
||||
hash_map::Entry::Vacant(e) => {
|
||||
e.insert((Instant::now(), 1));
|
||||
},
|
||||
hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
||||
hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1.saturating_add(1)),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1034,10 +1035,9 @@ pub(crate) fn fetch_and_handle_outliers<'a>(
|
||||
.get(&*next_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||
}
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_elapsed_duration =
|
||||
cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
info!("Backing off from {}", next_id);
|
||||
@@ -1143,10 +1143,9 @@ pub(crate) fn fetch_and_handle_outliers<'a>(
|
||||
.get(&**next_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
let mut min_elapsed_duration = Duration::from_secs(5 * 60) * (*tries) * (*tries);
|
||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||
}
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_elapsed_duration =
|
||||
cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off from {}", next_id);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use ruma::{
|
||||
api::{client::relations::get_relating_events, Direction},
|
||||
events::{relation::RelationType, TimelineEventType},
|
||||
EventId, RoomId, UInt, UserId,
|
||||
uint, EventId, RoomId, UInt, UserId,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -57,8 +57,9 @@ pub(crate) fn paginate_relations_with_filter(
|
||||
|
||||
// Use limit or else 10, with maximum 100
|
||||
let limit = limit
|
||||
.and_then(|u| u32::try_from(u).ok())
|
||||
.map_or(10_usize, |u| u as usize)
|
||||
.unwrap_or_else(|| uint!(10))
|
||||
.try_into()
|
||||
.unwrap_or(10)
|
||||
.min(100);
|
||||
|
||||
let next_token;
|
||||
|
||||
@@ -818,7 +818,7 @@ fn into_https_string(self) -> String {
|
||||
fn into_uri_string(self) -> String {
|
||||
match self {
|
||||
Self::Literal(addr) => addr.to_string(),
|
||||
Self::Named(host, port) => host + &port,
|
||||
Self::Named(host, port) => format!("{host}{port}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
88
src/utils/content_disposition.rs
Normal file
88
src/utils/content_disposition.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use infer::MatcherType;
|
||||
|
||||
/// Returns a Content-Disposition of `attachment` or `inline`, depending on the
|
||||
/// *parsed* contents of the file uploaded via format magic keys using `infer`
|
||||
/// crate (basically libmagic without needing libmagic).
|
||||
///
|
||||
/// This forbids trusting what the client or remote server says the file is from
|
||||
/// their `Content-Type` and we try to detect it ourselves. Also returns
|
||||
/// `attachment` if the Content-Type does not match what we detected.
|
||||
///
|
||||
/// TODO: add a "strict" function for comparing the Content-Type with what we
|
||||
/// detected: `file_type.mime_type() != content_type`
|
||||
pub(crate) fn content_disposition_type(buf: &[u8], _content_type: &Option<String>) -> &'static str {
|
||||
let Some(file_type) = infer::get(buf) else {
|
||||
return "attachment";
|
||||
};
|
||||
|
||||
match file_type.matcher_type() {
|
||||
MatcherType::Image | MatcherType::Audio | MatcherType::Text | MatcherType::Video => "inline",
|
||||
_ => "attachment",
|
||||
}
|
||||
}
|
||||
|
||||
/// sanitises the file name for the Content-Disposition using
|
||||
/// `sanitize_filename` crate
|
||||
#[tracing::instrument]
|
||||
pub(crate) fn sanitise_filename(filename: String) -> String {
|
||||
let options = sanitize_filename::Options {
|
||||
truncate: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
sanitize_filename::sanitize_with_options(filename, options)
|
||||
}
|
||||
|
||||
/// creates the final Content-Disposition based on whether the filename exists
|
||||
/// or not.
|
||||
///
|
||||
/// if filename exists: `Content-Disposition: attachment/inline;
|
||||
/// filename=filename.ext` else: `Content-Disposition: attachment/inline`
|
||||
#[tracing::instrument(skip(file))]
|
||||
pub(crate) fn make_content_disposition(
|
||||
file: &[u8], content_type: &Option<String>, content_disposition: Option<String>,
|
||||
) -> String {
|
||||
let filename = content_disposition.map_or_else(String::new, |content_disposition| {
|
||||
let (_, filename) = content_disposition
|
||||
.split_once("filename=")
|
||||
.unwrap_or(("", ""));
|
||||
|
||||
if filename.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
sanitise_filename(filename.to_owned())
|
||||
}
|
||||
});
|
||||
|
||||
if !filename.is_empty() {
|
||||
// Content-Disposition: attachment/inline; filename=filename.ext
|
||||
format!("{}; filename={}", content_disposition_type(file, content_type), filename)
|
||||
} else {
|
||||
// Content-Disposition: attachment/inline
|
||||
String::from(content_disposition_type(file, content_type))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn string_sanitisation() {
|
||||
const SAMPLE: &str =
|
||||
"🏳️⚧️this\\r\\n įs \r\\n ä \\r\nstrïng 🥴that\n\r ../../../../../../../may be\r\n malicious🏳️⚧️";
|
||||
const SANITISED: &str = "🏳️⚧️thisrn įs n ä rstrïng 🥴that ..............may be malicious🏳️⚧️";
|
||||
|
||||
let options = sanitize_filename::Options {
|
||||
windows: true,
|
||||
truncate: true,
|
||||
replacement: "",
|
||||
};
|
||||
|
||||
// cargo test -- --nocapture
|
||||
println!("{}", SAMPLE);
|
||||
println!("{}", sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
|
||||
println!("{:?}", SAMPLE);
|
||||
println!("{:?}", sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
|
||||
|
||||
assert_eq!(SANITISED, sanitize_filename::sanitize_with_options(SAMPLE, options.clone()));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub(crate) mod clap;
|
||||
pub(crate) mod content_disposition;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod server_name;
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
pub(crate) fn clamp<T: Ord>(val: T, min: T, max: T) -> T { cmp::min(cmp::max(val, min), max) }
|
||||
|
||||
#[allow(clippy::as_conversions)]
|
||||
pub(crate) fn millis_since_unix_epoch() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
@@ -196,10 +198,60 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// git commit hashes.
|
||||
pub(crate) fn conduwuit_version() -> String {
|
||||
match option_env!("CONDUWUIT_VERSION_EXTRA") {
|
||||
Some(extra) => format!("{} ({})", env!("CARGO_PKG_VERSION"), extra),
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||
Some(extra) => format!("{} ({})", env!("CARGO_PKG_VERSION"), extra),
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => env!("CARGO_PKG_VERSION").to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug-formats the given slice, but only up to the first `max_len` elements.
|
||||
/// Any further elements are replaced by an ellipsis.
|
||||
///
|
||||
/// See also [`debug_slice_truncated()`],
|
||||
pub(crate) struct TruncatedDebugSlice<'a, T> {
|
||||
inner: &'a [T],
|
||||
max_len: usize,
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for TruncatedDebugSlice<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.inner.len() <= self.max_len {
|
||||
write!(f, "{:?}", self.inner)
|
||||
} else {
|
||||
f.debug_list()
|
||||
.entries(&self.inner[..self.max_len])
|
||||
.entry(&"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`TruncatedDebugSlice`]. Useful for `#[instrument]`:
|
||||
///
|
||||
/// ```
|
||||
/// #[tracing::instrument(fields(
|
||||
/// foos = debug_slice_truncated(foos, N)
|
||||
/// ))]
|
||||
/// ```
|
||||
pub(crate) fn debug_slice_truncated<T: fmt::Debug>(
|
||||
slice: &[T], max_len: usize,
|
||||
) -> tracing::field::DebugValue<TruncatedDebugSlice<'_, T>> {
|
||||
tracing::field::debug(TruncatedDebugSlice {
|
||||
inner: slice,
|
||||
max_len,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user