diff --git a/src/blob.rs b/src/blob.rs index f3059ec0cc..44a4577ffb 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -1423,7 +1423,7 @@ mod tests { .set_config(Config::MediaQuality, Some(media_quality_config)) .await?; let file = alice.get_blobdir().join("file").with_extension(extension); - let file_name = format!("file{extension}"); + let file_name = format!("file.{extension}"); fs::write(&file, &bytes) .await @@ -1457,6 +1457,8 @@ mod tests { alice_msg.save_file(&alice, &file_saved).await?; check_image_size(file_saved, compressed_width, compressed_height); + println!("{}", sent.payload()); + let bob_msg = bob.recv_msg(&sent).await; assert_eq!(bob_msg.get_viewtype(), Viewtype::Image); assert_eq!(bob_msg.get_width() as u32, compressed_width); diff --git a/src/chat.rs b/src/chat.rs index 320fffb854..7eb5f312f0 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -893,13 +893,12 @@ impl ChatId { .context("no file stored in params")?; msg.param.set(Param::File, blob.as_name()); if msg.viewtype == Viewtype::File { - if let Some((better_type, _)) = - message::guess_msgtype_from_suffix(&blob.to_abs_path()) - // We do not do an automatic conversion to other viewtypes here so that - // users can send images as "files" to preserve the original quality - // (usually we compress images). The remaining conversions are done by - // `prepare_msg_blob()` later. - .filter(|&(vt, _)| vt == Viewtype::Webxdc || vt == Viewtype::Vcard) + if let Some((better_type, _)) = message::guess_msgtype_from_suffix(msg) + // We do not do an automatic conversion to other viewtypes here so that + // users can send images as "files" to preserve the original quality + // (usually we compress images). The remaining conversions are done by + // `prepare_msg_blob()` later. + .filter(|&(vt, _)| vt == Viewtype::Webxdc || vt == Viewtype::Vcard) { msg.viewtype = better_type; } @@ -2695,8 +2694,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { // Typical conversions: // - from FILE to AUDIO/VIDEO/IMAGE // - from FILE/IMAGE to GIF */ - if let Some((better_type, _)) = message::guess_msgtype_from_suffix(&blob.to_abs_path()) - { + if let Some((better_type, _)) = message::guess_msgtype_from_suffix(msg) { if better_type != Viewtype::Webxdc || context .ensure_sendable_webxdc_file(&blob.to_abs_path()) @@ -2729,6 +2727,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { } } msg.param.set(Param::File, blob.as_name()); + // TODO not sure if we still need the next part if let (Some(filename), Some(blob_ext)) = (msg.param.get(Param::Filename), blob.suffix()) { let stem = match filename.rsplit_once('.') { Some((stem, _)) => stem, @@ -2739,7 +2738,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { } if !msg.param.exists(Param::MimeType) { - if let Some((_, mime)) = message::guess_msgtype_from_suffix(&blob.to_abs_path()) { + if let Some((_, mime)) = message::guess_msgtype_from_suffix(msg) { msg.param.set(Param::MimeType, mime); } } diff --git a/src/message.rs b/src/message.rs index e35785269e..95363db402 100644 --- a/src/message.rs +++ b/src/message.rs @@ -626,8 +626,8 @@ impl Message { pub fn get_filemime(&self) -> Option { if let Some(m) = self.param.get(Param::MimeType) { return Some(m.to_string()); - } else if let Some(file) = self.param.get(Param::File) { - if let Some((_, mime)) = guess_msgtype_from_suffix(Path::new(file)) { + } else if self.param.exists(Param::File) { + if let Some((_, mime)) = guess_msgtype_from_suffix(self) { return Some(mime.to_string()); } // we have a file but no mimetype, let's use a generic one @@ -1109,6 +1109,7 @@ impl Message { } /// Creates a new blob and sets it as a file associated with a message. + /// TODO this could also deduplicate pub async fn set_file_from_bytes( &mut self, context: &Context, @@ -1459,7 +1460,14 @@ pub async fn get_msg_read_receipts( .await } -pub(crate) fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> { +pub(crate) fn guess_msgtype_from_suffix(msg: &Message) -> Option<(Viewtype, &'static str)> { + msg.param + .get(Param::Filename) + .or_else(|| msg.param.get(Param::File)) + .and_then(|file| guess_msgtype_from_path_suffix(Path::new(file))) +} + +pub(crate) fn guess_msgtype_from_path_suffix(path: &Path) -> Option<(Viewtype, &'static str)> { let extension: &str = &path.extension()?.to_str()?.to_lowercase(); let info = match extension { // before using viewtype other than Viewtype::File, @@ -2201,15 +2209,15 @@ mod tests { #[test] fn test_guess_msgtype_from_suffix() { assert_eq!( - guess_msgtype_from_suffix(Path::new("foo/bar-sth.mp3")), + guess_msgtype_from_path_suffix(Path::new("foo/bar-sth.mp3")), Some((Viewtype::Audio, "audio/mpeg")) ); assert_eq!( - guess_msgtype_from_suffix(Path::new("foo/file.html")), + guess_msgtype_from_path_suffix(Path::new("foo/file.html")), Some((Viewtype::File, "text/html")) ); assert_eq!( - guess_msgtype_from_suffix(Path::new("foo/file.xdc")), + guess_msgtype_from_path_suffix(Path::new("foo/file.xdc")), Some((Viewtype::Webxdc, "application/webxdc+zip")) ); } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 8780073041..c1feed2bcc 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -1,6 +1,7 @@ //! # MIME message production. use std::collections::HashSet; +use std::path::Path; use anyhow::{bail, Context as _, Result}; use base64::Engine as _; @@ -1511,14 +1512,19 @@ pub(crate) fn wrapped_base64_encode(buf: &[u8]) -> String { async fn build_body_file( context: &Context, msg: &Message, - base_name: &str, + base_name: &str, // TODO unused parameter ) -> Result<(PartBuilder, String)> { + let file_name = msg.get_filename().context("msg has no file")?; + let suffix = Path::new(&file_name) + .extension() + .and_then(|e| e.to_str()) + .unwrap_or("dat"); + let blob = msg .param .get_blob(Param::File, context) .await? .context("msg has no file")?; - let suffix = blob.suffix().unwrap_or("dat"); // Get file name to use for sending. For privacy purposes, we do // not transfer the original filenames eg. for images; these names @@ -1562,18 +1568,14 @@ async fn build_body_file( ), &suffix ), - _ => msg - .param - .get(Param::Filename) - .unwrap_or_else(|| blob.as_file_name()) - .to_string(), + _ => file_name, }; /* check mimetype */ let mimetype: mime::Mime = match msg.param.get(Param::MimeType) { Some(mtype) => mtype.parse()?, None => { - if let Some(res) = message::guess_msgtype_from_suffix(blob.as_rel_path()) { + if let Some(res) = message::guess_msgtype_from_suffix(msg) { res.1.parse()? } else { mime::APPLICATION_OCTET_STREAM diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 4eb80b4d9e..3cbe32c130 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -2030,10 +2030,12 @@ fn get_mime_type( } mime::APPLICATION => match mimetype.subtype() { mime::OCTET_STREAM => match filename { - Some(filename) => match message::guess_msgtype_from_suffix(Path::new(&filename)) { - Some((viewtype, _)) => viewtype, - None => Viewtype::File, - }, + Some(filename) => { + match message::guess_msgtype_from_path_suffix(Path::new(&filename)) { + Some((viewtype, _)) => viewtype, + None => Viewtype::File, + } + } None => Viewtype::File, }, _ => Viewtype::File,