mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-14 04:37:00 +00:00
feat(media): Remove legacy unauthenticated media support
This commit is contained in:
1 parent
77e8a6b41b
commit
7afe5885e9
9 files changed
+19
-710
No files matched your search
@@ -0,0 +1 @@
|
||||
Remove support for legacy, unauthenticated media, including the `allow_legacy_media` and `freeze_legacy_media` options.
|
||||
@@ -31,7 +31,6 @@ rocksdb_log_level = "info"
|
||||
rocksdb_max_log_files = 1
|
||||
rocksdb_recovery_mode = 0
|
||||
rocksdb_paranoid_file_checks = true
|
||||
allow_legacy_media = true
|
||||
startup_netburst = true
|
||||
startup_netburst_keep = -1
|
||||
allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure = true
|
||||
|
||||
@@ -1733,29 +1733,6 @@
|
||||
# env: CONTINUWUITY_BROTLI_COMPRESSION
|
||||
#brotli_compression = false
|
||||
|
||||
# Enable the legacy unauthenticated Matrix media repository endpoints.
|
||||
# These endpoints consist of:
|
||||
# - /_matrix/media/*/config
|
||||
# - /_matrix/media/*/upload
|
||||
# - /_matrix/media/*/preview_url
|
||||
# - /_matrix/media/*/download/*
|
||||
# - /_matrix/media/*/thumbnail/*
|
||||
#
|
||||
# The authenticated equivalent endpoints are always enabled.
|
||||
#
|
||||
# Defaults to false, as unauthenticated media has been sunsetted.
|
||||
#
|
||||
# env: CONTINUWUITY_ALLOW_LEGACY_MEDIA
|
||||
#allow_legacy_media = false
|
||||
|
||||
# If set to true, prevents fetching new legacy remote media.
|
||||
#
|
||||
# Legacy media may still be accessible if it was already fetched
|
||||
# previously and allow_legacy_media is true.
|
||||
#
|
||||
# env: CONTINUWUITY_FREEZE_LEGACY_MEDIA
|
||||
#freeze_legacy_media = true
|
||||
|
||||
# Check consistency of the media directory at startup:
|
||||
# 1. When `media_compat_file_link` is enabled, this check will upgrade
|
||||
# media when switching back and forth between Conduit and conduwuit.
|
||||
|
||||
@@ -1,398 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, err,
|
||||
utils::{content_disposition::make_content_disposition, math::ruma_from_usize},
|
||||
};
|
||||
use conduwuit_service::media::{CORP_CROSS_ORIGIN, Dim, FileMeta};
|
||||
use reqwest::Url;
|
||||
use ruma::{
|
||||
api::client::media::{
|
||||
create_content, get_content, get_content_as_filename, get_content_thumbnail,
|
||||
get_media_config, get_media_preview,
|
||||
},
|
||||
assign,
|
||||
};
|
||||
use service::media::mxc::Mxc;
|
||||
|
||||
use crate::{Ruma, RumaResponse, client::create_content_route};
|
||||
|
||||
/// # `GET /_matrix/media/v3/config`
|
||||
///
|
||||
/// Returns max upload size.
|
||||
pub(crate) async fn get_media_config_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
_body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<get_media_config::v3::Response> {
|
||||
Ok(get_media_config::v3::Response::new(Some(ruma_from_usize(
|
||||
services.server.config.max_request_size,
|
||||
))))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/config`
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// Returns max upload size.
|
||||
pub(crate) async fn get_media_config_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
||||
get_media_config_legacy_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
pub(crate) async fn get_media_preview_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let url = &body.url;
|
||||
let url = Url::parse(&body.url).map_err(|e| {
|
||||
err!(Request(InvalidParam(
|
||||
debug_warn!(%sender_user, %url, "Requested URL is not valid: {e}")
|
||||
)))
|
||||
})?;
|
||||
|
||||
if !services.media.url_preview_allowed(&url) {
|
||||
return Err!(Request(Forbidden(
|
||||
debug_warn!(%sender_user, %url, "URL is not allowed to be previewed")
|
||||
)));
|
||||
}
|
||||
|
||||
let preview = services.media.get_url_preview(&url).await.map_err(|e| {
|
||||
err!(Request(Unknown(
|
||||
debug_error!(%sender_user, %url, "Failed to fetch a URL preview: {e}")
|
||||
)))
|
||||
})?;
|
||||
|
||||
serde_json::value::to_raw_value(&preview)
|
||||
.map(get_media_preview::v3::Response::from_raw_value)
|
||||
.map_err(|error| {
|
||||
err!(Request(Unknown(
|
||||
debug_error!(%sender_user, %url, "Failed to parse URL preview: {error}")
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/preview_url`
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// Returns URL preview.
|
||||
pub(crate) async fn get_media_preview_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||
get_media_preview_legacy_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/media/v1/upload`
|
||||
///
|
||||
/// Permanently save media in the server.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Some metadata will be saved in the database
|
||||
/// - Media will be saved in the media/ directory
|
||||
pub(crate) async fn create_content_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||
create_content_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<get_content::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
media_id: &body.media_id,
|
||||
};
|
||||
|
||||
match services.media.get(&mxc).await? {
|
||||
| Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
content_disposition,
|
||||
}) => {
|
||||
let content_disposition = make_content_disposition(
|
||||
content_disposition.as_ref(),
|
||||
content_type.as_deref(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(assign!(
|
||||
get_content::v3::Response::new(
|
||||
content.expect("entire file contents"),
|
||||
content_type.unwrap_or_default(),
|
||||
content_disposition,
|
||||
),
|
||||
{
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.into()),
|
||||
}
|
||||
))
|
||||
},
|
||||
| _ =>
|
||||
if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||
let response = services
|
||||
.media
|
||||
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
|
||||
})?;
|
||||
|
||||
let content_disposition = make_content_disposition(
|
||||
response.content_disposition.as_ref(),
|
||||
response.content_type.as_deref(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(assign!(
|
||||
get_content::v3::Response::new(
|
||||
response.file,
|
||||
response.content_type.unwrap_or_default(),
|
||||
content_disposition,
|
||||
),
|
||||
{
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.into()),
|
||||
}
|
||||
))
|
||||
} else {
|
||||
Err!(Request(NotFound("Media not found.")))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||
get_content_legacy_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_as_filename_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<get_content_as_filename::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
media_id: &body.media_id,
|
||||
};
|
||||
|
||||
match services.media.get(&mxc).await? {
|
||||
| Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
content_disposition,
|
||||
}) => {
|
||||
let content_disposition = make_content_disposition(
|
||||
content_disposition.as_ref(),
|
||||
content_type.as_deref(),
|
||||
Some(&body.filename),
|
||||
);
|
||||
|
||||
Ok(assign!(get_content_as_filename::v3::Response::new(
|
||||
content.expect("entire file contents"),
|
||||
content_type.unwrap_or_default(),
|
||||
content_disposition,
|
||||
),
|
||||
{
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.into()),
|
||||
}
|
||||
))
|
||||
},
|
||||
| _ =>
|
||||
if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||
let response = services
|
||||
.media
|
||||
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
|
||||
})?;
|
||||
|
||||
let content_disposition = make_content_disposition(
|
||||
response.content_disposition.as_ref(),
|
||||
response.content_type.as_deref(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(assign!(
|
||||
get_content_as_filename::v3::Response::new(
|
||||
response.file,
|
||||
response.content_type.unwrap_or_default(),
|
||||
content_disposition,
|
||||
),
|
||||
{
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.into()),
|
||||
}
|
||||
))
|
||||
} else {
|
||||
Err!(Request(NotFound("Media not found.")))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||
get_content_as_filename_legacy_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<get_content_thumbnail::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
media_id: &body.media_id,
|
||||
};
|
||||
|
||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||
match services.media.get_thumbnail(&mxc, &dim).await? {
|
||||
| Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
content_disposition,
|
||||
}) => {
|
||||
let content_disposition = make_content_disposition(
|
||||
content_disposition.as_ref(),
|
||||
content_type.as_deref(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(assign!(
|
||||
get_content_thumbnail::v3::Response::new(
|
||||
content.expect("entire file contents"),
|
||||
content_type.unwrap_or_default(),
|
||||
content_disposition,
|
||||
),
|
||||
{
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
}
|
||||
))
|
||||
},
|
||||
| _ =>
|
||||
if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||
let response = services
|
||||
.media
|
||||
.fetch_remote_thumbnail_legacy(&body)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
|
||||
})?;
|
||||
|
||||
let content_disposition = make_content_disposition(
|
||||
response.content_disposition.as_ref(),
|
||||
response.content_type.as_deref(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(assign!(
|
||||
get_content_thumbnail::v3::Response::new(
|
||||
response.file,
|
||||
response.content_type.unwrap_or_default(),
|
||||
content_disposition,
|
||||
),
|
||||
{
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
}
|
||||
))
|
||||
} else {
|
||||
Err!(Request(NotFound("Media not found.")))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_thumbnail_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||
get_content_thumbnail_legacy_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -12,7 +12,6 @@ pub(super) mod directory;
|
||||
pub(super) mod filter;
|
||||
pub(super) mod keys;
|
||||
pub(super) mod media;
|
||||
pub(super) mod media_legacy;
|
||||
pub(super) mod membership;
|
||||
pub(super) mod message;
|
||||
pub(super) mod mutual_rooms;
|
||||
@@ -57,7 +56,6 @@ pub(super) use directory::*;
|
||||
pub(super) use filter::*;
|
||||
pub(super) use keys::*;
|
||||
pub(super) use media::*;
|
||||
pub(super) use media_legacy::*;
|
||||
pub(super) use membership::*;
|
||||
pub use membership::{leave_all_rooms, leave_room, remote_leave_room};
|
||||
pub(super) use message::*;
|
||||
|
||||
+11
-40
@@ -8,7 +8,7 @@ use std::str::FromStr;
|
||||
use axum::{
|
||||
Router,
|
||||
response::{IntoResponse, Redirect},
|
||||
routing::{any, get, post},
|
||||
routing::{any, get},
|
||||
};
|
||||
use conduwuit::err;
|
||||
pub(super) use conduwuit_service::state::State;
|
||||
@@ -247,45 +247,16 @@ pub fn build(router: Router<State>, state: State) -> Router<State> {
|
||||
// and other functionality will continue to work
|
||||
router = router.ruma_route(&server::get_openid_userinfo_route);
|
||||
|
||||
if config.allow_legacy_media {
|
||||
router = router
|
||||
.ruma_route(&client::get_media_config_legacy_route)
|
||||
.ruma_route(&client::get_media_preview_legacy_route)
|
||||
.ruma_route(&client::get_content_legacy_route)
|
||||
.ruma_route(&client::get_content_as_filename_legacy_route)
|
||||
.ruma_route(&client::get_content_thumbnail_legacy_route)
|
||||
.route("/_matrix/media/v1/config", get(client::get_media_config_legacy_legacy_route))
|
||||
.route("/_matrix/media/v1/upload", post(client::create_content_legacy_route))
|
||||
.route(
|
||||
"/_matrix/media/v1/preview_url",
|
||||
get(client::get_media_preview_legacy_legacy_route),
|
||||
)
|
||||
.route(
|
||||
"/_matrix/media/v1/download/{server_name}/{media_id}",
|
||||
get(client::get_content_legacy_legacy_route),
|
||||
)
|
||||
.route(
|
||||
"/_matrix/media/v1/download/{server_name}/{media_id}/{file_name}",
|
||||
get(client::get_content_as_filename_legacy_legacy_route),
|
||||
)
|
||||
.route(
|
||||
"/_matrix/media/v1/thumbnail/{server_name}/{media_id}",
|
||||
get(client::get_content_thumbnail_legacy_legacy_route),
|
||||
);
|
||||
} else {
|
||||
router = router
|
||||
.route("/_matrix/media/v1/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/config", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/download/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/thumbnail/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/preview_url", any(redirect_legacy_preview))
|
||||
.route("/_matrix/media/r0/config", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/r0/download/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/r0/thumbnail/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/r0/preview_url", any(redirect_legacy_preview));
|
||||
}
|
||||
|
||||
router
|
||||
.route("/_matrix/media/v1/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/config", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/download/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/thumbnail/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/v3/preview_url", any(redirect_legacy_preview))
|
||||
.route("/_matrix/media/r0/config", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/r0/download/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/r0/thumbnail/{*path}", any(legacy_media_disabled))
|
||||
.route("/_matrix/media/r0/preview_url", any(redirect_legacy_preview))
|
||||
}
|
||||
|
||||
async fn redirect_legacy_preview(uri: Uri) -> impl IntoResponse {
|
||||
@@ -306,7 +277,7 @@ async fn redirect_legacy_preview(uri: Uri) -> impl IntoResponse {
|
||||
}
|
||||
|
||||
async fn legacy_media_disabled() -> impl IntoResponse {
|
||||
err!(Request(Forbidden("Unauthenticated media is disabled.")))
|
||||
err!(Request(NotFound("Unauthenticated media is disabled.")))
|
||||
}
|
||||
|
||||
async fn federation_disabled() -> impl IntoResponse {
|
||||
|
||||
@@ -1847,31 +1847,6 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub brotli_compression: bool,
|
||||
|
||||
/// Enable the legacy unauthenticated Matrix media repository endpoints.
|
||||
/// These endpoints consist of:
|
||||
/// - /_matrix/media/*/config
|
||||
/// - /_matrix/media/*/upload
|
||||
/// - /_matrix/media/*/preview_url
|
||||
/// - /_matrix/media/*/download/*
|
||||
/// - /_matrix/media/*/thumbnail/*
|
||||
///
|
||||
/// The authenticated equivalent endpoints are always enabled.
|
||||
///
|
||||
/// Defaults to false, as unauthenticated media has been sunsetted.
|
||||
///
|
||||
/// default: false
|
||||
#[serde(default)]
|
||||
pub allow_legacy_media: bool,
|
||||
|
||||
/// If set to true, prevents fetching new legacy remote media.
|
||||
///
|
||||
/// Legacy media may still be accessible if it was already fetched
|
||||
/// previously and allow_legacy_media is true.
|
||||
///
|
||||
/// default: true
|
||||
#[serde(default = "true_fn")]
|
||||
pub freeze_legacy_media: bool,
|
||||
|
||||
/// Check consistency of the media directory at startup:
|
||||
/// 1. When `media_compat_file_link` is enabled, this check will upgrade
|
||||
/// media when switching back and forth between Conduit and conduwuit.
|
||||
|
||||
+5
-197
@@ -1,7 +1,7 @@
|
||||
use std::{fmt::Debug, time::Duration};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug_warn, err,
|
||||
Err, Result, debug_warn, err,
|
||||
utils::{content_disposition::make_content_disposition, response::LimitReadExt},
|
||||
};
|
||||
use http::header::{CONTENT_DISPOSITION, CONTENT_TYPE, HeaderValue};
|
||||
@@ -9,9 +9,6 @@ use ruma::{
|
||||
ServerName, UserId,
|
||||
api::{
|
||||
OutgoingRequest,
|
||||
auth_scheme::NoAccessToken,
|
||||
client::media,
|
||||
error::ErrorKind::{NotFound, Unrecognized},
|
||||
federation::{
|
||||
self,
|
||||
authenticated_media::{Content, FileOrLocation},
|
||||
@@ -35,17 +32,8 @@ impl super::Service {
|
||||
) -> Result<FileMeta> {
|
||||
self.check_fetch_authorized(mxc)?;
|
||||
|
||||
let result = self
|
||||
.fetch_thumbnail_authenticated(mxc, user, server, timeout_ms, dim)
|
||||
.await;
|
||||
|
||||
if let Err(Error::Request(NotFound, ..)) = &result {
|
||||
return self
|
||||
.fetch_thumbnail_unauthenticated(mxc, user, server, timeout_ms, dim)
|
||||
.await;
|
||||
}
|
||||
|
||||
result
|
||||
self.fetch_thumbnail_authenticated(mxc, user, server, timeout_ms, dim)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn fetch_remote_content(
|
||||
@@ -57,8 +45,7 @@ impl super::Service {
|
||||
) -> Result<FileMeta> {
|
||||
self.check_fetch_authorized(mxc)?;
|
||||
|
||||
let result = self
|
||||
.fetch_content_authenticated(mxc, user, server, timeout_ms)
|
||||
self.fetch_content_authenticated(mxc, user, server, timeout_ms)
|
||||
.await
|
||||
.inspect_err(|error| {
|
||||
debug_warn!(
|
||||
@@ -68,15 +55,7 @@ impl super::Service {
|
||||
?error,
|
||||
"Authenticated fetch of remote content failed"
|
||||
);
|
||||
});
|
||||
|
||||
if let Err(Error::Request(Unrecognized, ..)) = &result {
|
||||
return self
|
||||
.fetch_content_unauthenticated(mxc, user, server, timeout_ms)
|
||||
.await;
|
||||
}
|
||||
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_thumbnail_authenticated(
|
||||
@@ -127,66 +106,6 @@ impl super::Service {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
async fn fetch_thumbnail_unauthenticated(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
user: Option<&UserId>,
|
||||
server: Option<&ServerName>,
|
||||
timeout_ms: Duration,
|
||||
dim: &Dim,
|
||||
) -> Result<FileMeta> {
|
||||
use media::get_content_thumbnail::v3::{Request, Response};
|
||||
|
||||
let mut request = Request::new(
|
||||
mxc.media_id.into(),
|
||||
mxc.server_name.into(),
|
||||
dim.width.into(),
|
||||
dim.height.into(),
|
||||
);
|
||||
request.allow_redirect = true;
|
||||
request.allow_remote = true;
|
||||
request.animated = Some(true);
|
||||
request.method = Some(dim.method.clone());
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
let Response {
|
||||
file, content_type, content_disposition, ..
|
||||
} = self
|
||||
.federation_request_legacy_media(mxc, server, request)
|
||||
.await?;
|
||||
|
||||
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
||||
|
||||
self.handle_thumbnail_file(mxc, user, dim, content).await
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
async fn fetch_content_unauthenticated(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
user: Option<&UserId>,
|
||||
server: Option<&ServerName>,
|
||||
timeout_ms: Duration,
|
||||
) -> Result<FileMeta> {
|
||||
use media::get_content::v3::{Request, Response};
|
||||
|
||||
let mut request = Request::new(mxc.media_id.into(), mxc.server_name.into());
|
||||
request.allow_remote = true;
|
||||
request.allow_redirect = true;
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
let Response {
|
||||
file, content_type, content_disposition, ..
|
||||
} = self
|
||||
.federation_request_legacy_media(mxc, server, request)
|
||||
.await?;
|
||||
|
||||
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
||||
|
||||
self.handle_content_file(mxc, user, content).await
|
||||
}
|
||||
|
||||
async fn handle_thumbnail_file(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
@@ -319,108 +238,6 @@ impl super::Service {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn federation_request_legacy_media<'i, Request>(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
server: Option<&ServerName>,
|
||||
request: Request,
|
||||
) -> Result<Request::IncomingResponse>
|
||||
where
|
||||
Request: OutgoingRequest<
|
||||
Authentication = NoAccessToken,
|
||||
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||
> + Debug
|
||||
+ Send,
|
||||
{
|
||||
self.services
|
||||
.sending
|
||||
.send_legacy_media_request(server.unwrap_or(mxc.server_name), request)
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub async fn fetch_remote_thumbnail_legacy(
|
||||
&self,
|
||||
body: &media::get_content_thumbnail::v3::Request,
|
||||
) -> Result<media::get_content_thumbnail::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
media_id: &body.media_id,
|
||||
};
|
||||
|
||||
let mut request = media::get_content_thumbnail::v3::Request::new(
|
||||
body.media_id.clone(),
|
||||
body.server_name.clone(),
|
||||
body.width,
|
||||
body.height,
|
||||
);
|
||||
request.method.clone_from(&body.method);
|
||||
request.allow_remote = body.allow_remote;
|
||||
request.allow_redirect = body.allow_redirect;
|
||||
request.animated = body.animated;
|
||||
request.timeout_ms = body.timeout_ms;
|
||||
|
||||
self.check_legacy_freeze()?;
|
||||
self.check_fetch_authorized(&mxc)?;
|
||||
let response = self
|
||||
.services
|
||||
.sending
|
||||
.send_legacy_media_request(mxc.server_name, request)
|
||||
.await?;
|
||||
|
||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||
self.upload_thumbnail(
|
||||
&mxc,
|
||||
None,
|
||||
None,
|
||||
response.content_type.as_deref(),
|
||||
&dim,
|
||||
&response.file,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub async fn fetch_remote_content_legacy(
|
||||
&self,
|
||||
mxc: &Mxc<'_>,
|
||||
allow_redirect: bool,
|
||||
timeout_ms: Duration,
|
||||
) -> Result<media::get_content::v3::Response, Error> {
|
||||
let mut request =
|
||||
media::get_content::v3::Request::new(mxc.media_id.into(), mxc.server_name.into());
|
||||
request.allow_remote = true;
|
||||
request.allow_redirect = allow_redirect;
|
||||
request.timeout_ms = timeout_ms;
|
||||
|
||||
self.check_legacy_freeze()?;
|
||||
self.check_fetch_authorized(mxc)?;
|
||||
let response = self
|
||||
.services
|
||||
.sending
|
||||
.send_legacy_media_request(mxc.server_name, request)
|
||||
.await?;
|
||||
|
||||
let content_disposition = make_content_disposition(
|
||||
response.content_disposition.as_ref(),
|
||||
response.content_type.as_deref(),
|
||||
None,
|
||||
);
|
||||
|
||||
self.create(
|
||||
mxc,
|
||||
None,
|
||||
Some(&content_disposition),
|
||||
response.content_type.as_deref(),
|
||||
&response.file,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn check_fetch_authorized(&self, mxc: &Mxc<'_>) -> Result<()> {
|
||||
if self
|
||||
.services
|
||||
@@ -436,13 +253,4 @@ impl super::Service {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_legacy_freeze(&self) -> Result<()> {
|
||||
self.services
|
||||
.server
|
||||
.config
|
||||
.freeze_legacy_media
|
||||
.then_some(())
|
||||
.ok_or(err!(Request(NotFound("Remote media is frozen."))))
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,8 @@ use futures::{FutureExt, Stream, StreamExt};
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, ServerName, UserId,
|
||||
api::{
|
||||
OutgoingRequest,
|
||||
auth_scheme::{NoAccessToken, NoAuthentication, SendAccessToken},
|
||||
federation::authentication::ServerSignatures,
|
||||
path_builder::PathBuilder,
|
||||
OutgoingRequest, auth_scheme::NoAuthentication,
|
||||
federation::authentication::ServerSignatures, path_builder::PathBuilder,
|
||||
},
|
||||
};
|
||||
use tokio::{task, task::JoinSet};
|
||||
@@ -420,26 +418,6 @@ impl Service {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send an unauthenticated federation request with no X-Matrix header.
|
||||
#[inline]
|
||||
pub async fn send_legacy_media_request<'i, T>(
|
||||
&self,
|
||||
dest: &ServerName,
|
||||
request: T,
|
||||
) -> Result<T::IncomingResponse>
|
||||
where
|
||||
T: OutgoingRequest<
|
||||
Authentication = NoAccessToken,
|
||||
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||
> + Debug
|
||||
+ Send,
|
||||
{
|
||||
self.services
|
||||
.federation
|
||||
.execute_on(&self.services.client.federation, dest, request, SendAccessToken::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Clean up queued sending event data
|
||||
///
|
||||
/// Used after we remove an appservice registration or a user deletes a push
|
||||
|
||||
Reference in new issue
Block a user