aboutsummaryrefslogtreecommitdiff
path: root/src/imap/mail_view.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap/mail_view.rs')
-rw-r--r--src/imap/mail_view.rs306
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,
- }
- }
-}