diff --git a/src/chat.rs b/src/chat.rs index 9d7726055..cc9b5734f 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -677,20 +677,10 @@ fn prepare_msg_common(context: &Context, chat_id: u32, msg: &mut Message) -> Res if msg.type_0 == Viewtype::Text { // the caller should check if the message text is empty } else if msgtype_has_file(msg.type_0) { - let param = msg.param.get(Param::File).ok_or_else(|| { - format_err!("Attachment missing for message of type #{}.", msg.type_0) - })?; - let file = ParamsFile::from_param(context, param)?; - let blob = match file { - ParamsFile::FsPath(path) => { - if msg.is_increation() { - BlobObject::from_path(context, path)? - } else { - BlobObject::create_from_path(context, path)? - } - } - ParamsFile::Blob(blob) => blob, - }; + let blob = msg + .param + .get_blob(Param::File, context, !msg.is_increation())? + .ok_or_else(|| format_err!("Attachment missing for message of type #{}", msg.type_0))?; msg.param.set(Param::File, blob.as_name()); if msg.type_0 == Viewtype::File || msg.type_0 == Viewtype::Image { // Correct the type, take care not to correct already very special @@ -892,21 +882,10 @@ fn do_set_draft(context: &Context, chat_id: u32, msg: &mut Message) -> Result<() None => bail!("No text in draft"), }, _ => { - let param = msg + let blob = msg .param - .get(Param::File) + .get_blob(Param::File, context, !msg.is_increation())? .ok_or_else(|| format_err!("No file stored in params"))?; - let file = ParamsFile::from_param(context, param)?; - let blob = match file { - ParamsFile::FsPath(path) => { - if msg.is_increation() { - BlobObject::from_path(context, path)? - } else { - BlobObject::create_from_path(context, path)? - } - } - ParamsFile::Blob(blob) => blob, - }; msg.param.set(Param::File, blob.as_name()); } } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 452b21bfb..7db594ae7 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1228,14 +1228,8 @@ unsafe fn create_or_lookup_group( if part.typ == Viewtype::Image { grpimage = part .param - .get(Param::File) - .and_then(|param| ParamsFile::from_param(context, param).ok()) - .and_then(|file| match file { - ParamsFile::FsPath(path) => { - BlobObject::create_from_path(context, path).ok() - } - ParamsFile::Blob(blob) => Some(blob), - }); + .get_blob(Param::File, context, true) + .unwrap_or(None); info!(context, "found image {:?}", grpimage); changed = true; } diff --git a/src/job.rs b/src/job.rs index 63820783e..6daf72cf2 100644 --- a/src/job.rs +++ b/src/job.rs @@ -139,15 +139,7 @@ impl Job { } } - if let Some(filename) = self - .param - .get(Param::File) - .and_then(|param| ParamsFile::from_param(context, param).ok()) - .map(|file| match file { - ParamsFile::FsPath(path) => path, - ParamsFile::Blob(blob) => blob.to_abs_path(), - }) - { + if let Some(filename) = self.param.get_path(Param::File, context).unwrap_or(None) { if let Ok(body) = dc_read_file(context, &filename) { if let Some(recipients) = self.param.get(Param::Recipients) { let recipients_list = recipients @@ -573,15 +565,7 @@ pub fn job_send_msg(context: &Context, msg_id: u32) -> Result<(), Error> { let mut mimefactory = MimeFactory::load_msg(context, msg_id)?; if chat::msgtype_has_file(mimefactory.msg.type_0) { - let file_param = mimefactory - .msg - .param - .get(Param::File) - .and_then(|param| ParamsFile::from_param(context, param).ok()) - .map(|file| match file { - ParamsFile::FsPath(path) => path, - ParamsFile::Blob(blob) => blob.to_abs_path(), - }); + let file_param = mimefactory.msg.param.get_path(Param::File, context)?; if let Some(pathNfilename) = file_param { if (mimefactory.msg.type_0 == Viewtype::Image || mimefactory.msg.type_0 == Viewtype::Gif) diff --git a/src/message.rs b/src/message.rs index 46eab4e7b..8a1bf4d50 100644 --- a/src/message.rs +++ b/src/message.rs @@ -145,13 +145,7 @@ impl Message { } pub fn get_file(&self, context: &Context) -> Option { - self.param - .get(Param::File) - .map_or(None, |param| ParamsFile::from_param(context, param).ok()) - .map(|file| match file { - ParamsFile::FsPath(path) => path, - ParamsFile::Blob(blob) => blob.to_abs_path(), - }) + self.param.get_path(Param::File, context).unwrap_or(None) } /// Check if a message has a location bound to it. @@ -241,12 +235,8 @@ impl Message { pub fn get_filebytes(&self, context: &Context) -> u64 { self.param - .get(Param::File) - .and_then(|param| ParamsFile::from_param(context, param).ok()) - .map(|file| match file { - ParamsFile::FsPath(path) => path, - ParamsFile::Blob(blob) => blob.to_abs_path(), - }) + .get_path(Param::File, context) + .unwrap_or(None) .map(|path| dc_get_filebytes(context, &path)) .unwrap_or_default() } @@ -829,22 +819,14 @@ pub fn get_summarytext_by_raw( .stock_str(StockMessage::AcSetupMsgSubject) .to_string() } else { - let file_name: String = if let Some(param) = param.get(Param::File) { - ParamsFile::from_param(context, param) - .ok() - .map(|file| match file { - ParamsFile::FsPath(path) => path, - ParamsFile::Blob(blob) => blob.to_abs_path(), - }) - .and_then(|path| { - path.file_name() - .map(|fname| fname.to_string_lossy().into_owned()) - }) - } else { - None - } - .unwrap_or_else(|| "ErrFileName".to_string()); - + let file_name: String = param + .get_path(Param::File, context) + .unwrap_or(None) + .and_then(|path| { + path.file_name() + .map(|fname| fname.to_string_lossy().into_owned()) + }) + .unwrap_or_else(|| String::from("ErrFileName")); let label = context.stock_str(if viewtype == Viewtype::Audio { StockMessage::Audio } else { diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 826f10744..4905629f0 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -11,7 +11,6 @@ use mmime::mailmime::write_mem::*; use mmime::mmapstring::*; use mmime::other::*; -use crate::blob::BlobObject; use crate::chat::{self, Chat}; use crate::config::Config; use crate::constants::*; @@ -794,15 +793,10 @@ fn build_body_file( msg: &Message, base_name: &str, ) -> Result<(*mut Mailmime, String), Error> { - let param = msg + let blob = msg .param - .get(Param::File) + .get_blob(Param::File, context, true)? .ok_or_else(|| format_err!("msg has no filename"))?; - let file = ParamsFile::from_param(context, param)?; - let blob = match file { - ParamsFile::FsPath(path) => BlobObject::create_from_path(context, path)?, - ParamsFile::Blob(blob) => blob, - }; let suffix = blob.suffix().unwrap_or("dat"); // Get file name to use for sending. For privacy purposes, we do @@ -908,14 +902,7 @@ pub(crate) fn vec_contains_lowercase(vec: &[String], part: &str) -> bool { } fn is_file_size_okay(context: &Context, msg: &Message) -> bool { - match msg - .param - .get(Param::File) - .and_then(|param| ParamsFile::from_param(context, param).ok()) - .map(|file| match file { - ParamsFile::FsPath(path) => path, - ParamsFile::Blob(blob) => blob.to_abs_path(), - }) { + match msg.param.get_path(Param::File, context).unwrap_or(None) { Some(path) => { let bytes = dc_get_filebytes(context, &path); bytes <= (49 * 1024 * 1024 / 4 * 3) diff --git a/src/param.rs b/src/param.rs index e653f80c3..9ad8e1316 100644 --- a/src/param.rs +++ b/src/param.rs @@ -194,6 +194,72 @@ impl Params { self.get(key).and_then(|s| s.parse().ok()) } + /// Gets the given parameter and parse as [ParamsFile]. + /// + /// See also [Params::get_blob] and [Params::get_path] which may + /// be more convenient. + pub fn get_file<'a>( + &self, + key: Param, + context: &'a Context, + ) -> Result>, BlobError> { + let val = match self.get(key) { + Some(val) => val, + None => return Ok(None), + }; + ParamsFile::from_param(context, val).map(|file| Some(file)) + } + + /// Gets the parameter and returns a [BlobObject] for it. + /// + /// This parses the parameter value as a [ParamsFile] and than + /// tries to return a [BlobObject] for that file. If the file is + /// not yet a valid blob, one will be created by copying the file + /// only if `create` is set to `true`, otherwise the a [BlobError] + /// will result. + /// + /// Note that in the [ParamsFile::FsPath] case the blob can be + /// created without copying if the path already referes to a valid + /// blob. If so a [BlobObject] will be returned regardless of the + /// `create` argument. + pub fn get_blob<'a>( + &self, + key: Param, + context: &'a Context, + create: bool, + ) -> Result>, BlobError> { + let val = match self.get(key) { + Some(val) => val, + None => return Ok(None), + }; + let file = ParamsFile::from_param(context, val)?; + let blob = match file { + ParamsFile::FsPath(path) => match create { + true => BlobObject::create_from_path(context, path)?, + false => BlobObject::from_path(context, path)?, + }, + ParamsFile::Blob(blob) => blob, + }; + Ok(Some(blob)) + } + + /// Gets the parameter and returns a [PathBuf] for it. + /// + /// This parses the parameter value as a [ParamsFile] and returns + /// a [PathBuf] to the file. + pub fn get_path(&self, key: Param, context: &Context) -> Result, BlobError> { + let val = match self.get(key) { + Some(val) => val, + None => return Ok(None), + }; + let file = ParamsFile::from_param(context, val)?; + let path = match file { + ParamsFile::FsPath(path) => path, + ParamsFile::Blob(blob) => blob.to_abs_path(), + }; + Ok(Some(path)) + } + /// Set the given paramter to the passed in `i32`. pub fn set_int(&mut self, key: Param, value: i32) -> &mut Self { self.set(key, format!("{}", value)); @@ -213,18 +279,18 @@ impl Params { /// UTF-8 string it is always safe to convert the value contained /// within the [ParamsFile::FsPath] back to a [String] or [&str]. /// Despite the type itself does not guarantee this. -#[derive(Debug)] -pub enum ParamsFile<'c> { +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ParamsFile<'a> { FsPath(PathBuf), - Blob(BlobObject<'c>), + Blob(BlobObject<'a>), } -impl<'c> ParamsFile<'c> { +impl<'a> ParamsFile<'a> { /// Parse the [Param::File] value into an object. /// /// If the value was stored into the [Params] correctly this /// should not fail. - pub fn from_param(context: &'c Context, src: &str) -> Result, BlobError> { + pub fn from_param(context: &'a Context, src: &str) -> Result, BlobError> { let param = match src.starts_with("$BLOBDIR/") { true => ParamsFile::Blob(BlobObject::from_name(context, src.to_string())?), false => ParamsFile::FsPath(PathBuf::from(src)), @@ -237,8 +303,10 @@ impl<'c> ParamsFile<'c> { mod tests { use super::*; + use std::fs; use std::path::Path; + use crate::blob::BlobErrorKind; use crate::test_utils::*; #[test] @@ -304,4 +372,44 @@ mod tests { assert!(false, "Wrong enum variant"); } } + + // Tests for Params::get_file(), Params::get_path() and Params::get_blob(). + #[test] + fn test_params_get_fileparam() { + let t = dummy_context(); + let fname = t.dir.path().join("foo"); + let mut p = Params::new(); + p.set(Param::File, fname.to_str().unwrap()); + + let file = p.get_file(Param::File, &t.ctx).unwrap().unwrap(); + assert_eq!(file, ParamsFile::FsPath(fname.clone())); + + let path = p.get_path(Param::File, &t.ctx).unwrap().unwrap(); + assert_eq!(path, fname); + + // Blob does not exist yet, expect BlobError. + let err = p.get_blob(Param::File, &t.ctx, false).unwrap_err(); + assert_eq!(err.kind(), BlobErrorKind::WrongBlobdir); + + fs::write(fname, b"boo").unwrap(); + let blob = p.get_blob(Param::File, &t.ctx, true).unwrap().unwrap(); + assert_eq!( + blob, + BlobObject::from_name(&t.ctx, "foo".to_string()).unwrap() + ); + + // Blob in blobdir, expect blob. + let bar = t.ctx.get_blobdir().join("bar"); + p.set(Param::File, bar.to_str().unwrap()); + let blob = p.get_blob(Param::File, &t.ctx, false).unwrap().unwrap(); + assert_eq!( + blob, + BlobObject::from_name(&t.ctx, "bar".to_string()).unwrap() + ); + + p.remove(Param::File); + assert!(p.get_file(Param::File, &t.ctx).unwrap().is_none()); + assert!(p.get_path(Param::File, &t.ctx).unwrap().is_none()); + assert!(p.get_blob(Param::File, &t.ctx, false).unwrap().is_none()); + } }