tuimail/src/inbox.rs
2026-02-17 20:47:10 +01:00

88 lines
No EOL
2.9 KiB
Rust

use regex::Regex;
use crate::config::Config;
use crate::connect::ImapSession;
use crate::{connect, Email};
const MAX_FETCH: u32 = 50;
/// Refresh inbox using NOOP + fetch. Reconnects on error.
pub(crate) fn refresh(
session: &mut Option<ImapSession>,
config: &Config,
) -> Result<Vec<Email>, String> {
// If we have a session, try NOOP to keep alive / detect changes
if let Some(s) = session.as_mut() {
if s.noop().is_ok() {
return fetch_inbox(s);
}
}
// Session is dead or missing — reconnect
*session = None;
let mut new_session = connect::connect(config)?;
let result = fetch_inbox(&mut new_session);
*session = Some(new_session);
result
}
fn fetch_range(exists: u32) -> String {
let start = exists.saturating_sub(MAX_FETCH - 1).max(1);
format!("{}:{}", start, exists)
}
fn fetch_inbox(session: &mut ImapSession) -> Result<Vec<Email>, String> {
match session {
ImapSession::Plain(s) => {
let mailbox = s.select("INBOX").map_err(|e| e.to_string())?;
if mailbox.exists == 0 {
return Ok(Vec::new());
}
let range = fetch_range(mailbox.exists);
let messages = s
.fetch(range, "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]")
.map_err(|e| e.to_string())?;
let mut emails = parse_emails(&messages);
emails.reverse();
Ok(emails)
}
ImapSession::Tls(s) => {
let mailbox = s.select("INBOX").map_err(|e| e.to_string())?;
if mailbox.exists == 0 {
return Ok(Vec::new());
}
let range = fetch_range(mailbox.exists);
let messages = s
.fetch(range, "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]")
.map_err(|e| e.to_string())?;
let mut emails = parse_emails(&messages);
emails.reverse();
Ok(emails)
}
}
}
fn parse_emails(fetches: &[imap::types::Fetch]) -> Vec<Email> {
let mut emails = Vec::new();
for message in fetches {
if let Some(body) = message.header() {
let header = String::from_utf8_lossy(body);
let mut subject = String::new();
let mut from = String::new();
let mut date = String::new();
for line in header.lines() {
if let Some(val) = line.strip_prefix("Subject: ") {
subject = val.to_string();
} else if let Some(val) = line.strip_prefix("From: ") {
from = val.to_string();
} else if let Some(val) = line.strip_prefix("Date: ") {
date = Regex::new(r#" \(...\)$"#).unwrap().replace_all(val, "").to_string();
}
}
emails.push(Email { subject, from, date });
}
}
emails
}