parent
580f0be8a4
commit
06d99c7f92
|
@ -172,7 +172,15 @@ impl AttachmentBuilder {
|
||||||
let mut name: Option<String> = None;
|
let mut name: Option<String> = None;
|
||||||
for (n, v) in params {
|
for (n, v) in params {
|
||||||
if n.eq_ignore_ascii_case(b"name") {
|
if n.eq_ignore_ascii_case(b"name") {
|
||||||
name = Some(String::from_utf8_lossy(v).into());
|
if let Ok(v) = crate::email::parser::phrase(v.trim())
|
||||||
|
.to_full_result()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|r| Ok(String::from_utf8_lossy(r).to_string()))
|
||||||
|
{
|
||||||
|
name = Some(v);
|
||||||
|
} else {
|
||||||
|
name = Some(String::from_utf8_lossy(v).into());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,6 +342,10 @@ impl fmt::Display for Attachment {
|
||||||
ContentType::OctetStream { ref name } => {
|
ContentType::OctetStream { ref name } => {
|
||||||
write!(f, "{}", name.clone().unwrap_or_else(|| self.mime_type()))
|
write!(f, "{}", name.clone().unwrap_or_else(|| self.mime_type()))
|
||||||
}
|
}
|
||||||
|
ContentType::Other {
|
||||||
|
name: Some(ref name),
|
||||||
|
..
|
||||||
|
} => write!(f, "\"{}\", [{}]", name, self.mime_type()),
|
||||||
ContentType::Other { .. } => write!(f, "Data attachment of type {}", self.mime_type()),
|
ContentType::Other { .. } => write!(f, "Data attachment of type {}", self.mime_type()),
|
||||||
ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
|
ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
|
||||||
ContentType::Multipart {
|
ContentType::Multipart {
|
||||||
|
|
|
@ -957,6 +957,112 @@ impl Component for MailView {
|
||||||
UIEvent::EnvelopeRename(old_hash, new_hash) if self.coordinates.2 == old_hash => {
|
UIEvent::EnvelopeRename(old_hash, new_hash) if self.coordinates.2 == old_hash => {
|
||||||
self.coordinates.2 = new_hash;
|
self.coordinates.2 = new_hash;
|
||||||
}
|
}
|
||||||
|
UIEvent::Action(View(ViewAction::SaveAttachment(a_i, ref path))) => {
|
||||||
|
use std::io::Write;
|
||||||
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
|
let op = account.operation(envelope.hash());
|
||||||
|
|
||||||
|
let attachments = match envelope.body(op) {
|
||||||
|
Ok(body) => body.attachments(),
|
||||||
|
Err(e) => {
|
||||||
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
Some("Failed to open e-mail".to_string()),
|
||||||
|
e.to_string(),
|
||||||
|
Some(NotificationType::ERROR),
|
||||||
|
));
|
||||||
|
log(
|
||||||
|
format!(
|
||||||
|
"Failed to open envelope {}: {}",
|
||||||
|
envelope.message_id_display(),
|
||||||
|
e.to_string()
|
||||||
|
),
|
||||||
|
ERROR,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(u) = attachments.get(a_i) {
|
||||||
|
match u.content_type() {
|
||||||
|
ContentType::MessageRfc822
|
||||||
|
| ContentType::Text { .. }
|
||||||
|
| ContentType::PGPSignature => {
|
||||||
|
debug!(path);
|
||||||
|
let mut f = match std::fs::File::create(path) {
|
||||||
|
Err(e) => {
|
||||||
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
Some(format!("Failed to create file at {}", path)),
|
||||||
|
e.to_string(),
|
||||||
|
Some(NotificationType::ERROR),
|
||||||
|
));
|
||||||
|
log(
|
||||||
|
format!(
|
||||||
|
"Failed to create file at {}: {}",
|
||||||
|
path,
|
||||||
|
e.to_string()
|
||||||
|
),
|
||||||
|
ERROR,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Ok(f) => f,
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_all(&decode(u, None)).unwrap();
|
||||||
|
f.flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentType::Multipart { .. } => {
|
||||||
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
|
StatusEvent::DisplayMessage(
|
||||||
|
"Multipart attachments are not supported yet.".to_string(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ContentType::OctetStream { name: ref _name }
|
||||||
|
| ContentType::Other {
|
||||||
|
name: ref _name, ..
|
||||||
|
} => {
|
||||||
|
let mut f = match std::fs::File::create(path.trim()) {
|
||||||
|
Err(e) => {
|
||||||
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
Some(format!("Failed to create file at {}", path)),
|
||||||
|
e.to_string(),
|
||||||
|
Some(NotificationType::ERROR),
|
||||||
|
));
|
||||||
|
log(
|
||||||
|
format!(
|
||||||
|
"Failed to create file at {}: {}",
|
||||||
|
path,
|
||||||
|
e.to_string()
|
||||||
|
),
|
||||||
|
ERROR,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Ok(f) => f,
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_all(&decode(u, None)).unwrap();
|
||||||
|
f.flush().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
None,
|
||||||
|
format!("Saved at {}", &path),
|
||||||
|
Some(NotificationType::INFO),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!(
|
||||||
|
"Attachment `{}` not found.",
|
||||||
|
a_i
|
||||||
|
))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
UIEvent::Action(MailingListAction(ref e)) => {
|
UIEvent::Action(MailingListAction(ref e)) => {
|
||||||
let unsafe_context = context as *mut Context;
|
let unsafe_context = context as *mut Context;
|
||||||
let account = &context.accounts[self.coordinates.0];
|
let account = &context.accounts[self.coordinates.0];
|
||||||
|
|
|
@ -531,7 +531,7 @@ impl Component for Pager {
|
||||||
UIEvent::ChangeMode(UIMode::Normal) => {
|
UIEvent::ChangeMode(UIMode::Normal) => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
UIEvent::Action(Pager(Pipe(ref bin, ref args))) => {
|
UIEvent::Action(View(Pipe(ref bin, ref args))) => {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
let mut command_obj = match Command::new(bin)
|
let mut command_obj = match Command::new(bin)
|
||||||
|
@ -987,6 +987,7 @@ impl Component for StatusBar {
|
||||||
self.cmd_history.push(self.ex_buffer.as_str().to_string());
|
self.cmd_history.push(self.ex_buffer.as_str().to_string());
|
||||||
}
|
}
|
||||||
self.ex_buffer.clear();
|
self.ex_buffer.clear();
|
||||||
|
self.ex_buffer_cmd_history_pos.take();
|
||||||
}
|
}
|
||||||
UIMode::Execute => {
|
UIMode::Execute => {
|
||||||
self.height = 2;
|
self.height = 2;
|
||||||
|
@ -1019,6 +1020,7 @@ impl Component for StatusBar {
|
||||||
UIEvent::ExInput(Key::Ctrl('u')) => {
|
UIEvent::ExInput(Key::Ctrl('u')) => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.ex_buffer.clear();
|
self.ex_buffer.clear();
|
||||||
|
self.ex_buffer_cmd_history_pos.take();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::ExInput(k @ Key::Backspace) | UIEvent::ExInput(k @ Key::Ctrl('h')) => {
|
UIEvent::ExInput(k @ Key::Backspace) | UIEvent::ExInput(k @ Key::Ctrl('h')) => {
|
||||||
|
|
|
@ -32,8 +32,8 @@ pub use crate::actions::Action::{self, *};
|
||||||
pub use crate::actions::ComposeAction::{self, *};
|
pub use crate::actions::ComposeAction::{self, *};
|
||||||
pub use crate::actions::ListingAction::{self, *};
|
pub use crate::actions::ListingAction::{self, *};
|
||||||
pub use crate::actions::MailingListAction::{self, *};
|
pub use crate::actions::MailingListAction::{self, *};
|
||||||
pub use crate::actions::PagerAction::{self, *};
|
|
||||||
pub use crate::actions::TabAction::{self, *};
|
pub use crate::actions::TabAction::{self, *};
|
||||||
|
pub use crate::actions::ViewAction::{self, *};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/* Create a const table with every command part that can be auto-completed and its description */
|
/* Create a const table with every command part that can be auto-completed and its description */
|
||||||
|
@ -180,12 +180,12 @@ define_commands!([
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> args: separated_list!(is_a!(" "), is_not!(" "))
|
>> args: separated_list!(is_a!(" "), is_not!(" "))
|
||||||
>> ({
|
>> ({
|
||||||
Pager(Pipe(bin.to_string(), args.into_iter().map(|v| String::from_utf8(v.to_vec()).unwrap()).collect::<Vec<String>>()))
|
View(Pipe(bin.to_string(), args.into_iter().map(|v| String::from_utf8(v.to_vec()).unwrap()).collect::<Vec<String>>()))
|
||||||
})) | do_parse!(
|
})) | do_parse!(
|
||||||
ws!(tag!("pipe"))
|
ws!(tag!("pipe"))
|
||||||
>> bin: ws!(map_res!(is_not!(" "), std::str::from_utf8))
|
>> bin: ws!(map_res!(is_not!(" "), std::str::from_utf8))
|
||||||
>> ({
|
>> ({
|
||||||
Pager(Pipe(bin.to_string(), Vec::new()))
|
View(Pipe(bin.to_string(), Vec::new()))
|
||||||
})
|
})
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
@ -320,6 +320,19 @@ define_commands!([
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
{ tags: ["save-attachment "],
|
||||||
|
desc: "save-attachment INDEX PATH",
|
||||||
|
parser:(
|
||||||
|
named!( save_attachment<Action>,
|
||||||
|
do_parse!(
|
||||||
|
ws!(tag!("save-attachment"))
|
||||||
|
>> idx: map_res!(map_res!(is_not!(" "), std::str::from_utf8), usize::from_str)
|
||||||
|
>> path: ws!(map_res!(call!(not_line_ending), std::str::from_utf8))
|
||||||
|
>> (View(SaveAttachment(idx, path.to_string())))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -379,6 +392,8 @@ named!(
|
||||||
|
|
||||||
named!(account_action<Action>, alt_complete!(reindex));
|
named!(account_action<Action>, alt_complete!(reindex));
|
||||||
|
|
||||||
|
named!(view<Action>, alt_complete!(pipe | save_attachment));
|
||||||
|
|
||||||
named!(pub parse_command<Action>,
|
named!(pub parse_command<Action>,
|
||||||
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | pipe | compose_action | create_folder | sub_folder | unsub_folder | delete_folder | rename_folder | account_action )
|
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | view | compose_action | create_folder | sub_folder | unsub_folder | delete_folder | rename_folder | account_action )
|
||||||
);
|
);
|
||||||
|
|
|
@ -63,8 +63,9 @@ pub enum MailingListAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PagerAction {
|
pub enum ViewAction {
|
||||||
Pipe(String, Vec<String>),
|
Pipe(String, Vec<String>),
|
||||||
|
SaveAttachment(usize, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -88,7 +89,7 @@ pub enum Action {
|
||||||
Tab(TabAction),
|
Tab(TabAction),
|
||||||
ToggleThreadSnooze,
|
ToggleThreadSnooze,
|
||||||
MailingListAction(MailingListAction),
|
MailingListAction(MailingListAction),
|
||||||
Pager(PagerAction),
|
View(ViewAction),
|
||||||
SetEnv(String, String),
|
SetEnv(String, String),
|
||||||
PrintEnv(String),
|
PrintEnv(String),
|
||||||
Compose(ComposeAction),
|
Compose(ComposeAction),
|
||||||
|
|
Loading…
Reference in New Issue