Enable CONDSTORE on STORE/FETCH modifier

This commit is contained in:
Quentin 2024-01-11 17:13:59 +01:00
parent 60a166185a
commit d24eb9918e
Signed by: quentin
GPG key ID: E9602264D639FF68
4 changed files with 49 additions and 18 deletions

View file

@ -1,9 +1,11 @@
use imap_codec::imap_types::command::SelectExamineModifier; use imap_codec::imap_types::command::{FetchModifier, StoreModifier, SelectExamineModifier};
use imap_codec::imap_types::core::NonEmptyVec; use imap_codec::imap_types::core::NonEmptyVec;
use imap_codec::imap_types::extensions::enable::{CapabilityEnable, Utf8Kind}; use imap_codec::imap_types::extensions::enable::{CapabilityEnable, Utf8Kind};
use imap_codec::imap_types::response::Capability; use imap_codec::imap_types::response::Capability;
use std::collections::HashSet; use std::collections::HashSet;
use crate::imap::attributes::AttributesProxy;
fn capability_unselect() -> Capability<'static> { fn capability_unselect() -> Capability<'static> {
Capability::try_from("UNSELECT").unwrap() Capability::try_from("UNSELECT").unwrap()
} }
@ -91,6 +93,24 @@ impl ClientCapability {
self.condstore = self.condstore.enable(); self.condstore = self.condstore.enable();
} }
pub fn attributes_enable(&mut self, ap: &AttributesProxy) {
if ap.is_enabling_condstore() {
self.enable_condstore()
}
}
pub fn fetch_modifiers_enable(&mut self, mods: &[FetchModifier]) {
if mods.iter().any(|x| matches!(x, FetchModifier::ChangedSince(..))) {
self.enable_condstore()
}
}
pub fn store_modifiers_enable(&mut self, mods: &[StoreModifier]) {
if mods.iter().any(|x| matches!(x, StoreModifier::UnchangedSince(..))) {
self.enable_condstore()
}
}
pub fn select_enable(&mut self, mods: &[SelectExamineModifier]) { pub fn select_enable(&mut self, mods: &[SelectExamineModifier]) {
for m in mods.iter() { for m in mods.iter() {
match m { match m {

View file

@ -7,6 +7,7 @@ use imap_codec::imap_types::fetch::MacroOrMessageDataItemNames;
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;
use crate::imap::attributes::AttributesProxy;
use crate::imap::capability::{ClientCapability, ServerCapability}; use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anystate, authenticated}; use crate::imap::command::{anystate, authenticated};
use crate::imap::flow; use crate::imap::flow;
@ -92,11 +93,15 @@ impl<'a> ExaminedContext<'a> {
modifiers: &[FetchModifier], modifiers: &[FetchModifier],
uid: &bool, uid: &bool,
) -> Result<(Response<'static>, flow::Transition)> { ) -> Result<(Response<'static>, flow::Transition)> {
match self.mailbox.fetch(sequence_set, attributes, uid).await { let ap = AttributesProxy::new(attributes, *uid);
Ok((resp, enable_condstore)) => {
if enable_condstore { match self.mailbox.fetch(sequence_set, &ap, uid).await {
self.client_capabilities.enable_condstore(); Ok(resp) => {
} // Capabilities enabling logic only on successful command
// (according to my understanding of the spec)
self.client_capabilities.attributes_enable(&ap);
self.client_capabilities.fetch_modifiers_enable(modifiers);
Ok(( Ok((
Response::build() Response::build()
.to_req(self.req) .to_req(self.req)

View file

@ -15,7 +15,7 @@ use crate::imap::command::{anystate, authenticated, MailboxName};
use crate::imap::flow; use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView; use crate::imap::mailbox_view::MailboxView;
use crate::imap::response::Response; use crate::imap::response::Response;
use crate::imap::attributes::AttributesProxy;
use crate::mail::user::User; use crate::mail::user::User;
pub struct SelectedContext<'a> { pub struct SelectedContext<'a> {
@ -118,11 +118,16 @@ impl<'a> SelectedContext<'a> {
modifiers: &[FetchModifier], modifiers: &[FetchModifier],
uid: &bool, uid: &bool,
) -> Result<(Response<'static>, flow::Transition)> { ) -> Result<(Response<'static>, flow::Transition)> {
match self.mailbox.fetch(sequence_set, attributes, uid).await { let ap = AttributesProxy::new(attributes, *uid);
Ok((resp, enable_condstore)) => {
if enable_condstore { match self.mailbox.fetch(sequence_set, &ap, uid).await {
self.client_capabilities.enable_condstore(); Ok(resp) => {
} // Capabilities enabling logic only on successful command
// (according to my understanding of the spec)
self.client_capabilities.attributes_enable(&ap);
self.client_capabilities.fetch_modifiers_enable(modifiers);
// Response to the client
Ok(( Ok((
Response::build() Response::build()
.to_req(self.req) .to_req(self.req)
@ -199,12 +204,13 @@ impl<'a> SelectedContext<'a> {
modifiers: &[StoreModifier], modifiers: &[StoreModifier],
uid: &bool, uid: &bool,
) -> Result<(Response<'static>, flow::Transition)> { ) -> Result<(Response<'static>, flow::Transition)> {
tracing::info!(modifiers=?modifiers);
let data = self let data = self
.mailbox .mailbox
.store(sequence_set, kind, response, flags, uid) .store(sequence_set, kind, response, flags, uid)
.await?; .await?;
self.client_capabilities.store_modifiers_enable(modifiers);
Ok(( Ok((
Response::build() Response::build()
.to_req(self.req) .to_req(self.req)

View file

@ -6,7 +6,7 @@ use anyhow::{anyhow, Error, Result};
use futures::stream::{FuturesOrdered, StreamExt}; 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::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, CodeOther, 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;
@ -257,13 +257,13 @@ impl MailboxView {
pub async fn fetch<'b>( pub async fn fetch<'b>(
&self, &self,
sequence_set: &SequenceSet, sequence_set: &SequenceSet,
attributes: &'b MacroOrMessageDataItemNames<'static>, ap: &AttributesProxy,
is_uid_fetch: &bool, is_uid_fetch: &bool,
) -> Result<(Vec<Body<'static>>, bool)> { ) -> Result<Vec<Body<'static>>> {
// [1/6] Pre-compute data // [1/6] Pre-compute data
// a. what are the uuids of the emails we want? // a. what are the uuids of the emails we want?
// b. do we need to fetch the full body? // b. do we need to fetch the full body?
let ap = AttributesProxy::new(attributes, *is_uid_fetch); //let ap = AttributesProxy::new(attributes, *is_uid_fetch);
let query_scope = match ap.need_body() { let query_scope = match ap.need_body() {
true => QueryScope::Full, true => QueryScope::Full,
_ => QueryScope::Partial, _ => QueryScope::Partial,
@ -316,7 +316,7 @@ impl MailboxView {
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
// [6/6] Build the final result that will be sent to the client. // [6/6] Build the final result that will be sent to the client.
Ok((imap_ret, ap.is_enabling_condstore())) Ok(imap_ret)
} }
/// A naive search implementation... /// A naive search implementation...