diff --git a/src/command.rs b/src/command.rs index a12222e1b..c20631b98 100644 --- a/src/command.rs +++ b/src/command.rs @@ -701,6 +701,19 @@ Alternatives(&[to_stream!(One(Literal("add-attachment")), One(Filepath)), to_str } ) }, + { tags: ["export-mail "], + desc: "export-mail PATH", + tokens: &[One(Literal("export-mail")), One(Filepath)], + parser:( + fn export_mail(input: &[u8]) -> IResult<&[u8], Action> { + let (input, _) = tag("export-mail")(input.trim())?; + let (input, _) = is_a(" ")(input)?; + let (input, path) = quoted_argument(input.trim())?; + let (input, _) = eof(input)?; + Ok((input, View(ExportMail(path.to_string())))) + } + ) + }, { tags: ["tag", "tag add", "tag remove"], desc: "tag [add/remove], edits message's tags.", tokens: &[One(Literal("tag")), One(Alternatives(&[to_stream!(One(Literal("add"))), to_stream!(One(Literal("remove")))]))], @@ -836,7 +849,7 @@ fn account_action(input: &[u8]) -> IResult<&[u8], Action> { } fn view(input: &[u8]) -> IResult<&[u8], Action> { - alt((pipe, save_attachment))(input) + alt((pipe, save_attachment, export_mail))(input) } pub fn parse_command(input: &[u8]) -> Result { diff --git a/src/command/actions.rs b/src/command/actions.rs index afe2865c8..9faad818d 100644 --- a/src/command/actions.rs +++ b/src/command/actions.rs @@ -75,6 +75,7 @@ pub enum MailingListAction { pub enum ViewAction { Pipe(String, Vec), SaveAttachment(usize, String), + ExportMail(String), } #[derive(Debug)] diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index 3757a02cf..d4891d013 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -751,9 +751,6 @@ impl MailView { lidx: usize, context: &mut Context, ) -> Option<&'_ melib::Attachment> { - if lidx == 0 { - return None; - } let display = if let MailViewState::Loaded { ref display, .. } = self.state { display } else { @@ -821,7 +818,18 @@ impl MailView { None } - return find_attachment(root_attachment, &path[1..]); + let ret = find_attachment(root_attachment, &path[1..]); + if lidx == 0 { + return ret.and_then(|a| { + if a.content_disposition.kind.is_attachment() { + Some(a) + } else { + None + } + }); + } else { + return ret; + } } context .replies @@ -1941,7 +1949,8 @@ impl Component for MailView { UIEvent::EnvelopeRename(old_hash, new_hash) if self.coordinates.2 == old_hash => { self.coordinates.2 = new_hash; } - UIEvent::Action(View(ViewAction::SaveAttachment(a_i, ref path))) => { + UIEvent::Action(View(ViewAction::ExportMail(ref path))) => { + // Save entire message as eml let account = &context.accounts[&self.coordinates.0]; if !account.contains_key(self.coordinates.2) { /* The envelope has been renamed or removed, so wait for the appropriate event to @@ -1967,40 +1976,67 @@ impl Component for MailView { }; let mut path = std::path::Path::new(path).to_path_buf(); - if a_i == 0 { - // Save entire message as eml - if path.is_dir() { - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - path.push(format!("{}.eml", envelope.message_id_display())); - } - match save_attachment(&path, bytes) { - Err(err) => { - context.replies.push_back(UIEvent::Notification( - Some(format!("Failed to create file at {}", path.display())), - err.to_string(), - Some(NotificationType::Error(melib::ErrorKind::External)), - )); - log( - format!( - "Failed to create file at {}: {}", - path.display(), - err.to_string() - ), - ERROR, - ); - return true; - } - Ok(()) => { - context.replies.push_back(UIEvent::Notification( - None, - format!("Saved at {}", &path.display()), - Some(NotificationType::Info), - )); - } - } - return true; + if path.is_dir() { + let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); + path.push(format!("{}.eml", envelope.message_id_raw())); } + match save_attachment(&path, bytes) { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some(format!("Failed to create file at {}", path.display())), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), + )); + log( + format!( + "Failed to create file at {}: {}", + path.display(), + err.to_string() + ), + ERROR, + ); + return true; + } + Ok(()) => { + context.replies.push_back(UIEvent::Notification( + None, + format!("Saved at {}", &path.display()), + Some(NotificationType::Info), + )); + } + } + + return true; + } + UIEvent::Action(View(ViewAction::SaveAttachment(a_i, ref path))) => { + { + let account = &context.accounts[&self.coordinates.0]; + if !account.contains_key(self.coordinates.2) { + /* The envelope has been renamed or removed, so wait for the appropriate event to + * arrive */ + return true; + } + } + let bytes = if let MailViewState::Loaded { ref bytes, .. } = self.state { + bytes + } else if let MailViewState::Error { ref err } = self.state { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + err.to_string(), + Some(NotificationType::Error(err.kind)), + )); + log( + format!("Failed to open envelope: {}", err.to_string()), + ERROR, + ); + self.init_futures(context); + return true; + } else { + return true; + }; + + let mut path = std::path::Path::new(path).to_path_buf(); if let Some(u) = self.open_attachment(a_i, context) { if path.is_dir() { @@ -2035,6 +2071,40 @@ impl Component for MailView { )); } } + } else if a_i == 0 { + let account = &context.accounts[&self.coordinates.0]; + // Save entire message as eml + if path.is_dir() { + let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); + path.push(format!("{}.eml", envelope.message_id_raw())); + } + match save_attachment(&path, bytes) { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some(format!("Failed to create file at {}", path.display())), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), + )); + log( + format!( + "Failed to create file at {}: {}", + path.display(), + err.to_string() + ), + ERROR, + ); + return true; + } + Ok(()) => { + context.replies.push_back(UIEvent::Notification( + None, + format!("Saved at {}", &path.display()), + Some(NotificationType::Info), + )); + } + } + + return true; } else { context .replies