aboutsummaryrefslogtreecommitdiff
path: root/aero-proto/imap/capability.rs
diff options
context:
space:
mode:
Diffstat (limited to 'aero-proto/imap/capability.rs')
-rw-r--r--aero-proto/imap/capability.rs159
1 files changed, 159 insertions, 0 deletions
diff --git a/aero-proto/imap/capability.rs b/aero-proto/imap/capability.rs
new file mode 100644
index 0000000..c76b51c
--- /dev/null
+++ b/aero-proto/imap/capability.rs
@@ -0,0 +1,159 @@
+use imap_codec::imap_types::command::{FetchModifier, SelectExamineModifier, StoreModifier};
+use imap_codec::imap_types::core::Vec1;
+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()
+}
+
+fn capability_condstore() -> Capability<'static> {
+ Capability::try_from("CONDSTORE").unwrap()
+}
+
+fn capability_uidplus() -> Capability<'static> {
+ Capability::try_from("UIDPLUS").unwrap()
+}
+
+fn capability_liststatus() -> Capability<'static> {
+ Capability::try_from("LIST-STATUS").unwrap()
+}
+
+/*
+fn capability_qresync() -> Capability<'static> {
+ Capability::try_from("QRESYNC").unwrap()
+}
+*/
+
+#[derive(Debug, Clone)]
+pub struct ServerCapability(HashSet<Capability<'static>>);
+
+impl Default for ServerCapability {
+ fn default() -> Self {
+ Self(HashSet::from([
+ Capability::Imap4Rev1,
+ Capability::Enable,
+ Capability::Move,
+ Capability::LiteralPlus,
+ Capability::Idle,
+ capability_unselect(),
+ capability_condstore(),
+ capability_uidplus(),
+ capability_liststatus(),
+ //capability_qresync(),
+ ]))
+ }
+}
+
+impl ServerCapability {
+ pub fn to_vec(&self) -> Vec1<Capability<'static>> {
+ self.0
+ .iter()
+ .map(|v| v.clone())
+ .collect::<Vec<_>>()
+ .try_into()
+ .unwrap()
+ }
+
+ #[allow(dead_code)]
+ pub fn support(&self, cap: &Capability<'static>) -> bool {
+ self.0.contains(cap)
+ }
+}
+
+#[derive(Clone)]
+pub enum ClientStatus {
+ NotSupportedByServer,
+ Disabled,
+ Enabled,
+}
+impl ClientStatus {
+ pub fn is_enabled(&self) -> bool {
+ matches!(self, Self::Enabled)
+ }
+
+ pub fn enable(&self) -> Self {
+ match self {
+ Self::Disabled => Self::Enabled,
+ other => other.clone(),
+ }
+ }
+}
+
+pub struct ClientCapability {
+ pub condstore: ClientStatus,
+ pub utf8kind: Option<Utf8Kind>,
+}
+
+impl ClientCapability {
+ pub fn new(sc: &ServerCapability) -> Self {
+ Self {
+ condstore: match sc.0.contains(&capability_condstore()) {
+ true => ClientStatus::Disabled,
+ _ => ClientStatus::NotSupportedByServer,
+ },
+ utf8kind: None,
+ }
+ }
+
+ pub fn enable_condstore(&mut self) {
+ 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 {
+ SelectExamineModifier::Condstore => self.enable_condstore(),
+ }
+ }
+ }
+
+ pub fn try_enable(
+ &mut self,
+ caps: &[CapabilityEnable<'static>],
+ ) -> Vec<CapabilityEnable<'static>> {
+ let mut enabled = vec![];
+ for cap in caps {
+ match cap {
+ CapabilityEnable::CondStore if matches!(self.condstore, ClientStatus::Disabled) => {
+ self.condstore = ClientStatus::Enabled;
+ enabled.push(cap.clone());
+ }
+ CapabilityEnable::Utf8(kind) if Some(kind) != self.utf8kind.as_ref() => {
+ self.utf8kind = Some(kind.clone());
+ enabled.push(cap.clone());
+ }
+ _ => (),
+ }
+ }
+
+ enabled
+ }
+}