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::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 {

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::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)

View File

@ -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)

View File

@ -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<Body<'static>>, bool)> {
) -> Result<Vec<Body<'static>>> {
// [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::<Result<_, _>>()?;
// [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...