From fc2d9a684dac1a4b0bbb93a51a499c5607402fa1 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 18 Nov 2019 13:00:43 +0200 Subject: [PATCH] melib/imap: set has_attachments based on BODYSTRUCTURE fetch BODYSTRUCTURE along with ENVELOPE from server and set has_attachments based on the MIME structure of the envelope. Notes: BODYSTRUCTURE returns the MIME structure of the envelope without the data, so if it includes a multipart/mixed it *should* have attachments. ENVELOPE returns basic headers of the message like Sender, Subject, Date etc. --- melib/src/backends/imap.rs | 2 +- melib/src/backends/imap/protocol_parser.rs | 15 ++++++++++++++- melib/src/email.rs | 5 +++++ melib/src/email/parser.rs | 7 +++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index 8cc7aed3..42305545 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -179,7 +179,7 @@ impl MailBackend for ImapType { while exists > 1 { let mut envelopes = vec![]; exit_on_error!(&tx, - conn.send_command(format!("UID FETCH {}:{} (UID FLAGS ENVELOPE)", std::cmp::max(exists.saturating_sub(20000), 1), exists).as_bytes()) + conn.send_command(format!("UID FETCH {}:{} (UID FLAGS ENVELOPE BODYSTRUCTURE)", std::cmp::max(exists.saturating_sub(20000), 1), exists).as_bytes()) conn.read_response(&mut response) ); debug!( diff --git a/melib/src/backends/imap/protocol_parser.rs b/melib/src/backends/imap/protocol_parser.rs index 6d806aa9..ca286acd 100644 --- a/melib/src/backends/imap/protocol_parser.rs +++ b/melib/src/backends/imap/protocol_parser.rs @@ -1,4 +1,5 @@ use super::*; +use crate::email::parser::BytesExt; use nom::{digit, is_digit, rest, IResult}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -597,8 +598,20 @@ named!( >> uid_flags: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), opt!(preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")"))))) >> tag!(" ENVELOPE ") >> env: ws!(envelope) + >> tag!("BODYSTRUCTURE ") + >> bodystructure: take_until!(")\r\n") >> tag!(")\r\n") - >> ((uid_flags.0, uid_flags.1, env)) + >> ({ + let mut env = env; + debug!(unsafe { std::str::from_utf8_unchecked(bodystructure) }); + let has_attachments = bodystructure_has_attachments(bodystructure); + env.set_has_attachments(has_attachments); + (uid_flags.0, uid_flags.1, env) + }) ) ) ); + +pub fn bodystructure_has_attachments(input: &[u8]) -> bool { + input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some() +} diff --git a/melib/src/email.rs b/melib/src/email.rs index 20f83aeb..494ba4e7 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -588,6 +588,11 @@ impl Envelope { pub fn is_seen(&self) -> bool { self.flags.contains(Flag::SEEN) } + + pub fn set_has_attachments(&mut self, new_val: bool) { + self.has_attachments = new_val; + } + pub fn has_attachments(&self) -> bool { self.has_attachments } diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 747ac747..ef03353e 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -53,6 +53,7 @@ pub trait BytesExt { fn ltrim(&self) -> &Self; fn trim(&self) -> &Self; fn find(&self, needle: &[u8]) -> Option; + fn rfind(&self, needle: &[u8]) -> Option; fn replace(&self, from: &[u8], to: &[u8]) -> Vec; } @@ -79,6 +80,12 @@ impl BytesExt for [u8] { self.windows(needle.len()) .position(|window| window == needle) } + + fn rfind(&self, needle: &[u8]) -> Option { + self.windows(needle.len()) + .rposition(|window| window == needle) + } + fn replace(&self, from: &[u8], to: &[u8]) -> Vec { let mut ret = self.to_vec(); if let Some(idx) = self.find(from) {