Browse Source

Add save-attachment option for entire message as eml

async
Manos Pitsidianakis 2 years ago
parent
commit
02c881ac00
Signed by untrusted user: epilys GPG Key ID: 73627C2F690DF710
  1. 7
      meli.1
  2. 64
      src/components/mail/view.rs
  3. 31
      src/execute.rs

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

64
src/components/mail/view.rs

@ -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(),

31
src/execute.rs

@ -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…
Cancel
Save