From 1cb0a25e168b284bc545bc544ec3f74965ff081d Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 8 Nov 2025 00:17:44 +0000 Subject: [PATCH] fix: do not ignore I/O errors in `BlobObject::store_from_base64` --- src/blob.rs | 11 ++++++++--- src/config.rs | 2 +- src/contact.rs | 9 ++++----- src/mimeparser.rs | 30 +++++++++++++++--------------- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/blob.rs b/src/blob.rs index 1b11cfea3..b7c83b1d2 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -234,8 +234,13 @@ impl<'a> BlobObject<'a> { /// If `data` represents an image of known format, this adds the corresponding extension. /// /// Even though this function is not async, it's OK to call it from an async context. - pub(crate) fn store_from_base64(context: &Context, data: &str) -> Result { - let buf = base64::engine::general_purpose::STANDARD.decode(data)?; + /// + /// Returns an error if there is an I/O problem, + /// but in case of a failure to decode base64 returns `Ok(None)`. + pub(crate) fn store_from_base64(context: &Context, data: &str) -> Result> { + let Ok(buf) = base64::engine::general_purpose::STANDARD.decode(data) else { + return Ok(None); + }; let name = if let Ok(format) = image::guess_format(&buf) { if let Some(ext) = format.extensions_str().first() { format!("file.{ext}") @@ -246,7 +251,7 @@ impl<'a> BlobObject<'a> { String::new() }; let blob = BlobObject::create_and_deduplicate_from_bytes(context, &buf, &name)?; - Ok(blob.as_name().to_string()) + Ok(Some(blob.as_name().to_string())) } /// Recode image to avatar size. diff --git a/src/config.rs b/src/config.rs index 130b0df78..7cb7617f1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -665,7 +665,7 @@ impl Context { Config::Selfavatar if value.is_empty() => None, Config::Selfavatar => { config_value = BlobObject::store_from_base64(self, value)?; - Some(config_value.as_str()) + config_value.as_deref() } _ => Some(value), }; diff --git a/src/contact.rs b/src/contact.rs index f15790260..989495965 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -369,16 +369,15 @@ async fn import_vcard_contact(context: &Context, contact: &VcardContact) -> Resu return Ok(id); } let path = match &contact.profile_image { - Some(image) => match BlobObject::store_from_base64(context, image) { - Err(e) => { + Some(image) => match BlobObject::store_from_base64(context, image)? { + None => { warn!( context, - "import_vcard_contact: Could not decode and save avatar for {}: {e:#}.", - contact.addr + "import_vcard_contact: Could not decode avatar for {}.", contact.addr ); None } - Ok(path) => Some(path), + Some(path) => Some(path), }, None => None, }; diff --git a/src/mimeparser.rs b/src/mimeparser.rs index e04f1bc98..5d103eef6 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -714,14 +714,16 @@ impl MimeMessage { } /// Parses avatar action headers. - fn parse_avatar_headers(&mut self, context: &Context) { + fn parse_avatar_headers(&mut self, context: &Context) -> Result<()> { if let Some(header_value) = self.get_header(HeaderDef::ChatGroupAvatar) { - self.group_avatar = self.avatar_action_from_header(context, header_value.to_string()); + self.group_avatar = + self.avatar_action_from_header(context, header_value.to_string())?; } if let Some(header_value) = self.get_header(HeaderDef::ChatUserAvatar) { - self.user_avatar = self.avatar_action_from_header(context, header_value.to_string()); + self.user_avatar = self.avatar_action_from_header(context, header_value.to_string())?; } + Ok(()) } fn parse_videochat_headers(&mut self) { @@ -828,7 +830,7 @@ impl MimeMessage { async fn parse_headers(&mut self, context: &Context) -> Result<()> { self.parse_system_message_headers(context); - self.parse_avatar_headers(context); + self.parse_avatar_headers(context)?; self.parse_videochat_headers(); if self.delivery_report.is_none() { self.squash_attachment_parts(); @@ -930,21 +932,18 @@ impl MimeMessage { &mut self, context: &Context, header_value: String, - ) -> Option { - if header_value == "0" { + ) -> Result> { + let res = if header_value == "0" { Some(AvatarAction::Delete) } else if let Some(base64) = header_value .split_ascii_whitespace() .collect::() .strip_prefix("base64:") { - match BlobObject::store_from_base64(context, base64) { - Ok(path) => Some(AvatarAction::Change(path)), - Err(err) => { - warn!( - context, - "Could not decode and save avatar to blob file: {:#}", err, - ); + match BlobObject::store_from_base64(context, base64)? { + Some(path) => Some(AvatarAction::Change(path)), + None => { + warn!(context, "Could not decode avatar base64"); None } } @@ -958,7 +957,7 @@ impl MimeMessage { if let Some(blob) = part.param.get(Param::File) { let res = Some(AvatarAction::Change(blob.to_string())); self.parts.remove(i); - return res; + return Ok(res); } break; } @@ -966,7 +965,8 @@ impl MimeMessage { i += 1; } None - } + }; + Ok(res) } /// Returns true if the message was encrypted as defined in