imap: add status_response() parser

async
Manos Pitsidianakis 2020-06-23 12:32:58 +03:00
parent c7835ccc13
commit c08ceae97c
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 79 additions and 1 deletions

View File

@ -300,7 +300,7 @@ impl MailBackend for ImapType {
.chain_err_summary(|| {
format!("Could not select mailbox {}", mailbox_path)
})?;
let examine_response = protocol_parser::select_response(&response)
let mut examine_response = protocol_parser::select_response(&response)
.chain_err_summary(|| {
format!(
"Could not parse select response for mailbox {}",
@ -345,6 +345,24 @@ impl MailBackend for ImapType {
let mut exists: usize = examine_response.exists;
/* reselecting the same mailbox with EXAMINE prevents expunging it */
conn.examine_mailbox(mailbox_hash, &mut response)?;
if examine_response.uidnext == 0 {
/* UIDNEXT shouldn't be 0, since exists != 0 at this point */
conn.send_command(
format!("STATUS \"{}\" (UIDNEXT)", mailbox_path).as_bytes(),
)?;
conn.read_response(&mut response, RequiredResponses::STATUS)?;
let (_, status) = protocol_parser::status_response(response.as_bytes())?;
if let Some(uidnext) = status.uidnext {
if uidnext == 0 {
return Err(MeliError::new(
"IMAP server error: zero UIDNEXt with nonzero exists.",
));
}
examine_response.uidnext = uidnext;
} else {
return Err(MeliError::new("IMAP server did not reply with UIDNEXT"));
}
}
let mut tag_lck = uid_store.tag_index.write().unwrap();

View File

@ -1402,6 +1402,66 @@ pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some()
}
#[derive(Debug, Default, Clone)]
pub struct StatusResponse {
pub messages: Option<usize>,
pub recent: Option<usize>,
pub uidnext: Option<usize>,
pub uidvalidity: Option<usize>,
pub unseen: Option<usize>,
}
// status = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")"
// status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / "UNSEEN"
pub fn status_response(input: &[u8]) -> IResult<&[u8], StatusResponse> {
let (input, _) = tag("* STATUS ")(input)?;
let (input, _) = take_until(" (")(input)?;
let (input, _) = tag(" (")(input)?;
let (input, result) = permutation((
opt(preceded(
tag("MESSAGES "),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
)),
opt(preceded(
tag("RECENT "),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
)),
opt(preceded(
tag("UIDNEXT "),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
)),
opt(preceded(
tag("UIDVALIDITY "),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
)),
opt(preceded(
tag("UNSEEN "),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
)),
))(input)?;
let (input, _) = tag(")\r\n")(input)?;
Ok((
input,
StatusResponse {
messages: result.0,
recent: result.1,
uidnext: result.2,
uidvalidity: result.3,
unseen: result.4,
},
))
}
// mailbox = "INBOX" / astring
// ; INBOX is case-insensitive. All case variants of
// ; INBOX (e.g., "iNbOx") MUST be interpreted as INBOX