imap: add UntaggedResponse::UIDFetch

memfd
Manos Pitsidianakis 2020-07-26 01:11:42 +03:00
parent f41a1ffe3a
commit 031e81ac8f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 84 additions and 34 deletions

View File

@ -129,7 +129,7 @@ impl BackendOp for ImapOp {
response.len(),
response.lines().collect::<Vec<&str>>().len()
);
let v = protocol_parser::uid_fetch_flags_response(response.as_bytes())
let v = protocol_parser::uid_fetch_flags_responses(response.as_bytes())
.map(|(_, v)| v)
.map_err(MeliError::from)?;
if v.len() != 1 {

View File

@ -139,7 +139,7 @@ fn test_imap_required_responses() {
}
}
assert_eq!(&ret, "* 1040 FETCH (UID 1064 FLAGS ())\r\n");
let v = protocol_parser::uid_fetch_flags_response(response.as_bytes())
let v = protocol_parser::uid_fetch_flags_responses(response.as_bytes())
.unwrap()
.1;
assert_eq!(v.len(), 1);
@ -727,24 +727,28 @@ pub fn uid_fetch_response_(
)(input)
}
pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], Vec<(usize, (Flag, Vec<String>))>> {
many0(|input| -> IResult<&[u8], (usize, (Flag, Vec<String>))> {
let (input, _) = tag("* ")(input)?;
let (input, _msn) = take_while(is_digit)(input)?;
let (input, _) = tag(" FETCH (")(input)?;
let (input, uid_flags) = permutation((
preceded(
alt((tag("UID "), tag(" UID "))),
map_res(digit1, |s| usize::from_str(to_str!(s))),
),
preceded(
alt((tag("FLAGS "), tag(" FLAGS "))),
delimited(tag("("), byte_flags, tag(")")),
),
))(input)?;
let (input, _) = tag(")\r\n")(input)?;
Ok((input, (uid_flags.0, uid_flags.1)))
})(input)
pub fn uid_fetch_flags_responses(
input: &[u8],
) -> IResult<&[u8], Vec<(usize, (Flag, Vec<String>))>> {
many0(uid_fetch_flags_response)(input)
}
pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], (usize, (Flag, Vec<String>))> {
let (input, _) = tag("* ")(input)?;
let (input, _msn) = take_while(is_digit)(input)?;
let (input, _) = tag(" FETCH (")(input)?;
let (input, uid_flags) = permutation((
preceded(
alt((tag("UID "), tag(" UID "))),
map_res(digit1, |s| usize::from_str(to_str!(s))),
),
preceded(
alt((tag("FLAGS "), tag(" FLAGS "))),
delimited(tag("("), byte_flags, tag(")")),
),
))(input)?;
let (input, _) = tag(")\r\n")(input)?;
Ok((input, (uid_flags.0, uid_flags.1)))
}
macro_rules! flags_to_imap_list {
@ -871,18 +875,20 @@ pub enum UntaggedResponse {
/// ```
Recent(usize),
Fetch(usize, (Flag, Vec<String>)),
UIDFetch(UID, (Flag, Vec<String>)),
Bye {
reason: String,
},
}
pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> {
let orig_input = input;
let (input, _) = tag("* ")(input)?;
let (input, num) = map_res(digit1, |s| usize::from_str(to_str!(s)))(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _tag) = take_until("\r\n")(input)?;
let (input, _) = tag("\r\n")(input)?;
debug!("Parse untagged response from {:?}", to_str!(input));
debug!("Parse untagged response from {:?}", to_str!(orig_input));
Ok((input, {
use UntaggedResponse::*;
match _tag {
@ -890,18 +896,23 @@ pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedRespons
b"EXISTS" => Some(Exists(num)),
b"RECENT" => Some(Recent(num)),
_ if _tag.starts_with(b"FETCH ") => {
let f = flags(unsafe {
std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..])
})
.map(|(_, flags)| Fetch(num, flags));
if let Err(ref err) = f {
debug!(
"untagged_response malformed fetch: {} {}",
unsafe { std::str::from_utf8_unchecked(_tag) },
err
)
if to_str!(_tag).contains("UID") {
let (uid, flags) = uid_fetch_flags_response(orig_input)?.1;
Some(UIDFetch(uid, flags))
} else {
let f = flags(unsafe {
std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..])
})
.map(|(_, flags)| Fetch(num, flags));
if let Err(ref err) = f {
debug!(
"untagged_response malformed fetch: {} {}",
unsafe { std::str::from_utf8_unchecked(_tag) },
err
)
}
f.ok()
}
f.ok()
}
_ => {
debug!("unknown untagged_response: {}", to_str!(_tag));

View File

@ -279,6 +279,24 @@ impl ImapConnection {
}
}
}
UntaggedResponse::UIDFetch(uid, flags) => {
debug!("fetch uid {} {:?}", uid, flags);
let lck = self.uid_store.uid_index.lock().unwrap();
let env_hash = lck.get(&(mailbox_hash, uid)).copied();
drop(lck);
if let Some(env_hash) = env_hash {
if !flags.0.intersects(crate::email::Flag::SEEN) {
mailbox.unseen.lock().unwrap().insert_new(env_hash);
} else {
mailbox.unseen.lock().unwrap().remove(env_hash);
}
self.add_refresh_event(RefreshEvent {
account_hash: self.uid_store.account_hash,
mailbox_hash,
kind: NewFlags(env_hash, flags),
});
};
}
UntaggedResponse::Fetch(msg_seq, flags) => {
/* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq
* and send update
@ -288,7 +306,7 @@ impl ImapConnection {
mailbox_hash,
self.send_command(
&[
b"UID SEARCH ",
b"UID SEARCH",
format!("{}", msg_seq).as_bytes(),
]
.join(&b' '),

View File

@ -444,6 +444,27 @@ pub async fn idle(kit: ImapWatchKit) -> Result<()> {
}
}
}
Ok(Some(UIDFetch(uid, flags))) => {
let res = uid_store
.uid_index
.lock()
.unwrap()
.get(&(mailbox_hash, uid))
.map(|h| *h);
if let Some(env_hash) = res {
if !flags.0.intersects(crate::email::Flag::SEEN) {
mailbox.unseen.lock().unwrap().insert_new(env_hash);
} else {
mailbox.unseen.lock().unwrap().remove(env_hash);
}
let mut conn = main_conn.lock().await;
conn.add_refresh_event(RefreshEvent {
account_hash: uid_store.account_hash,
mailbox_hash,
kind: NewFlags(env_hash, flags),
});
}
}
Ok(Some(Fetch(msg_seq, flags))) => {
/* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq
* and send update
@ -456,7 +477,7 @@ pub async fn idle(kit: ImapWatchKit) -> Result<()> {
conn.examine_mailbox(mailbox_hash, &mut response, false).await
conn.send_command(
&[
b"UID SEARCH ",
b"UID SEARCH",
format!("{}", msg_seq).as_bytes(),
]
.join(&b' '),