From b71d9678892814a62d422af11eb91e61943836a2 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 29 Jun 2022 20:00:38 +0200 Subject: Fetch ENVELOPE works \o/ --- src/imap/command/selected.rs | 13 +++---- src/imap/mailbox_view.rs | 85 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 8 deletions(-) (limited to 'src/imap') diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index e10eab6..b3a2ffd 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -46,12 +46,13 @@ impl<'a> SelectedContext<'a> { attributes: &MacroOrFetchAttributes, uid: &bool, ) -> Result<(Response, flow::Transition)> { - let resp = self.mailbox.fetch(sequence_set, attributes, uid).await?; - - Ok(( - Response::ok("FETCH completed")?.with_body(resp), - flow::Transition::None, - )) + match self.mailbox.fetch(sequence_set, attributes, uid).await { + Ok(resp) => Ok(( + Response::ok("FETCH completed")?.with_body(resp), + flow::Transition::None, + )), + Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)), + } } pub async fn noop(self) -> Result<(Response, flow::Transition)> { diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs index 6b24c8a..9a956b6 100644 --- a/src/imap/mailbox_view.rs +++ b/src/imap/mailbox_view.rs @@ -1,10 +1,13 @@ +use std::borrow::Borrow; use std::num::NonZeroU32; use std::sync::Arc; use anyhow::{anyhow, bail, Error, Result}; use boitalettres::proto::res::body::Data as Body; use futures::stream::{FuturesOrdered, StreamExt}; +use imap_codec::types::address::Address; use imap_codec::types::core::{Atom, IString, NString}; +use imap_codec::types::envelope::Envelope; use imap_codec::types::fetch_attributes::{FetchAttribute, MacroOrFetchAttributes}; use imap_codec::types::flag::Flag; use imap_codec::types::response::{Code, Data, MessageAttribute, Status}; @@ -210,7 +213,7 @@ impl MailboxView { }; let mut ret = vec![]; - for (i, uid, uuid, meta, _body) in mails { + for (i, uid, uuid, meta, body) in mails { let mut attributes = vec![MessageAttribute::Uid(uid)]; let (_uid2, flags) = self @@ -219,6 +222,14 @@ impl MailboxView { .get(&uuid) .ok_or_else(|| anyhow!("Mail not in uidindex table: {}", uuid))?; + let parsed = match &body { + Some(m) => { + mail_parser::Message::parse(m).ok_or_else(|| anyhow!("Invalid mail body"))? + } + None => mail_parser::Message::parse(&meta.headers) + .ok_or_else(|| anyhow!("Invalid mail headers"))?, + }; + for attr in fetch_attrs.iter() { match attr { FetchAttribute::Uid => (), @@ -235,7 +246,9 @@ impl MailboxView { IString::Literal(meta.headers.clone().try_into().unwrap()), )))) } - + FetchAttribute::Envelope => { + attributes.push(MessageAttribute::Envelope(message_envelope(&parsed))) + } // TODO _ => (), } @@ -247,6 +260,7 @@ impl MailboxView { })); } + tracing::info!("Fetch result: {:?}", ret); Ok(ret) } @@ -354,3 +368,70 @@ fn string_to_flag(f: &str) -> Option { None => None, } } + +fn message_envelope(msg: &mail_parser::Message<'_>) -> Envelope { + Envelope { + date: NString( + msg.get_date() + .map(|d| IString::try_from(d.to_iso8601()).unwrap()), + ), + subject: NString( + msg.get_subject() + .map(|d| IString::try_from(d.to_string()).unwrap()), + ), + from: convert_addresses(msg.get_from()), + sender: convert_addresses(msg.get_sender()), + reply_to: convert_addresses(msg.get_reply_to()), + to: convert_addresses(msg.get_to()), + cc: convert_addresses(msg.get_cc()), + bcc: convert_addresses(msg.get_bcc()), + in_reply_to: NString(None), // TODO + message_id: NString( + msg.get_message_id() + .map(|d| IString::try_from(d.to_string()).unwrap()), + ), + } +} + +fn convert_addresses(a: &mail_parser::HeaderValue<'_>) -> Vec
{ + match a { + mail_parser::HeaderValue::Address(a) => vec![convert_address(a)], + mail_parser::HeaderValue::AddressList(a) => { + let mut ret = vec![]; + for aa in a { + ret.push(convert_address(aa)); + } + ret + } + mail_parser::HeaderValue::Empty => vec![], + mail_parser::HeaderValue::Collection(c) => { + let mut ret = vec![]; + for cc in c.iter() { + ret.extend(convert_addresses(cc).into_iter()); + } + ret + } + _ => panic!("Invalid address header"), + } +} + +fn convert_address(a: &mail_parser::Addr<'_>) -> Address { + let (user, host) = match &a.address { + None => (None, None), + Some(x) => match x.split_once('@') { + Some((u, h)) => (Some(u.to_string()), Some(h.to_string())), + None => (Some(x.to_string()), None), + }, + }; + + Address::new( + NString( + a.name + .as_ref() + .map(|x| IString::try_from(x.to_string()).unwrap()), + ), + NString(None), + NString(user.map(|x| IString::try_from(x).unwrap())), + NString(host.map(|x| IString::try_from(x).unwrap())), + ) +} -- cgit v1.2.3