From f58904f5bb3dbd429555c406c867f850654843a6 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 6 Jan 2024 18:01:44 +0100 Subject: Search can now filter on index data --- src/imap/mail_view.rs | 74 ++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index de9bfe3..fc36e21 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -52,6 +52,44 @@ impl<'a> MailView<'a> { }) } + pub fn filter(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> { + let mut seen = SeenFlag::DoNothing; + let res_attrs = ap + .attrs + .iter() + .map(|attr| match attr { + MessageDataItemName::Uid => Ok(self.uid()), + MessageDataItemName::Flags => Ok(self.flags()), + MessageDataItemName::Rfc822Size => self.rfc_822_size(), + MessageDataItemName::Rfc822Header => self.rfc_822_header(), + MessageDataItemName::Rfc822Text => self.rfc_822_text(), + MessageDataItemName::Rfc822 => self.rfc822(), + MessageDataItemName::Envelope => Ok(self.envelope()), + MessageDataItemName::Body => self.body(), + MessageDataItemName::BodyStructure => self.body_structure(), + MessageDataItemName::BodyExt { + section, + partial, + peek, + } => { + let (body, has_seen) = self.body_ext(section, partial, peek)?; + seen = has_seen; + Ok(body) + } + MessageDataItemName::InternalDate => self.internal_date(), + }) + .collect::, _>>()?; + + Ok(( + Body::Data(Data::Fetch { + seq: self.in_idx.i, + items: res_attrs.try_into()?, + }), + seen, + )) + } + + // Private function, mainly for filter! fn uid(&self) -> MessageDataItem<'static> { MessageDataItem::Uid(self.in_idx.uid.clone()) } @@ -168,42 +206,6 @@ impl<'a> MailView<'a> { Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt))) } - pub fn filter(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> { - let mut seen = SeenFlag::DoNothing; - let res_attrs = ap - .attrs - .iter() - .map(|attr| match attr { - MessageDataItemName::Uid => Ok(self.uid()), - MessageDataItemName::Flags => Ok(self.flags()), - MessageDataItemName::Rfc822Size => self.rfc_822_size(), - MessageDataItemName::Rfc822Header => self.rfc_822_header(), - MessageDataItemName::Rfc822Text => self.rfc_822_text(), - MessageDataItemName::Rfc822 => self.rfc822(), - MessageDataItemName::Envelope => Ok(self.envelope()), - MessageDataItemName::Body => self.body(), - MessageDataItemName::BodyStructure => self.body_structure(), - MessageDataItemName::BodyExt { - section, - partial, - peek, - } => { - let (body, has_seen) = self.body_ext(section, partial, peek)?; - seen = has_seen; - Ok(body) - } - MessageDataItemName::InternalDate => self.internal_date(), - }) - .collect::, _>>()?; - - Ok(( - Body::Data(Data::Fetch { - seq: self.in_idx.i, - items: res_attrs.try_into()?, - }), - seen, - )) - } } pub enum SeenFlag { -- cgit v1.2.3 From ea1772df425cb7faa4628b1c6c398ae3f77fca34 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 6 Jan 2024 20:40:18 +0100 Subject: Searching on storage date is now possible --- src/imap/mail_view.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index fc36e21..3fef145 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -1,7 +1,7 @@ use std::num::NonZeroU32; use anyhow::{anyhow, bail, Result}; -use chrono::{Offset, TimeZone, Utc}; +use chrono::{Offset, TimeZone, Utc, DateTime as ChronoDateTime, Local, naive::NaiveDate}; use imap_codec::imap_types::core::NString; use imap_codec::imap_types::datetime::DateTime; @@ -20,7 +20,7 @@ use crate::mail::query::QueryResult; use crate::imap::attributes::AttributesProxy; use crate::imap::flags; -use crate::imap::imf_view::message_envelope; +use crate::imap::imf_view::ImfView; use crate::imap::index::MailIndex; use crate::imap::mime_view; use crate::imap::response::Body; @@ -52,6 +52,10 @@ impl<'a> MailView<'a> { }) } + pub fn imf(&self) -> Option { + self.content.imf().map(ImfView) + } + pub fn filter(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> { let mut seen = SeenFlag::DoNothing; let res_attrs = ap @@ -89,6 +93,16 @@ impl<'a> MailView<'a> { )) } + pub fn stored_naive_date(&self) -> Result { + let mail_meta = self.query_result.metadata().expect("metadata were fetched"); + let mail_ts: i64 = mail_meta.internaldate.try_into()?; + let msg_date: ChronoDateTime = ChronoDateTime::from_timestamp(mail_ts, 0) + .ok_or(anyhow!("unable to parse timestamp"))? + .with_timezone(&Local); + + Ok(msg_date.date_naive()) + } + // Private function, mainly for filter! fn uid(&self) -> MessageDataItem<'static> { MessageDataItem::Uid(self.in_idx.uid.clone()) @@ -135,7 +149,7 @@ impl<'a> MailView<'a> { } fn envelope(&self) -> MessageDataItem<'static> { - MessageDataItem::Envelope(message_envelope(self.content.imf().clone())) + MessageDataItem::Envelope(self.imf().expect("an imf object is derivable from fetchedmail").message_envelope()) } fn body(&self) -> Result> { @@ -239,11 +253,11 @@ impl<'a> FetchedMail<'a> { } } - fn imf(&self) -> &imf::Imf<'a> { + fn imf(&self) -> Option<&imf::Imf<'a>> { match self { - FetchedMail::Full(AnyPart::Msg(x)) => &x.imf, - FetchedMail::Partial(x) => &x, - _ => panic!("Can't contain AnyPart that is not a message"), + FetchedMail::Full(AnyPart::Msg(x)) => Some(&x.imf), + FetchedMail::Partial(x) => Some(&x), + _ => None, } } } -- cgit v1.2.3 From 5622a71cd163e4b18a3eabe8a28a5aedb23ee25d Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 6 Jan 2024 22:53:41 +0100 Subject: Search MIME headers --- src/imap/mail_view.rs | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index 3fef145..365e535 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -40,12 +40,12 @@ impl<'a> MailView<'a> { QueryResult::FullResult { content, .. } => { let (_, parsed) = eml_codec::parse_message(&content).or(Err(anyhow!("Invalid mail body")))?; - FetchedMail::new_from_message(parsed) + FetchedMail::full_from_message(parsed) } QueryResult::PartialResult { metadata, .. } => { - let (_, parsed) = eml_codec::parse_imf(&metadata.headers) + let (_, parsed) = eml_codec::parse_message(&metadata.headers) .or(Err(anyhow!("unable to parse email headers")))?; - FetchedMail::Partial(parsed) + FetchedMail::partial_from_message(parsed) } QueryResult::IndexResult { .. } => FetchedMail::IndexOnly, }, @@ -53,7 +53,11 @@ impl<'a> MailView<'a> { } pub fn imf(&self) -> Option { - self.content.imf().map(ImfView) + self.content.as_imf().map(ImfView) + } + + pub fn selected_mime(&'a self) -> Option> { + self.content.as_anypart().ok().map(mime_view::SelectedMime) } pub fn filter(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> { @@ -103,6 +107,20 @@ impl<'a> MailView<'a> { Ok(msg_date.date_naive()) } + pub fn is_header_contains_pattern(&self, hdr: &[u8], pattern: &[u8]) -> bool { + let mime = match self.selected_mime() { + None => return false, + Some(x) => x, + }; + + let val = match mime.header_value(hdr) { + None => return false, + Some(x) => x, + }; + + val.windows(pattern.len()).any(|win| win == pattern) + } + // Private function, mainly for filter! fn uid(&self) -> MessageDataItem<'static> { MessageDataItem::Uid(self.in_idx.uid.clone()) @@ -139,12 +157,12 @@ impl<'a> MailView<'a> { } fn rfc_822_text(&self) -> Result> { - let txt: NString = self.content.as_full()?.raw_body.to_vec().try_into()?; + let txt: NString = self.content.as_msg()?.raw_body.to_vec().try_into()?; Ok(MessageDataItem::Rfc822Text(txt)) } fn rfc822(&self) -> Result> { - let full: NString = self.content.as_full()?.raw_part.to_vec().try_into()?; + let full: NString = self.content.as_msg()?.raw_part.to_vec().try_into()?; Ok(MessageDataItem::Rfc822(full)) } @@ -154,13 +172,13 @@ impl<'a> MailView<'a> { fn body(&self) -> Result> { Ok(MessageDataItem::Body(mime_view::bodystructure( - self.content.as_full()?.child.as_ref(), + self.content.as_msg()?.child.as_ref(), )?)) } fn body_structure(&self) -> Result> { Ok(MessageDataItem::Body(mime_view::bodystructure( - self.content.as_full()?.child.as_ref(), + self.content.as_msg()?.child.as_ref(), )?)) } @@ -231,32 +249,38 @@ pub enum SeenFlag { pub enum FetchedMail<'a> { IndexOnly, - Partial(imf::Imf<'a>), + Partial(AnyPart<'a>), Full(AnyPart<'a>), } impl<'a> FetchedMail<'a> { - pub fn new_from_message(msg: Message<'a>) -> Self { + pub fn full_from_message(msg: Message<'a>) -> Self { Self::Full(AnyPart::Msg(msg)) } + pub fn partial_from_message(msg: Message<'a>) -> Self { + Self::Partial(AnyPart::Msg(msg)) + } + fn as_anypart(&self) -> Result<&AnyPart<'a>> { match self { FetchedMail::Full(x) => Ok(&x), + FetchedMail::Partial(x) => Ok(&x), _ => bail!("The full message must be fetched, not only its headers"), } } - fn as_full(&self) -> Result<&Message<'a>> { + fn as_msg(&self) -> Result<&Message<'a>> { match self { FetchedMail::Full(AnyPart::Msg(x)) => Ok(&x), + FetchedMail::Partial(AnyPart::Msg(x)) => Ok(&x), _ => bail!("The full message must be fetched, not only its headers AND it must be an AnyPart::Msg."), } } - fn imf(&self) -> Option<&imf::Imf<'a>> { + fn as_imf(&self) -> Option<&imf::Imf<'a>> { match self { FetchedMail::Full(AnyPart::Msg(x)) => Some(&x.imf), - FetchedMail::Partial(x) => Some(&x), + FetchedMail::Partial(AnyPart::Msg(x)) => Some(&x.imf), _ => None, } } -- cgit v1.2.3 From 4e3cbf79d03c84028733b0ad5f9bd06a8a13757b Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 6 Jan 2024 23:24:44 +0100 Subject: implemented text search --- src/imap/mail_view.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index 365e535..baeb2af 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -261,7 +261,7 @@ impl<'a> FetchedMail<'a> { Self::Partial(AnyPart::Msg(msg)) } - fn as_anypart(&self) -> Result<&AnyPart<'a>> { + pub fn as_anypart(&self) -> Result<&AnyPart<'a>> { match self { FetchedMail::Full(x) => Ok(&x), FetchedMail::Partial(x) => Ok(&x), @@ -269,7 +269,7 @@ impl<'a> FetchedMail<'a> { } } - fn as_msg(&self) -> Result<&Message<'a>> { + pub fn as_msg(&self) -> Result<&Message<'a>> { match self { FetchedMail::Full(AnyPart::Msg(x)) => Ok(&x), FetchedMail::Partial(AnyPart::Msg(x)) => Ok(&x), @@ -277,7 +277,7 @@ impl<'a> FetchedMail<'a> { } } - fn as_imf(&self) -> Option<&imf::Imf<'a>> { + pub fn as_imf(&self) -> Option<&imf::Imf<'a>> { match self { FetchedMail::Full(AnyPart::Msg(x)) => Some(&x.imf), FetchedMail::Partial(AnyPart::Msg(x)) => Some(&x.imf), -- cgit v1.2.3 From 1d84b0ffd006b895aeb15c621fcd0ced826a0599 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 6 Jan 2024 23:35:23 +0100 Subject: Format code --- src/imap/mail_view.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index baeb2af..2c8723e 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -1,7 +1,7 @@ use std::num::NonZeroU32; use anyhow::{anyhow, bail, Result}; -use chrono::{Offset, TimeZone, Utc, DateTime as ChronoDateTime, Local, naive::NaiveDate}; +use chrono::{naive::NaiveDate, DateTime as ChronoDateTime, Local, Offset, TimeZone, Utc}; use imap_codec::imap_types::core::NString; use imap_codec::imap_types::datetime::DateTime; @@ -167,7 +167,11 @@ impl<'a> MailView<'a> { } fn envelope(&self) -> MessageDataItem<'static> { - MessageDataItem::Envelope(self.imf().expect("an imf object is derivable from fetchedmail").message_envelope()) + MessageDataItem::Envelope( + self.imf() + .expect("an imf object is derivable from fetchedmail") + .message_envelope(), + ) } fn body(&self) -> Result> { @@ -237,7 +241,6 @@ impl<'a> MailView<'a> { .ok_or(anyhow!("Unable to parse internal date"))?; Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt))) } - } pub enum SeenFlag { -- cgit v1.2.3 From 558e32fbd27be9a81144571b4baf318293be1344 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 8 Jan 2024 11:13:13 +0100 Subject: UID sequence are now correctly fetched --- src/imap/mail_view.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index 2c8723e..8dd68b5 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -26,13 +26,13 @@ use crate::imap::mime_view; use crate::imap::response::Body; pub struct MailView<'a> { - pub in_idx: MailIndex<'a>, + pub in_idx: &'a MailIndex<'a>, pub query_result: &'a QueryResult<'a>, pub content: FetchedMail<'a>, } impl<'a> MailView<'a> { - pub fn new(query_result: &'a QueryResult<'a>, in_idx: MailIndex<'a>) -> Result> { + pub fn new(query_result: &'a QueryResult<'a>, in_idx: &'a MailIndex<'a>) -> Result> { Ok(Self { in_idx, query_result, -- cgit v1.2.3 From 72f9a221ed2318d8ca3452b6574c900be923d3d5 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 8 Jan 2024 11:14:34 +0100 Subject: Formatting & tests --- src/imap/mail_view.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/imap/mail_view.rs') diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs index 8dd68b5..7da21c4 100644 --- a/src/imap/mail_view.rs +++ b/src/imap/mail_view.rs @@ -32,7 +32,10 @@ pub struct MailView<'a> { } impl<'a> MailView<'a> { - pub fn new(query_result: &'a QueryResult<'a>, in_idx: &'a MailIndex<'a>) -> Result> { + pub fn new( + query_result: &'a QueryResult<'a>, + in_idx: &'a MailIndex<'a>, + ) -> Result> { Ok(Self { in_idx, query_result, -- cgit v1.2.3