From 1a0247e9352619bed45dfb8101133261cfecb512 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Wed, 17 Jan 2024 10:14:48 +0100 Subject: [PATCH] WIP idle --- src/imap/command/selected.rs | 5 +++- src/imap/flow.rs | 11 +++++---- src/imap/mod.rs | 48 ++++++++++++++++++++++++++++++------ src/imap/request.rs | 2 +- src/imap/response.rs | 3 ++- src/imap/session.rs | 10 ++++---- 6 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index c9c5337..b62e2cb 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -81,7 +81,10 @@ pub async fn dispatch<'a>( // IDLE extension (rfc2177) CommandBody::Idle => { - unimplemented!() + Ok(( + Response::build().to_req(ctx.req).message("DUMMY response due to anti-pattern").ok()?, + flow::Transition::Idle(tokio::sync::Notify::new()), + )) } // In selected mode, we fallback to authenticated when needed diff --git a/src/imap/flow.rs b/src/imap/flow.rs index ff348ca..d1e27d4 100644 --- a/src/imap/flow.rs +++ b/src/imap/flow.rs @@ -1,6 +1,7 @@ use std::error::Error as StdError; use std::fmt; use std::sync::Arc; +use tokio::sync::Notify; use crate::imap::mailbox_view::MailboxView; use crate::mail::user::User; @@ -20,7 +21,7 @@ pub enum State { NotAuthenticated, Authenticated(Arc), Selected(Arc, MailboxView, MailboxPerm), - Idle(Arc, MailboxView, MailboxPerm), + Idle(Arc, MailboxView, MailboxPerm, Notify), Logout, } @@ -34,7 +35,7 @@ pub enum Transition { None, Authenticate(Arc), Select(MailboxView, MailboxPerm), - Idle, + Idle(Notify), UnIdle, Unselect, Logout, @@ -54,10 +55,10 @@ impl State { (State::Selected(u, _, _) , Transition::Unselect) => { State::Authenticated(u.clone()) } - (State::Selected(u, m, p), Transition::Idle) => { - State::Idle(u, m, p) + (State::Selected(u, m, p), Transition::Idle(s)) => { + State::Idle(u, m, p, s) }, - (State::Idle(u, m, p), Transition::UnIdle) => { + (State::Idle(u, m, p, _), Transition::UnIdle) => { State::Selected(u, m, p) }, (_, Transition::Logout) => State::Logout, diff --git a/src/imap/mod.rs b/src/imap/mod.rs index baa15f7..edfbbc4 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -101,9 +101,10 @@ use tokio::sync::mpsc::*; enum LoopMode { Quit, Interactive, - IdleUntil(tokio::sync::Notify), + Idle, } +// @FIXME a full refactor of this part of the code will be needed sooner or later struct NetLoop { ctx: ClientContext, server: ServerFlow, @@ -189,7 +190,7 @@ impl NetLoop { loop { mode = match mode { LoopMode::Interactive => self.interactive_mode().await?, - LoopMode::IdleUntil(notif) => self.idle_mode(notif).await?, + LoopMode::Idle => self.idle_mode().await?, LoopMode::Quit => break, } } @@ -237,15 +238,18 @@ impl NetLoop { } self.server.enqueue_status(response.completion); }, - Some(ResponseOrIdle::Idle) => { - let cr = CommandContinuationRequest::basic(None, "idling")?; + Some(ResponseOrIdle::StartIdle) => { + let cr = CommandContinuationRequest::basic(None, "Idling")?; self.server.enqueue_continuation(cr); - return Ok(LoopMode::IdleUntil(tokio::sync::Notify::new())) + self.cmd_tx.try_send(Request::Idle)?; + return Ok(LoopMode::Idle) }, None => { self.server.enqueue_status(Status::bye(None, "Internal session exited").unwrap()); tracing::error!("session task exited for {:?}, quitting", self.ctx.addr); }, + Some(_) => unreachable!(), + }, // When receiving a CTRL+C @@ -256,7 +260,37 @@ impl NetLoop { Ok(LoopMode::Interactive) } - async fn idle_mode(&mut self, notif: tokio::sync::Notify) -> Result { - Ok(LoopMode::IdleUntil(notif)) + async fn idle_mode(&mut self) -> Result { + tokio::select! { + maybe_msg = self.resp_rx.recv() => match maybe_msg { + Some(ResponseOrIdle::Response(response)) => { + for body_elem in response.body.into_iter() { + let _handle = match body_elem { + Body::Data(d) => self.server.enqueue_data(d), + Body::Status(s) => self.server.enqueue_status(s), + }; + } + self.server.enqueue_status(response.completion); + return Ok(LoopMode::Interactive) + }, + Some(ResponseOrIdle::IdleEvent(elems)) => { + for body_elem in elems.into_iter() { + let _handle = match body_elem { + Body::Data(d) => self.server.enqueue_data(d), + Body::Status(s) => self.server.enqueue_status(s), + }; + } + return Ok(LoopMode::Idle) + }, + 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!(), + } + }; + /*self.cmd_tx.try_send(Request::Idle).unwrap(); + Ok(LoopMode::Idle)*/ } } diff --git a/src/imap/request.rs b/src/imap/request.rs index c458276..2382b09 100644 --- a/src/imap/request.rs +++ b/src/imap/request.rs @@ -4,5 +4,5 @@ use tokio::sync::Notify; #[derive(Debug)] pub enum Request { ImapCommand(Command<'static>), - IdleUntil(Notify), + Idle, } diff --git a/src/imap/response.rs b/src/imap/response.rs index a9978e1..7b7f92d 100644 --- a/src/imap/response.rs +++ b/src/imap/response.rs @@ -116,5 +116,6 @@ impl<'a> Response<'a> { #[derive(Debug)] pub enum ResponseOrIdle { Response(Response<'static>), - Idle, + StartIdle, + IdleEvent(Vec>), } diff --git a/src/imap/session.rs b/src/imap/session.rs index 11c2764..d15016f 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -27,14 +27,14 @@ impl Instance { pub async fn request(&mut self, req: Request) -> ResponseOrIdle { match req { - Request::IdleUntil(stop) => ResponseOrIdle::Response(self.idle(stop).await), + Request::Idle => ResponseOrIdle::Response(self.idle().await), Request::ImapCommand(cmd) => self.command(cmd).await, } } - pub async fn idle(&mut self, stop: tokio::sync::Notify) -> Response<'static> { - let (user, mbx) = match &mut self.state { - flow::State::Idle(ref user, ref mut mailbox, ref perm) => (user, mailbox), + pub async fn idle(&mut self) -> Response<'static> { + let (user, mbx, perm, stop) = match &mut self.state { + flow::State::Idle(ref user, ref mut mailbox, ref perm, ref stop) => (user, mailbox, perm, stop), _ => unreachable!(), }; @@ -109,7 +109,7 @@ impl Instance { } match self.state { - flow::State::Idle(..) => ResponseOrIdle::Idle, + flow::State::Idle(..) => ResponseOrIdle::StartIdle, _ => ResponseOrIdle::Response(resp), } }