CONDSTORE #71

Merged
quentin merged 21 commits from feat/condstore-try-2 into main 2024-01-15 07:07:07 +00:00
5 changed files with 58 additions and 35 deletions
Showing only changes of commit 6e798b90f5 - Show all commits

View file

@ -48,15 +48,21 @@ impl ServerCapability {
} }
} }
enum ClientStatus { pub enum ClientStatus {
NotSupportedByServer, NotSupportedByServer,
Disabled, Disabled,
Enabled, Enabled,
} }
impl ClientStatus {
pub fn is_enabled(&self) -> bool {
matches!(self, Self::Enabled)
}
}
pub struct ClientCapability { pub struct ClientCapability {
condstore: ClientStatus, pub condstore: ClientStatus,
utf8kind: Option<Utf8Kind>, pub utf8kind: Option<Utf8Kind>,
} }
impl ClientCapability { impl ClientCapability {

View file

@ -292,7 +292,7 @@ impl<'a> AuthenticatedContext<'a> {
} }
}; };
let view = MailboxView::new(mb).await; let view = MailboxView::new(mb, self.client_capabilities.condstore.is_enabled()).await;
let mut ret_attrs = vec![]; let mut ret_attrs = vec![];
for attr in attributes.iter() { for attr in attributes.iter() {
@ -439,7 +439,7 @@ impl<'a> AuthenticatedContext<'a> {
}; };
tracing::info!(username=%self.user.username, mailbox=%name, "mailbox.selected"); tracing::info!(username=%self.user.username, mailbox=%name, "mailbox.selected");
let mb = MailboxView::new(mb).await; let mb = MailboxView::new(mb, self.client_capabilities.condstore.is_enabled()).await;
let data = mb.summary()?; let data = mb.summary()?;
Ok(( Ok((
@ -474,7 +474,7 @@ impl<'a> AuthenticatedContext<'a> {
}; };
tracing::info!(username=%self.user.username, mailbox=%name, "mailbox.examined"); tracing::info!(username=%self.user.username, mailbox=%name, "mailbox.examined");
let mb = MailboxView::new(mb).await; let mb = MailboxView::new(mb, self.client_capabilities.condstore.is_enabled()).await;
let data = mb.summary()?; let data = mb.summary()?;
Ok(( Ok((

View file

@ -127,7 +127,7 @@ impl<'a> ExaminedContext<'a> {
} }
pub async fn noop(self) -> Result<(Response<'static>, flow::Transition)> { pub async fn noop(self) -> Result<(Response<'static>, flow::Transition)> {
self.mailbox.0.mailbox.force_sync().await?; self.mailbox.internal.mailbox.force_sync().await?;
let updates = self.mailbox.update().await?; let updates = self.mailbox.update().await?;
Ok(( Ok((

View file

@ -152,7 +152,7 @@ impl<'a> SelectedContext<'a> {
} }
pub async fn noop(self) -> Result<(Response<'static>, flow::Transition)> { pub async fn noop(self) -> Result<(Response<'static>, flow::Transition)> {
self.mailbox.0.mailbox.force_sync().await?; self.mailbox.internal.mailbox.force_sync().await?;
let updates = self.mailbox.update().await?; let updates = self.mailbox.update().await?;
Ok(( Ok((

View file

@ -8,7 +8,7 @@ use futures::stream::{FuturesOrdered, StreamExt};
use imap_codec::imap_types::core::Charset; use imap_codec::imap_types::core::Charset;
use imap_codec::imap_types::fetch::{MacroOrMessageDataItemNames, MessageDataItem}; 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, CodeOther, Data, Status};
use imap_codec::imap_types::search::SearchKey; use imap_codec::imap_types::search::SearchKey;
use imap_codec::imap_types::sequence::SequenceSet; use imap_codec::imap_types::sequence::SequenceSet;
@ -39,12 +39,18 @@ const DEFAULT_FLAGS: [Flag; 5] = [
/// To do this, it keeps a variable `known_state` that corresponds to /// To do this, it keeps a variable `known_state` that corresponds to
/// what the client knows, and produces IMAP messages to be sent to the /// what the client knows, and produces IMAP messages to be sent to the
/// client that go along updates to `known_state`. /// client that go along updates to `known_state`.
pub struct MailboxView(pub FrozenMailbox); pub struct MailboxView {
pub internal: FrozenMailbox,
pub is_condstore: bool,
}
impl MailboxView { impl MailboxView {
/// Creates a new IMAP view into a mailbox. /// Creates a new IMAP view into a mailbox.
pub async fn new(mailbox: Arc<Mailbox>) -> Self { pub async fn new(mailbox: Arc<Mailbox>, is_cond: bool) -> Self {
Self(mailbox.frozen().await) Self {
internal: mailbox.frozen().await,
is_condstore: is_cond,
}
} }
/// Create an updated view, useful to make a diff /// Create an updated view, useful to make a diff
@ -54,8 +60,8 @@ impl MailboxView {
/// This does NOT trigger a sync, it bases itself on what is currently /// This does NOT trigger a sync, it bases itself on what is currently
/// loaded in RAM by Bayou. /// loaded in RAM by Bayou.
pub async fn update(&mut self) -> Result<Vec<Body<'static>>> { pub async fn update(&mut self) -> Result<Vec<Body<'static>>> {
let old_snapshot = self.0.update().await; let old_snapshot = self.internal.update().await;
let new_snapshot = &self.0.snapshot; let new_snapshot = &self.internal.snapshot;
let mut data = Vec::<Body>::new(); let mut data = Vec::<Body>::new();
@ -130,6 +136,9 @@ impl MailboxView {
data.extend(self.flags_status()?.into_iter()); data.extend(self.flags_status()?.into_iter());
data.push(self.uidvalidity_status()?); data.push(self.uidvalidity_status()?);
data.push(self.uidnext_status()?); data.push(self.uidnext_status()?);
if self.is_condstore {
data.push(self.highestmodseq_status()?);
}
/*self.unseen_first_status()? /*self.unseen_first_status()?
.map(|unseen_status| data.push(unseen_status));*/ .map(|unseen_status| data.push(unseen_status));*/
@ -144,7 +153,7 @@ impl MailboxView {
flags: &[Flag<'a>], flags: &[Flag<'a>],
is_uid_store: &bool, is_uid_store: &bool,
) -> Result<Vec<Body<'static>>> { ) -> Result<Vec<Body<'static>>> {
self.0.sync().await?; self.internal.sync().await?;
let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>(); let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>();
@ -153,13 +162,13 @@ impl MailboxView {
for mi in mails.iter() { for mi in mails.iter() {
match kind { match kind {
StoreType::Add => { StoreType::Add => {
self.0.mailbox.add_flags(mi.uuid, &flags[..]).await?; self.internal.mailbox.add_flags(mi.uuid, &flags[..]).await?;
} }
StoreType::Remove => { StoreType::Remove => {
self.0.mailbox.del_flags(mi.uuid, &flags[..]).await?; self.internal.mailbox.del_flags(mi.uuid, &flags[..]).await?;
} }
StoreType::Replace => { StoreType::Replace => {
self.0.mailbox.set_flags(mi.uuid, &flags[..]).await?; self.internal.mailbox.set_flags(mi.uuid, &flags[..]).await?;
} }
} }
} }
@ -169,8 +178,8 @@ impl MailboxView {
} }
pub async fn expunge(&mut self) -> Result<Vec<Body<'static>>> { pub async fn expunge(&mut self) -> Result<Vec<Body<'static>>> {
self.0.sync().await?; self.internal.sync().await?;
let state = self.0.peek().await; let state = self.internal.peek().await;
let deleted_flag = Flag::Deleted.to_string(); let deleted_flag = Flag::Deleted.to_string();
let msgs = state let msgs = state
@ -180,7 +189,7 @@ impl MailboxView {
.map(|(uuid, _)| *uuid); .map(|(uuid, _)| *uuid);
for msg in msgs { for msg in msgs {
self.0.mailbox.delete(msg).await?; self.internal.mailbox.delete(msg).await?;
} }
self.update().await self.update().await
@ -197,7 +206,7 @@ impl MailboxView {
let mut new_uuids = vec![]; let mut new_uuids = vec![];
for mi in mails.iter() { for mi in mails.iter() {
new_uuids.push(to.copy_from(&self.0.mailbox, mi.uuid).await?); new_uuids.push(to.copy_from(&self.internal.mailbox, mi.uuid).await?);
} }
let mut ret = vec![]; let mut ret = vec![];
@ -224,7 +233,7 @@ impl MailboxView {
let mails = idx.fetch(sequence_set, *is_uid_copy)?; let mails = idx.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.internal.mailbox, mi.uuid).await?;
} }
let mut ret = vec![]; let mut ret = vec![];
@ -268,7 +277,7 @@ impl MailboxView {
.iter() .iter()
.map(|midx| midx.uuid) .map(|midx| midx.uuid)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let query_result = self.0.query(&uuids, query_scope).fetch().await?; let query_result = self.internal.query(&uuids, query_scope).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 let views = query_result
@ -294,7 +303,7 @@ impl MailboxView {
.filter(|(_mv, seen)| matches!(seen, SeenFlag::MustAdd)) .filter(|(_mv, seen)| matches!(seen, SeenFlag::MustAdd))
.map(|(mv, _seen)| async move { .map(|(mv, _seen)| async move {
let seen_flag = Flag::Seen.to_string(); let seen_flag = Flag::Seen.to_string();
self.0 self.internal
.mailbox .mailbox
.add_flags(*mv.query_result.uuid(), &[seen_flag]) .add_flags(*mv.query_result.uuid(), &[seen_flag])
.await?; .await?;
@ -332,7 +341,7 @@ impl MailboxView {
// 4. Fetch additional info about the emails // 4. Fetch additional info about the emails
let query_scope = crit.query_scope(); let query_scope = crit.query_scope();
let uuids = to_fetch.iter().map(|midx| midx.uuid).collect::<Vec<_>>(); let uuids = to_fetch.iter().map(|midx| midx.uuid).collect::<Vec<_>>();
let query_result = self.0.query(&uuids, query_scope).fetch().await?; let query_result = self.internal.query(&uuids, query_scope).fetch().await?;
// 5. If needed, filter the selection based on the body // 5. If needed, filter the selection based on the body
let kept_query = crit.filter_on_query(&to_fetch, &query_result)?; let kept_query = crit.filter_on_query(&to_fetch, &query_result)?;
@ -354,7 +363,7 @@ impl MailboxView {
/// It's not trivial to refactor the code to do that, so we are doing /// It's not trivial to refactor the code to do that, so we are doing
/// some useless computation for now... /// some useless computation for now...
fn index<'a>(&'a self) -> Result<Index<'a>> { fn index<'a>(&'a self) -> Result<Index<'a>> {
Index::new(&self.0.snapshot) Index::new(&self.internal.snapshot)
} }
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state` /// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
@ -369,7 +378,7 @@ impl MailboxView {
} }
pub(crate) fn uidvalidity(&self) -> ImapUidvalidity { pub(crate) fn uidvalidity(&self) -> ImapUidvalidity {
self.0.snapshot.uidvalidity self.internal.snapshot.uidvalidity
} }
/// Produce an OK [UIDNEXT _] message corresponding to `known_state` /// Produce an OK [UIDNEXT _] message corresponding to `known_state`
@ -384,7 +393,15 @@ impl MailboxView {
} }
pub(crate) fn uidnext(&self) -> ImapUid { pub(crate) fn uidnext(&self) -> ImapUid {
self.0.snapshot.uidnext self.internal.snapshot.uidnext
}
pub(crate) fn highestmodseq_status(&self) -> Result<Body<'static>> {
Ok(Body::Status(Status::ok(
None,
Some(Code::Other(CodeOther::unvalidated(format!("HIGHESTMODSEQ {}", 0).into_bytes()))),
"Highest",
)?))
} }
/// Produce an EXISTS message corresponding to the number of mails /// Produce an EXISTS message corresponding to the number of mails
@ -394,7 +411,7 @@ impl MailboxView {
} }
pub(crate) fn exists(&self) -> Result<u32> { pub(crate) fn exists(&self) -> Result<u32> {
Ok(u32::try_from(self.0.snapshot.idx_by_uid.len())?) Ok(u32::try_from(self.internal.snapshot.idx_by_uid.len())?)
} }
/// Produce a RECENT message corresponding to the number of /// Produce a RECENT message corresponding to the number of
@ -416,7 +433,7 @@ impl MailboxView {
#[allow(dead_code)] #[allow(dead_code)]
fn unseen_first(&self) -> Result<Option<NonZeroU32>> { fn unseen_first(&self) -> Result<Option<NonZeroU32>> {
Ok(self Ok(self
.0 .internal
.snapshot .snapshot
.table .table
.values() .values()
@ -428,7 +445,7 @@ impl MailboxView {
pub(crate) fn recent(&self) -> Result<u32> { pub(crate) fn recent(&self) -> Result<u32> {
let recent = self let recent = self
.0 .internal
.snapshot .snapshot
.idx_by_flag .idx_by_flag
.get(&"\\Recent".to_string()) .get(&"\\Recent".to_string())
@ -445,7 +462,7 @@ impl MailboxView {
// 1. Collecting all the possible flags in the mailbox // 1. Collecting all the possible flags in the mailbox
// 1.a Fetch them from our index // 1.a Fetch them from our index
let mut known_flags: Vec<Flag> = self let mut known_flags: Vec<Flag> = self
.0 .internal
.snapshot .snapshot
.idx_by_flag .idx_by_flag
.flags() .flags()
@ -485,9 +502,9 @@ impl MailboxView {
} }
pub(crate) fn unseen_count(&self) -> usize { pub(crate) fn unseen_count(&self) -> usize {
let total = self.0.snapshot.table.len(); let total = self.internal.snapshot.table.len();
let seen = self let seen = self
.0 .internal
.snapshot .snapshot
.idx_by_flag .idx_by_flag
.get(&Flag::Seen.to_string()) .get(&Flag::Seen.to_string())