aboutsummaryrefslogtreecommitdiff
path: root/src/imap
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-01-17 16:56:05 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-01-17 16:56:05 +0100
commite1161cab0e71ec604e376d2d87f7d1226f3f0244 (patch)
tree54cc16089536d3451bb1602b5830844e9bb9c4cb /src/imap
parent4a15ceacf1f45b15ae9b926110f48447c258ba1c (diff)
downloadaerogramme-e1161cab0e71ec604e376d2d87f7d1226f3f0244.tar.gz
aerogramme-e1161cab0e71ec604e376d2d87f7d1226f3f0244.zip
idle sync
Diffstat (limited to 'src/imap')
-rw-r--r--src/imap/command/selected.rs2
-rw-r--r--src/imap/flow.rs4
-rw-r--r--src/imap/mod.rs62
-rw-r--r--src/imap/request.rs1
-rw-r--r--src/imap/response.rs4
-rw-r--r--src/imap/session.rs14
6 files changed, 69 insertions, 18 deletions
diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs
index b62e2cb..4eb4e61 100644
--- a/src/imap/command/selected.rs
+++ b/src/imap/command/selected.rs
@@ -82,7 +82,7 @@ pub async fn dispatch<'a>(
// IDLE extension (rfc2177)
CommandBody::Idle => {
Ok((
- Response::build().to_req(ctx.req).message("DUMMY response due to anti-pattern").ok()?,
+ Response::build().to_req(ctx.req).message("DUMMY command due to anti-pattern in the code").ok()?,
flow::Transition::Idle(tokio::sync::Notify::new()),
))
}
diff --git a/src/imap/flow.rs b/src/imap/flow.rs
index d1e27d4..37f225b 100644
--- a/src/imap/flow.rs
+++ b/src/imap/flow.rs
@@ -21,7 +21,7 @@ pub enum State {
NotAuthenticated,
Authenticated(Arc<User>),
Selected(Arc<User>, MailboxView, MailboxPerm),
- Idle(Arc<User>, MailboxView, MailboxPerm, Notify),
+ Idle(Arc<User>, MailboxView, MailboxPerm, Arc<Notify>),
Logout,
}
@@ -56,7 +56,7 @@ impl State {
State::Authenticated(u.clone())
}
(State::Selected(u, m, p), Transition::Idle(s)) => {
- State::Idle(u, m, p, s)
+ State::Idle(u, m, p, Arc::new(s))
},
(State::Idle(u, m, p, _), Transition::UnIdle) => {
State::Selected(u, m, p)
diff --git a/src/imap/mod.rs b/src/imap/mod.rs
index edfbbc4..c50c3fc 100644
--- a/src/imap/mod.rs
+++ b/src/imap/mod.rs
@@ -97,11 +97,14 @@ impl Server {
}
}
-use tokio::sync::mpsc::*;
+use tokio::sync::mpsc::*;
+use tokio_util::bytes::BytesMut;
+use tokio::sync::Notify;
+use std::sync::Arc;
enum LoopMode {
Quit,
Interactive,
- Idle,
+ Idle(BytesMut, Arc<Notify>),
}
// @FIXME a full refactor of this part of the code will be needed sooner or later
@@ -190,7 +193,7 @@ impl NetLoop {
loop {
mode = match mode {
LoopMode::Interactive => self.interactive_mode().await?,
- LoopMode::Idle => self.idle_mode().await?,
+ LoopMode::Idle(buff, stop) => self.idle_mode(buff, stop).await?,
LoopMode::Quit => break,
}
}
@@ -238,11 +241,11 @@ impl NetLoop {
}
self.server.enqueue_status(response.completion);
},
- Some(ResponseOrIdle::StartIdle) => {
+ Some(ResponseOrIdle::StartIdle(stop)) => {
let cr = CommandContinuationRequest::basic(None, "Idling")?;
self.server.enqueue_continuation(cr);
self.cmd_tx.try_send(Request::Idle)?;
- return Ok(LoopMode::Idle)
+ return Ok(LoopMode::Idle(BytesMut::new(), stop))
},
None => {
self.server.enqueue_status(Status::bye(None, "Internal session exited").unwrap());
@@ -260,9 +263,19 @@ impl NetLoop {
Ok(LoopMode::Interactive)
}
- async fn idle_mode(&mut self) -> Result<LoopMode> {
+ async fn idle_mode(&mut self, mut buff: BytesMut, stop: Arc<Notify>) -> Result<LoopMode> {
+ // Flush send
+ loop {
+ match self.server.progress_send().await? {
+ Some(..) => continue,
+ None => break,
+ }
+ }
+
tokio::select! {
+ // Receiving IDLE event from background
maybe_msg = self.resp_rx.recv() => match maybe_msg {
+ // Session decided idle is terminated
Some(ResponseOrIdle::Response(response)) => {
for body_elem in response.body.into_iter() {
let _handle = match body_elem {
@@ -273,6 +286,7 @@ impl NetLoop {
self.server.enqueue_status(response.completion);
return Ok(LoopMode::Interactive)
},
+ // Session has some information for user
Some(ResponseOrIdle::IdleEvent(elems)) => {
for body_elem in elems.into_iter() {
let _handle = match body_elem {
@@ -280,17 +294,43 @@ impl NetLoop {
Body::Status(s) => self.server.enqueue_status(s),
};
}
- return Ok(LoopMode::Idle)
+ self.cmd_tx.try_send(Request::Idle)?;
+ return Ok(LoopMode::Idle(buff, stop))
},
+
+ // Session crashed
None => {
self.server.enqueue_status(Status::bye(None, "Internal session exited").unwrap());
tracing::error!("session task exited for {:?}, quitting", self.ctx.addr);
return Ok(LoopMode::Interactive)
},
- Some(ResponseOrIdle::StartIdle) => unreachable!(),
- }
+
+ // Session can't start idling while already idling, it's a logic error!
+ Some(ResponseOrIdle::StartIdle(..)) => bail!("can't start idling while already idling!"),
+ },
+
+ // User is trying to interact with us
+ _read_client_bytes = self.server.stream.read(&mut buff) => {
+ use imap_codec::decode::Decoder;
+ let codec = imap_codec::IdleDoneCodec::new();
+ match codec.decode(&buff) {
+ Ok(([], imap_codec::imap_types::extensions::idle::IdleDone)) => {
+ // Session will be informed that it must stop idle
+ // It will generate the "done" message and change the loop mode
+ stop.notify_one()
+ },
+ Err(_) => (),
+ _ => bail!("Client sent data after terminating the continuation without waiting for the server. This is an unsupported behavior and bug in Aerogramme, quitting."),
+ };
+
+ return Ok(LoopMode::Idle(buff, stop))
+ },
+
+ // When receiving a CTRL+C
+ _ = self.ctx.must_exit.changed() => {
+ self.server.enqueue_status(Status::bye(None, "Server is being shutdown").unwrap());
+ return Ok(LoopMode::Interactive)
+ },
};
- /*self.cmd_tx.try_send(Request::Idle).unwrap();
- Ok(LoopMode::Idle)*/
}
}
diff --git a/src/imap/request.rs b/src/imap/request.rs
index 2382b09..49b4992 100644
--- a/src/imap/request.rs
+++ b/src/imap/request.rs
@@ -1,5 +1,4 @@
use imap_codec::imap_types::command::Command;
-use tokio::sync::Notify;
#[derive(Debug)]
pub enum Request {
diff --git a/src/imap/response.rs b/src/imap/response.rs
index 7b7f92d..afcb29f 100644
--- a/src/imap/response.rs
+++ b/src/imap/response.rs
@@ -1,4 +1,6 @@
+use std::sync::Arc;
use anyhow::Result;
+use tokio::sync::Notify;
use imap_codec::imap_types::command::Command;
use imap_codec::imap_types::core::Tag;
use imap_codec::imap_types::response::{Code, Data, Status};
@@ -116,6 +118,6 @@ impl<'a> Response<'a> {
#[derive(Debug)]
pub enum ResponseOrIdle {
Response(Response<'static>),
- StartIdle,
+ StartIdle(Arc<Notify>),
IdleEvent(Vec<Body<'static>>),
}
diff --git a/src/imap/session.rs b/src/imap/session.rs
index d15016f..1d473ed 100644
--- a/src/imap/session.rs
+++ b/src/imap/session.rs
@@ -38,6 +38,16 @@ impl Instance {
_ => unreachable!(),
};
+ tokio::select! {
+ _ = stop.notified() => {
+ return Response::build()
+ .tag(imap_codec::imap_types::core::Tag::try_from("FIXME").unwrap())
+ .message("IDLE completed")
+ .ok()
+ .unwrap()
+ }
+ }
+
unimplemented!();
}
@@ -108,8 +118,8 @@ impl Instance {
.unwrap());
}
- match self.state {
- flow::State::Idle(..) => ResponseOrIdle::StartIdle,
+ match &self.state {
+ flow::State::Idle(_, _, _, n) => ResponseOrIdle::StartIdle(n.clone()),
_ => ResponseOrIdle::Response(resp),
}
}