88 lines
No EOL
2.9 KiB
Rust
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
|
|
} |