From d24eb9918e3ab0c69af05c8cb92424ecaba903f3 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Thu, 11 Jan 2024 17:13:59 +0100 Subject: [PATCH] Enable CONDSTORE on STORE/FETCH modifier --- src/imap/capability.rs | 22 +++++++++++++++++++++- src/imap/command/examined.rs | 15 ++++++++++----- src/imap/command/selected.rs | 20 +++++++++++++------- src/imap/mailbox_view.rs | 10 +++++----- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/imap/capability.rs b/src/imap/capability.rs index 53d7b7d..6533ccb 100644 --- a/src/imap/capability.rs +++ b/src/imap/capability.rs @@ -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::extensions::enable::{CapabilityEnable, Utf8Kind}; use imap_codec::imap_types::response::Capability; use std::collections::HashSet; +use crate::imap::attributes::AttributesProxy; + fn capability_unselect() -> Capability<'static> { Capability::try_from("UNSELECT").unwrap() } @@ -91,6 +93,24 @@ impl ClientCapability { 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]) { for m in mods.iter() { match m { diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs index a8077e3..cdebc6d 100644 --- a/src/imap/command/examined.rs +++ b/src/imap/command/examined.rs @@ -7,6 +7,7 @@ use imap_codec::imap_types::fetch::MacroOrMessageDataItemNames; use imap_codec::imap_types::search::SearchKey; use imap_codec::imap_types::sequence::SequenceSet; +use crate::imap::attributes::AttributesProxy; use crate::imap::capability::{ClientCapability, ServerCapability}; use crate::imap::command::{anystate, authenticated}; use crate::imap::flow; @@ -92,11 +93,15 @@ impl<'a> ExaminedContext<'a> { modifiers: &[FetchModifier], uid: &bool, ) -> Result<(Response<'static>, flow::Transition)> { - match self.mailbox.fetch(sequence_set, attributes, uid).await { - Ok((resp, enable_condstore)) => { - if enable_condstore { - self.client_capabilities.enable_condstore(); - } + let ap = AttributesProxy::new(attributes, *uid); + + match self.mailbox.fetch(sequence_set, &ap, uid).await { + 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(( Response::build() .to_req(self.req) diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index 862d4aa..d7aa94f 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -15,7 +15,7 @@ use crate::imap::command::{anystate, authenticated, MailboxName}; use crate::imap::flow; use crate::imap::mailbox_view::MailboxView; use crate::imap::response::Response; - +use crate::imap::attributes::AttributesProxy; use crate::mail::user::User; pub struct SelectedContext<'a> { @@ -118,11 +118,16 @@ impl<'a> SelectedContext<'a> { modifiers: &[FetchModifier], uid: &bool, ) -> Result<(Response<'static>, flow::Transition)> { - match self.mailbox.fetch(sequence_set, attributes, uid).await { - Ok((resp, enable_condstore)) => { - if enable_condstore { - self.client_capabilities.enable_condstore(); - } + let ap = AttributesProxy::new(attributes, *uid); + + match self.mailbox.fetch(sequence_set, &ap, uid).await { + 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(( Response::build() .to_req(self.req) @@ -199,12 +204,13 @@ impl<'a> SelectedContext<'a> { modifiers: &[StoreModifier], uid: &bool, ) -> Result<(Response<'static>, flow::Transition)> { - tracing::info!(modifiers=?modifiers); let data = self .mailbox .store(sequence_set, kind, response, flags, uid) .await?; + self.client_capabilities.store_modifiers_enable(modifiers); + Ok(( Response::build() .to_req(self.req) diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs index 9e9e785..c3900cf 100644 --- a/src/imap/mailbox_view.rs +++ b/src/imap/mailbox_view.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Error, Result}; use futures::stream::{FuturesOrdered, StreamExt}; 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::response::{Code, CodeOther, Data, Status}; use imap_codec::imap_types::search::SearchKey; @@ -257,13 +257,13 @@ impl MailboxView { pub async fn fetch<'b>( &self, sequence_set: &SequenceSet, - attributes: &'b MacroOrMessageDataItemNames<'static>, + ap: &AttributesProxy, is_uid_fetch: &bool, - ) -> Result<(Vec>, bool)> { + ) -> Result>> { // [1/6] Pre-compute data // a. what are the uuids of the emails we want? // 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() { true => QueryScope::Full, _ => QueryScope::Partial, @@ -316,7 +316,7 @@ impl MailboxView { .collect::>()?; // [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...