aerogramme/src/imap/session.rs

174 lines
6.4 KiB
Rust
Raw Normal View History

2024-01-03 19:53:07 +00:00
use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anonymous, authenticated, selected};
2022-06-20 16:09:20 +00:00
use crate::imap::flow;
2024-01-17 07:22:15 +00:00
use crate::imap::request::Request;
use crate::imap::response::{Response, ResponseOrIdle};
2022-06-20 16:09:20 +00:00
use crate::login::ArcLoginProvider;
2024-02-22 16:31:03 +00:00
use anyhow::{anyhow, bail, Context, Result};
use imap_codec::imap_types::{command::Command, core::Tag};
2022-06-13 09:44:02 +00:00
2022-06-17 16:39:36 +00:00
//-----
2022-06-13 09:44:02 +00:00
pub struct Instance {
2022-06-17 16:39:36 +00:00
pub login_provider: ArcLoginProvider,
2024-01-03 11:29:19 +00:00
pub server_capabilities: ServerCapability,
2024-01-03 19:53:07 +00:00
pub client_capabilities: ClientCapability,
2022-06-17 16:39:36 +00:00
pub state: flow::State,
2022-06-13 09:44:02 +00:00
}
impl Instance {
2024-01-03 11:29:19 +00:00
pub fn new(login_provider: ArcLoginProvider, cap: ServerCapability) -> Self {
2024-01-03 19:53:07 +00:00
let client_cap = ClientCapability::new(&cap);
2022-06-14 08:19:24 +00:00
Self {
2022-06-15 16:40:39 +00:00
login_provider,
2022-06-17 16:39:36 +00:00
state: flow::State::NotAuthenticated,
2024-01-03 11:29:19 +00:00
server_capabilities: cap,
2024-01-03 19:53:07 +00:00
client_capabilities: client_cap,
2022-06-14 08:19:24 +00:00
}
2022-06-13 09:44:02 +00:00
}
2024-01-17 07:22:15 +00:00
pub async fn request(&mut self, req: Request) -> ResponseOrIdle {
match req {
2024-02-22 16:30:40 +00:00
Request::IdleStart(tag) => self.idle_init(tag),
Request::IdlePoll => self.idle_poll().await,
2024-01-17 07:22:15 +00:00
Request::ImapCommand(cmd) => self.command(cmd).await,
}
}
2024-02-22 16:30:40 +00:00
pub fn idle_init(&mut self, tag: Tag<'static>) -> ResponseOrIdle {
// Build transition
//@FIXME the notifier should be hidden inside the state and thus not part of the transition!
let transition = flow::Transition::Idle(tag.clone(), tokio::sync::Notify::new());
// Try to apply the transition and get the stop notifier
let maybe_stop = self
.state
.apply(transition)
.context("IDLE transition failed")
2024-02-22 16:31:03 +00:00
.and_then(|_| {
self.state
.notify()
.ok_or(anyhow!("IDLE state has no Notify object"))
});
2024-02-22 16:30:40 +00:00
// Build an appropriate response
match maybe_stop {
Ok(stop) => ResponseOrIdle::IdleAccept(stop),
Err(e) => {
tracing::error!(err=?e, "unable to init idle due to a transition error");
//ResponseOrIdle::IdleReject(tag)
let no = Response::build()
.tag(tag)
.message(
"Internal error, processing command triggered an illegal IMAP state transition",
)
.no()
.unwrap();
ResponseOrIdle::IdleReject(no)
}
}
}
pub async fn idle_poll(&mut self) -> ResponseOrIdle {
match self.idle_poll_happy().await {
2024-01-18 16:33:57 +00:00
Ok(r) => r,
Err(e) => {
tracing::error!(err=?e, "something bad happened in idle");
ResponseOrIdle::Response(Response::bye().unwrap())
}
}
}
2024-02-22 16:30:40 +00:00
pub async fn idle_poll_happy(&mut self) -> Result<ResponseOrIdle> {
2024-01-18 16:33:57 +00:00
let (mbx, tag, stop) = match &mut self.state {
flow::State::Idle(_, ref mut mbx, _, tag, stop) => (mbx, tag.clone(), stop.clone()),
_ => bail!("Invalid session state, can't idle"),
2024-01-17 07:22:15 +00:00
};
2024-01-17 15:56:05 +00:00
tokio::select! {
_ = stop.notified() => {
2024-01-18 16:33:57 +00:00
self.state.apply(flow::Transition::UnIdle)?;
return Ok(ResponseOrIdle::Response(Response::build()
.tag(tag.clone())
2024-01-17 15:56:05 +00:00
.message("IDLE completed")
2024-01-18 16:33:57 +00:00
.ok()?))
},
change = mbx.idle_sync() => {
tracing::debug!("idle event");
return Ok(ResponseOrIdle::IdleEvent(change?));
2024-01-17 15:56:05 +00:00
}
}
2024-01-17 07:22:15 +00:00
}
pub async fn command(&mut self, cmd: Command<'static>) -> ResponseOrIdle {
2024-01-02 19:23:33 +00:00
// Command behavior is modulated by the state.
// To prevent state error, we handle the same command in separate code paths.
let (resp, tr) = match &mut self.state {
flow::State::NotAuthenticated => {
let ctx = anonymous::AnonymousContext {
req: &cmd,
login_provider: &self.login_provider,
2024-01-03 11:29:19 +00:00
server_capabilities: &self.server_capabilities,
2024-01-02 19:23:33 +00:00
};
anonymous::dispatch(ctx).await
}
flow::State::Authenticated(ref user) => {
2024-01-03 14:00:05 +00:00
let ctx = authenticated::AuthenticatedContext {
req: &cmd,
2024-01-03 11:29:19 +00:00
server_capabilities: &self.server_capabilities,
2024-01-03 19:53:07 +00:00
client_capabilities: &mut self.client_capabilities,
2024-01-03 14:00:05 +00:00
user,
};
2024-01-02 19:23:33 +00:00
authenticated::dispatch(ctx).await
}
2024-01-17 07:22:15 +00:00
flow::State::Selected(ref user, ref mut mailbox, ref perm) => {
2024-01-02 19:23:33 +00:00
let ctx = selected::SelectedContext {
req: &cmd,
2024-01-03 11:29:19 +00:00
server_capabilities: &self.server_capabilities,
2024-01-03 19:53:07 +00:00
client_capabilities: &mut self.client_capabilities,
2024-01-02 19:23:33 +00:00
user,
mailbox,
2024-01-17 07:22:15 +00:00
perm,
2024-01-02 19:23:33 +00:00
};
selected::dispatch(ctx).await
2022-06-28 13:52:55 +00:00
}
2024-01-17 07:22:15 +00:00
flow::State::Idle(..) => Err(anyhow!("can not receive command while idling")),
2024-01-02 19:23:33 +00:00
flow::State::Logout => Response::build()
.tag(cmd.tag.clone())
.message("No commands are allowed in the LOGOUT state.")
.bad()
.map(|r| (r, flow::Transition::None)),
}
.unwrap_or_else(|err| {
tracing::error!("Command error {:?} occured while processing {:?}", err, cmd);
(
Response::build()
.to_req(&cmd)
.message("Internal error while processing command")
.bad()
.unwrap(),
flow::Transition::None,
)
});
if let Err(e) = self.state.apply(tr) {
tracing::error!(
"Transition error {:?} occured while processing on command {:?}",
e,
cmd
);
2024-01-17 07:22:15 +00:00
return ResponseOrIdle::Response(Response::build()
2024-01-02 19:23:33 +00:00
.to_req(&cmd)
.message(
"Internal error, processing command triggered an illegal IMAP state transition",
)
.bad()
2024-01-17 07:22:15 +00:00
.unwrap());
2022-06-22 15:26:52 +00:00
}
2024-02-22 16:30:40 +00:00
ResponseOrIdle::Response(resp)
2022-06-20 16:09:20 +00:00
2024-02-22 16:30:40 +00:00
/*match &self.state {
2024-01-18 16:33:57 +00:00
flow::State::Idle(_, _, _, _, n) => ResponseOrIdle::StartIdle(n.clone()),
2024-01-17 07:22:15 +00:00
_ => ResponseOrIdle::Response(resp),
2024-02-22 16:30:40 +00:00
}*/
2022-06-22 15:26:52 +00:00
}
2022-06-13 09:44:02 +00:00
}