imap: support LIST-STATUS

master
Manos Pitsidianakis 2020-07-22 11:12:59 +03:00
parent 350c8033b1
commit 6121f77853
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 39 additions and 4 deletions

View File

@ -60,6 +60,7 @@ pub static SUPPORTED_CAPABILITIES: &[&str] = &[
"IDLE", "IDLE",
"LOGIN", "LOGIN",
"LOGINDISABLED", "LOGINDISABLED",
"LIST-STATUS",
"ENABLE", "ENABLE",
"IMAP4REV1", "IMAP4REV1",
"SPECIAL-USE", "SPECIAL-USE",
@ -914,10 +915,26 @@ impl ImapType {
let mut mailboxes: HashMap<MailboxHash, ImapMailbox> = Default::default(); let mut mailboxes: HashMap<MailboxHash, ImapMailbox> = Default::default();
let mut res = String::with_capacity(8 * 1024); let mut res = String::with_capacity(8 * 1024);
let mut conn = connection.lock().await; let mut conn = connection.lock().await;
conn.send_command(b"LIST \"\" \"*\"").await?; let has_list_status: bool = conn
let _ = conn .uid_store
.read_response(&mut res, RequiredResponses::LIST_REQUIRED) .capabilities
.lock()
.unwrap()
.iter()
.any(|cap| cap.eq_ignore_ascii_case(b"LIST-STATUS"));
if has_list_status {
conn.send_command(b"LIST \"\" \"*\" RETURN (STATUS (MESSAGES UNSEEN))")
.await?;
conn.read_response(
&mut res,
RequiredResponses::LIST_REQUIRED | RequiredResponses::STATUS,
)
.await?; .await?;
} else {
conn.send_command(b"LIST \"\" \"*\"").await?;
conn.read_response(&mut res, RequiredResponses::LIST_REQUIRED)
.await?;
}
debug!("out: {}", &res); debug!("out: {}", &res);
let mut lines = res.split_rn(); let mut lines = res.split_rn();
/* Remove "M__ OK .." line */ /* Remove "M__ OK .." line */
@ -951,6 +968,20 @@ impl ImapType {
} else { } else {
mailboxes.insert(mailbox.hash, mailbox); mailboxes.insert(mailbox.hash, mailbox);
} }
} else if let Ok(status) =
protocol_parser::status_response(l.as_bytes()).map(|(_, v)| v)
{
if let Some(mailbox_hash) = status.mailbox {
if mailboxes.contains_key(&mailbox_hash) {
let entry = mailboxes.entry(mailbox_hash).or_default();
if let Some(total) = status.messages {
entry.exists.lock().unwrap().set_not_yet_seen(total);
}
if let Some(total) = status.unseen {
entry.unseen.lock().unwrap().set_not_yet_seen(total);
}
}
}
} else { } else {
debug!("parse error for {:?}", l); debug!("parse error for {:?}", l);
} }

View File

@ -1459,6 +1459,7 @@ pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct StatusResponse { pub struct StatusResponse {
pub mailbox: Option<MailboxHash>,
pub messages: Option<usize>, pub messages: Option<usize>,
pub recent: Option<usize>, pub recent: Option<usize>,
pub uidnext: Option<usize>, pub uidnext: Option<usize>,
@ -1468,9 +1469,11 @@ pub struct StatusResponse {
// status = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")" // status = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")"
// status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / "UNSEEN" // status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / "UNSEEN"
//* STATUS INBOX (MESSAGES 1057 UNSEEN 0)
pub fn status_response(input: &[u8]) -> IResult<&[u8], StatusResponse> { pub fn status_response(input: &[u8]) -> IResult<&[u8], StatusResponse> {
let (input, _) = tag("* STATUS ")(input)?; let (input, _) = tag("* STATUS ")(input)?;
let (input, _) = take_until(" (")(input)?; let (input, mailbox) = take_until(" (")(input)?;
let mailbox = mailbox_token(mailbox).map(|(_, m)| get_path_hash!(m)).ok();
let (input, _) = tag(" (")(input)?; let (input, _) = tag(" (")(input)?;
let (input, result) = permutation(( let (input, result) = permutation((
opt(preceded( opt(preceded(
@ -1508,6 +1511,7 @@ pub fn status_response(input: &[u8]) -> IResult<&[u8], StatusResponse> {
Ok(( Ok((
input, input,
StatusResponse { StatusResponse {
mailbox,
messages: result.0, messages: result.0,
recent: result.1, recent: result.1,
uidnext: result.2, uidnext: result.2,