Skip to content
Snippets Groups Projects
Commit 152ae705 authored by Jason Volk's avatar Jason Volk
Browse files

refactor for structured Mxc type


Signed-off-by: default avatarJason Volk <jason@zemos.net>
parent 54e6a414
No related branches found
No related tags found
3 merge requests!561morguldir/sliding sync fixes,!559Auth Media,!553Misc
...@@ -2958,7 +2958,7 @@ dependencies = [ ...@@ -2958,7 +2958,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma" name = "ruma"
version = "0.10.1" version = "0.10.1"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"assign", "assign",
"js_int", "js_int",
...@@ -2980,7 +2980,7 @@ dependencies = [ ...@@ -2980,7 +2980,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-appservice-api" name = "ruma-appservice-api"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"js_int", "js_int",
"ruma-common", "ruma-common",
...@@ -2992,7 +2992,7 @@ dependencies = [ ...@@ -2992,7 +2992,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-client-api" name = "ruma-client-api"
version = "0.18.0" version = "0.18.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"as_variant", "as_variant",
"assign", "assign",
...@@ -3015,7 +3015,7 @@ dependencies = [ ...@@ -3015,7 +3015,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-common" name = "ruma-common"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"as_variant", "as_variant",
"base64 0.22.1", "base64 0.22.1",
...@@ -3045,7 +3045,7 @@ dependencies = [ ...@@ -3045,7 +3045,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-events" name = "ruma-events"
version = "0.28.1" version = "0.28.1"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"as_variant", "as_variant",
"indexmap 2.4.0", "indexmap 2.4.0",
...@@ -3069,7 +3069,7 @@ dependencies = [ ...@@ -3069,7 +3069,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-federation-api" name = "ruma-federation-api"
version = "0.9.0" version = "0.9.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"bytes", "bytes",
"http", "http",
...@@ -3087,7 +3087,7 @@ dependencies = [ ...@@ -3087,7 +3087,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-identifiers-validation" name = "ruma-identifiers-validation"
version = "0.9.5" version = "0.9.5"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"js_int", "js_int",
"thiserror", "thiserror",
...@@ -3096,7 +3096,7 @@ dependencies = [ ...@@ -3096,7 +3096,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-identity-service-api" name = "ruma-identity-service-api"
version = "0.9.0" version = "0.9.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"js_int", "js_int",
"ruma-common", "ruma-common",
...@@ -3106,7 +3106,7 @@ dependencies = [ ...@@ -3106,7 +3106,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-macros" name = "ruma-macros"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"proc-macro-crate", "proc-macro-crate",
...@@ -3121,7 +3121,7 @@ dependencies = [ ...@@ -3121,7 +3121,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-push-gateway-api" name = "ruma-push-gateway-api"
version = "0.9.0" version = "0.9.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"js_int", "js_int",
"ruma-common", "ruma-common",
...@@ -3133,7 +3133,7 @@ dependencies = [ ...@@ -3133,7 +3133,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-server-util" name = "ruma-server-util"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"headers", "headers",
"http", "http",
...@@ -3146,7 +3146,7 @@ dependencies = [ ...@@ -3146,7 +3146,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-signatures" name = "ruma-signatures"
version = "0.15.0" version = "0.15.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"ed25519-dalek", "ed25519-dalek",
...@@ -3162,7 +3162,7 @@ dependencies = [ ...@@ -3162,7 +3162,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-state-res" name = "ruma-state-res"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d23a8412bd8f875cf81bbd7e20cefa03263fcd0e#d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" source = "git+https://github.com/girlbossceo/ruwuma?rev=25fbd64b968c5d5088c07750aaa4873e072831b0#25fbd64b968c5d5088c07750aaa4873e072831b0"
dependencies = [ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"js_int", "js_int",
......
...@@ -311,7 +311,7 @@ version = "0.1.2" ...@@ -311,7 +311,7 @@ version = "0.1.2"
[workspace.dependencies.ruma] [workspace.dependencies.ruma]
git = "https://github.com/girlbossceo/ruwuma" git = "https://github.com/girlbossceo/ruwuma"
#branch = "conduwuit-changes" #branch = "conduwuit-changes"
rev = "d23a8412bd8f875cf81bbd7e20cefa03263fcd0e" rev = "25fbd64b968c5d5088c07750aaa4873e072831b0"
features = [ features = [
"compat", "compat",
"rand", "rand",
......
...@@ -15,7 +15,10 @@ pub(super) async fn delete( ...@@ -15,7 +15,10 @@ pub(super) async fn delete(
if let Some(mxc) = mxc { if let Some(mxc) = mxc {
debug!("Got MXC URL: {mxc}"); debug!("Got MXC URL: {mxc}");
self.services.media.delete(mxc.as_ref()).await?; self.services
.media
.delete(&mxc.as_str().try_into()?)
.await?;
return Ok(RoomMessageEventContent::text_plain( return Ok(RoomMessageEventContent::text_plain(
"Deleted the MXC from our database and on our filesystem.", "Deleted the MXC from our database and on our filesystem.",
...@@ -123,7 +126,10 @@ pub(super) async fn delete( ...@@ -123,7 +126,10 @@ pub(super) async fn delete(
} }
for mxc_url in mxc_urls { for mxc_url in mxc_urls {
self.services.media.delete(&mxc_url).await?; self.services
.media
.delete(&mxc_url.as_str().try_into()?)
.await?;
mxc_deletion_count = mxc_deletion_count.saturating_add(1); mxc_deletion_count = mxc_deletion_count.saturating_add(1);
} }
...@@ -157,7 +163,7 @@ pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> { ...@@ -157,7 +163,7 @@ pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
for mxc in mxc_list { for mxc in mxc_list {
debug!("Deleting MXC {mxc} in bulk"); debug!("Deleting MXC {mxc} in bulk");
self.services.media.delete(mxc).await?; self.services.media.delete(&mxc.try_into()?).await?;
mxc_deletion_count = mxc_deletion_count mxc_deletion_count = mxc_deletion_count
.checked_add(1) .checked_add(1)
.expect("mxc_deletion_count should not get this high"); .expect("mxc_deletion_count should not get this high");
......
...@@ -7,8 +7,12 @@ ...@@ -7,8 +7,12 @@
utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize}, utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize},
Err, Result, Err, Result,
}; };
use ruma::api::client::media::{ use ruma::{
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config, get_media_preview, api::client::media::{
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
get_media_preview,
},
Mxc,
}; };
use service::media::{FileMeta, MXC_LENGTH}; use service::media::{FileMeta, MXC_LENGTH};
...@@ -106,16 +110,17 @@ pub(crate) async fn create_content_route( ...@@ -106,16 +110,17 @@ pub(crate) async fn create_content_route(
body: Ruma<create_content::v3::Request>, body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> { ) -> Result<create_content::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); 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));
let content_disposition = make_content_disposition(None, body.content_type.as_deref(), body.filename.as_deref()); let content_disposition = make_content_disposition(None, body.content_type.as_deref(), body.filename.as_deref());
let mxc = Mxc {
server_name: services.globals.server_name(),
media_id: &utils::random_string(MXC_LENGTH),
};
services services
.media .media
.create( .create(
Some(sender_user.clone()),
&mxc, &mxc,
Some(sender_user),
Some(&content_disposition), Some(&content_disposition),
body.content_type.as_deref(), body.content_type.as_deref(),
&body.file, &body.file,
...@@ -123,7 +128,7 @@ pub(crate) async fn create_content_route( ...@@ -123,7 +128,7 @@ pub(crate) async fn create_content_route(
.await?; .await?;
Ok(create_content::v3::Response { Ok(create_content::v3::Response {
content_uri: mxc.into(), content_uri: mxc.to_string().into(),
blurhash: None, blurhash: None,
}) })
} }
...@@ -161,7 +166,10 @@ pub(crate) async fn get_content_route( ...@@ -161,7 +166,10 @@ pub(crate) async fn get_content_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content::v3::Request>, body: Ruma<get_content::v3::Request>,
) -> Result<get_content::v3::Response> { ) -> Result<get_content::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); let mxc = Mxc {
server_name: &body.server_name,
media_id: &body.media_id,
};
if let Some(FileMeta { if let Some(FileMeta {
content, content,
...@@ -181,13 +189,7 @@ pub(crate) async fn get_content_route( ...@@ -181,13 +189,7 @@ pub(crate) async fn get_content_route(
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote { } else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
let response = services let response = services
.media .media
.fetch_remote_content( .fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
&mxc,
&body.server_name,
body.media_id.clone(),
body.allow_redirect,
body.timeout_ms,
)
.await .await
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?; .map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
...@@ -241,7 +243,10 @@ pub(crate) async fn get_content_as_filename_route( ...@@ -241,7 +243,10 @@ pub(crate) async fn get_content_as_filename_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_as_filename::v3::Request>, body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<get_content_as_filename::v3::Response> { ) -> Result<get_content_as_filename::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); let mxc = Mxc {
server_name: &body.server_name,
media_id: &body.media_id,
};
if let Some(FileMeta { if let Some(FileMeta {
content, content,
...@@ -262,13 +267,7 @@ pub(crate) async fn get_content_as_filename_route( ...@@ -262,13 +267,7 @@ pub(crate) async fn get_content_as_filename_route(
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote { } else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
let response = services let response = services
.media .media
.fetch_remote_content( .fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
&mxc,
&body.server_name,
body.media_id.clone(),
body.allow_redirect,
body.timeout_ms,
)
.await .await
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?; .map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
...@@ -322,7 +321,10 @@ pub(crate) async fn get_content_thumbnail_route( ...@@ -322,7 +321,10 @@ pub(crate) async fn get_content_thumbnail_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_thumbnail::v3::Request>, body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<get_content_thumbnail::v3::Response> { ) -> Result<get_content_thumbnail::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); let mxc = Mxc {
server_name: &body.server_name,
media_id: &body.media_id,
};
if let Some(FileMeta { if let Some(FileMeta {
content, content,
...@@ -353,7 +355,7 @@ pub(crate) async fn get_content_thumbnail_route( ...@@ -353,7 +355,7 @@ pub(crate) async fn get_content_thumbnail_route(
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote { } else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
let response = services let response = services
.media .media
.fetch_remote_thumbnail(&mxc, &body) .fetch_remote_thumbnail_legacy(&body)
.await .await
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?; .map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
......
...@@ -98,6 +98,8 @@ pub enum Error { ...@@ -98,6 +98,8 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
IntoHttp(#[from] ruma::api::error::IntoHttpError), IntoHttp(#[from] ruma::api::error::IntoHttpError),
#[error(transparent)] #[error(transparent)]
Mxc(#[from] ruma::MxcUriError),
#[error(transparent)]
Mxid(#[from] ruma::IdParseError), Mxid(#[from] ruma::IdParseError),
#[error("from {0}: {1}")] #[error("from {0}: {1}")]
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError), Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
......
use std::sync::Arc; use std::sync::Arc;
use conduit::{debug, debug_info, utils::string_from_bytes, Error, Result}; use conduit::{
debug, debug_info, trace,
utils::{str_from_bytes, string_from_bytes},
Err, Error, Result,
};
use database::{Database, Map}; use database::{Database, Map};
use ruma::{api::client::error::ErrorKind, http_headers::ContentDisposition}; use ruma::{api::client::error::ErrorKind, http_headers::ContentDisposition, Mxc, UserId};
use super::preview::UrlPreviewData; use super::preview::UrlPreviewData;
...@@ -29,10 +33,12 @@ pub(super) fn new(db: &Arc<Database>) -> Self { ...@@ -29,10 +33,12 @@ pub(super) fn new(db: &Arc<Database>) -> Self {
} }
pub(super) fn create_file_metadata( pub(super) fn create_file_metadata(
&self, sender_user: Option<&str>, mxc: &str, width: u32, height: u32, &self, mxc: &Mxc<'_>, user: Option<&UserId>, width: u32, height: u32,
content_disposition: Option<&ContentDisposition>, content_type: Option<&str>, content_disposition: Option<&ContentDisposition>, content_type: Option<&str>,
) -> Result<Vec<u8>> { ) -> Result<Vec<u8>> {
let mut key = mxc.as_bytes().to_vec(); let mut key: Vec<u8> = Vec::new();
key.extend_from_slice(mxc.server_name.as_bytes());
key.extend_from_slice(mxc.media_id.as_bytes());
key.push(0xFF); key.push(0xFF);
key.extend_from_slice(&width.to_be_bytes()); key.extend_from_slice(&width.to_be_bytes());
key.extend_from_slice(&height.to_be_bytes()); key.extend_from_slice(&height.to_be_bytes());
...@@ -53,8 +59,10 @@ pub(super) fn create_file_metadata( ...@@ -53,8 +59,10 @@ pub(super) fn create_file_metadata(
self.mediaid_file.insert(&key, &[])?; self.mediaid_file.insert(&key, &[])?;
if let Some(user) = sender_user { if let Some(user) = user {
let key = mxc.as_bytes().to_vec(); let mut key: Vec<u8> = Vec::new();
key.extend_from_slice(mxc.server_name.as_bytes());
key.extend_from_slice(mxc.media_id.as_bytes());
let user = user.as_bytes().to_vec(); let user = user.as_bytes().to_vec();
self.mediaid_user.insert(&key, &user)?; self.mediaid_user.insert(&key, &user)?;
} }
...@@ -62,22 +70,23 @@ pub(super) fn create_file_metadata( ...@@ -62,22 +70,23 @@ pub(super) fn create_file_metadata(
Ok(key) Ok(key)
} }
pub(super) fn delete_file_mxc(&self, mxc: &str) -> Result<()> { pub(super) fn delete_file_mxc(&self, mxc: &Mxc<'_>) -> Result<()> {
debug!("MXC URI: {:?}", mxc); debug!("MXC URI: {mxc}");
let mut prefix = mxc.as_bytes().to_vec(); let mut prefix: Vec<u8> = Vec::new();
prefix.extend_from_slice(mxc.server_name.as_bytes());
prefix.extend_from_slice(mxc.media_id.as_bytes());
prefix.push(0xFF); prefix.push(0xFF);
debug!("MXC db prefix: {prefix:?}"); trace!("MXC db prefix: {prefix:?}");
for (key, _) in self.mediaid_file.scan_prefix(prefix.clone()) {
for (key, _) in self.mediaid_file.scan_prefix(prefix) {
debug!("Deleting key: {:?}", key); debug!("Deleting key: {:?}", key);
self.mediaid_file.remove(&key)?; self.mediaid_file.remove(&key)?;
} }
for (key, value) in self.mediaid_user.scan_prefix(mxc.as_bytes().to_vec()) { for (key, value) in self.mediaid_user.scan_prefix(prefix.clone()) {
if key == mxc.as_bytes().to_vec() { if key.starts_with(&prefix) {
let user = string_from_bytes(&value).unwrap_or_default(); let user = str_from_bytes(&value).unwrap_or_default();
debug_info!("Deleting key \"{key:?}\" which was uploaded by user {user}"); debug_info!("Deleting key \"{key:?}\" which was uploaded by user {user}");
self.mediaid_user.remove(&key)?; self.mediaid_user.remove(&key)?;
...@@ -88,10 +97,12 @@ pub(super) fn delete_file_mxc(&self, mxc: &str) -> Result<()> { ...@@ -88,10 +97,12 @@ pub(super) fn delete_file_mxc(&self, mxc: &str) -> Result<()> {
} }
/// Searches for all files with the given MXC /// Searches for all files with the given MXC
pub(super) fn search_mxc_metadata_prefix(&self, mxc: &str) -> Result<Vec<Vec<u8>>> { pub(super) fn search_mxc_metadata_prefix(&self, mxc: &Mxc<'_>) -> Result<Vec<Vec<u8>>> {
debug!("MXC URI: {:?}", mxc); debug!("MXC URI: {mxc}");
let mut prefix = mxc.as_bytes().to_vec(); let mut prefix: Vec<u8> = Vec::new();
prefix.extend_from_slice(mxc.server_name.as_bytes());
prefix.extend_from_slice(mxc.media_id.as_bytes());
prefix.push(0xFF); prefix.push(0xFF);
let keys: Vec<Vec<u8>> = self let keys: Vec<Vec<u8>> = self
...@@ -101,18 +112,18 @@ pub(super) fn search_mxc_metadata_prefix(&self, mxc: &str) -> Result<Vec<Vec<u8> ...@@ -101,18 +112,18 @@ pub(super) fn search_mxc_metadata_prefix(&self, mxc: &str) -> Result<Vec<Vec<u8>
.collect(); .collect();
if keys.is_empty() { if keys.is_empty() {
return Err(Error::bad_database( return Err!(Database("Failed to find any keys in database for `{mxc}`",));
"Failed to find any keys in database with the provided MXC.",
));
} }
debug!("Got the following keys: {:?}", keys); debug!("Got the following keys: {keys:?}");
Ok(keys) Ok(keys)
} }
pub(super) fn search_file_metadata(&self, mxc: &str, width: u32, height: u32) -> Result<Metadata> { pub(super) fn search_file_metadata(&self, mxc: &Mxc<'_>, width: u32, height: u32) -> Result<Metadata> {
let mut prefix = mxc.as_bytes().to_vec(); let mut prefix: Vec<u8> = Vec::new();
prefix.extend_from_slice(mxc.server_name.as_bytes());
prefix.extend_from_slice(mxc.media_id.as_bytes());
prefix.push(0xFF); prefix.push(0xFF);
prefix.extend_from_slice(&width.to_be_bytes()); prefix.extend_from_slice(&width.to_be_bytes());
prefix.extend_from_slice(&height.to_be_bytes()); prefix.extend_from_slice(&height.to_be_bytes());
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
use base64::{engine::general_purpose, Engine as _}; use base64::{engine::general_purpose, Engine as _};
use conduit::{debug, debug_error, err, error, trace, utils, utils::MutexMap, Err, Result, Server}; use conduit::{debug, debug_error, err, error, trace, utils, utils::MutexMap, Err, Result, Server};
use data::{Data, Metadata}; use data::{Data, Metadata};
use ruma::{http_headers::ContentDisposition, OwnedMxcUri, OwnedUserId}; use ruma::{http_headers::ContentDisposition, Mxc, OwnedMxcUri, UserId};
use tokio::{ use tokio::{
fs, fs,
io::{AsyncReadExt, AsyncWriteExt, BufReader}, io::{AsyncReadExt, AsyncWriteExt, BufReader},
...@@ -68,17 +68,13 @@ fn name(&self) -> &str { crate::service::make_name(std::module_path!()) } ...@@ -68,17 +68,13 @@ fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
impl Service { impl Service {
/// Uploads a file. /// Uploads a file.
pub async fn create( pub async fn create(
&self, sender_user: Option<OwnedUserId>, mxc: &str, content_disposition: Option<&ContentDisposition>, &self, mxc: &Mxc<'_>, user: Option<&UserId>, content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>, file: &[u8], content_type: Option<&str>, file: &[u8],
) -> Result<()> { ) -> Result<()> {
// Width, Height = 0 if it's not a thumbnail // Width, Height = 0 if it's not a thumbnail
let key = if let Some(user) = sender_user { let key = self
self.db .db
.create_file_metadata(Some(user.as_str()), mxc, 0, 0, content_disposition, content_type)? .create_file_metadata(mxc, user, 0, 0, content_disposition, content_type)?;
} else {
self.db
.create_file_metadata(None, mxc, 0, 0, content_disposition, content_type)?
};
//TODO: Dangling metadata in database if creation fails //TODO: Dangling metadata in database if creation fails
let mut f = self.create_media_file(&key).await?; let mut f = self.create_media_file(&key).await?;
...@@ -88,7 +84,7 @@ pub async fn create( ...@@ -88,7 +84,7 @@ pub async fn create(
} }
/// Deletes a file in the database and from the media directory via an MXC /// Deletes a file in the database and from the media directory via an MXC
pub async fn delete(&self, mxc: &str) -> Result<()> { pub async fn delete(&self, mxc: &Mxc<'_>) -> Result<()> {
if let Ok(keys) = self.db.search_mxc_metadata_prefix(mxc) { if let Ok(keys) = self.db.search_mxc_metadata_prefix(mxc) {
for key in keys { for key in keys {
trace!(?mxc, ?key, "Deleting from filesystem"); trace!(?mxc, ?key, "Deleting from filesystem");
...@@ -111,7 +107,7 @@ pub async fn delete(&self, mxc: &str) -> Result<()> { ...@@ -111,7 +107,7 @@ pub async fn delete(&self, mxc: &str) -> Result<()> {
} }
/// Downloads a file. /// Downloads a file.
pub async fn get(&self, mxc: &str) -> Result<Option<FileMeta>> { pub async fn get(&self, mxc: &Mxc<'_>) -> Result<Option<FileMeta>> {
if let Ok(Metadata { if let Ok(Metadata {
content_disposition, content_disposition,
content_type, content_type,
...@@ -213,6 +209,7 @@ pub async fn delete_all_remote_media_at_after_time(&self, time: String, force: b ...@@ -213,6 +209,7 @@ pub async fn delete_all_remote_media_at_after_time(&self, time: String, force: b
debug!("Deleting media now in the past {user_duration:?}."); debug!("Deleting media now in the past {user_duration:?}.");
let mut deletion_count: usize = 0; let mut deletion_count: usize = 0;
for mxc in remote_mxcs { for mxc in remote_mxcs {
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
debug!("Deleting MXC {mxc} from database and filesystem"); debug!("Deleting MXC {mxc} from database and filesystem");
self.delete(&mxc).await?; self.delete(&mxc).await?;
deletion_count = deletion_count.saturating_add(1); deletion_count = deletion_count.saturating_add(1);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
use conduit_core::implement; use conduit_core::implement;
use image::ImageReader as ImgReader; use image::ImageReader as ImgReader;
use ipaddress::IPAddress; use ipaddress::IPAddress;
use ruma::Mxc;
use serde::Serialize; use serde::Serialize;
use url::Url; use url::Url;
use webpage::HTML; use webpage::HTML;
...@@ -44,13 +45,12 @@ pub async fn set_url_preview(&self, url: &str, data: &UrlPreviewData) -> Result< ...@@ -44,13 +45,12 @@ pub async fn set_url_preview(&self, url: &str, data: &UrlPreviewData) -> Result<
pub async fn download_image(&self, url: &str) -> Result<UrlPreviewData> { pub async fn download_image(&self, url: &str) -> Result<UrlPreviewData> {
let client = &self.services.client.url_preview; let client = &self.services.client.url_preview;
let image = client.get(url).send().await?.bytes().await?; let image = client.get(url).send().await?.bytes().await?;
let mxc = format!( let mxc = Mxc {
"mxc://{}/{}", server_name: self.services.globals.server_name(),
self.services.globals.server_name(), media_id: &utils::random_string(MXC_LENGTH),
utils::random_string(MXC_LENGTH) };
);
self.create(None, &mxc, None, None, &image).await?; self.create(&mxc, None, None, None, &image).await?;
let (width, height) = match ImgReader::new(Cursor::new(&image)).with_guessed_format() { let (width, height) = match ImgReader::new(Cursor::new(&image)).with_guessed_format() {
Err(_) => (None, None), Err(_) => (None, None),
...@@ -61,7 +61,7 @@ pub async fn download_image(&self, url: &str) -> Result<UrlPreviewData> { ...@@ -61,7 +61,7 @@ pub async fn download_image(&self, url: &str) -> Result<UrlPreviewData> {
}; };
Ok(UrlPreviewData { Ok(UrlPreviewData {
image: Some(mxc), image: Some(mxc.to_string()),
image_size: Some(image.len()), image_size: Some(image.len()),
image_width: width, image_width: width,
image_height: height, image_height: height,
......
use std::time::Duration; use std::time::Duration;
use conduit::{debug_warn, err, implement, utils::content_disposition::make_content_disposition, Err, Error, Result}; use conduit::{debug_warn, err, implement, utils::content_disposition::make_content_disposition, Err, Error, Result};
use ruma::{ use ruma::{api::client::media, Mxc};
api::client::media::{get_content, get_content_thumbnail},
ServerName,
};
#[implement(super::Service)] #[implement(super::Service)]
#[allow(deprecated)] #[allow(deprecated)]
pub async fn fetch_remote_thumbnail( pub async fn fetch_remote_thumbnail_legacy(
&self, mxc: &str, body: &get_content_thumbnail::v3::Request, &self, body: &media::get_content_thumbnail::v3::Request,
) -> Result<get_content_thumbnail::v3::Response> { ) -> Result<media::get_content_thumbnail::v3::Response> {
let server_name = &body.server_name; let mxc = Mxc {
self.check_fetch_authorized(mxc, server_name)?; server_name: &body.server_name,
media_id: &body.media_id,
};
self.check_fetch_authorized(&mxc)?;
let reponse = self let reponse = self
.services .services
.sending .sending
.send_federation_request( .send_federation_request(
server_name, mxc.server_name,
get_content_thumbnail::v3::Request { media::get_content_thumbnail::v3::Request {
allow_remote: body.allow_remote, allow_remote: body.allow_remote,
height: body.height, height: body.height,
width: body.width, width: body.width,
...@@ -34,8 +34,8 @@ pub async fn fetch_remote_thumbnail( ...@@ -34,8 +34,8 @@ pub async fn fetch_remote_thumbnail(
.await?; .await?;
self.upload_thumbnail( self.upload_thumbnail(
&mxc,
None, None,
mxc,
None, None,
reponse.content_type.as_deref(), reponse.content_type.as_deref(),
body.width body.width
...@@ -53,20 +53,19 @@ pub async fn fetch_remote_thumbnail( ...@@ -53,20 +53,19 @@ pub async fn fetch_remote_thumbnail(
#[implement(super::Service)] #[implement(super::Service)]
#[allow(deprecated)] #[allow(deprecated)]
pub async fn fetch_remote_content( pub async fn fetch_remote_content_legacy(
&self, mxc: &str, server_name: &ServerName, media_id: String, allow_redirect: bool, timeout_ms: Duration, &self, mxc: &Mxc<'_>, allow_redirect: bool, timeout_ms: Duration,
) -> Result<get_content::v3::Response, Error> { ) -> Result<media::get_content::v3::Response, Error> {
self.check_fetch_authorized(mxc, server_name)?; self.check_fetch_authorized(mxc)?;
let response = self let response = self
.services .services
.sending .sending
.send_federation_request( .send_federation_request(
server_name, mxc.server_name,
get_content::v3::Request { media::get_content::v3::Request {
allow_remote: true, allow_remote: true,
server_name: server_name.to_owned(), server_name: mxc.server_name.into(),
media_id, media_id: mxc.media_id.into(),
timeout_ms, timeout_ms,
allow_redirect, allow_redirect,
}, },
...@@ -77,8 +76,8 @@ pub async fn fetch_remote_content( ...@@ -77,8 +76,8 @@ pub async fn fetch_remote_content(
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None); make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
self.create( self.create(
None,
mxc, mxc,
None,
Some(&content_disposition), Some(&content_disposition),
response.content_type.as_deref(), response.content_type.as_deref(),
&response.file, &response.file,
...@@ -89,14 +88,14 @@ pub async fn fetch_remote_content( ...@@ -89,14 +88,14 @@ pub async fn fetch_remote_content(
} }
#[implement(super::Service)] #[implement(super::Service)]
fn check_fetch_authorized(&self, mxc: &str, server_name: &ServerName) -> Result<()> { fn check_fetch_authorized(&self, mxc: &Mxc<'_>) -> Result<()> {
if self if self
.services .services
.server .server
.config .config
.prevent_media_downloads_from .prevent_media_downloads_from
.iter() .iter()
.any(|entry| entry == server_name) .any(|entry| entry == mxc.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.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
use conduit::{checked, Result}; use conduit::{checked, Result};
use image::{imageops::FilterType, DynamicImage}; use image::{imageops::FilterType, DynamicImage};
use ruma::{http_headers::ContentDisposition, OwnedUserId}; use ruma::{http_headers::ContentDisposition, Mxc, UserId};
use tokio::{ use tokio::{
fs, fs,
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
...@@ -14,16 +14,12 @@ impl super::Service { ...@@ -14,16 +14,12 @@ impl super::Service {
/// Uploads or replaces a file thumbnail. /// Uploads or replaces a file thumbnail.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn upload_thumbnail( pub async fn upload_thumbnail(
&self, sender_user: Option<OwnedUserId>, mxc: &str, content_disposition: Option<&ContentDisposition>, &self, mxc: &Mxc<'_>, user: Option<&UserId>, content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>, width: u32, height: u32, file: &[u8], content_type: Option<&str>, width: u32, height: u32, file: &[u8],
) -> Result<()> { ) -> Result<()> {
let key = if let Some(user) = sender_user { let key = self
self.db .db
.create_file_metadata(Some(user.as_str()), mxc, width, height, content_disposition, content_type)? .create_file_metadata(mxc, user, width, height, content_disposition, content_type)?;
} else {
self.db
.create_file_metadata(None, mxc, width, height, content_disposition, content_type)?
};
//TODO: Dangling metadata in database if creation fails //TODO: Dangling metadata in database if creation fails
let mut f = self.create_media_file(&key).await?; let mut f = self.create_media_file(&key).await?;
...@@ -46,7 +42,7 @@ pub async fn upload_thumbnail( ...@@ -46,7 +42,7 @@ pub async fn upload_thumbnail(
/// For width,height <= 96 the server uses another thumbnailing algorithm /// For width,height <= 96 the server uses another thumbnailing algorithm
/// which crops the image afterwards. /// which crops the image afterwards.
#[tracing::instrument(skip(self), name = "thumbnail", level = "debug")] #[tracing::instrument(skip(self), name = "thumbnail", level = "debug")]
pub async fn get_thumbnail(&self, mxc: &str, width: u32, height: u32) -> Result<Option<FileMeta>> { pub async fn get_thumbnail(&self, mxc: &Mxc<'_>, width: u32, height: u32) -> Result<Option<FileMeta>> {
// 0, 0 because that's the original file // 0, 0 because that's the original file
let (width, height, crop) = thumbnail_properties(width, height).unwrap_or((0, 0, false)); let (width, height, crop) = thumbnail_properties(width, height).unwrap_or((0, 0, false));
...@@ -76,7 +72,7 @@ async fn get_thumbnail_saved(&self, data: Metadata) -> Result<Option<FileMeta>> ...@@ -76,7 +72,7 @@ async fn get_thumbnail_saved(&self, data: Metadata) -> Result<Option<FileMeta>>
/// Generate a thumbnail /// Generate a thumbnail
#[tracing::instrument(skip(self), name = "generate", level = "debug")] #[tracing::instrument(skip(self), name = "generate", level = "debug")]
async fn get_thumbnail_generate( async fn get_thumbnail_generate(
&self, mxc: &str, width: u32, height: u32, crop: bool, data: Metadata, &self, mxc: &Mxc<'_>, width: u32, height: u32, crop: bool, data: Metadata,
) -> Result<Option<FileMeta>> { ) -> Result<Option<FileMeta>> {
let mut content = Vec::new(); let mut content = Vec::new();
let path = self.get_media_file(&data.key); let path = self.get_media_file(&data.key);
...@@ -100,8 +96,8 @@ async fn get_thumbnail_generate( ...@@ -100,8 +96,8 @@ async fn get_thumbnail_generate(
// Save thumbnail in database so we don't have to generate it again next time // Save thumbnail in database so we don't have to generate it again next time
let thumbnail_key = self.db.create_file_metadata( let thumbnail_key = self.db.create_file_metadata(
None,
mxc, mxc,
None,
width, width,
height, height,
data.content_disposition.as_ref(), data.content_disposition.as_ref(),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment