diff --git a/melib/src/email/attachments.rs b/melib/src/email/attachments.rs index 8879bd67..ed1a8e48 100644 --- a/melib/src/email/attachments.rs +++ b/melib/src/email/attachments.rs @@ -92,6 +92,16 @@ impl AttachmentBuilder { self } + /// Set body to the entire raw contents, use this if raw contains only data and no headers + /// If raw contains data and headers pass it through AttachmentBuilder::new(). + pub fn set_body_to_raw(&mut self) -> &mut Self { + self.body = StrBuilder { + offset: 0, + length: self.raw.len(), + }; + self + } + pub fn set_content_type(&mut self, val: ContentType) -> &mut Self { self.content_type = val; self diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index abb39e84..d2d7dde2 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -272,9 +272,6 @@ impl Draft { ); ret.push('\n'); ret.push_str(&self.body); - } else if self.attachments.len() == 1 && self.body.is_empty() { - let attachment: Attachment = self.attachments.remove(0).into(); - ret.extend(attachment.into_raw().chars()); } else { let mut parts = Vec::with_capacity(self.attachments.len() + 1); let attachments = std::mem::replace(&mut self.attachments, Vec::new()); @@ -453,9 +450,10 @@ where let mut file = std::fs::File::open(&path)?; let mut contents = Vec::new(); file.read_to_end(&mut contents)?; - let mut attachment = AttachmentBuilder::new(b""); + let mut attachment = AttachmentBuilder::default(); attachment .set_raw(contents) + .set_body_to_raw() .set_content_type(ContentType::Other { name: path.file_name().map(|s| s.to_string_lossy().into()), tag: b"application/octet-stream".to_vec(), diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 376422ae..377fa344 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -115,6 +115,7 @@ enum ViewMode { Edit, Embed, SelectRecipients(Selector
), + Send(Selector), } impl ViewMode { @@ -364,6 +365,66 @@ impl Composer { } } } + + fn save_draft(&mut self, context: &mut Context, folder_type: SpecialUseMailbox) { + let mut failure = true; + let draft = std::mem::replace(&mut self.draft, Draft::default()); + + let draft = draft.finalise().unwrap(); + for folder in &[ + &context.accounts[self.account_cursor].special_use_folder(folder_type), + &context.accounts[self.account_cursor].special_use_folder(SpecialUseMailbox::Inbox), + &context.accounts[self.account_cursor].special_use_folder(SpecialUseMailbox::Normal), + ] { + if folder.is_none() { + continue; + } + let folder = folder.unwrap(); + if let Err(e) = context.accounts[self.account_cursor].save( + draft.as_bytes(), + folder, + Some(Flag::SEEN | Flag::DRAFT), + ) { + debug!("{:?} could not save draft msg", e); + log( + format!( + "Could not save draft in '{}' folder: {}.", + folder, + e.to_string() + ), + ERROR, + ); + context.replies.push_back(UIEvent::Notification( + Some(format!("Could not save draft in '{}' folder.", folder)), + e.into(), + Some(NotificationType::ERROR), + )); + } else { + failure = false; + break; + } + } + + if failure { + let file = create_temp_file(draft.as_bytes(), None, None, false); + debug!("message saved in {}", file.path.display()); + log( + format!( + "Message was stored in {} so that you can restore it manually.", + file.path.display() + ), + INFO, + ); + context.replies.push_back(UIEvent::Notification( + Some("Could not save in any folder".into()), + format!( + "Message was stored in {} so that you can restore it manually.", + file.path.display() + ), + Some(NotificationType::INFO), + )); + } + } } impl Component for Composer { @@ -543,6 +604,9 @@ impl Component for Composer { match self.mode { ViewMode::Edit | ViewMode::Embed => {} + ViewMode::Send(ref mut s) => { + s.draw(grid, center_area(area, s.content.size()), context); + } ViewMode::SelectRecipients(ref mut s) => { s.draw(grid, center_area(area, s.content.size()), context); } @@ -565,6 +629,34 @@ impl Component for Composer { return true; } } + (ViewMode::Send(ref mut selector), _, _) => { + if selector.process_event(event, context) { + if selector.is_done() { + let s = match std::mem::replace(&mut self.mode, ViewMode::Edit) { + ViewMode::Send(s) => s, + _ => unreachable!(), + }; + let result: bool = s.collect()[0]; + if result { + self.update_draft(); + if send_draft( + self.sign_mail, + context, + self.account_cursor, + self.draft.clone(), + ) { + self.save_draft(context, SpecialUseMailbox::Sent); + context + .replies + .push_back(UIEvent::Action(Tab(Kill(self.id)))); + } else { + self.save_draft(context, SpecialUseMailbox::Drafts); + } + } + } + return true; + } + } (ViewMode::SelectRecipients(ref mut selector), _, _) => { if selector.process_event(event, context) { if selector.is_done() { @@ -601,70 +693,7 @@ impl Component for Composer { } 'n' => {} 'y' => { - let mut failure = true; - let draft = std::mem::replace(&mut self.draft, Draft::default()); - - let draft = draft.finalise().unwrap(); - for folder in &[ - &context.accounts[self.account_cursor] - .special_use_folder(SpecialUseMailbox::Drafts), - &context.accounts[self.account_cursor] - .special_use_folder(SpecialUseMailbox::Inbox), - &context.accounts[self.account_cursor] - .special_use_folder(SpecialUseMailbox::Normal), - ] { - if folder.is_none() { - continue; - } - let folder = folder.unwrap(); - if let Err(e) = context.accounts[self.account_cursor].save( - draft.as_bytes(), - folder, - Some(Flag::SEEN | Flag::DRAFT), - ) { - debug!("{:?} could not save draft msg", e); - log( - format!( - "Could not save draft in '{}' folder: {}.", - folder, - e.to_string() - ), - ERROR, - ); - context.replies.push_back(UIEvent::Notification( - Some(format!( - "Could not save draft in '{}' folder.", - folder - )), - e.into(), - Some(NotificationType::ERROR), - )); - } else { - failure = false; - break; - } - } - - if failure { - let file = - create_temp_file(draft.as_bytes(), None, None, false); - debug!("message saved in {}", file.path.display()); - log( - format!( - "Message was stored in {} so that you can restore it manually.", - file.path.display() - ), - INFO, - ); - context.replies.push_back(UIEvent::Notification( - Some("Could not save in any folder".into()), - format!( - "Message was stored in {} so that you can restore it manually.", - file.path.display() - ), - Some(NotificationType::INFO), - )); - } + self.save_draft(context, SpecialUseMailbox::Drafts); context.replies.push_back(UIEvent::Action(Tab(Kill(u)))); return true; } @@ -720,18 +749,15 @@ impl Component for Composer { self.cursor = Cursor::Body; self.dirty = true; } - UIEvent::Input(Key::Char('s')) => { + UIEvent::Input(Key::Char('s')) if self.mode.is_edit() => { self.update_draft(); - if send_draft( - self.sign_mail, + self.mode = ViewMode::Send(Selector::new( + "send mail?", + vec![(true, "yes".to_string()), (false, "no".to_string())], + /* only one choice */ + true, context, - self.account_cursor, - self.draft.clone(), - ) { - context - .replies - .push_back(UIEvent::Action(Tab(Kill(self.id)))); - } + )); return true; } UIEvent::EmbedInput((Key::Ctrl('z'), _)) => { @@ -1071,10 +1097,17 @@ pub fn send_draft( ) -> bool { use std::io::Write; use std::process::{Command, Stdio}; - let mut failure = true; let settings = &context.settings; let format_flowed = settings.composing.format_flowed; let parts = split_command!(settings.composing.mailer_cmd); + if parts.is_empty() { + context.replies.push_back(UIEvent::Notification( + None, + String::from("mailer_cmd configuration value is empty"), + Some(NotificationType::ERROR), + )); + return false; + } let (cmd, args) = (parts[0], &parts[1..]); let mut msmtp = Command::new(cmd) .args(args) @@ -1147,53 +1180,6 @@ pub fn send_draft( stdin .write_all(draft.as_bytes()) .expect("Failed to write to stdin"); - for folder in &[ - &context.accounts[account_cursor].special_use_folder(SpecialUseMailbox::Sent), - &context.accounts[account_cursor].special_use_folder(SpecialUseMailbox::Inbox), - &context.accounts[account_cursor].special_use_folder(SpecialUseMailbox::Normal), - ] { - if folder.is_none() { - continue; - } - let folder = folder.unwrap(); - if let Err(e) = - context.accounts[account_cursor].save(draft.as_bytes(), folder, Some(Flag::SEEN)) - { - debug!("{:?} could not save sent msg", e); - log( - format!("Could not save in '{}' folder: {}.", folder, e.to_string()), - ERROR, - ); - context.replies.push_back(UIEvent::Notification( - Some(format!("Could not save in '{}' folder.", folder)), - e.into(), - Some(NotificationType::ERROR), - )); - } else { - failure = false; - break; - } - } - - if failure { - let file = create_temp_file(draft.as_bytes(), None, None, false); - debug!("message saved in {}", file.path.display()); - log( - format!( - "Message was stored in {} so that you can restore it manually.", - file.path.display() - ), - INFO, - ); - context.replies.push_back(UIEvent::Notification( - Some("Could not save in any folder".into()), - format!( - "Message was stored in {} so that you can restore it manually.", - file.path.display() - ), - Some(NotificationType::INFO), - )); - } } let output = msmtp.wait().expect("Failed to wait on mailer"); if output.success() { @@ -1203,23 +1189,23 @@ pub fn send_draft( None, )); } else { - if let Some(exit_code) = output.code() { - log( - format!( - "Could not send e-mail using `{}`: Process exited with {}", - cmd, exit_code - ), - ERROR, - ); + let error_message = if let Some(exit_code) = output.code() { + format!( + "Could not send e-mail using `{}`: Process exited with {}", + cmd, exit_code + ) } else { - log( - format!( - "Could not send e-mail using `{}`: Process was killed by signal", - cmd - ), - ERROR, - ); - } + format!( + "Could not send e-mail using `{}`: Process was killed by signal", + cmd + ) + }; + context.replies.push_back(UIEvent::Notification( + Some("Message not sent.".into()), + error_message.clone(), + Some(NotificationType::ERROR), + )); + log(error_message, ERROR); } - !failure + true } diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index ddf1d156..da486101 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -1763,6 +1763,13 @@ impl Component for Selector { self.done = true; return true; } + (UIEvent::Input(Key::Esc), _) => { + for e in self.entries.iter_mut() { + e.1 = false; + } + self.done = true; + return true; + } (UIEvent::Input(Key::Char('\n')), SelectorCursor::Cancel) if !self.single_only => { for e in self.entries.iter_mut() { e.1 = false;