aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-01-03 20:53:07 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-01-03 20:53:07 +0100
commita6a0e1994db2b43f559e5c2e8ae5c58342e5136f (patch)
tree3a48395a42f3128af21d32cf23c94dc0898e6287
parent7de1c66d86dbfe8e8ef4ed1183e5a9b6a3a636bf (diff)
downloadaerogramme-a6a0e1994db2b43f559e5c2e8ae5c58342e5136f.tar.gz
aerogramme-a6a0e1994db2b43f559e5c2e8ae5c58342e5136f.zip
ENABLE is now supported
-rw-r--r--src/imap/capability.rs87
-rw-r--r--src/imap/command/authenticated.rs25
-rw-r--r--src/imap/command/examined.rs4
-rw-r--r--src/imap/command/selected.rs4
-rw-r--r--src/imap/session.rs8
5 files changed, 72 insertions, 56 deletions
diff --git a/src/imap/capability.rs b/src/imap/capability.rs
index f6b5a51..3929558 100644
--- a/src/imap/capability.rs
+++ b/src/imap/capability.rs
@@ -1,5 +1,7 @@
use imap_codec::imap_types::core::NonEmptyVec;
use imap_codec::imap_types::response::Capability;
+use imap_codec::imap_types::extensions::enable::{CapabilityEnable, Utf8Kind};
+use std::collections::HashSet;
fn capability_unselect() -> Capability<'static> {
Capability::try_from("UNSELECT").unwrap()
@@ -14,84 +16,67 @@ fn capability_qresync() -> Capability<'static> {
}
#[derive(Debug, Clone)]
-pub struct ServerCapability {
- r#move: bool,
- unselect: bool,
- condstore: bool,
- qresync: bool,
-}
+pub struct ServerCapability(HashSet<Capability<'static>>);
impl Default for ServerCapability {
fn default() -> Self {
- Self {
- r#move: true,
- unselect: true,
- condstore: false,
- qresync: false,
- }
+ Self(HashSet::from([
+ Capability::Imap4Rev1,
+ Capability::Move,
+ capability_unselect(),
+ //capability_condstore(),
+ //capability_qresync(),
+ ]))
}
}
impl ServerCapability {
pub fn to_vec(&self) -> NonEmptyVec<Capability<'static>> {
- let mut acc = vec![Capability::Imap4Rev1];
- if self.r#move {
- acc.push(Capability::Move);
- }
- if self.unselect {
- acc.push(capability_unselect());
- }
- if self.condstore {
- acc.push(capability_condstore());
- }
- if self.qresync {
- acc.push(capability_qresync());
- }
- acc.try_into().unwrap()
+ self.0.iter().map(|v| v.clone()).collect::<Vec<_>>().try_into().unwrap()
}
+ #[allow(dead_code)]
pub fn support(&self, cap: &Capability<'static>) -> bool {
- match cap {
- Capability::Imap4Rev1 => true,
- Capability::Move => self.r#move,
- x if *x == capability_condstore() => self.condstore,
- x if *x == capability_qresync() => self.qresync,
- x if *x == capability_unselect() => self.unselect,
- _ => false,
- }
+ self.0.contains(cap)
}
}
+enum ClientStatus {
+ NotSupportedByServer,
+ Disabled,
+ Enabled,
+}
+
pub struct ClientCapability {
- condstore: bool,
- qresync: bool,
+ condstore: ClientStatus,
+ utf8kind: Option<Utf8Kind>,
}
-impl Default for ClientCapability {
- fn default() -> Self {
+impl ClientCapability {
+ pub fn new(sc: &ServerCapability) -> Self {
Self {
- condstore: false,
- qresync: false,
+ condstore: match sc.0.contains(&capability_condstore()) {
+ true => ClientStatus::Disabled,
+ _ => ClientStatus::NotSupportedByServer,
+ },
+ utf8kind: None,
}
}
-}
-impl ClientCapability {
pub fn try_enable(
&mut self,
- srv: &ServerCapability,
- caps: &[Capability<'static>],
- ) -> Vec<Capability<'static>> {
+ caps: &[CapabilityEnable<'static>],
+ ) -> Vec<CapabilityEnable<'static>> {
let mut enabled = vec![];
for cap in caps {
match cap {
- x if *x == capability_condstore() && srv.condstore && !self.condstore => {
- self.condstore = true;
- enabled.push(x.clone());
+ CapabilityEnable::CondStore if matches!(self.condstore, ClientStatus::Disabled) => {
+ self.condstore = ClientStatus::Enabled;
+ enabled.push(cap.clone());
}
- x if *x == capability_qresync() && srv.qresync && !self.qresync => {
- self.qresync = true;
- enabled.push(x.clone());
+ CapabilityEnable::Utf8(kind) if Some(kind) != self.utf8kind.as_ref() => {
+ self.utf8kind = Some(kind.clone());
+ enabled.push(cap.clone());
}
_ => (),
}
diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs
index fbf29f9..ee7c8f3 100644
--- a/src/imap/command/authenticated.rs
+++ b/src/imap/command/authenticated.rs
@@ -3,14 +3,15 @@ use std::sync::Arc;
use anyhow::{anyhow, bail, Result};
use imap_codec::imap_types::command::{Command, CommandBody};
-use imap_codec::imap_types::core::{Atom, Literal, QuotedChar};
+use imap_codec::imap_types::core::{Atom, Literal, QuotedChar, NonEmptyVec};
use imap_codec::imap_types::datetime::DateTime;
use imap_codec::imap_types::flag::{Flag, FlagNameAttribute};
use imap_codec::imap_types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
use imap_codec::imap_types::response::{Code, CodeOther, Data};
use imap_codec::imap_types::status::{StatusDataItem, StatusDataItemName};
+use imap_codec::imap_types::extensions::enable::CapabilityEnable;
-use crate::imap::capability::ServerCapability;
+use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anystate, MailboxName};
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
@@ -24,6 +25,7 @@ use crate::mail::IMF;
pub struct AuthenticatedContext<'a> {
pub req: &'a Command<'static>,
pub server_capabilities: &'a ServerCapability,
+ pub client_capabilities: &'a mut ClientCapability,
pub user: &'a Arc<User>,
}
@@ -65,6 +67,11 @@ pub async fn dispatch<'a>(
message,
} => ctx.append(mailbox, flags, date, message).await,
+ // rfc5161 ENABLE
+ CommandBody::Enable { capabilities } => {
+ ctx.enable(capabilities)
+ },
+
// Collect other commands
_ => anystate::wrong_state(ctx.req.tag.clone()),
}
@@ -511,6 +518,20 @@ impl<'a> AuthenticatedContext<'a> {
}
}
+ fn enable(self, cap_enable: &NonEmptyVec<CapabilityEnable<'static>>) -> Result<(Response<'static>, flow::Transition)> {
+ let mut response_builder = Response::build().to_req(self.req);
+ let capabilities = self.client_capabilities.try_enable(cap_enable.as_ref());
+ if capabilities.len() > 0 {
+ response_builder = response_builder.data(Data::Enabled { capabilities });
+ }
+ Ok((
+ response_builder
+ .message("ENABLE completed")
+ .ok()?,
+ flow::Transition::None,
+ ))
+ }
+
pub(crate) async fn append_internal(
self,
mailbox: &MailboxCodec<'a>,
diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs
index bddd0f9..0d688c0 100644
--- a/src/imap/command/examined.rs
+++ b/src/imap/command/examined.rs
@@ -7,7 +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::capability::ServerCapability;
+use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anystate, authenticated};
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
@@ -19,6 +19,7 @@ pub struct ExaminedContext<'a> {
pub user: &'a Arc<User>,
pub mailbox: &'a mut MailboxView,
pub server_capabilities: &'a ServerCapability,
+ pub client_capabilities: &'a mut ClientCapability,
}
pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, flow::Transition)> {
@@ -60,6 +61,7 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl
authenticated::dispatch(authenticated::AuthenticatedContext {
req: ctx.req,
server_capabilities: ctx.server_capabilities,
+ client_capabilities: ctx.client_capabilities,
user: ctx.user,
})
.await
diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs
index 28ebbe8..c8cc680 100644
--- a/src/imap/command/selected.rs
+++ b/src/imap/command/selected.rs
@@ -10,7 +10,7 @@ use imap_codec::imap_types::response::{Code, CodeOther};
use imap_codec::imap_types::search::SearchKey;
use imap_codec::imap_types::sequence::SequenceSet;
-use crate::imap::capability::ServerCapability;
+use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anystate, authenticated, MailboxName};
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
@@ -23,6 +23,7 @@ pub struct SelectedContext<'a> {
pub user: &'a Arc<User>,
pub mailbox: &'a mut MailboxView,
pub server_capabilities: &'a ServerCapability,
+ pub client_capabilities: &'a mut ClientCapability,
}
pub async fn dispatch<'a>(
@@ -76,6 +77,7 @@ pub async fn dispatch<'a>(
authenticated::dispatch(authenticated::AuthenticatedContext {
req: ctx.req,
server_capabilities: ctx.server_capabilities,
+ client_capabilities: ctx.client_capabilities,
user: ctx.user,
})
.await
diff --git a/src/imap/session.rs b/src/imap/session.rs
index 55026b9..6b26478 100644
--- a/src/imap/session.rs
+++ b/src/imap/session.rs
@@ -1,4 +1,4 @@
-use crate::imap::capability::ServerCapability;
+use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anonymous, authenticated, examined, selected};
use crate::imap::flow;
use crate::imap::response::Response;
@@ -9,14 +9,17 @@ use imap_codec::imap_types::command::Command;
pub struct Instance {
pub login_provider: ArcLoginProvider,
pub server_capabilities: ServerCapability,
+ pub client_capabilities: ClientCapability,
pub state: flow::State,
}
impl Instance {
pub fn new(login_provider: ArcLoginProvider, cap: ServerCapability) -> Self {
+ let client_cap = ClientCapability::new(&cap);
Self {
login_provider,
state: flow::State::NotAuthenticated,
server_capabilities: cap,
+ client_capabilities: client_cap,
}
}
@@ -36,6 +39,7 @@ impl Instance {
let ctx = authenticated::AuthenticatedContext {
req: &cmd,
server_capabilities: &self.server_capabilities,
+ client_capabilities: &mut self.client_capabilities,
user,
};
authenticated::dispatch(ctx).await
@@ -44,6 +48,7 @@ impl Instance {
let ctx = selected::SelectedContext {
req: &cmd,
server_capabilities: &self.server_capabilities,
+ client_capabilities: &mut self.client_capabilities,
user,
mailbox,
};
@@ -53,6 +58,7 @@ impl Instance {
let ctx = examined::ExaminedContext {
req: &cmd,
server_capabilities: &self.server_capabilities,
+ client_capabilities: &mut self.client_capabilities,
user,
mailbox,
};