Mailbox View made more readable
This commit is contained in:
parent
4806f7ff84
commit
a84ba4d42f
6 changed files with 143 additions and 259 deletions
80
src/imap/index.rs
Normal file
80
src/imap/index.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use imap_codec::imap_types::sequence::{self, SequenceSet};
|
||||||
|
|
||||||
|
use crate::mail::uidindex::{ImapUid, UidIndex};
|
||||||
|
use crate::mail::unique_ident::UniqueIdent;
|
||||||
|
|
||||||
|
pub struct Index<'a>(pub &'a UidIndex);
|
||||||
|
impl<'a> Index<'a> {
|
||||||
|
pub fn fetch(self: &Index<'a>, sequence_set: &SequenceSet, by_uid: bool) -> Result<Vec<MailIndex<'a>>> {
|
||||||
|
let mail_vec = self
|
||||||
|
.0
|
||||||
|
.idx_by_uid
|
||||||
|
.iter()
|
||||||
|
.map(|(uid, uuid)| (*uid, *uuid))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut mails = vec![];
|
||||||
|
|
||||||
|
if by_uid {
|
||||||
|
if mail_vec.is_empty() {
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
let iter_strat = sequence::Strategy::Naive {
|
||||||
|
largest: mail_vec.last().unwrap().0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
for uid in sequence_set.iter(iter_strat) {
|
||||||
|
while mail_vec.get(i).map(|mail| mail.0 < uid).unwrap_or(false) {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if let Some(mail) = mail_vec.get(i) {
|
||||||
|
if mail.0 == uid {
|
||||||
|
mails.push(MailIndex {
|
||||||
|
i: NonZeroU32::try_from(i as u32 + 1).unwrap(),
|
||||||
|
uid: mail.0,
|
||||||
|
uuid: mail.1,
|
||||||
|
flags: self.0.table.get(&mail.1).ok_or(anyhow!("mail is missing from index"))?.1.as_ref(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if mail_vec.is_empty() {
|
||||||
|
bail!("No such message (mailbox is empty)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let iter_strat = sequence::Strategy::Naive {
|
||||||
|
largest: NonZeroU32::try_from((mail_vec.len()) 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(MailIndex {
|
||||||
|
i,
|
||||||
|
uid: mail.0,
|
||||||
|
uuid: mail.1,
|
||||||
|
flags: self.0.table.get(&mail.1).ok_or(anyhow!("mail is missing from index"))?.1.as_ref(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
bail!("No such mail: {}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mails)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MailIndex<'a> {
|
||||||
|
pub i: NonZeroU32,
|
||||||
|
pub uid: ImapUid,
|
||||||
|
pub uuid: UniqueIdent,
|
||||||
|
pub flags: &'a Vec<String>
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result, Context};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use chrono::{Offset, TimeZone, Utc};
|
use chrono::{Offset, TimeZone, Utc};
|
||||||
|
|
||||||
use imap_codec::imap_types::core::{IString, NString};
|
use imap_codec::imap_types::core::NString;
|
||||||
use imap_codec::imap_types::datetime::DateTime;
|
use imap_codec::imap_types::datetime::DateTime;
|
||||||
use imap_codec::imap_types::fetch::{
|
use imap_codec::imap_types::fetch::{
|
||||||
MessageDataItem, MessageDataItemName, Section as FetchSection,
|
MessageDataItem, MessageDataItemName, Section as FetchSection,
|
||||||
|
@ -16,87 +16,73 @@ use eml_codec::{
|
||||||
part::{composite::Message, AnyPart},
|
part::{composite::Message, AnyPart},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
use crate::mail::query::QueryResult;
|
||||||
|
|
||||||
use crate::imap::attributes::AttributesProxy;
|
use crate::imap::attributes::AttributesProxy;
|
||||||
use crate::imap::flags;
|
use crate::imap::flags;
|
||||||
use crate::imap::imf_view::message_envelope;
|
use crate::imap::imf_view::message_envelope;
|
||||||
use crate::imap::mailbox_view::MailIdentifiers;
|
|
||||||
use crate::imap::mime_view;
|
use crate::imap::mime_view;
|
||||||
use crate::imap::response::Body;
|
use crate::imap::response::Body;
|
||||||
use crate::mail::query::QueryResult;
|
use crate::imap::index::MailIndex;
|
||||||
|
|
||||||
pub struct MailView<'a> {
|
pub struct MailView<'a> {
|
||||||
|
pub in_idx: MailIndex<'a>,
|
||||||
pub query_result: &'a QueryResult<'a>,
|
pub query_result: &'a QueryResult<'a>,
|
||||||
pub content: FetchedMail<'a>,
|
pub content: FetchedMail<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MailView<'a> {
|
impl<'a> MailView<'a> {
|
||||||
pub fn new(query_result: &'a QueryResult<'a>) -> Result<Self> {
|
pub fn new(query_result: &'a QueryResult<'a>, in_idx: MailIndex<'a>) -> Result<MailView<'a>> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
in_idx,
|
||||||
query_result,
|
query_result,
|
||||||
content: match query_result {
|
content: match query_result {
|
||||||
QueryResult::FullResult { content, .. } => {
|
QueryResult::FullResult { content, .. } => {
|
||||||
let (_, parsed) = eml_codec::parse_message(content).context("Invalid mail body")?;
|
let (_, parsed) = eml_codec::parse_message(&content).or(Err(anyhow!("Invalid mail body")))?;
|
||||||
FetchedMail::new_from_message(parsed)
|
FetchedMail::new_from_message(parsed)
|
||||||
},
|
},
|
||||||
QueryResult::PartialResult { metadata, .. } => {
|
QueryResult::PartialResult { metadata, .. } => {
|
||||||
let (_, parsed) = eml_codec::parse_imf(&metadata.headers).context("Invalid mail headers")?;
|
let (_, parsed) = eml_codec::parse_imf(&metadata.headers).or(Err(anyhow!("unable to parse email headers")))?;
|
||||||
FetchedMail::Partial(parsed)
|
FetchedMail::Partial(parsed)
|
||||||
}
|
}
|
||||||
QueryResult::IndexResult { .. } => FetchedMail::None,
|
QueryResult::IndexResult { .. } => FetchedMail::IndexOnly,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uid(&self) -> MessageDataItem<'static> {
|
fn uid(&self) -> MessageDataItem<'static> {
|
||||||
MessageDataItem::Uid(self.ids.uid.clone())
|
MessageDataItem::Uid(self.in_idx.uid.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flags(&self) -> MessageDataItem<'static> {
|
fn flags(&self) -> MessageDataItem<'static> {
|
||||||
MessageDataItem::Flags(
|
MessageDataItem::Flags(
|
||||||
self.flags
|
self.in_idx
|
||||||
|
.flags
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|f| flags::from_str(f))
|
.filter_map(|f| flags::from_str(f))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rfc_822_size(&self) -> MessageDataItem<'static> {
|
fn rfc_822_size(&self) -> Result<MessageDataItem<'static>> {
|
||||||
MessageDataItem::Rfc822Size(self.meta.rfc822_size as u32)
|
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) -> MessageDataItem<'static> {
|
fn rfc_822_header(&self) -> Result<MessageDataItem<'static>> {
|
||||||
MessageDataItem::Rfc822Header(NString(
|
let hdrs: NString = self.query_result.metadata().ok_or(anyhow!("mail metadata are required"))?.headers.to_vec().try_into()?;
|
||||||
self.meta
|
Ok(MessageDataItem::Rfc822Header(hdrs))
|
||||||
.headers
|
|
||||||
.to_vec()
|
|
||||||
.try_into()
|
|
||||||
.ok()
|
|
||||||
.map(IString::Literal),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> {
|
fn rfc_822_text(&self) -> Result<MessageDataItem<'static>> {
|
||||||
Ok(MessageDataItem::Rfc822Text(NString(
|
let txt: NString = self.content.as_full()?.raw_body.to_vec().try_into()?;
|
||||||
self.content
|
Ok(MessageDataItem::Rfc822Text(txt))
|
||||||
.as_full()?
|
|
||||||
.raw_body
|
|
||||||
.to_vec()
|
|
||||||
.try_into()
|
|
||||||
.ok()
|
|
||||||
.map(IString::Literal),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rfc822(&self) -> Result<MessageDataItem<'static>> {
|
fn rfc822(&self) -> Result<MessageDataItem<'static>> {
|
||||||
Ok(MessageDataItem::Rfc822(NString(
|
let full: NString = self.content.as_full()?.raw_part.to_vec().try_into()?;
|
||||||
self.content
|
Ok(MessageDataItem::Rfc822(full))
|
||||||
.as_full()?
|
|
||||||
.raw_part
|
|
||||||
.to_vec()
|
|
||||||
.try_into()
|
|
||||||
.ok()
|
|
||||||
.map(IString::Literal),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn envelope(&self) -> MessageDataItem<'static> {
|
fn envelope(&self) -> MessageDataItem<'static> {
|
||||||
|
@ -119,16 +105,16 @@ impl<'a> MailView<'a> {
|
||||||
/// peek does not implicitly set the \Seen flag
|
/// peek does not implicitly set the \Seen flag
|
||||||
/// eg. BODY[HEADER.FIELDS (DATE FROM)]
|
/// eg. BODY[HEADER.FIELDS (DATE FROM)]
|
||||||
/// eg. BODY[]<0.2048>
|
/// eg. BODY[]<0.2048>
|
||||||
fn body_ext<'b>(
|
fn body_ext(
|
||||||
&self,
|
&self,
|
||||||
section: &Option<FetchSection<'b>>,
|
section: &Option<FetchSection<'static>>,
|
||||||
partial: &Option<(u32, NonZeroU32)>,
|
partial: &Option<(u32, NonZeroU32)>,
|
||||||
peek: &bool,
|
peek: &bool,
|
||||||
) -> Result<(MessageDataItem<'b>, SeenFlag)> {
|
) -> Result<(MessageDataItem<'static>, SeenFlag)> {
|
||||||
// Manage Seen flag
|
// Manage Seen flag
|
||||||
let mut seen = SeenFlag::DoNothing;
|
let mut seen = SeenFlag::DoNothing;
|
||||||
let seen_flag = Flag::Seen.to_string();
|
let seen_flag = Flag::Seen.to_string();
|
||||||
if !peek && !self.flags.iter().any(|x| *x == seen_flag) {
|
if !peek && !self.in_idx.flags.iter().any(|x| *x == seen_flag) {
|
||||||
// Add \Seen flag
|
// Add \Seen flag
|
||||||
//self.mailbox.add_flags(uuid, &[seen_flag]).await?;
|
//self.mailbox.add_flags(uuid, &[seen_flag]).await?;
|
||||||
seen = SeenFlag::MustAdd;
|
seen = SeenFlag::MustAdd;
|
||||||
|
@ -141,7 +127,7 @@ impl<'a> MailView<'a> {
|
||||||
mime_view::BodySection::Slice { body, origin_octet } => (body, Some(origin_octet)),
|
mime_view::BodySection::Slice { body, origin_octet } => (body, Some(origin_octet)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = NString(text.to_vec().try_into().ok().map(IString::Literal));
|
let data: NString = text.to_vec().try_into()?;
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
MessageDataItem::BodyExt {
|
MessageDataItem::BodyExt {
|
||||||
|
@ -156,13 +142,13 @@ impl<'a> MailView<'a> {
|
||||||
fn internal_date(&self) -> Result<MessageDataItem<'static>> {
|
fn internal_date(&self) -> Result<MessageDataItem<'static>> {
|
||||||
let dt = Utc
|
let dt = Utc
|
||||||
.fix()
|
.fix()
|
||||||
.timestamp_opt(i64::try_from(self.meta.internaldate / 1000)?, 0)
|
.timestamp_opt(i64::try_from(self.query_result.metadata().ok_or(anyhow!("mail metadata were not fetched"))?.internaldate / 1000)?, 0)
|
||||||
.earliest()
|
.earliest()
|
||||||
.ok_or(anyhow!("Unable to parse internal date"))?;
|
.ok_or(anyhow!("Unable to parse internal date"))?;
|
||||||
Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt)))
|
Ok(MessageDataItem::InternalDate(DateTime::unvalidated(dt)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter<'b>(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> {
|
pub fn filter(&self, ap: &AttributesProxy) -> Result<(Body<'static>, SeenFlag)> {
|
||||||
let mut seen = SeenFlag::DoNothing;
|
let mut seen = SeenFlag::DoNothing;
|
||||||
let res_attrs = ap
|
let res_attrs = ap
|
||||||
.attrs
|
.attrs
|
||||||
|
@ -170,8 +156,8 @@ impl<'a> MailView<'a> {
|
||||||
.map(|attr| match attr {
|
.map(|attr| match attr {
|
||||||
MessageDataItemName::Uid => Ok(self.uid()),
|
MessageDataItemName::Uid => Ok(self.uid()),
|
||||||
MessageDataItemName::Flags => Ok(self.flags()),
|
MessageDataItemName::Flags => Ok(self.flags()),
|
||||||
MessageDataItemName::Rfc822Size => Ok(self.rfc_822_size()),
|
MessageDataItemName::Rfc822Size => self.rfc_822_size(),
|
||||||
MessageDataItemName::Rfc822Header => Ok(self.rfc_822_header()),
|
MessageDataItemName::Rfc822Header => self.rfc_822_header(),
|
||||||
MessageDataItemName::Rfc822Text => self.rfc_822_text(),
|
MessageDataItemName::Rfc822Text => self.rfc_822_text(),
|
||||||
MessageDataItemName::Rfc822 => self.rfc822(),
|
MessageDataItemName::Rfc822 => self.rfc822(),
|
||||||
MessageDataItemName::Envelope => Ok(self.envelope()),
|
MessageDataItemName::Envelope => Ok(self.envelope()),
|
||||||
|
@ -192,7 +178,7 @@ impl<'a> MailView<'a> {
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Body::Data(Data::Fetch {
|
Body::Data(Data::Fetch {
|
||||||
seq: self.ids.i,
|
seq: self.in_idx.i,
|
||||||
items: res_attrs.try_into()?,
|
items: res_attrs.try_into()?,
|
||||||
}),
|
}),
|
||||||
seen,
|
seen,
|
||||||
|
@ -208,19 +194,15 @@ pub enum SeenFlag {
|
||||||
// -------------------
|
// -------------------
|
||||||
|
|
||||||
pub enum FetchedMail<'a> {
|
pub enum FetchedMail<'a> {
|
||||||
None,
|
IndexOnly,
|
||||||
Partial(imf::Imf<'a>),
|
Partial(imf::Imf<'a>),
|
||||||
Full(AnyPart<'a>),
|
Full(AnyPart<'a>),
|
||||||
}
|
}
|
||||||
impl<'a> FetchedMail<'a> {
|
impl<'a> FetchedMail<'a> {
|
||||||
pub fn new_from_message(msg: Message<'a>) -> Self {
|
pub fn new_from_message(msg: Message<'a>) -> Self {
|
||||||
FetchedMail::Full(AnyPart::Msg(msg))
|
Self::Full(AnyPart::Msg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn new_from_header(hdr: imf::Imf<'a>) -> Self {
|
|
||||||
FetchedMail::Partial(hdr)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
fn as_anypart(&self) -> Result<&AnyPart<'a>> {
|
fn as_anypart(&self) -> Result<&AnyPart<'a>> {
|
||||||
match self {
|
match self {
|
||||||
FetchedMail::Full(x) => Ok(&x),
|
FetchedMail::Full(x) => Ok(&x),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Error, Result};
|
use anyhow::{anyhow, Error, Result};
|
||||||
|
|
||||||
use futures::stream::{FuturesOrdered, StreamExt};
|
use futures::stream::{FuturesOrdered, StreamExt};
|
||||||
|
|
||||||
|
@ -10,19 +10,19 @@ use imap_codec::imap_types::fetch::{MacroOrMessageDataItemNames, MessageDataItem
|
||||||
use imap_codec::imap_types::flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType};
|
use imap_codec::imap_types::flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType};
|
||||||
use imap_codec::imap_types::response::{Code, Data, Status};
|
use imap_codec::imap_types::response::{Code, Data, Status};
|
||||||
use imap_codec::imap_types::search::SearchKey;
|
use imap_codec::imap_types::search::SearchKey;
|
||||||
use imap_codec::imap_types::sequence::{self, SequenceSet};
|
use imap_codec::imap_types::sequence::SequenceSet;
|
||||||
|
|
||||||
use crate::mail::mailbox::Mailbox;
|
use crate::mail::mailbox::Mailbox;
|
||||||
use crate::mail::snapshot::FrozenMailbox;
|
use crate::mail::snapshot::FrozenMailbox;
|
||||||
use crate::mail::query::QueryScope;
|
use crate::mail::query::QueryScope;
|
||||||
use crate::mail::uidindex::{ImapUid, ImapUidvalidity};
|
use crate::mail::uidindex::{ImapUid, ImapUidvalidity};
|
||||||
use crate::mail::unique_ident::UniqueIdent;
|
|
||||||
|
|
||||||
use crate::imap::attributes::AttributesProxy;
|
use crate::imap::attributes::AttributesProxy;
|
||||||
use crate::imap::flags;
|
use crate::imap::flags;
|
||||||
use crate::imap::mail_view::{MailView, SeenFlag};
|
use crate::imap::mail_view::{MailView, SeenFlag};
|
||||||
use crate::imap::response::Body;
|
use crate::imap::response::Body;
|
||||||
use crate::imap::search;
|
//use crate::imap::search;
|
||||||
|
use crate::imap::index::Index;
|
||||||
|
|
||||||
|
|
||||||
const DEFAULT_FLAGS: [Flag; 5] = [
|
const DEFAULT_FLAGS: [Flag; 5] = [
|
||||||
|
@ -147,7 +147,7 @@ impl MailboxView {
|
||||||
|
|
||||||
let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
||||||
|
|
||||||
let mails = self.get_mail_ids(sequence_set, *is_uid_store)?;
|
let mails = self.index().fetch(sequence_set, *is_uid_store)?;
|
||||||
for mi in mails.iter() {
|
for mi in mails.iter() {
|
||||||
match kind {
|
match kind {
|
||||||
StoreType::Add => {
|
StoreType::Add => {
|
||||||
|
@ -190,7 +190,7 @@ impl MailboxView {
|
||||||
to: Arc<Mailbox>,
|
to: Arc<Mailbox>,
|
||||||
is_uid_copy: &bool,
|
is_uid_copy: &bool,
|
||||||
) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>)> {
|
) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>)> {
|
||||||
let mails = self.get_mail_ids(sequence_set, *is_uid_copy)?;
|
let mails = self.index().fetch(sequence_set, *is_uid_copy)?;
|
||||||
|
|
||||||
let mut new_uuids = vec![];
|
let mut new_uuids = vec![];
|
||||||
for mi in mails.iter() {
|
for mi in mails.iter() {
|
||||||
|
@ -217,7 +217,7 @@ impl MailboxView {
|
||||||
to: Arc<Mailbox>,
|
to: Arc<Mailbox>,
|
||||||
is_uid_copy: &bool,
|
is_uid_copy: &bool,
|
||||||
) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>, Vec<Body<'static>>)> {
|
) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>, Vec<Body<'static>>)> {
|
||||||
let mails = self.get_mail_ids(sequence_set, *is_uid_copy)?;
|
let mails = self.index().fetch(sequence_set, *is_uid_copy)?;
|
||||||
|
|
||||||
for mi in mails.iter() {
|
for mi in mails.iter() {
|
||||||
to.move_from(&self.0.mailbox, mi.uuid).await?;
|
to.move_from(&self.0.mailbox, mi.uuid).await?;
|
||||||
|
@ -255,16 +255,17 @@ impl MailboxView {
|
||||||
true => QueryScope::Full,
|
true => QueryScope::Full,
|
||||||
_ => QueryScope::Partial,
|
_ => QueryScope::Partial,
|
||||||
};
|
};
|
||||||
let mids = MailIdentifiersList(self.get_mail_ids(sequence_set, *is_uid_fetch)?);
|
let mail_idx_list = self.index().fetch(sequence_set, *is_uid_fetch)?;
|
||||||
let uuids = mids.uuids();
|
|
||||||
|
|
||||||
// [2/6] Fetch the emails
|
// [2/6] Fetch the emails
|
||||||
|
let uuids = mail_idx_list.iter().map(|midx| midx.uuid).collect::<Vec<_>>();
|
||||||
let query = self.0.query(&uuids, query_scope);
|
let query = self.0.query(&uuids, query_scope);
|
||||||
let query_result = query.fetch().await?;
|
let query_result = query.fetch().await?;
|
||||||
|
|
||||||
// [3/6] Derive an IMAP-specific view from the results, apply the filters
|
// [3/6] Derive an IMAP-specific view from the results, apply the filters
|
||||||
let views = query_result.iter()
|
let views = query_result.iter()
|
||||||
.map(MailView::new)
|
.zip(mail_idx_list.into_iter())
|
||||||
|
.map(|(qr, midx)| MailView::new(qr, midx))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
// [4/6] Apply the IMAP transformation to keep only relevant fields
|
// [4/6] Apply the IMAP transformation to keep only relevant fields
|
||||||
|
@ -296,9 +297,10 @@ impl MailboxView {
|
||||||
pub async fn search<'a>(
|
pub async fn search<'a>(
|
||||||
&self,
|
&self,
|
||||||
_charset: &Option<Charset<'a>>,
|
_charset: &Option<Charset<'a>>,
|
||||||
search_key: &SearchKey<'a>,
|
_search_key: &SearchKey<'a>,
|
||||||
uid: bool,
|
_uid: bool,
|
||||||
) -> Result<Vec<Body<'static>>> {
|
) -> Result<Vec<Body<'static>>> {
|
||||||
|
/*
|
||||||
// 1. Compute the subset of sequence identifiers we need to fetch
|
// 1. Compute the subset of sequence identifiers we need to fetch
|
||||||
let query = search::Criteria(search_key);
|
let query = search::Criteria(search_key);
|
||||||
let (seq_set, seq_type) = query.to_sequence_set();
|
let (seq_set, seq_type) = query.to_sequence_set();
|
||||||
|
@ -313,79 +315,15 @@ impl MailboxView {
|
||||||
let _need_body = query.need_body();
|
let _need_body = query.need_body();
|
||||||
|
|
||||||
Ok(vec![Body::Data(Data::Search(mail_u32))])
|
Ok(vec![Body::Data(Data::Search(mail_u32))])
|
||||||
|
*/
|
||||||
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
fn index<'a>(&'a self) -> Index<'a> {
|
||||||
// Gets the IMAP ID, the IMAP UIDs and, the Aerogramme UUIDs of mails identified by a SequenceSet of
|
Index(&self.0.snapshot)
|
||||||
// sequence numbers (~ IMAP selector)
|
|
||||||
fn get_mail_ids(
|
|
||||||
&self,
|
|
||||||
sequence_set: &SequenceSet,
|
|
||||||
by_uid: bool,
|
|
||||||
) -> Result<Vec<MailIdentifiers>> {
|
|
||||||
let mail_vec = self
|
|
||||||
.0
|
|
||||||
.snapshot
|
|
||||||
.idx_by_uid
|
|
||||||
.iter()
|
|
||||||
.map(|(uid, uuid)| (*uid, *uuid))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut mails = vec![];
|
|
||||||
|
|
||||||
if by_uid {
|
|
||||||
if mail_vec.is_empty() {
|
|
||||||
return Ok(vec![]);
|
|
||||||
}
|
|
||||||
let iter_strat = sequence::Strategy::Naive {
|
|
||||||
largest: mail_vec.last().unwrap().0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
for uid in sequence_set.iter(iter_strat) {
|
|
||||||
while mail_vec.get(i).map(|mail| mail.0 < uid).unwrap_or(false) {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
if let Some(mail) = mail_vec.get(i) {
|
|
||||||
if mail.0 == uid {
|
|
||||||
mails.push(MailIdentifiers {
|
|
||||||
i: NonZeroU32::try_from(i as u32 + 1).unwrap(),
|
|
||||||
uid: mail.0,
|
|
||||||
uuid: mail.1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if mail_vec.is_empty() {
|
|
||||||
bail!("No such message (mailbox is empty)");
|
|
||||||
}
|
|
||||||
|
|
||||||
let iter_strat = sequence::Strategy::Naive {
|
|
||||||
largest: NonZeroU32::try_from((mail_vec.len()) 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(MailIdentifiers {
|
|
||||||
i,
|
|
||||||
uid: mail.0,
|
|
||||||
uuid: mail.1,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
bail!("No such mail: {}", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(mails)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
|
||||||
|
|
||||||
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
|
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
|
||||||
fn uidvalidity_status(&self) -> Result<Body<'static>> {
|
fn uidvalidity_status(&self) -> Result<Body<'static>> {
|
||||||
let uid_validity = Status::ok(
|
let uid_validity = Status::ok(
|
||||||
|
@ -501,25 +439,6 @@ impl MailboxView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MailIdentifiers {
|
|
||||||
pub i: NonZeroU32,
|
|
||||||
pub uid: ImapUid,
|
|
||||||
pub uuid: UniqueIdent,
|
|
||||||
}
|
|
||||||
pub struct MailIdentifiersList(Vec<MailIdentifiers>);
|
|
||||||
|
|
||||||
impl MailIdentifiersList {
|
|
||||||
fn ids(&self) -> Vec<NonZeroU32> {
|
|
||||||
self.0.iter().map(|mi| mi.i).collect()
|
|
||||||
}
|
|
||||||
fn uids(&self) -> Vec<ImapUid> {
|
|
||||||
self.0.iter().map(|mi| mi.uid).collect()
|
|
||||||
}
|
|
||||||
fn uuids(&self) -> Vec<UniqueIdent> {
|
|
||||||
self.0.iter().map(|mi| mi.uuid).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -558,7 +477,7 @@ mod tests {
|
||||||
message_key: key,
|
message_key: key,
|
||||||
rfc822_size: 8usize,
|
rfc822_size: 8usize,
|
||||||
};
|
};
|
||||||
let ids = MailIdentifiers {
|
let ids = MailIndex {
|
||||||
i: NonZeroU32::MIN,
|
i: NonZeroU32::MIN,
|
||||||
uid: NonZeroU32::MIN,
|
uid: NonZeroU32::MIN,
|
||||||
uuid: unique_ident::gen_ident(),
|
uuid: unique_ident::gen_ident(),
|
||||||
|
|
|
@ -4,12 +4,12 @@ mod command;
|
||||||
mod flags;
|
mod flags;
|
||||||
mod flow;
|
mod flow;
|
||||||
mod imf_view;
|
mod imf_view;
|
||||||
|
mod index;
|
||||||
mod mail_view;
|
mod mail_view;
|
||||||
mod mailbox_view;
|
mod mailbox_view;
|
||||||
mod mime_view;
|
mod mime_view;
|
||||||
mod response;
|
mod response;
|
||||||
mod search;
|
mod search;
|
||||||
mod selectors;
|
|
||||||
mod session;
|
mod session;
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
use std::iter::zip;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
|
|
||||||
use crate::cryptoblob::Key;
|
|
||||||
use crate::imap::mail_view::{FetchedMail, MailView};
|
|
||||||
use crate::imap::mailbox_view::MailIdentifiers;
|
|
||||||
use crate::mail::mailbox::MailMeta;
|
|
||||||
use crate::mail::unique_ident::UniqueIdent;
|
|
||||||
|
|
||||||
pub struct BodyIdentifier<'a> {
|
|
||||||
pub msg_uuid: &'a UniqueIdent,
|
|
||||||
pub msg_key: &'a Key,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MailSelectionBuilder<'a> {
|
|
||||||
//attrs: AttributeProxy,
|
|
||||||
mail_count: usize,
|
|
||||||
need_body: bool,
|
|
||||||
mi: &'a [MailIdentifiers],
|
|
||||||
meta: &'a [MailMeta],
|
|
||||||
flags: &'a [&'a Vec<String>],
|
|
||||||
bodies: &'a [Vec<u8>],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MailSelectionBuilder<'a> {
|
|
||||||
pub fn new(need_body: bool, mail_count: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
mail_count,
|
|
||||||
need_body,
|
|
||||||
..MailSelectionBuilder::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_mail_identifiers(&mut self, mi: &'a [MailIdentifiers]) -> &mut Self {
|
|
||||||
self.mi = mi;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_metadata(&mut self, meta: &'a [MailMeta]) -> &mut Self {
|
|
||||||
self.meta = meta;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_flags(&mut self, flags: &'a [&'a Vec<String>]) -> &mut Self {
|
|
||||||
self.flags = flags;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bodies_to_collect(&self) -> Vec<BodyIdentifier> {
|
|
||||||
if !self.need_body {
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
zip(self.mi, self.meta)
|
|
||||||
.map(|(mi, meta)| BodyIdentifier {
|
|
||||||
msg_uuid: &mi.uuid,
|
|
||||||
msg_key: &meta.message_key,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_bodies(&mut self, rbodies: &'a [Vec<u8>]) -> &mut Self {
|
|
||||||
self.bodies = rbodies;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(&self) -> Result<Vec<MailView<'a>>> {
|
|
||||||
let mut bodies = vec![];
|
|
||||||
|
|
||||||
if !self.need_body {
|
|
||||||
for m in self.meta.iter() {
|
|
||||||
let (_, hdrs) =
|
|
||||||
eml_codec::parse_imf(&m.headers).or(Err(anyhow!("Invalid mail headers")))?;
|
|
||||||
bodies.push(FetchedMail::Partial(hdrs));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for rb in self.bodies.iter() {
|
|
||||||
let (_, p) = eml_codec::parse_message(&rb).or(Err(anyhow!("Invalid mail body")))?;
|
|
||||||
bodies.push(FetchedMail::new_from_message(p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.mi.len() != self.mail_count && self.meta.len() != self.mail_count
|
|
||||||
|| self.flags.len() != self.mail_count
|
|
||||||
|| bodies.len() != self.mail_count
|
|
||||||
{
|
|
||||||
return Err(anyhow!("Can't build a mail view selection as parts were not correctly registered into the builder."));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(zip(self.mi, zip(self.meta, zip(self.flags, bodies)))
|
|
||||||
.map(|(ids, (meta, (flags, content)))| MailView {
|
|
||||||
ids,
|
|
||||||
meta,
|
|
||||||
flags,
|
|
||||||
content,
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,6 +13,7 @@ pub struct Query<'a,'b> {
|
||||||
pub scope: QueryScope,
|
pub scope: QueryScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub enum QueryScope {
|
pub enum QueryScope {
|
||||||
Index,
|
Index,
|
||||||
Partial,
|
Partial,
|
||||||
|
@ -106,6 +107,7 @@ impl<'a> QueryResult<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn index(&self) -> &IndexEntry {
|
pub fn index(&self) -> &IndexEntry {
|
||||||
match self {
|
match self {
|
||||||
Self::IndexResult { index, .. } => index,
|
Self::IndexResult { index, .. } => index,
|
||||||
|
@ -114,7 +116,7 @@ impl<'a> QueryResult<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata(&self) -> Option<&MailMeta> {
|
pub fn metadata(&'a self) -> Option<&'a MailMeta> {
|
||||||
match self {
|
match self {
|
||||||
Self::IndexResult { .. } => None,
|
Self::IndexResult { .. } => None,
|
||||||
Self::PartialResult { metadata, .. } => Some(metadata),
|
Self::PartialResult { metadata, .. } => Some(metadata),
|
||||||
|
@ -122,7 +124,8 @@ impl<'a> QueryResult<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content(&self) -> Option<&[u8]> {
|
#[allow(dead_code)]
|
||||||
|
pub fn content(&'a self) -> Option<&'a [u8]> {
|
||||||
match self {
|
match self {
|
||||||
Self::FullResult { content, .. } => Some(content),
|
Self::FullResult { content, .. } => Some(content),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
Loading…
Reference in a new issue