use crate::mailbox_registry::MailboxEvent;
use rusmes_proto::Username;
use rusmes_storage::MailboxId;
use std::time::Duration;
use tokio::sync::broadcast;
#[derive(Debug, Clone, PartialEq)]
pub enum ImapState {
NotAuthenticated,
Authenticated,
Selected { mailbox_id: MailboxId },
Idle { mailbox_id: MailboxId },
Logout,
}
#[derive(Debug, Clone)]
pub struct MailboxSnapshot {
pub exists: u32,
pub recent: u32,
}
pub struct ImapSession {
pub state: ImapState,
pub tag: Option<String>,
pub username: Option<Username>,
pub mailbox_snapshot: Option<MailboxSnapshot>,
pub idle_timeout: Duration,
pub mailbox_event_rx: Option<broadcast::Receiver<MailboxEvent>>,
pub compress_pending: bool,
}
impl ImapSession {
pub fn new() -> Self {
Self::new_with_timeout(Duration::from_secs(1800))
}
pub fn new_with_timeout(idle_timeout: Duration) -> Self {
Self {
state: ImapState::NotAuthenticated,
tag: None,
username: None,
mailbox_snapshot: None,
idle_timeout,
mailbox_event_rx: None,
compress_pending: false,
}
}
pub fn state(&self) -> &ImapState {
&self.state
}
pub fn update_snapshot(&mut self, exists: u32, recent: u32) {
self.mailbox_snapshot = Some(MailboxSnapshot { exists, recent });
}
pub fn mailbox_id(&self) -> Option<&MailboxId> {
match &self.state {
ImapState::Selected { mailbox_id } | ImapState::Idle { mailbox_id } => Some(mailbox_id),
_ => None,
}
}
pub fn drain_mailbox_events(&mut self) -> Vec<String> {
let rx = match self.mailbox_event_rx.as_mut() {
Some(r) => r,
None => return Vec::new(),
};
let mut lines = Vec::new();
loop {
match rx.try_recv() {
Ok(event) => {
if let Some(line) = format_mailbox_event(&event) {
lines.push(line);
}
}
Err(broadcast::error::TryRecvError::Empty) => break,
Err(broadcast::error::TryRecvError::Lagged(n)) => {
tracing::warn!(
"IMAP session lagged {n} mailbox events โ some notifications dropped"
);
}
Err(broadcast::error::TryRecvError::Closed) => {
self.mailbox_event_rx = None;
break;
}
}
}
lines
}
}
fn format_mailbox_event(event: &MailboxEvent) -> Option<String> {
match event {
MailboxEvent::Exists { count } => Some(format!("* {count} EXISTS")),
MailboxEvent::Recent { count } => Some(format!("* {count} RECENT")),
MailboxEvent::Expunge { seq } => Some(format!("* {seq} EXPUNGE")),
MailboxEvent::FlagsChanged { uid, flags } => {
let flag_str = flags.join(" ");
Some(format!("* {uid} FETCH (UID {uid} FLAGS ({flag_str}))"))
}
}
}
pub fn format_mailbox_event_pub(event: &MailboxEvent) -> Option<String> {
format_mailbox_event(event)
}
impl Default for ImapSession {
fn default() -> Self {
Self::new()
}
}