aboutsummaryrefslogtreecommitdiff
path: root/src/imap/mail_view.rs
diff options
context:
space:
mode:
authorQuentin <quentin@dufour.io>2024-01-08 10:39:26 +0000
committerQuentin <quentin@dufour.io>2024-01-08 10:39:26 +0000
commitd7788e29a8a64550e9b274001ff3fb9a7bf3473b (patch)
treee43a11753472f1917ce4aa6ddba24ae3a513bd50 /src/imap/mail_view.rs
parent152d5b7604337fe19a7aea7fc37b3d4615ca7393 (diff)
parent42a54b2c500294c594f3efdd25db28c18f5ac238 (diff)
downloadaerogramme-d7788e29a8a64550e9b274001ff3fb9a7bf3473b.tar.gz
aerogramme-d7788e29a8a64550e9b274001ff3fb9a7bf3473b.zip
Merge pull request 'Implement search' (#61) from feat/search into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/aerogramme/pulls/61
Diffstat (limited to 'src/imap/mail_view.rs')
-rw-r--r--src/imap/mail_view.rs160
1 files changed, 103 insertions, 57 deletions
diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs
index de9bfe3..7da21c4 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::{naive::NaiveDate, DateTime as ChronoDateTime, Local, Offset, TimeZone, Utc};
use imap_codec::imap_types::core::NString;
use imap_codec::imap_types::datetime::DateTime;
@@ -20,19 +20,22 @@ 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;
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<MailView<'a>> {
+ pub fn new(
+ query_result: &'a QueryResult<'a>,
+ in_idx: &'a MailIndex<'a>,
+ ) -> Result<MailView<'a>> {
Ok(Self {
in_idx,
query_result,
@@ -40,18 +43,88 @@ 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,
},
})
}
+ pub fn imf(&self) -> Option<ImfView> {
+ self.content.as_imf().map(ImfView)
+ }
+
+ pub fn selected_mime(&'a self) -> Option<mime_view::SelectedMime<'a>> {
+ self.content.as_anypart().ok().map(mime_view::SelectedMime)
+ }
+
+ 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::<Result<Vec<_>, _>>()?;
+
+ Ok((
+ Body::Data(Data::Fetch {
+ seq: self.in_idx.i,
+ items: res_attrs.try_into()?,
+ }),
+ seen,
+ ))
+ }
+
+ pub fn stored_naive_date(&self) -> Result<NaiveDate> {
+ let mail_meta = self.query_result.metadata().expect("metadata were fetched");
+ let mail_ts: i64 = mail_meta.internaldate.try_into()?;
+ let msg_date: ChronoDateTime<Local> = ChronoDateTime::from_timestamp(mail_ts, 0)
+ .ok_or(anyhow!("unable to parse timestamp"))?
+ .with_timezone(&Local);
+
+ 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())
}
@@ -87,28 +160,32 @@ impl<'a> MailView<'a> {
}
fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> {
- 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<MessageDataItem<'static>> {
- 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))
}
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<MessageDataItem<'static>> {
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<MessageDataItem<'static>> {
Ok(MessageDataItem::Body(mime_view::bodystructure(
- self.content.as_full()?.child.as_ref(),
+ self.content.as_msg()?.child.as_ref(),
)?))
}
@@ -167,43 +244,6 @@ impl<'a> MailView<'a> {
.ok_or(anyhow!("Unable to parse internal date"))?;
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::<Result<Vec<_>, _>>()?;
-
- Ok((
- Body::Data(Data::Fetch {
- seq: self.in_idx.i,
- items: res_attrs.try_into()?,
- }),
- seen,
- ))
- }
}
pub enum SeenFlag {
@@ -215,33 +255,39 @@ 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))
}
- fn as_anypart(&self) -> Result<&AnyPart<'a>> {
+ pub fn partial_from_message(msg: Message<'a>) -> Self {
+ Self::Partial(AnyPart::Msg(msg))
+ }
+
+ pub 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>> {
+ pub 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) -> &imf::Imf<'a> {
+ pub fn as_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(AnyPart::Msg(x)) => Some(&x.imf),
+ _ => None,
}
}
}