diff options
Diffstat (limited to 'src/imap/mail_view.rs')
-rw-r--r-- | src/imap/mail_view.rs | 306 |
1 files changed, 0 insertions, 306 deletions
diff --git a/src/imap/mail_view.rs b/src/imap/mail_view.rs deleted file mode 100644 index a8db733..0000000 --- a/src/imap/mail_view.rs +++ /dev/null @@ -1,306 +0,0 @@ -use std::num::NonZeroU32; - -use anyhow::{anyhow, bail, Result}; -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; -use imap_codec::imap_types::fetch::{ - MessageDataItem, MessageDataItemName, Section as FetchSection, -}; -use imap_codec::imap_types::flag::Flag; -use imap_codec::imap_types::response::Data; - -use eml_codec::{ - imf, - part::{composite::Message, AnyPart}, -}; - -use crate::mail::query::QueryResult; - -use crate::imap::attributes::AttributesProxy; -use crate::imap::flags; -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: &'a MailIndex<'a>, - pub query_result: &'a QueryResult, - pub content: FetchedMail<'a>, -} - -impl<'a> MailView<'a> { - pub fn new(query_result: &'a QueryResult, in_idx: &'a MailIndex<'a>) -> Result<MailView<'a>> { - Ok(Self { - in_idx, - query_result, - content: match query_result { - QueryResult::FullResult { content, .. } => { - let (_, parsed) = - eml_codec::parse_message(&content).or(Err(anyhow!("Invalid mail body")))?; - FetchedMail::full_from_message(parsed) - } - QueryResult::PartialResult { metadata, .. } => { - let (_, parsed) = eml_codec::parse_message(&metadata.headers) - .or(Err(anyhow!("unable to parse email headers")))?; - 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 => { - if self.is_not_yet_seen() { - seen = SeenFlag::MustAdd; - } - 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(), - MessageDataItemName::ModSeq => Ok(self.modseq()), - }) - .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()) - } - - fn flags(&self) -> MessageDataItem<'static> { - MessageDataItem::Flags( - self.in_idx - .flags - .iter() - .filter_map(|f| flags::from_str(f)) - .collect(), - ) - } - - fn rfc_822_size(&self) -> Result<MessageDataItem<'static>> { - let sz = self - .query_result - .metadata() - .ok_or(anyhow!("mail metadata are required"))? - .rfc822_size; - Ok(MessageDataItem::Rfc822Size(sz as u32)) - } - - fn rfc_822_header(&self) -> Result<MessageDataItem<'static>> { - let hdrs: NString = self - .query_result - .metadata() - .ok_or(anyhow!("mail metadata are required"))? - .headers - .to_vec() - .try_into()?; - Ok(MessageDataItem::Rfc822Header(hdrs)) - } - - fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> { - 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_msg()?.raw_part.to_vec().try_into()?; - Ok(MessageDataItem::Rfc822(full)) - } - - fn envelope(&self) -> MessageDataItem<'static> { - 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_msg()?.child.as_ref(), - false, - )?)) - } - - fn body_structure(&self) -> Result<MessageDataItem<'static>> { - Ok(MessageDataItem::BodyStructure(mime_view::bodystructure( - self.content.as_msg()?.child.as_ref(), - true, - )?)) - } - - fn is_not_yet_seen(&self) -> bool { - let seen_flag = Flag::Seen.to_string(); - !self.in_idx.flags.iter().any(|x| *x == seen_flag) - } - - /// maps to BODY[<section>]<<partial>> and BODY.PEEK[<section>]<<partial>> - /// peek does not implicitly set the \Seen flag - /// eg. BODY[HEADER.FIELDS (DATE FROM)] - /// eg. BODY[]<0.2048> - fn body_ext( - &self, - section: &Option<FetchSection<'static>>, - partial: &Option<(u32, NonZeroU32)>, - peek: &bool, - ) -> Result<(MessageDataItem<'static>, SeenFlag)> { - // Manage Seen flag - let mut seen = SeenFlag::DoNothing; - if !peek && self.is_not_yet_seen() { - // Add \Seen flag - //self.mailbox.add_flags(uuid, &[seen_flag]).await?; - seen = SeenFlag::MustAdd; - } - - // Process message - let (text, origin) = - match mime_view::body_ext(self.content.as_anypart()?, section, partial)? { - mime_view::BodySection::Full(body) => (body, None), - mime_view::BodySection::Slice { body, origin_octet } => (body, Some(origin_octet)), - }; - - let data: NString = text.to_vec().try_into()?; - - return Ok(( - MessageDataItem::BodyExt { - section: section.as_ref().map(|fs| fs.clone()), - origin, - data, - }, - seen, - )); - } - - fn internal_date(&self) -> Result<MessageDataItem<'static>> { - let dt = Utc - .fix() - .timestamp_opt( - i64::try_from( - self.query_result - .metadata() - .ok_or(anyhow!("mail metadata were not fetched"))? - .internaldate - / 1000, - )?, - 0, - ) - .earliest() - .ok_or(anyhow!("Unable to parse internal date"))?; - Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt))) - } - - fn modseq(&self) -> MessageDataItem<'static> { - MessageDataItem::ModSeq(self.in_idx.modseq) - } -} - -pub enum SeenFlag { - DoNothing, - MustAdd, -} - -// ------------------- - -pub enum FetchedMail<'a> { - IndexOnly, - Partial(AnyPart<'a>), - Full(AnyPart<'a>), -} -impl<'a> FetchedMail<'a> { - 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)) - } - - 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"), - } - } - - 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."), - } - } - - 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), - _ => None, - } - } -} |