diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index ada2bd7c..3f483c2b 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -188,11 +188,11 @@ async fn get_content( ) -> Result { let mxc = format!("mxc://{}/{}", server_name, media_id); - if let Some(FileMeta { + if let Ok(Some(FileMeta { content_disposition, content_type, file, - }) = services().media.get(mxc.clone()).await? + })) = services().media.get(mxc.clone()).await { Ok(get_content::v1::Response { file, @@ -264,9 +264,9 @@ async fn get_content_as_filename( ) -> Result { let mxc = format!("mxc://{}/{}", server_name, media_id); - if let Some(FileMeta { + if let Ok(Some(FileMeta { file, content_type, .. - }) = services().media.get(mxc.clone()).await? + })) = services().media.get(mxc.clone()).await { Ok(get_content_as_filename::v1::Response { file, @@ -348,9 +348,9 @@ async fn get_content_thumbnail( ) -> Result { let mxc = format!("mxc://{}/{}", server_name, media_id); - if let Some(FileMeta { + if let Ok(Some(FileMeta { file, content_type, .. - }) = services() + })) = services() .media .get_thumbnail( mxc.clone(), @@ -361,7 +361,7 @@ async fn get_content_thumbnail( .try_into() .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, ) - .await? + .await { Ok(get_content_thumbnail::v1::Response { file, content_type }) } else if server_name != services().globals.server_name() && allow_remote { diff --git a/src/database/mod.rs b/src/database/mod.rs index 5171d4bb..a8d44820 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -6,6 +6,7 @@ use crate::{ SERVICES, }; use abstraction::{KeyValueDatabaseEngine, KvTree}; +use base64::{engine::general_purpose, Engine}; use directories::ProjectDirs; use lru_cache::LruCache; @@ -424,7 +425,7 @@ impl KeyValueDatabase { } // If the database has any data, perform data migrations before starting - let latest_database_version = 13; + let latest_database_version = 14; if services().users.count()? > 0 { // MIGRATIONS @@ -941,6 +942,64 @@ impl KeyValueDatabase { warn!("Migration: 12 -> 13 finished"); } + if services().globals.database_version()? < 14 { + // Reconstruct all media using the filesystem + db.mediaid_file.clear().unwrap(); + + for file in fs::read_dir(services().globals.get_media_folder()).unwrap() { + let file = file.unwrap(); + let mediaid = general_purpose::URL_SAFE_NO_PAD + .decode(file.file_name().into_string().unwrap()) + .unwrap(); + + let mut parts = mediaid.rsplit(|&b| b == 0xff); + + let mut removed_bytes = 0; + + let content_type_bytes = parts.next().unwrap(); + removed_bytes += content_type_bytes.len() + 1; + + let content_disposition_bytes = parts + .next() + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; + removed_bytes += content_disposition_bytes.len(); + + let mut content_disposition = + utils::string_from_bytes(content_disposition_bytes).map_err(|_| { + Error::bad_database("Content Disposition in mediaid_file is invalid.") + })?; + + if content_disposition.contains("filename=") + && !content_disposition.contains("filename=\"") + { + println!("{}", &content_disposition); + content_disposition = + content_disposition.replacen("filename=", "filename=\"", 1); + content_disposition.push('"'); + println!("{}", &content_disposition); + + let mut new_key = mediaid[..(mediaid.len() - removed_bytes)].to_vec(); + assert!(*new_key.last().unwrap() == 0xff); + + new_key.extend_from_slice(content_disposition.to_string().as_bytes()); + new_key.push(0xff); + new_key.extend_from_slice(content_type_bytes); + + // Some file names are too long. Ignore those. + let _ = fs::rename( + services().globals.get_media_file(&mediaid), + services().globals.get_media_file(&new_key), + ); + db.mediaid_file.insert(&new_key, &[])?; + } else { + db.mediaid_file.insert(&mediaid, &[])?; + } + } + services().globals.bump_database_version(14)?; + + warn!("Migration: 13 -> 14 finished"); + } + assert_eq!( services().globals.database_version().unwrap(), latest_database_version