parent
458f8da332
commit
ce646abc7a
|
@ -92,6 +92,16 @@ impl AttachmentBuilder {
|
||||||
self
|
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 {
|
pub fn set_content_type(&mut self, val: ContentType) -> &mut Self {
|
||||||
self.content_type = val;
|
self.content_type = val;
|
||||||
self
|
self
|
||||||
|
|
|
@ -272,9 +272,6 @@ impl Draft {
|
||||||
);
|
);
|
||||||
ret.push('\n');
|
ret.push('\n');
|
||||||
ret.push_str(&self.body);
|
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 {
|
} else {
|
||||||
let mut parts = Vec::with_capacity(self.attachments.len() + 1);
|
let mut parts = Vec::with_capacity(self.attachments.len() + 1);
|
||||||
let attachments = std::mem::replace(&mut self.attachments, Vec::new());
|
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 file = std::fs::File::open(&path)?;
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
file.read_to_end(&mut contents)?;
|
file.read_to_end(&mut contents)?;
|
||||||
let mut attachment = AttachmentBuilder::new(b"");
|
let mut attachment = AttachmentBuilder::default();
|
||||||
attachment
|
attachment
|
||||||
.set_raw(contents)
|
.set_raw(contents)
|
||||||
|
.set_body_to_raw()
|
||||||
.set_content_type(ContentType::Other {
|
.set_content_type(ContentType::Other {
|
||||||
name: path.file_name().map(|s| s.to_string_lossy().into()),
|
name: path.file_name().map(|s| s.to_string_lossy().into()),
|
||||||
tag: b"application/octet-stream".to_vec(),
|
tag: b"application/octet-stream".to_vec(),
|
||||||
|
|
|
@ -115,6 +115,7 @@ enum ViewMode {
|
||||||
Edit,
|
Edit,
|
||||||
Embed,
|
Embed,
|
||||||
SelectRecipients(Selector<Address>),
|
SelectRecipients(Selector<Address>),
|
||||||
|
Send(Selector<bool>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewMode {
|
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 {
|
impl Component for Composer {
|
||||||
|
@ -543,6 +604,9 @@ impl Component for Composer {
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Edit | ViewMode::Embed => {}
|
ViewMode::Edit | ViewMode::Embed => {}
|
||||||
|
ViewMode::Send(ref mut s) => {
|
||||||
|
s.draw(grid, center_area(area, s.content.size()), context);
|
||||||
|
}
|
||||||
ViewMode::SelectRecipients(ref mut s) => {
|
ViewMode::SelectRecipients(ref mut s) => {
|
||||||
s.draw(grid, center_area(area, s.content.size()), context);
|
s.draw(grid, center_area(area, s.content.size()), context);
|
||||||
}
|
}
|
||||||
|
@ -565,6 +629,34 @@ impl Component for Composer {
|
||||||
return true;
|
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), _, _) => {
|
(ViewMode::SelectRecipients(ref mut selector), _, _) => {
|
||||||
if selector.process_event(event, context) {
|
if selector.process_event(event, context) {
|
||||||
if selector.is_done() {
|
if selector.is_done() {
|
||||||
|
@ -601,70 +693,7 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
'n' => {}
|
'n' => {}
|
||||||
'y' => {
|
'y' => {
|
||||||
let mut failure = true;
|
self.save_draft(context, SpecialUseMailbox::Drafts);
|
||||||
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),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
context.replies.push_back(UIEvent::Action(Tab(Kill(u))));
|
context.replies.push_back(UIEvent::Action(Tab(Kill(u))));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -720,18 +749,15 @@ impl Component for Composer {
|
||||||
self.cursor = Cursor::Body;
|
self.cursor = Cursor::Body;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char('s')) => {
|
UIEvent::Input(Key::Char('s')) if self.mode.is_edit() => {
|
||||||
self.update_draft();
|
self.update_draft();
|
||||||
if send_draft(
|
self.mode = ViewMode::Send(Selector::new(
|
||||||
self.sign_mail,
|
"send mail?",
|
||||||
|
vec![(true, "yes".to_string()), (false, "no".to_string())],
|
||||||
|
/* only one choice */
|
||||||
|
true,
|
||||||
context,
|
context,
|
||||||
self.account_cursor,
|
));
|
||||||
self.draft.clone(),
|
|
||||||
) {
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::Action(Tab(Kill(self.id))));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::EmbedInput((Key::Ctrl('z'), _)) => {
|
UIEvent::EmbedInput((Key::Ctrl('z'), _)) => {
|
||||||
|
@ -1071,10 +1097,17 @@ pub fn send_draft(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
let mut failure = true;
|
|
||||||
let settings = &context.settings;
|
let settings = &context.settings;
|
||||||
let format_flowed = settings.composing.format_flowed;
|
let format_flowed = settings.composing.format_flowed;
|
||||||
let parts = split_command!(settings.composing.mailer_cmd);
|
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 (cmd, args) = (parts[0], &parts[1..]);
|
||||||
let mut msmtp = Command::new(cmd)
|
let mut msmtp = Command::new(cmd)
|
||||||
.args(args)
|
.args(args)
|
||||||
|
@ -1147,53 +1180,6 @@ pub fn send_draft(
|
||||||
stdin
|
stdin
|
||||||
.write_all(draft.as_bytes())
|
.write_all(draft.as_bytes())
|
||||||
.expect("Failed to write to stdin");
|
.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");
|
let output = msmtp.wait().expect("Failed to wait on mailer");
|
||||||
if output.success() {
|
if output.success() {
|
||||||
|
@ -1203,23 +1189,23 @@ pub fn send_draft(
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
if let Some(exit_code) = output.code() {
|
let error_message = if let Some(exit_code) = output.code() {
|
||||||
log(
|
format!(
|
||||||
format!(
|
"Could not send e-mail using `{}`: Process exited with {}",
|
||||||
"Could not send e-mail using `{}`: Process exited with {}",
|
cmd, exit_code
|
||||||
cmd, exit_code
|
)
|
||||||
),
|
|
||||||
ERROR,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
log(
|
format!(
|
||||||
format!(
|
"Could not send e-mail using `{}`: Process was killed by signal",
|
||||||
"Could not send e-mail using `{}`: Process was killed by signal",
|
cmd
|
||||||
cmd
|
)
|
||||||
),
|
};
|
||||||
ERROR,
|
context.replies.push_back(UIEvent::Notification(
|
||||||
);
|
Some("Message not sent.".into()),
|
||||||
}
|
error_message.clone(),
|
||||||
|
Some(NotificationType::ERROR),
|
||||||
|
));
|
||||||
|
log(error_message, ERROR);
|
||||||
}
|
}
|
||||||
!failure
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1763,6 +1763,13 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
|
||||||
self.done = true;
|
self.done = true;
|
||||||
return 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 => {
|
(UIEvent::Input(Key::Char('\n')), SelectorCursor::Cancel) if !self.single_only => {
|
||||||
for e in self.entries.iter_mut() {
|
for e in self.entries.iter_mut() {
|
||||||
e.1 = false;
|
e.1 = false;
|
||||||
|
|
Loading…
Reference in New Issue