aboutsummaryrefslogtreecommitdiff
path: root/src/imap
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap')
-rw-r--r--src/imap/command/selected.rs15
-rw-r--r--src/imap/mailbox_view.rs122
2 files changed, 130 insertions, 7 deletions
diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs
index 3a44a3f..e10eab6 100644
--- a/src/imap/command/selected.rs
+++ b/src/imap/command/selected.rs
@@ -42,17 +42,22 @@ pub async fn dispatch<'a>(ctx: SelectedContext<'a>) -> Result<(Response, flow::T
impl<'a> SelectedContext<'a> {
pub async fn fetch(
self,
- _sequence_set: &SequenceSet,
- _attributes: &MacroOrFetchAttributes,
- _uid: &bool,
+ sequence_set: &SequenceSet,
+ attributes: &MacroOrFetchAttributes,
+ uid: &bool,
) -> Result<(Response, flow::Transition)> {
- Ok((Response::bad("Not implemented")?, flow::Transition::None))
+ let resp = self.mailbox.fetch(sequence_set, attributes, uid).await?;
+
+ Ok((
+ Response::ok("FETCH completed")?.with_body(resp),
+ flow::Transition::None,
+ ))
}
pub async fn noop(self) -> Result<(Response, flow::Transition)> {
let updates = self.mailbox.update().await?;
Ok((
- Response::ok("Noop completed.")?.with_body(updates),
+ Response::ok("NOOP completed.")?.with_body(updates),
flow::Transition::None,
))
}
diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs
index 6066528..df9c3fe 100644
--- a/src/imap/mailbox_view.rs
+++ b/src/imap/mailbox_view.rs
@@ -1,11 +1,14 @@
use std::num::NonZeroU32;
use std::sync::Arc;
-use anyhow::{Error, Result};
+use anyhow::{anyhow, bail, Error, Result};
use boitalettres::proto::res::body::Data as Body;
-use imap_codec::types::core::Atom;
+use futures::stream::{FuturesOrdered, StreamExt};
+use imap_codec::types::core::{Atom, IString, NString};
+use imap_codec::types::fetch_attributes::{FetchAttribute, MacroOrFetchAttributes};
use imap_codec::types::flag::Flag;
use imap_codec::types::response::{Code, Data, MessageAttribute, Status};
+use imap_codec::types::sequence::{self, SequenceSet};
use crate::mail::mailbox::Mailbox;
use crate::mail::uidindex::UidIndex;
@@ -132,6 +135,121 @@ impl MailboxView {
Ok(data)
}
+ /// Looks up state changes in the mailbox and produces a set of IMAP
+ /// responses describing the new state.
+ pub async fn fetch(
+ &self,
+ sequence_set: &SequenceSet,
+ attributes: &MacroOrFetchAttributes,
+ uid: &bool,
+ ) -> Result<Vec<Body>> {
+ if *uid {
+ bail!("UID FETCH not implemented");
+ }
+
+ let mail_vec = self
+ .known_state
+ .idx_by_uid
+ .iter()
+ .map(|(uid, uuid)| (*uid, *uuid))
+ .collect::<Vec<_>>();
+
+ let mut mails = vec![];
+ let iter_strat = sequence::Strategy::Naive {
+ largest: NonZeroU32::try_from((self.known_state.idx_by_uid.len() + 1) as u32).unwrap(),
+ };
+ for i in sequence_set.iter(iter_strat) {
+ if let Some(mail) = mail_vec.get(i.get() as usize - 1) {
+ mails.push((i, *mail));
+ } else {
+ bail!("No such mail: {}", i);
+ }
+ }
+
+ let mails_uuid = mails
+ .iter()
+ .map(|(_i, (_uid, uuid))| *uuid)
+ .collect::<Vec<_>>();
+ let mails_meta = self.mailbox.fetch_meta(&mails_uuid).await?;
+
+ let fetch_attrs = match attributes {
+ MacroOrFetchAttributes::Macro(m) => m.expand(),
+ MacroOrFetchAttributes::FetchAttributes(a) => a.clone(),
+ };
+ let need_body = fetch_attrs.iter().any(|x| {
+ matches!(
+ x,
+ FetchAttribute::Body
+ | FetchAttribute::BodyExt { .. }
+ | FetchAttribute::Rfc822
+ | FetchAttribute::Rfc822Text
+ | FetchAttribute::BodyStructure
+ )
+ });
+
+ let mails = if need_body {
+ let mut iter = mails
+ .into_iter()
+ .zip(mails_meta.into_iter())
+ .map(|((i, (uid, uuid)), meta)| async move {
+ let body = self.mailbox.fetch_full(uuid, &meta.message_key).await?;
+ Ok::<_, anyhow::Error>((i, uid, uuid, meta, Some(body)))
+ })
+ .collect::<FuturesOrdered<_>>();
+ let mut mails = vec![];
+ while let Some(m) = iter.next().await {
+ mails.push(m?);
+ }
+ mails
+ } else {
+ mails
+ .into_iter()
+ .zip(mails_meta.into_iter())
+ .map(|((i, (uid, uuid)), meta)| (i, uid, uuid, meta, None))
+ .collect::<Vec<_>>()
+ };
+
+ let mut ret = vec![];
+ for (i, uid, uuid, meta, body) in mails {
+ let mut attributes = vec![MessageAttribute::Uid(uid)];
+
+ let (uid2, flags) = self
+ .known_state
+ .table
+ .get(&uuid)
+ .ok_or_else(|| anyhow!("Mail not in uidindex table: {}", uuid))?;
+
+ for attr in fetch_attrs.iter() {
+ match attr {
+ FetchAttribute::Uid => (),
+ FetchAttribute::Flags => {
+ attributes.push(MessageAttribute::Flags(
+ flags.iter().filter_map(|f| string_to_flag(f)).collect(),
+ ));
+ }
+ FetchAttribute::Rfc822Size => {
+ attributes.push(MessageAttribute::Rfc822Size(meta.rfc822_size as u32))
+ }
+ FetchAttribute::Rfc822Header => {
+ attributes.push(MessageAttribute::Rfc822Header(NString(Some(
+ IString::Literal(meta.headers.clone().try_into().unwrap()),
+ ))))
+ }
+
+ // TODO
+ _ => (),
+ }
+ }
+
+ ret.push(Body::Data(Data::Fetch {
+ seq_or_uid: i,
+ attributes,
+ }));
+ }
+
+ Ok(ret)
+ }
+
// ----
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`