Add save-attachment option for entire message as eml
parent
d7e4bd9379
commit
02c881ac00
7
meli.1
7
meli.1
|
@ -121,6 +121,13 @@ See
|
|||
for the location of the mailcap files and
|
||||
.Xr mailcap 5
|
||||
for their syntax.
|
||||
You can save individual attachments with the
|
||||
.Cm save-attachment Ar INDEX Ar path-to-file
|
||||
where
|
||||
.Ar INDEX
|
||||
is the attachment's index in the listing.
|
||||
If the zeroth index is provided, the entire message is saved.
|
||||
If the path provided is a directory, the message is saved as an eml file with its filename set to the messages message-id.
|
||||
.Sh SEARCH
|
||||
Each e-mail storage backend has its default search method.
|
||||
.Em IMAP
|
||||
|
|
|
@ -1233,7 +1233,69 @@ impl Component for MailView {
|
|||
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 mut op = account.operation(envelope.hash());
|
||||
|
||||
if a_i == 0 {
|
||||
let mut path = std::path::Path::new(path).to_path_buf();
|
||||
// Save entire message as eml
|
||||
if path.is_dir() {
|
||||
path.push(format!("{}.eml", envelope.message_id_display()));
|
||||
}
|
||||
let bytes = match op.as_bytes() {
|
||||
Ok(b) => b,
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
Some("Failed to open e-mail".to_string()),
|
||||
err.to_string(),
|
||||
Some(NotificationType::ERROR),
|
||||
));
|
||||
log(
|
||||
format!(
|
||||
"Failed to open envelope {}: {}",
|
||||
envelope.message_id_display(),
|
||||
err.to_string()
|
||||
),
|
||||
ERROR,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
let mut f = match std::fs::File::create(&path) {
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
Some(format!("Failed to create file at {}", path.display())),
|
||||
err.to_string(),
|
||||
Some(NotificationType::ERROR),
|
||||
));
|
||||
log(
|
||||
format!(
|
||||
"Failed to create file at {}: {}",
|
||||
path.display(),
|
||||
err.to_string()
|
||||
),
|
||||
ERROR,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
Ok(f) => f,
|
||||
};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let metadata = f.metadata().unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
|
||||
permissions.set_mode(0o600); // Read/write for owner only.
|
||||
f.set_permissions(permissions).unwrap();
|
||||
|
||||
f.write_all(bytes).unwrap();
|
||||
f.flush().unwrap();
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
None,
|
||||
format!("Saved at {}", &path.display()),
|
||||
Some(NotificationType::INFO),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let attachments = match envelope.body(op) {
|
||||
Ok(body) => body.attachments(),
|
||||
|
|
|
@ -124,7 +124,9 @@ impl TokenStream {
|
|||
}
|
||||
Seq(_s) => {}
|
||||
RestOfStringValue => {}
|
||||
IndexValue
|
||||
AttachmentIndexValue
|
||||
| MailboxIndexValue
|
||||
| IndexValue
|
||||
| Filepath
|
||||
| AccountName
|
||||
| MailboxPath
|
||||
|
@ -169,7 +171,9 @@ impl TokenStream {
|
|||
tokens.push((*s, *t.inner()));
|
||||
return tokens;
|
||||
}
|
||||
IndexValue
|
||||
AttachmentIndexValue
|
||||
| MailboxIndexValue
|
||||
| IndexValue
|
||||
| Filepath
|
||||
| AccountName
|
||||
| MailboxPath
|
||||
|
@ -179,8 +183,8 @@ impl TokenStream {
|
|||
while ptr + 1 < s.len() && !s.as_bytes()[ptr].is_ascii_whitespace() {
|
||||
ptr += 1;
|
||||
}
|
||||
tokens.push((&s[..ptr], *t.inner()));
|
||||
*s = &s[ptr..];
|
||||
tokens.push((&s[..ptr + 1], *t.inner()));
|
||||
*s = &s[ptr + 1..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,6 +224,8 @@ pub enum Token {
|
|||
QuotedStringValue,
|
||||
RestOfStringValue,
|
||||
AlphanumericStringValue,
|
||||
AttachmentIndexValue,
|
||||
MailboxIndexValue,
|
||||
IndexValue,
|
||||
}
|
||||
|
||||
|
@ -305,7 +311,7 @@ define_commands!([
|
|||
},
|
||||
{ tags: ["go"],
|
||||
desc: "go [n], switch to nth mailbox in this account",
|
||||
tokens: &[One(Literal("goto")), One(IndexValue)],
|
||||
tokens: &[One(Literal("goto")), One(MailboxIndexValue)],
|
||||
parser: (
|
||||
fn goto(input: &[u8]) -> IResult<&[u8], Action> {
|
||||
let (input, _) = tag("go")(input)?;
|
||||
|
@ -494,6 +500,7 @@ define_commands!([
|
|||
parser:(
|
||||
fn create_mailbox(input: &[u8]) -> IResult<&[u8], Action> {
|
||||
let (input, _) = tag("create-mailbox")(input.trim())?;
|
||||
let (input, _) = is_a(" ")(input)?;
|
||||
let (input, account) = quoted_argument(input)?;
|
||||
let (input, _) = is_a(" ")(input)?;
|
||||
let (input, path) = quoted_argument(input)?;
|
||||
|
@ -583,7 +590,7 @@ define_commands!([
|
|||
},
|
||||
{ tags: ["save-attachment "],
|
||||
desc: "save-attachment INDEX PATH",
|
||||
tokens: &[One(Literal("save-attachment")), One(IndexValue), One(Filepath)],
|
||||
tokens: &[One(Literal("save-attachment")), One(AttachmentIndexValue), One(Filepath)],
|
||||
parser:(
|
||||
fn save_attachment(input: &[u8]) -> IResult<&[u8], Action> {
|
||||
let (input, _) = tag("save-attachment")(input.trim())?;
|
||||
|
@ -749,9 +756,21 @@ fn test_parser() {
|
|||
|
||||
/// Get command suggestions for input
|
||||
pub fn command_completion_suggestions(input: &str) -> Vec<String> {
|
||||
use crate::melib::ShellExpandTrait;
|
||||
let mut sugg = Default::default();
|
||||
for (_tags, _desc, tokens) in COMMAND_COMPLETION.iter() {
|
||||
let _m = tokens.matches(&mut &(*input), &mut sugg);
|
||||
if _m.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some((s, Filepath)) = _m.last() {
|
||||
let p = std::path::Path::new(s);
|
||||
sugg.extend(
|
||||
p.complete(true)
|
||||
.into_iter()
|
||||
.map(|m| m.into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
sugg.into_iter()
|
||||
.map(|s| {
|
||||
|
|
Loading…
Reference in New Issue