Compare commits

..

15 Commits

Author SHA1 Message Date
strawberry 0188a01871 feat(media): add /_matrix/media/v1/create endpoint
https://spec.matrix.org/latest/client-server-api/#post_matrixmediav1create

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-13 23:16:05 -04:00
strawberry 434b5118cc media: return our detected MIME type for Content-Type
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 15:54:22 -04:00
strawberry 4185a33747 fix: we should be checking for xml MIME type instead
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 15:54:22 -04:00
strawberry 829307c83b disallow svg MIME types to be inline Content-Disposition
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 11:37:50 -04:00
strawberry 2bd7a92256 complement: add -tags="conduwuit_blacklist"
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry bfa33f8713 unpin rust-rocksdb version
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 040cf29051 ci: add lix binary cache, update .gitlab-ci file
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 80bc1cd78a ci: output 100 failure summary lines instead of 50
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 78994deb1e nix: simplify isDarwin lib check
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 714b3e7144 s/nix/lix in a couple places
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 1cd57f40f6 upload complement OCI image from CI, document where it can be found, use main instead of dev for tag
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry da9a0eb77b docs: fix broken systemd unit link
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry 37b2c90e62 chore(nix): bump flake
• Updated input 'complement':
    'github:matrix-org/complement/891d18872c153d39a9ce63b545045efddb845738' (2024-04-30)
  → 'github:matrix-org/complement/370a014dca0f720614e0c8f68b9a3e66ecf7f516' (2024-05-02)
• Updated input 'crane':
    'github:ipetkov/crane/f6c6a2fb1b8bd9b65d65ca9342dd0eb180a63f11' (2024-04-21)
  → 'github:ipetkov/crane/27025ab71bdca30e7ed0a16c88fd74c5970fc7f5' (2024-05-09)
• Updated input 'fenix':
    'github:nix-community/fenix/73124e1356bde9411b163d636b39fe4804b7ca45' (2024-05-01)
  → 'github:nix-community/fenix/297c756ba6249d483c1dafe42378560458842173' (2024-05-10)
• Updated input 'fenix/rust-analyzer-src':
    'github:rust-lang/rust-analyzer/55d9a533b309119c8acd13061581b43ae8840823' (2024-04-20)
  → 'github:rust-lang/rust-analyzer/5bf2f85c8054d80424899fa581db1b192230efb5' (2024-05-09)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/5c24cf2f0a12ad855f444c30b2421d044120c66f' (2024-04-19)
  → 'github:NixOS/nixpkgs/f1010e0469db743d14519a1efd37e23f8513d714' (2024-05-09)

Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry ba150a1185 nix: stop running unnecessary cargo check on builds
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
strawberry ddce9496f2 nix: fix building rust on macOS (Security apple_sdk framework)
Signed-off-by: strawberry <strawberry@puppygock.gay>
2024-05-12 03:05:34 -04:00
17 changed files with 165 additions and 56 deletions
+11 -3
View File
@@ -75,8 +75,8 @@ jobs:
- name: Apply Nix binary cache configuration - name: Apply Nix binary cache configuration
run: | run: |
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems
extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg= conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw= extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg= conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
EOF EOF
- name: Use alternative Nix binary caches if specified - name: Use alternative Nix binary caches if specified
@@ -107,6 +107,14 @@ jobs:
- name: Run Complement tests - name: Run Complement tests
run: | run: |
direnv exec . bin/complement 'complement_src' 'complement_test_logs.jsonl' 'complement_test_results.jsonl' direnv exec . bin/complement 'complement_src' 'complement_test_logs.jsonl' 'complement_test_results.jsonl'
cp -v -f result complement_oci_image.tar.gz
- name: Upload Complement OCI image
uses: actions/upload-artifact@v4
with:
name: complement_oci_image.tar.gz
path: complement_oci_image.tar.gz
if-no-files-found: error
- name: Upload Complement logs - name: Upload Complement logs
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -132,7 +140,7 @@ jobs:
run: | run: |
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY echo '```diff' >> $GITHUB_STEP_SUMMARY
tail -n 50 complement_test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY tail -n 100 complement_test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY
- name: Update Job Summary - name: Update Job Summary
+8 -1
View File
@@ -35,6 +35,10 @@ before_script:
- if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ]; then echo "extra-substituters = $ATTIC_ENDPOINT" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ]; then echo "extra-substituters = $ATTIC_ENDPOINT" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi
# Add Lix binary cache
- if command -v nix > /dev/null; then echo "extra-substituters = https://cache.lix.systems" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=" >> /etc/nix/nix.conf; fi
# Add crane binary cache # Add crane binary cache
- if command -v nix > /dev/null; then echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null; then echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf; fi
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf; fi - if command -v nix > /dev/null; then echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf; fi
@@ -49,12 +53,15 @@ before_script:
# Allow .envrc # Allow .envrc
- if command -v nix > /dev/null; then direnv allow; fi - if command -v nix > /dev/null; then direnv allow; fi
# Cache attic client
- if command -v nix > /dev/null; then ./bin/nix-build-and-cache --inputs-from . attic; fi
# Set CARGO_HOME to a cacheable path # Set CARGO_HOME to a cacheable path
- export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo" - export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo"
ci: ci:
stage: ci stage: ci
image: nixos/nix:2.22.0 image: nixos/nix:2.22.1
script: script:
# Cache CI dependencies # Cache CI dependencies
- ./bin/nix-build-and-cache ci - ./bin/nix-build-and-cache ci
Generated
+2 -2
View File
@@ -2931,7 +2931,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-librocksdb-sys" name = "rust-librocksdb-sys"
version = "0.21.0+9.1.1" version = "0.21.0+9.1.1"
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=c5cd6bd25152ef1f8a488761351da0c3d29ed93a#c5cd6bd25152ef1f8a488761351da0c3d29ed93a" source = "git+https://github.com/zaidoon1/rust-rocksdb?branch=master#6f0afedb3c29239b1d8a15a97ed8e6b74e0a9b33"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"bzip2-sys", "bzip2-sys",
@@ -2948,7 +2948,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-rocksdb" name = "rust-rocksdb"
version = "0.25.0" version = "0.25.0"
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=c5cd6bd25152ef1f8a488761351da0c3d29ed93a#c5cd6bd25152ef1f8a488761351da0c3d29ed93a" source = "git+https://github.com/zaidoon1/rust-rocksdb?branch=master#6f0afedb3c29239b1d8a15a97ed8e6b74e0a9b33"
dependencies = [ dependencies = [
"libc", "libc",
"rust-librocksdb-sys", "rust-librocksdb-sys",
+1 -2
View File
@@ -299,8 +299,7 @@ default-features = false
[dependencies.rust-rocksdb] [dependencies.rust-rocksdb]
git = "https://github.com/zaidoon1/rust-rocksdb" git = "https://github.com/zaidoon1/rust-rocksdb"
rev = "c5cd6bd25152ef1f8a488761351da0c3d29ed93a" branch = "master"
#branch = "master"
optional = true optional = true
default-features = true default-features = true
features = ["multi-threaded-cf", "zstd"] features = ["multi-threaded-cf", "zstd"]
+2 -2
View File
@@ -15,7 +15,7 @@ LOG_FILE="$2"
# A `.jsonl` file to write test results to # A `.jsonl` file to write test results to
RESULTS_FILE="$3" RESULTS_FILE="$3"
OCI_IMAGE="complement-conduit:dev" OCI_IMAGE="complement-conduit:main"
toplevel="$(git rev-parse --show-toplevel)" toplevel="$(git rev-parse --show-toplevel)"
@@ -31,7 +31,7 @@ set +o pipefail
env \ env \
-C "$COMPLEMENT_SRC" \ -C "$COMPLEMENT_SRC" \
COMPLEMENT_BASE_IMAGE="$OCI_IMAGE" \ COMPLEMENT_BASE_IMAGE="$OCI_IMAGE" \
go test -vet=off -timeout 1h -json ./tests | tee "$LOG_FILE" go test -tags="conduwuit_blacklist" -v -timeout 1h -json ./tests | tee "$LOG_FILE"
set -o pipefail set -o pipefail
# Post-process the results into an easy-to-compare format, sorted by Test name for reproducible results # Post-process the results into an easy-to-compare format, sorted by Test name for reproducible results
+1 -1
View File
@@ -22,7 +22,7 @@ file size for download/upload, enabling federation, etc.
Running Running
------- -------
The package uses the `conduwuit.service` systemd unit file to start and The package uses the [`conduwuit.service`](../configuration.md#example-systemd-unit-file) systemd unit file to start and
stop conduwuit. It loads the configuration file mentioned above to set up the stop conduwuit. It loads the configuration file mentioned above to set up the
environment before running the server. environment before running the server.
+6
View File
@@ -3,3 +3,9 @@
``` toml ``` toml
{{#include ../conduwuit-example.toml}} {{#include ../conduwuit-example.toml}}
``` ```
# Example systemd unit file
```
{{#include ../debian/conduwuit.service}}
```
+1 -1
View File
@@ -43,7 +43,7 @@ If conduwuit runs behind a router or in a container and has a different public I
## Setting up a systemd service ## Setting up a systemd service
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. The systemd unit for conduwuit can be found [here](../configuration.md#example-systemd-unit-file). You may need to change the `ExecStart=` path to where you placed the conduwuit binary.
## Creating the conduwuit configuration file ## Creating the conduwuit configuration file
+2 -1
View File
@@ -1,6 +1,6 @@
# conduwuit for NixOS # conduwuit for NixOS
conduwuit can be acquired by Nix from various places: conduwuit can be acquired by [Lix][lix] from various places:
* The `flake.nix` at the root of the repo * The `flake.nix` at the root of the repo
* The `default.nix` at the root of the repo * The `default.nix` at the root of the repo
@@ -26,5 +26,6 @@ If you want to run the latest code, you should get Conduwuit from the `flake.nix
or `default.nix` and set [`services.matrix-conduit.package`][package] or `default.nix` and set [`services.matrix-conduit.package`][package]
appropriately. appropriately.
[lix]: https://lix.systems/
[module]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit [module]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit
[package]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit.package [package]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit.package
+7 -4
View File
@@ -5,13 +5,16 @@
Have a look at [Complement's repository][complement] for an explanation of what Have a look at [Complement's repository][complement] for an explanation of what
it is. it is.
To test against Complement, with Nix and direnv installed and set up, you can To test against Complement, with [Lix][lix] and direnv installed and set up, you can:
either:
* Run `./bin/complement "$COMPLEMENT_SRC" ./path/to/logs.jsonl ./path/to/results.jsonl` * Run `./bin/complement "$COMPLEMENT_SRC" ./path/to/logs.jsonl ./path/to/results.jsonl`
to build a Complement image, run the tests, and output the logs and results to build a Complement image, run the tests, and output the logs and results
to the specified paths to the specified paths. This will also output the OCI image at `result`
* Run `nix build .#complement` from the root of the repository to just build a * Run `nix build .#complement` from the root of the repository to just build a
Complement image Complement OCI image outputted to `result` (it's a `.tar.gz` file)
* Or download the latest Complement OCI image from the CI workflow artifacts output
from the commit/revision you want to test (e.g. from main) [here][ci-workflows]
[lix]: https://lix.systems/
[ci-workflows]: https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml?query=event%3Apush+is%3Asuccess+actor%3Agirlbossceo
[complement]: https://github.com/matrix-org/complement [complement]: https://github.com/matrix-org/complement
Generated
+15 -15
View File
@@ -26,11 +26,11 @@
"complement": { "complement": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1714472853, "lastModified": 1714661560,
"narHash": "sha256-CNRHSZe3TE+3tFj2dHNyxTMjDqL0MKY3P/3jqUgA7YE=", "narHash": "sha256-E1ZiUbOgo7rWo8zt2M2vzCVSykCxK0Ot2dUAxTL6cpU=",
"owner": "matrix-org", "owner": "matrix-org",
"repo": "complement", "repo": "complement",
"rev": "891d18872c153d39a9ce63b545045efddb845738", "rev": "370a014dca0f720614e0c8f68b9a3e66ecf7f516",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -68,11 +68,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1713738183, "lastModified": 1715274763,
"narHash": "sha256-qd/MuLm7OfKQKyd4FAMqV4H6zYyOfef5lLzRrmXwKJM=", "narHash": "sha256-3Iv1PGHJn9sV3HO4FlOVaaztOxa9uGLfOmUWrH7v7+A=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "f6c6a2fb1b8bd9b65d65ca9342dd0eb180a63f11", "rev": "27025ab71bdca30e7ed0a16c88fd74c5970fc7f5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -90,11 +90,11 @@
"rust-analyzer-src": "rust-analyzer-src" "rust-analyzer-src": "rust-analyzer-src"
}, },
"locked": { "locked": {
"lastModified": 1714544767, "lastModified": 1715322226,
"narHash": "sha256-kF1bX+YFMedf1g0PAJYwGUkzh22JmULtj8Rm4IXAQKs=", "narHash": "sha256-ezoe/FwfJpA7sskLoLP2iwfwkYnscEFCP6Vk5kPwh9k=",
"owner": "nix-community", "owner": "nix-community",
"repo": "fenix", "repo": "fenix",
"rev": "73124e1356bde9411b163d636b39fe4804b7ca45", "rev": "297c756ba6249d483c1dafe42378560458842173",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -221,11 +221,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1713537308, "lastModified": 1715266358,
"narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=", "narHash": "sha256-doPgfj+7FFe9rfzWo1siAV2mVCasW+Bh8I1cToAXEE4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f", "rev": "f1010e0469db743d14519a1efd37e23f8513d714",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -268,11 +268,11 @@
"rust-analyzer-src": { "rust-analyzer-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1713628977, "lastModified": 1715255944,
"narHash": "sha256-iN5QUlUq527lswmBC+RopfXdu6Xx7mmTaBSH2l59FtM=", "narHash": "sha256-vLLgYpdtKBaGYTamNLg1rbRo1bPXp4Jgded/gnprPVw=",
"owner": "rust-lang", "owner": "rust-lang",
"repo": "rust-analyzer", "repo": "rust-analyzer",
"rev": "55d9a533b309119c8acd13061581b43ae8840823", "rev": "5bf2f85c8054d80424899fa581db1b192230efb5",
"type": "github" "type": "github"
}, },
"original": { "original": {
+1
View File
@@ -16,6 +16,7 @@ stdenv.mkDerivation {
"conduwuit-example.toml" "conduwuit-example.toml"
"CONTRIBUTING.md" "CONTRIBUTING.md"
"README.md" "README.md"
"debian/conduwuit.service"
"debian/README.md" "debian/README.md"
"docs" "docs"
]; ];
+2 -2
View File
@@ -53,7 +53,7 @@ in
dockerTools.buildImage { dockerTools.buildImage {
name = "complement-${main.pname}"; name = "complement-${main.pname}";
tag = "dev"; tag = "main";
copyToRoot = buildEnv { copyToRoot = buildEnv {
name = "root"; name = "root";
@@ -81,7 +81,7 @@ dockerTools.buildImage {
Env = [ Env = [
"SSL_CERT_FILE=/complement/ca/ca.crt" "SSL_CERT_FILE=/complement/ca/ca.crt"
"CONDUIT_CONFIG=${./config.toml}" "CONDUWUIT_CONFIG=${./config.toml}"
]; ];
ExposedPorts = { ExposedPorts = {
+9 -3
View File
@@ -66,7 +66,14 @@ commonAttrs = {
# right thing here. # right thing here.
pkgsBuildHost.rustPlatform.bindgenHook pkgsBuildHost.rustPlatform.bindgenHook
] ]
++ lib.optionals stdenv.isDarwin [ libiconv ]; ++ lib.optionals stdenv.isDarwin [
# https://github.com/NixOS/nixpkgs/issues/206242
libiconv
# https://stackoverflow.com/questions/69869574/properly-adding-darwin-apple-sdk-to-a-nix-shell
# https://discourse.nixos.org/t/compile-a-rust-binary-on-macos-dbcrossbar/8612
pkgsBuildHost.darwin.apple_sdk.frameworks.Security
];
}; };
in in
@@ -85,8 +92,7 @@ craneLib.buildPackage ( commonAttrs // {
# This is redundant with CI # This is redundant with CI
cargoTestCommand = ""; cargoTestCommand = "";
cargoCheckCommand = "";
# This is redundant with CI
doCheck = false; doCheck = false;
# https://crane.dev/faq/rebuilds-bindgen.html # https://crane.dev/faq/rebuilds-bindgen.html
+50 -13
View File
@@ -6,7 +6,7 @@ use reqwest::Url;
use ruma::api::client::{ use ruma::api::client::{
error::{ErrorKind, RetryAfter}, error::{ErrorKind, RetryAfter},
media::{ media::{
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config, create_content, create_mxc_uri, get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
get_media_preview, get_media_preview,
}, },
}; };
@@ -19,7 +19,9 @@ use crate::{
services, services,
utils::{ utils::{
self, self,
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename}, content_disposition::{
content_disposition_type, make_content_disposition, make_content_type, sanitise_filename,
},
server_name::server_is_ours, server_name::server_is_ours,
}, },
Error, Result, Ruma, RumaResponse, Error, Result, Ruma, RumaResponse,
@@ -57,6 +59,29 @@ pub(crate) async fn get_media_config_v1_route(
get_media_config_route(body).await.map(RumaResponse) get_media_config_route(body).await.map(RumaResponse)
} }
/// # `POST /_matrix/media/v1/create`
///
/// Creates a MXC URI.
///
/// <https://spec.matrix.org/latest/client-server-api/#post_matrixmediav1create>
///
/// TODO: implement `unused_expires_at`, prevent MXC URI creation spam by
/// keeping track of created MXC URIs with no content pushed to them per-user
pub(crate) async fn create_mxc_uri(body: Ruma<create_mxc_uri::v1::Request>) -> Result<create_mxc_uri::v1::Response> {
let _sender_user = body.sender_user.as_ref().expect("user is authenticated");
let mxc = format!(
"mxc://{}/{}",
services().globals.server_name(),
utils::random_string(MXC_LENGTH)
);
Ok(create_mxc_uri::v1::Response {
content_uri: mxc.into(),
unused_expires_at: None,
})
}
/// # `GET /_matrix/media/v3/preview_url` /// # `GET /_matrix/media/v3/preview_url`
/// ///
/// Returns URL preview. /// Returns URL preview.
@@ -127,6 +152,8 @@ pub(crate) async fn create_content_route(
utils::random_string(MXC_LENGTH) utils::random_string(MXC_LENGTH)
); );
let content_type = Some(make_content_type(&body.file, &body.content_type).to_owned());
services() services()
.media .media
.create( .create(
@@ -137,20 +164,18 @@ pub(crate) async fn create_content_route(
.map(|filename| { .map(|filename| {
format!( format!(
"{}; filename={}", "{}; filename={}",
content_disposition_type(&body.file, &body.content_type), content_disposition_type(&body.file, &content_type),
sanitise_filename(filename.to_owned()) sanitise_filename(filename.to_owned())
) )
}) })
.as_deref(), .as_deref(),
body.content_type.as_deref(), content_type.as_deref(),
&body.file, &body.file,
) )
.await?; .await?;
let content_uri = mxc.into();
Ok(create_content::v3::Response { Ok(create_content::v3::Response {
content_uri, content_uri: mxc.into(),
blurhash: None, blurhash: None,
}) })
} }
@@ -189,6 +214,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
}) = services().media.get(mxc.clone()).await? }) = services().media.get(mxc.clone()).await?
{ {
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition)); let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content::v3::Response { Ok(get_content::v3::Response {
file, file,
@@ -216,10 +242,11 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
&response.content_type, &response.content_type,
response.content_disposition, response.content_disposition,
)); ));
let content_type = Some(make_content_type(&response.file, &response.content_type).to_owned());
Ok(get_content::v3::Response { Ok(get_content::v3::Response {
file: response.file, file: response.file,
content_type: response.content_type, content_type,
content_disposition, content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
@@ -267,6 +294,7 @@ pub(crate) async fn get_content_as_filename_route(
}) = services().media.get(mxc.clone()).await? }) = services().media.get(mxc.clone()).await?
{ {
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition)); let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content_as_filename::v3::Response { Ok(get_content_as_filename::v3::Response {
file, file,
@@ -291,10 +319,13 @@ pub(crate) async fn get_content_as_filename_route(
&remote_content_response.content_type, &remote_content_response.content_type,
remote_content_response.content_disposition, remote_content_response.content_disposition,
)); ));
let content_type = Some(
make_content_type(&remote_content_response.file, &remote_content_response.content_type).to_owned(),
);
Ok(get_content_as_filename::v3::Response { Ok(get_content_as_filename::v3::Response {
content_disposition, content_disposition,
content_type: remote_content_response.content_type, content_type,
file: remote_content_response.file, file: remote_content_response.file,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
@@ -359,6 +390,7 @@ pub(crate) async fn get_content_thumbnail_route(
.await? .await?
{ {
let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition)); let content_disposition = Some(make_content_disposition(&file, &content_type, content_disposition));
let content_type = Some(make_content_type(&file, &content_type).to_owned());
Ok(get_content_thumbnail::v3::Response { Ok(get_content_thumbnail::v3::Response {
file, file,
@@ -371,7 +403,7 @@ pub(crate) async fn get_content_thumbnail_route(
if services() if services()
.globals .globals
.prevent_media_downloads_from() .prevent_media_downloads_from()
.contains(&body.server_name.clone()) .contains(&body.server_name)
{ {
// we'll lie to the client and say the blocked server's media was not found and // we'll lie to the client and say the blocked server's media was not found and
// log. the client has no way of telling anyways so this is a security bonus. // log. the client has no way of telling anyways so this is a security bonus.
@@ -415,10 +447,13 @@ pub(crate) async fn get_content_thumbnail_route(
&get_thumbnail_response.content_type, &get_thumbnail_response.content_type,
get_thumbnail_response.content_disposition, get_thumbnail_response.content_disposition,
)); ));
let content_type = Some(
make_content_type(&get_thumbnail_response.file, &get_thumbnail_response.content_type).to_owned(),
);
Ok(get_content_thumbnail::v3::Response { Ok(get_content_thumbnail::v3::Response {
file: get_thumbnail_response.file, file: get_thumbnail_response.file,
content_type: get_thumbnail_response.content_type, content_type,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
content_disposition, content_disposition,
@@ -486,20 +521,22 @@ async fn get_remote_content(
content_response.content_disposition, content_response.content_disposition,
)); ));
let content_type = Some(make_content_type(&content_response.file, &content_response.content_type).to_owned());
services() services()
.media .media
.create( .create(
None, None,
mxc.to_owned(), mxc.to_owned(),
content_disposition.as_deref(), content_disposition.as_deref(),
content_response.content_type.as_deref(), content_type.as_deref(),
&content_response.file, &content_response.file,
) )
.await?; .await?;
Ok(get_content::v3::Response { Ok(get_content::v3::Response {
file: content_response.file, file: content_response.file,
content_type: content_response.content_type, content_type,
content_disposition, content_disposition,
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()), cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()), cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
+1
View File
@@ -133,6 +133,7 @@ pub(crate) fn routes(config: &Config) -> Router {
.ruma_route(client_server::get_media_config_route) .ruma_route(client_server::get_media_config_route)
.ruma_route(client_server::get_media_preview_route) .ruma_route(client_server::get_media_preview_route)
.ruma_route(client_server::create_content_route) .ruma_route(client_server::create_content_route)
.ruma_route(client_server::create_mxc_uri)
// legacy v1 media routes // legacy v1 media routes
.route( .route(
"/_matrix/media/v1/preview_url", "/_matrix/media/v1/preview_url",
+46 -6
View File
@@ -1,5 +1,12 @@
use infer::MatcherType; use infer::MatcherType;
use crate::debug_info;
const ATTACHMENT: &str = "attachment";
const INLINE: &str = "inline";
const APPLICATION_OCTET_STREAM: &str = "application/octet-stream";
const IMAGE_SVG_XML: &str = "image/svg+xml";
/// Returns a Content-Disposition of `attachment` or `inline`, depending on the /// Returns a Content-Disposition of `attachment` or `inline`, depending on the
/// *parsed* contents of the file uploaded via format magic keys using `infer` /// *parsed* contents of the file uploaded via format magic keys using `infer`
/// crate (basically libmagic without needing libmagic). /// crate (basically libmagic without needing libmagic).
@@ -10,17 +17,48 @@ use infer::MatcherType;
/// ///
/// TODO: add a "strict" function for comparing the Content-Type with what we /// TODO: add a "strict" function for comparing the Content-Type with what we
/// detected: `file_type.mime_type() != content_type` /// detected: `file_type.mime_type() != content_type`
pub(crate) fn content_disposition_type(buf: &[u8], _content_type: &Option<String>) -> &'static str { #[tracing::instrument(skip(buf))]
pub(crate) fn content_disposition_type(buf: &[u8], content_type: &Option<String>) -> &'static str {
let Some(file_type) = infer::get(buf) else { let Some(file_type) = infer::get(buf) else {
return "attachment"; return ATTACHMENT;
}; };
debug_info!("MIME type: {}", file_type.mime_type());
match file_type.matcher_type() { match file_type.matcher_type() {
MatcherType::Image | MatcherType::Audio | MatcherType::Text | MatcherType::Video => "inline", MatcherType::Image | MatcherType::Audio | MatcherType::Text | MatcherType::Video => {
_ => "attachment", if file_type.mime_type().contains("xml") {
ATTACHMENT
} else {
INLINE
}
},
_ => ATTACHMENT,
} }
} }
/// overrides the Content-Type with what we detected
///
/// SVG is special-cased due to the MIME type being classified as `text/xml` but
/// browsers need `image/svg+xml`
#[tracing::instrument(skip(buf))]
pub(crate) fn make_content_type(buf: &[u8], content_type: &Option<String>) -> &'static str {
let Some(file_type) = infer::get(buf) else {
debug_info!("Failed to infer the file's contents");
return APPLICATION_OCTET_STREAM;
};
let Some(claimed_content_type) = content_type else {
return file_type.mime_type();
};
if claimed_content_type.contains("svg") && file_type.mime_type().contains("xml") {
return IMAGE_SVG_XML;
}
file_type.mime_type()
}
/// sanitises the file name for the Content-Disposition using /// sanitises the file name for the Content-Disposition using
/// `sanitize_filename` crate /// `sanitize_filename` crate
#[tracing::instrument] #[tracing::instrument]
@@ -36,8 +74,10 @@ pub(crate) fn sanitise_filename(filename: String) -> String {
/// creates the final Content-Disposition based on whether the filename exists /// creates the final Content-Disposition based on whether the filename exists
/// or not. /// or not.
/// ///
/// if filename exists: `Content-Disposition: attachment/inline; /// if filename exists:
/// filename=filename.ext` else: `Content-Disposition: attachment/inline` /// `Content-Disposition: attachment/inline; filename=filename.ext`
///
/// else: `Content-Disposition: attachment/inline`
#[tracing::instrument(skip(file))] #[tracing::instrument(skip(file))]
pub(crate) fn make_content_disposition( pub(crate) fn make_content_disposition(
file: &[u8], content_type: &Option<String>, content_disposition: Option<String>, file: &[u8], content_type: &Option<String>, content_disposition: Option<String>,