apply new rustfmt.toml changes, fix some clippy lints

Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-12-15 00:05:47 -05:00
commit 77e0b76408
No known key found for this signature in database
296 changed files with 7147 additions and 4300 deletions

View file

@ -34,7 +34,11 @@ impl Data {
}
pub(super) fn create_file_metadata(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, dim: &Dim, content_disposition: Option<&ContentDisposition>,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
dim: &Dim,
content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>,
) -> Result<Vec<u8>> {
let dim: &[u32] = &[dim.width, dim.height];
@ -63,7 +67,10 @@ impl Data {
.stream_prefix_raw(&prefix)
.ignore_err()
.ready_for_each(|(key, val)| {
debug_assert!(key.starts_with(mxc.to_string().as_bytes()), "key should start with the mxc");
debug_assert!(
key.starts_with(mxc.to_string().as_bytes()),
"key should start with the mxc"
);
let user = str_from_bytes(val).unwrap_or_default();
debug_info!("Deleting key {key:?} which was uploaded by user {user}");
@ -95,7 +102,11 @@ impl Data {
Ok(keys)
}
pub(super) async fn search_file_metadata(&self, mxc: &Mxc<'_>, dim: &Dim) -> Result<Metadata> {
pub(super) async fn search_file_metadata(
&self,
mxc: &Mxc<'_>,
dim: &Dim,
) -> Result<Metadata> {
let dim: &[u32] = &[dim.width, dim.height];
let prefix = (mxc, dim, Interfix);
@ -113,8 +124,9 @@ impl Data {
let content_type = parts
.next()
.map(|bytes| {
string_from_bytes(bytes)
.map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))
string_from_bytes(bytes).map_err(|_| {
Error::bad_database("Content type in mediaid_file is invalid unicode.")
})
})
.transpose()?;
@ -127,16 +139,16 @@ impl Data {
} else {
Some(
string_from_bytes(content_disposition_bytes)
.map_err(|_| Error::bad_database("Content Disposition in mediaid_file is invalid unicode."))?
.map_err(|_| {
Error::bad_database(
"Content Disposition in mediaid_file is invalid unicode.",
)
})?
.parse()?,
)
};
Ok(Metadata {
content_disposition,
content_type,
key,
})
Ok(Metadata { content_disposition, content_type, key })
}
/// Gets all the MXCs associated with a user
@ -144,7 +156,9 @@ impl Data {
self.mediaid_user
.stream()
.ignore_err()
.ready_filter_map(|(key, user): (&str, &UserId)| (user == user_id).then(|| key.into()))
.ready_filter_map(|(key, user): (&str, &UserId)| {
(user == user_id).then(|| key.into())
})
.collect()
.await
}
@ -166,7 +180,12 @@ impl Data {
Ok(())
}
pub(super) fn set_url_preview(&self, url: &str, data: &UrlPreviewData, timestamp: Duration) -> Result<()> {
pub(super) fn set_url_preview(
&self,
url: &str,
data: &UrlPreviewData,
timestamp: Duration,
) -> Result<()> {
let mut value = Vec::<u8>::new();
value.extend_from_slice(&timestamp.as_secs().to_be_bytes());
value.push(0xFF);
@ -218,43 +237,43 @@ impl Data {
.next()
.and_then(|b| String::from_utf8(b.to_vec()).ok())
{
Some(s) if s.is_empty() => None,
x => x,
| Some(s) if s.is_empty() => None,
| x => x,
};
let description = match values
.next()
.and_then(|b| String::from_utf8(b.to_vec()).ok())
{
Some(s) if s.is_empty() => None,
x => x,
| Some(s) if s.is_empty() => None,
| x => x,
};
let image = match values
.next()
.and_then(|b| String::from_utf8(b.to_vec()).ok())
{
Some(s) if s.is_empty() => None,
x => x,
| Some(s) if s.is_empty() => None,
| x => x,
};
let image_size = match values
.next()
.map(|b| usize::from_be_bytes(b.try_into().unwrap_or_default()))
{
Some(0) => None,
x => x,
| Some(0) => None,
| x => x,
};
let image_width = match values
.next()
.map(|b| u32::from_be_bytes(b.try_into().unwrap_or_default()))
{
Some(0) => None,
x => x,
| Some(0) => None,
| x => x,
};
let image_height = match values
.next()
.map(|b| u32::from_be_bytes(b.try_into().unwrap_or_default()))
{
Some(0) => None,
x => x,
| Some(0) => None,
| x => x,
};
Ok(UrlPreviewData {

View file

@ -83,7 +83,8 @@ pub(crate) async fn checkup_sha256_media(services: &Services) -> Result<()> {
for key in media.db.get_all_media_keys().await {
let new_path = media.get_media_file_sha256(&key).into_os_string();
let old_path = media.get_media_file_b64(&key).into_os_string();
if let Err(e) = handle_media_check(&dbs, config, &files, &key, &new_path, &old_path).await {
if let Err(e) = handle_media_check(&dbs, config, &files, &key, &new_path, &old_path).await
{
error!(
media_id = ?encode_key(&key), ?new_path, ?old_path,
"Failed to resolve media check failure: {e}"
@ -100,8 +101,12 @@ pub(crate) async fn checkup_sha256_media(services: &Services) -> Result<()> {
}
async fn handle_media_check(
dbs: &(&Arc<database::Map>, &Arc<database::Map>), config: &Config, files: &HashSet<OsString>, key: &[u8],
new_path: &OsStr, old_path: &OsStr,
dbs: &(&Arc<database::Map>, &Arc<database::Map>),
config: &Config,
files: &HashSet<OsString>,
key: &[u8],
new_path: &OsStr,
old_path: &OsStr,
) -> Result<()> {
use crate::media::encode_key;

View file

@ -80,13 +80,21 @@ impl crate::Service for Service {
impl Service {
/// Uploads a file.
pub async fn create(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>, file: &[u8],
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>,
file: &[u8],
) -> Result<()> {
// Width, Height = 0 if it's not a thumbnail
let key = self
.db
.create_file_metadata(mxc, user, &Dim::default(), content_disposition, content_type)?;
let key = self.db.create_file_metadata(
mxc,
user,
&Dim::default(),
content_disposition,
content_type,
)?;
//TODO: Dangling metadata in database if creation fails
let mut f = self.create_media_file(&key).await?;
@ -132,10 +140,10 @@ impl Service {
debug_info!(%deletion_count, "Deleting MXC {mxc} by user {user} from database and filesystem");
match self.delete(&mxc).await {
Ok(()) => {
| Ok(()) => {
deletion_count = deletion_count.saturating_add(1);
},
Err(e) => {
| Err(e) => {
debug_error!(%deletion_count, "Failed to delete {mxc} from user {user}, ignoring error: {e}");
},
}
@ -146,11 +154,8 @@ impl Service {
/// Downloads a file.
pub async fn get(&self, mxc: &Mxc<'_>) -> Result<Option<FileMeta>> {
if let Ok(Metadata {
content_disposition,
content_type,
key,
}) = self.db.search_file_metadata(mxc, &Dim::default()).await
if let Ok(Metadata { content_disposition, content_type, key }) =
self.db.search_file_metadata(mxc, &Dim::default()).await
{
let mut content = Vec::new();
let path = self.get_media_file(&key);
@ -181,13 +186,19 @@ impl Service {
let mxc = parts
.next()
.map(|bytes| {
utils::string_from_bytes(bytes)
.map_err(|e| err!(Database(error!("Failed to parse MXC unicode bytes from our database: {e}"))))
utils::string_from_bytes(bytes).map_err(|e| {
err!(Database(error!(
"Failed to parse MXC unicode bytes from our database: {e}"
)))
})
})
.transpose()?;
let Some(mxc_s) = mxc else {
debug_warn!(?mxc, "Parsed MXC URL unicode bytes from database but is still invalid");
debug_warn!(
?mxc,
"Parsed MXC URL unicode bytes from database but is still invalid"
);
continue;
};
@ -207,7 +218,11 @@ impl Service {
/// Deletes all remote only media files in the given at or after
/// time/duration. Returns a usize with the amount of media files deleted.
pub async fn delete_all_remote_media_at_after_time(
&self, time: SystemTime, before: bool, after: bool, yes_i_want_to_delete_local_media: bool,
&self,
time: SystemTime,
before: bool,
after: bool,
yes_i_want_to_delete_local_media: bool,
) -> Result<usize> {
let all_keys = self.db.get_all_media_keys().await;
let mut remote_mxcs = Vec::with_capacity(all_keys.len());
@ -218,19 +233,26 @@ impl Service {
let mxc = parts
.next()
.map(|bytes| {
utils::string_from_bytes(bytes)
.map_err(|e| err!(Database(error!("Failed to parse MXC unicode bytes from our database: {e}"))))
utils::string_from_bytes(bytes).map_err(|e| {
err!(Database(error!(
"Failed to parse MXC unicode bytes from our database: {e}"
)))
})
})
.transpose()?;
let Some(mxc_s) = mxc else {
debug_warn!(?mxc, "Parsed MXC URL unicode bytes from database but is still invalid");
debug_warn!(
?mxc,
"Parsed MXC URL unicode bytes from database but is still invalid"
);
continue;
};
trace!("Parsed MXC key to URL: {mxc_s}");
let mxc = OwnedMxcUri::from(mxc_s);
if (mxc.server_name() == Ok(self.services.globals.server_name()) && !yes_i_want_to_delete_local_media)
if (mxc.server_name() == Ok(self.services.globals.server_name())
&& !yes_i_want_to_delete_local_media)
|| !mxc.is_valid()
{
debug!("Ignoring local or broken media MXC: {mxc}");
@ -240,9 +262,12 @@ impl Service {
let path = self.get_media_file(&key);
let file_metadata = match fs::metadata(path.clone()).await {
Ok(file_metadata) => file_metadata,
Err(e) => {
error!("Failed to obtain file metadata for MXC {mxc} at file path \"{path:?}\", skipping: {e}");
| Ok(file_metadata) => file_metadata,
| Err(e) => {
error!(
"Failed to obtain file metadata for MXC {mxc} at file path \
\"{path:?}\", skipping: {e}"
);
continue;
},
};
@ -250,12 +275,12 @@ impl Service {
trace!(%mxc, ?path, "File metadata: {file_metadata:?}");
let file_created_at = match file_metadata.created() {
Ok(value) => value,
Err(err) if err.kind() == std::io::ErrorKind::Unsupported => {
| Ok(value) => value,
| Err(err) if err.kind() == std::io::ErrorKind::Unsupported => {
debug!("btime is unsupported, using mtime instead");
file_metadata.modified()?
},
Err(err) => {
| Err(err) => {
error!("Could not delete MXC {mxc} at path {path:?}: {err:?}. Skipping...");
continue;
},
@ -264,10 +289,16 @@ impl Service {
debug!("File created at: {file_created_at:?}");
if file_created_at >= time && before {
debug!("File is within (before) user duration, pushing to list of file paths and keys to delete.");
debug!(
"File is within (before) user duration, pushing to list of file paths and \
keys to delete."
);
remote_mxcs.push(mxc.to_string());
} else if file_created_at <= time && after {
debug!("File is not within (after) user duration, pushing to list of file paths and keys to delete.");
debug!(
"File is not within (after) user duration, pushing to list of file paths \
and keys to delete."
);
remote_mxcs.push(mxc.to_string());
}
}
@ -289,10 +320,10 @@ impl Service {
debug_info!("Deleting MXC {mxc} from database and filesystem");
match self.delete(&mxc).await {
Ok(()) => {
| Ok(()) => {
deletion_count = deletion_count.saturating_add(1);
},
Err(e) => {
| Err(e) => {
warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
continue;
},

View file

@ -53,10 +53,10 @@ pub async fn download_image(&self, url: &str) -> Result<UrlPreviewData> {
self.create(&mxc, None, None, None, &image).await?;
let (width, height) = match ImgReader::new(Cursor::new(&image)).with_guessed_format() {
Err(_) => (None, None),
Ok(reader) => match reader.into_dimensions() {
Err(_) => (None, None),
Ok((width, height)) => (Some(width), Some(height)),
| Err(_) => (None, None),
| Ok(reader) => match reader.into_dimensions() {
| Err(_) => (None, None),
| Ok((width, height)) => (Some(width), Some(height)),
},
};
@ -79,8 +79,8 @@ pub async fn get_url_preview(&self, url: &Url) -> Result<UrlPreviewData> {
let _request_lock = self.url_preview_mutex.lock(url.as_str()).await;
match self.db.get_url_preview(url.as_str()).await {
Ok(preview) => Ok(preview),
Err(_) => self.request_url_preview(url).await,
| Ok(preview) => Ok(preview),
| Err(_) => self.request_url_preview(url).await,
}
}
@ -111,9 +111,9 @@ async fn request_url_preview(&self, url: &Url) -> Result<UrlPreviewData> {
return Err!(Request(Unknown("Unknown Content-Type")));
};
let data = match content_type {
html if html.starts_with("text/html") => self.download_html(url.as_str()).await?,
img if img.starts_with("image/") => self.download_image(url.as_str()).await?,
_ => return Err!(Request(Unknown("Unsupported Content-Type"))),
| html if html.starts_with("text/html") => self.download_html(url.as_str()).await?,
| img if img.starts_with("image/") => self.download_image(url.as_str()).await?,
| _ => return Err!(Request(Unknown("Unsupported Content-Type"))),
};
self.set_url_preview(url.as_str(), &data).await?;
@ -131,8 +131,9 @@ async fn download_html(&self, url: &str) -> Result<UrlPreviewData> {
bytes.extend_from_slice(&chunk);
if bytes.len() > self.services.globals.url_preview_max_spider_size() {
debug!(
"Response body from URL {} exceeds url_preview_max_spider_size ({}), not processing the rest of the \
response body and assuming our necessary data is in this range.",
"Response body from URL {} exceeds url_preview_max_spider_size ({}), not \
processing the rest of the response body and assuming our necessary data is in \
this range.",
url,
self.services.globals.url_preview_max_spider_size()
);
@ -145,8 +146,8 @@ async fn download_html(&self, url: &str) -> Result<UrlPreviewData> {
};
let mut data = match html.opengraph.images.first() {
None => UrlPreviewData::default(),
Some(obj) => self.download_image(&obj.url).await?,
| None => UrlPreviewData::default(),
| Some(obj) => self.download_image(&obj.url).await?,
};
let props = html.opengraph.properties;
@ -169,11 +170,11 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool {
}
let host = match url.host_str() {
None => {
| None => {
debug!("Ignoring URL preview for a URL that does not have a host (?): {}", url);
return false;
},
Some(h) => h.to_owned(),
| Some(h) => h.to_owned(),
};
let allowlist_domain_contains = self
@ -205,7 +206,10 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool {
}
if allowlist_domain_explicit.contains(&host) {
debug!("Host {} is allowed by url_preview_domain_explicit_allowlist (check 2/4)", &host);
debug!(
"Host {} is allowed by url_preview_domain_explicit_allowlist (check 2/4)",
&host
);
return true;
}
@ -213,7 +217,10 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool {
.iter()
.any(|domain_s| domain_s.contains(&host.clone()))
{
debug!("Host {} is allowed by url_preview_domain_contains_allowlist (check 3/4)", &host);
debug!(
"Host {} is allowed by url_preview_domain_contains_allowlist (check 3/4)",
&host
);
return true;
}
@ -229,11 +236,12 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool {
if self.services.globals.url_preview_check_root_domain() {
debug!("Checking root domain");
match host.split_once('.') {
None => return false,
Some((_, root_domain)) => {
| None => return false,
| Some((_, root_domain)) => {
if denylist_domain_explicit.contains(&root_domain.to_owned()) {
debug!(
"Root domain {} is not allowed by url_preview_domain_explicit_denylist (check 1/3)",
"Root domain {} is not allowed by \
url_preview_domain_explicit_denylist (check 1/3)",
&root_domain
);
return true;
@ -241,7 +249,8 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool {
if allowlist_domain_explicit.contains(&root_domain.to_owned()) {
debug!(
"Root domain {} is allowed by url_preview_domain_explicit_allowlist (check 2/3)",
"Root domain {} is allowed by url_preview_domain_explicit_allowlist \
(check 2/3)",
&root_domain
);
return true;
@ -252,7 +261,8 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool {
.any(|domain_s| domain_s.contains(&root_domain.to_owned()))
{
debug!(
"Root domain {} is allowed by url_preview_domain_contains_allowlist (check 3/3)",
"Root domain {} is allowed by url_preview_domain_contains_allowlist \
(check 3/3)",
&root_domain
);
return true;

View file

@ -1,6 +1,9 @@
use std::{fmt::Debug, time::Duration};
use conduwuit::{debug_warn, err, implement, utils::content_disposition::make_content_disposition, Err, Error, Result};
use conduwuit::{
debug_warn, err, implement, utils::content_disposition::make_content_disposition, Err, Error,
Result,
};
use http::header::{HeaderValue, CONTENT_DISPOSITION, CONTENT_TYPE};
use ruma::{
api::{
@ -19,7 +22,12 @@ use super::{Dim, FileMeta};
#[implement(super::Service)]
pub async fn fetch_remote_thumbnail(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, timeout_ms: Duration, dim: &Dim,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
timeout_ms: Duration,
dim: &Dim,
) -> Result<FileMeta> {
self.check_fetch_authorized(mxc)?;
@ -38,7 +46,11 @@ pub async fn fetch_remote_thumbnail(
#[implement(super::Service)]
pub async fn fetch_remote_content(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, timeout_ms: Duration,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
timeout_ms: Duration,
) -> Result<FileMeta> {
self.check_fetch_authorized(mxc)?;
@ -57,7 +69,12 @@ pub async fn fetch_remote_content(
#[implement(super::Service)]
async fn fetch_thumbnail_authenticated(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, timeout_ms: Duration, dim: &Dim,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
timeout_ms: Duration,
dim: &Dim,
) -> Result<FileMeta> {
use federation::authenticated_media::get_content_thumbnail::v1::{Request, Response};
@ -70,20 +87,22 @@ async fn fetch_thumbnail_authenticated(
timeout_ms,
};
let Response {
content,
..
} = self.federation_request(mxc, user, server, request).await?;
let Response { content, .. } = self.federation_request(mxc, user, server, request).await?;
match content {
FileOrLocation::File(content) => self.handle_thumbnail_file(mxc, user, dim, content).await,
FileOrLocation::Location(location) => self.handle_location(mxc, user, &location).await,
| FileOrLocation::File(content) =>
self.handle_thumbnail_file(mxc, user, dim, content).await,
| FileOrLocation::Location(location) => self.handle_location(mxc, user, &location).await,
}
}
#[implement(super::Service)]
async fn fetch_content_authenticated(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, timeout_ms: Duration,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
timeout_ms: Duration,
) -> Result<FileMeta> {
use federation::authenticated_media::get_content::v1::{Request, Response};
@ -92,21 +111,23 @@ async fn fetch_content_authenticated(
timeout_ms,
};
let Response {
content,
..
} = self.federation_request(mxc, user, server, request).await?;
let Response { content, .. } = self.federation_request(mxc, user, server, request).await?;
match content {
FileOrLocation::File(content) => self.handle_content_file(mxc, user, content).await,
FileOrLocation::Location(location) => self.handle_location(mxc, user, &location).await,
| FileOrLocation::File(content) => self.handle_content_file(mxc, user, content).await,
| FileOrLocation::Location(location) => self.handle_location(mxc, user, &location).await,
}
}
#[allow(deprecated)]
#[implement(super::Service)]
async fn fetch_thumbnail_unauthenticated(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, timeout_ms: Duration, dim: &Dim,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
timeout_ms: Duration,
dim: &Dim,
) -> Result<FileMeta> {
use media::get_content_thumbnail::v3::{Request, Response};
@ -123,17 +144,10 @@ async fn fetch_thumbnail_unauthenticated(
};
let Response {
file,
content_type,
content_disposition,
..
file, content_type, content_disposition, ..
} = self.federation_request(mxc, user, server, request).await?;
let content = Content {
file,
content_type,
content_disposition,
};
let content = Content { file, content_type, content_disposition };
self.handle_thumbnail_file(mxc, user, dim, content).await
}
@ -141,7 +155,11 @@ async fn fetch_thumbnail_unauthenticated(
#[allow(deprecated)]
#[implement(super::Service)]
async fn fetch_content_unauthenticated(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, timeout_ms: Duration,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
timeout_ms: Duration,
) -> Result<FileMeta> {
use media::get_content::v3::{Request, Response};
@ -154,27 +172,27 @@ async fn fetch_content_unauthenticated(
};
let Response {
file,
content_type,
content_disposition,
..
file, content_type, content_disposition, ..
} = self.federation_request(mxc, user, server, request).await?;
let content = Content {
file,
content_type,
content_disposition,
};
let content = Content { file, content_type, content_disposition };
self.handle_content_file(mxc, user, content).await
}
#[implement(super::Service)]
async fn handle_thumbnail_file(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, dim: &Dim, content: Content,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
dim: &Dim,
content: Content,
) -> Result<FileMeta> {
let content_disposition =
make_content_disposition(content.content_disposition.as_ref(), content.content_type.as_deref(), None);
let content_disposition = make_content_disposition(
content.content_disposition.as_ref(),
content.content_type.as_deref(),
None,
);
self.upload_thumbnail(
mxc,
@ -193,9 +211,17 @@ async fn handle_thumbnail_file(
}
#[implement(super::Service)]
async fn handle_content_file(&self, mxc: &Mxc<'_>, user: Option<&UserId>, content: Content) -> Result<FileMeta> {
let content_disposition =
make_content_disposition(content.content_disposition.as_ref(), content.content_type.as_deref(), None);
async fn handle_content_file(
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
content: Content,
) -> Result<FileMeta> {
let content_disposition = make_content_disposition(
content.content_disposition.as_ref(),
content.content_type.as_deref(),
None,
);
self.create(
mxc,
@ -213,7 +239,12 @@ async fn handle_content_file(&self, mxc: &Mxc<'_>, user: Option<&UserId>, conten
}
#[implement(super::Service)]
async fn handle_location(&self, mxc: &Mxc<'_>, user: Option<&UserId>, location: &str) -> Result<FileMeta> {
async fn handle_location(
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
location: &str,
) -> Result<FileMeta> {
self.location_request(location).await.map_err(|error| {
err!(Request(NotFound(
debug_warn!(%mxc, ?user, ?location, ?error, "Fetching media from location failed")
@ -263,7 +294,11 @@ async fn location_request(&self, location: &str) -> Result<FileMeta> {
#[implement(super::Service)]
async fn federation_request<Request>(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, request: Request,
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
request: Request,
) -> Result<Request::IncomingResponse>
where
Request: OutgoingRequest + Send + Debug,
@ -277,7 +312,12 @@ where
// Handles and adjusts the error for the caller to determine if they should
// request the fallback endpoint or give up.
fn handle_federation_error(mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<&ServerName>, error: Error) -> Error {
fn handle_federation_error(
mxc: &Mxc<'_>,
user: Option<&UserId>,
server: Option<&ServerName>,
error: Error,
) -> Error {
let fallback = || {
err!(Request(NotFound(
debug_error!(%mxc, ?user, ?server, ?error, "Remote media not found")
@ -303,7 +343,8 @@ fn handle_federation_error(mxc: &Mxc<'_>, user: Option<&UserId>, server: Option<
#[implement(super::Service)]
#[allow(deprecated)]
pub async fn fetch_remote_thumbnail_legacy(
&self, body: &media::get_content_thumbnail::v3::Request,
&self,
body: &media::get_content_thumbnail::v3::Request,
) -> Result<media::get_content_thumbnail::v3::Response> {
let mxc = Mxc {
server_name: &body.server_name,
@ -315,20 +356,17 @@ pub async fn fetch_remote_thumbnail_legacy(
let reponse = self
.services
.sending
.send_federation_request(
mxc.server_name,
media::get_content_thumbnail::v3::Request {
allow_remote: body.allow_remote,
height: body.height,
width: body.width,
method: body.method.clone(),
server_name: body.server_name.clone(),
media_id: body.media_id.clone(),
timeout_ms: body.timeout_ms,
allow_redirect: body.allow_redirect,
animated: body.animated,
},
)
.send_federation_request(mxc.server_name, media::get_content_thumbnail::v3::Request {
allow_remote: body.allow_remote,
height: body.height,
width: body.width,
method: body.method.clone(),
server_name: body.server_name.clone(),
media_id: body.media_id.clone(),
timeout_ms: body.timeout_ms,
allow_redirect: body.allow_redirect,
animated: body.animated,
})
.await?;
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
@ -341,27 +379,30 @@ pub async fn fetch_remote_thumbnail_legacy(
#[implement(super::Service)]
#[allow(deprecated)]
pub async fn fetch_remote_content_legacy(
&self, mxc: &Mxc<'_>, allow_redirect: bool, timeout_ms: Duration,
&self,
mxc: &Mxc<'_>,
allow_redirect: bool,
timeout_ms: Duration,
) -> Result<media::get_content::v3::Response, Error> {
self.check_legacy_freeze()?;
self.check_fetch_authorized(mxc)?;
let response = self
.services
.sending
.send_federation_request(
mxc.server_name,
media::get_content::v3::Request {
allow_remote: true,
server_name: mxc.server_name.into(),
media_id: mxc.media_id.into(),
timeout_ms,
allow_redirect,
},
)
.send_federation_request(mxc.server_name, media::get_content::v3::Request {
allow_remote: true,
server_name: mxc.server_name.into(),
media_id: mxc.media_id.into(),
timeout_ms,
allow_redirect,
})
.await?;
let content_disposition =
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
let content_disposition = make_content_disposition(
response.content_disposition.as_ref(),
response.content_type.as_deref(),
None,
);
self.create(
mxc,

View file

@ -13,7 +13,12 @@ async fn long_file_names_works() {
impl Data for MockedKVDatabase {
fn create_file_metadata(
&self, _sender_user: Option<&str>, mxc: String, width: u32, height: u32, content_disposition: Option<&str>,
&self,
_sender_user: Option<&str>,
mxc: String,
width: u32,
height: u32,
content_disposition: Option<&str>,
content_type: Option<&str>,
) -> Result<Vec<u8>> {
// copied from src/database/key_value/media.rs
@ -46,14 +51,22 @@ async fn long_file_names_works() {
fn get_all_media_keys(&self) -> Vec<Vec<u8>> { todo!() }
fn search_file_metadata(
&self, _mxc: String, _width: u32, _height: u32,
&self,
_mxc: String,
_width: u32,
_height: u32,
) -> Result<(Option<String>, Option<String>, Vec<u8>)> {
todo!()
}
fn remove_url_preview(&self, _url: &str) -> Result<()> { todo!() }
fn set_url_preview(&self, _url: &str, _data: &UrlPreviewData, _timestamp: std::time::Duration) -> Result<()> {
fn set_url_preview(
&self,
_url: &str,
_data: &UrlPreviewData,
_timestamp: std::time::Duration,
) -> Result<()> {
todo!()
}
@ -64,11 +77,18 @@ async fn long_file_names_works() {
let mxc = "mxc://example.com/ascERGshawAWawugaAcauga".to_owned();
let width = 100;
let height = 100;
let content_disposition = "attachment; filename=\"this is a very long file name with spaces and special \
characters like äöüß and even emoji like 🦀.png\"";
let content_disposition = "attachment; filename=\"this is a very long file name with spaces \
and special characters like äöüß and even emoji like 🦀.png\"";
let content_type = "image/png";
let key = db
.create_file_metadata(None, mxc, width, height, Some(content_disposition), Some(content_type))
.create_file_metadata(
None,
mxc,
width,
height,
Some(content_disposition),
Some(content_type),
)
.unwrap();
let mut r = PathBuf::from("/tmp/media");
// r.push(base64::encode_config(key, base64::URL_SAFE_NO_PAD));

View file

@ -22,12 +22,17 @@ impl super::Service {
/// Uploads or replaces a file thumbnail.
#[allow(clippy::too_many_arguments)]
pub async fn upload_thumbnail(
&self, mxc: &Mxc<'_>, user: Option<&UserId>, content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>, dim: &Dim, file: &[u8],
&self,
mxc: &Mxc<'_>,
user: Option<&UserId>,
content_disposition: Option<&ContentDisposition>,
content_type: Option<&str>,
dim: &Dim,
file: &[u8],
) -> Result<()> {
let key = self
.db
.create_file_metadata(mxc, user, dim, content_disposition, content_type)?;
let key =
self.db
.create_file_metadata(mxc, user, dim, content_disposition, content_type)?;
//TODO: Dangling metadata in database if creation fails
let mut f = self.create_media_file(&key).await?;
@ -78,7 +83,12 @@ impl super::Service {
/// Generate a thumbnail
#[tracing::instrument(skip(self), name = "generate", level = "debug")]
async fn get_thumbnail_generate(&self, mxc: &Mxc<'_>, dim: &Dim, data: Metadata) -> Result<Option<FileMeta>> {
async fn get_thumbnail_generate(
&self,
mxc: &Mxc<'_>,
dim: &Dim,
data: Metadata,
) -> Result<Option<FileMeta>> {
let mut content = Vec::new();
let path = self.get_media_file(&data.key);
fs::File::open(path)
@ -117,11 +127,7 @@ impl super::Service {
fn thumbnail_generate(image: &DynamicImage, requested: &Dim) -> Result<DynamicImage> {
let thumbnail = if !requested.crop() {
let Dim {
width,
height,
..
} = requested.scaled(&Dim {
let Dim { width, height, .. } = requested.scaled(&Dim {
width: image.width(),
height: image.height(),
..Dim::default()
@ -202,12 +208,12 @@ impl Dim {
#[must_use]
pub fn normalized(&self) -> Self {
match (self.width, self.height) {
(0..=32, 0..=32) => Self::new(32, 32, Some(Method::Crop)),
(0..=96, 0..=96) => Self::new(96, 96, Some(Method::Crop)),
(0..=320, 0..=240) => Self::new(320, 240, Some(Method::Scale)),
(0..=640, 0..=480) => Self::new(640, 480, Some(Method::Scale)),
(0..=800, 0..=600) => Self::new(800, 600, Some(Method::Scale)),
_ => Self::default(),
| (0..=32, 0..=32) => Self::new(32, 32, Some(Method::Crop)),
| (0..=96, 0..=96) => Self::new(96, 96, Some(Method::Crop)),
| (0..=320, 0..=240) => Self::new(320, 240, Some(Method::Scale)),
| (0..=640, 0..=480) => Self::new(640, 480, Some(Method::Scale)),
| (0..=800, 0..=600) => Self::new(800, 600, Some(Method::Scale)),
| _ => Self::default(),
}
}