diff --git a/src/service/media.rs b/src/service/media.rs index 0d231c67..4129649d 100644 --- a/src/service/media.rs +++ b/src/service/media.rs @@ -63,9 +63,7 @@ impl Service { // Width, Height = 0 if it's not a thumbnail let key = self.db.create_file_metadata(mxc, 0, 0, &meta)?; - let path = services().globals.get_media_file(&key); - let mut f = File::create(path).await?; - f.write_all(file).await?; + self.write_content(&key, file).await?; Ok(meta) } @@ -86,10 +84,7 @@ impl Service { }; let key = self.db.create_file_metadata(mxc, width, height, &meta)?; - let path = services().globals.get_media_file(&key); - let mut f = File::create(path).await?; - f.write_all(file).await?; - + self.write_content(&key, file).await?; Ok(meta) } @@ -100,15 +95,7 @@ impl Service { mxc: OwnedMxcUri, ) -> Result)>> { if let Some((meta, key)) = self.db.search_file_metadata(mxc, 0, 0)? { - let path = services().globals.get_media_file(&key); - let mut file_data = Vec::new(); - let Ok(mut file) = File::open(path).await else { - return Ok(None); - }; - - file.read_to_end(&mut file_data).await?; - - Ok(Some((meta, file_data))) + Ok(self.read_content(&key).await?.map(|data| (meta, data))) } else { Ok(None) } @@ -370,11 +357,48 @@ impl Service { // again next time let thumbnail_key = self.db.create_file_metadata(mxc, width, height, &meta)?; - - let path = services().globals.get_media_file(&thumbnail_key); - let mut f = File::create(path).await?; - f.write_all(&thumbnail_bytes).await?; + self.write_content(&thumbnail_key, &thumbnail_bytes).await?; Ok(Some((meta, thumbnail_bytes.clone()))) } + + /// Writes contents for a media file to the fs. + /// + /// If a file already existed with the specified key, it is replaced. + async fn write_content( + &self, + key: &MediaFileKey, + data: &[u8], + ) -> Result<()> { + let path = services().globals.get_media_file(key); + let mut file = File::create(path).await?; + + file.write_all(data).await?; + + Ok(()) + } + + /// Returns the contents of a media file, read from the fs. + /// + /// If the file cannot be opened, returns `Ok(None)`. This is needed because + /// before media deletion admin commands were implemented, the only way to + /// delete abuse media was to remove the associated files from the fs. This + /// leaves the db in an inconsistent state, where media keys exist in the db + /// but their content files do not. We want to return `M_NOT_YET_UPLOADED` + /// in this case rather than the `M_UNKNOWN` we would normally use for db + /// consistency problems. + async fn read_content( + &self, + key: &MediaFileKey, + ) -> Result>> { + let path = services().globals.get_media_file(key); + let Ok(mut file) = File::open(path).await else { + return Ok(None); + }; + + let mut data = Vec::new(); + file.read_to_end(&mut data).await?; + + Ok(Some(data)) + } }