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.len(),
response.lines().collect::<Vec<&str>>().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(|(_, v)| v)
.map_err(MeliError::from)?; .map_err(MeliError::from)?;
if v.len() != 1 { 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"); 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() .unwrap()
.1; .1;
assert_eq!(v.len(), 1); assert_eq!(v.len(), 1);
@ -727,24 +727,28 @@ pub fn uid_fetch_response_(
)(input) )(input)
} }
pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], Vec<(usize, (Flag, Vec<String>))>> { pub fn uid_fetch_flags_responses(
many0(|input| -> IResult<&[u8], (usize, (Flag, Vec<String>))> { input: &[u8],
let (input, _) = tag("* ")(input)?; ) -> IResult<&[u8], Vec<(usize, (Flag, Vec<String>))>> {
let (input, _msn) = take_while(is_digit)(input)?; many0(uid_fetch_flags_response)(input)
let (input, _) = tag(" FETCH (")(input)?; }
let (input, uid_flags) = permutation((
preceded( pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], (usize, (Flag, Vec<String>))> {
alt((tag("UID "), tag(" UID "))), let (input, _) = tag("* ")(input)?;
map_res(digit1, |s| usize::from_str(to_str!(s))), let (input, _msn) = take_while(is_digit)(input)?;
), let (input, _) = tag(" FETCH (")(input)?;
preceded( let (input, uid_flags) = permutation((
alt((tag("FLAGS "), tag(" FLAGS "))), preceded(
delimited(tag("("), byte_flags, tag(")")), alt((tag("UID "), tag(" UID "))),
), map_res(digit1, |s| usize::from_str(to_str!(s))),
))(input)?; ),
let (input, _) = tag(")\r\n")(input)?; preceded(
Ok((input, (uid_flags.0, uid_flags.1))) alt((tag("FLAGS "), tag(" FLAGS "))),
})(input) 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 { macro_rules! flags_to_imap_list {
@ -871,18 +875,20 @@ pub enum UntaggedResponse {
/// ``` /// ```
Recent(usize), Recent(usize),
Fetch(usize, (Flag, Vec<String>)), Fetch(usize, (Flag, Vec<String>)),
UIDFetch(UID, (Flag, Vec<String>)),
Bye { Bye {
reason: String, reason: String,
}, },
} }
pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> { pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> {
let orig_input = input;
let (input, _) = tag("* ")(input)?; let (input, _) = tag("* ")(input)?;
let (input, num) = map_res(digit1, |s| usize::from_str(to_str!(s)))(input)?; let (input, num) = map_res(digit1, |s| usize::from_str(to_str!(s)))(input)?;
let (input, _) = tag(" ")(input)?; let (input, _) = tag(" ")(input)?;
let (input, _tag) = take_until("\r\n")(input)?; let (input, _tag) = take_until("\r\n")(input)?;
let (input, _) = tag("\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, { Ok((input, {
use UntaggedResponse::*; use UntaggedResponse::*;
match _tag { match _tag {
@ -890,18 +896,23 @@ pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedRespons
b"EXISTS" => Some(Exists(num)), b"EXISTS" => Some(Exists(num)),
b"RECENT" => Some(Recent(num)), b"RECENT" => Some(Recent(num)),
_ if _tag.starts_with(b"FETCH ") => { _ if _tag.starts_with(b"FETCH ") => {
let f = flags(unsafe { if to_str!(_tag).contains("UID") {
std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..]) let (uid, flags) = uid_fetch_flags_response(orig_input)?.1;
}) Some(UIDFetch(uid, flags))
.map(|(_, flags)| Fetch(num, flags)); } else {
if let Err(ref err) = f { let f = flags(unsafe {
debug!( std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..])
"untagged_response malformed fetch: {} {}", })
unsafe { std::str::from_utf8_unchecked(_tag) }, .map(|(_, flags)| Fetch(num, flags));
err 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)); 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) => { UntaggedResponse::Fetch(msg_seq, flags) => {
/* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq /* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq
* and send update * and send update
@ -288,7 +306,7 @@ impl ImapConnection {
mailbox_hash, mailbox_hash,
self.send_command( self.send_command(
&[ &[
b"UID SEARCH ", b"UID SEARCH",
format!("{}", msg_seq).as_bytes(), format!("{}", msg_seq).as_bytes(),
] ]
.join(&b' '), .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))) => { Ok(Some(Fetch(msg_seq, flags))) => {
/* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq /* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq
* and send update * and send update
@ -456,7 +477,7 @@ pub async fn idle(kit: ImapWatchKit) -> Result<()> {
conn.examine_mailbox(mailbox_hash, &mut response, false).await conn.examine_mailbox(mailbox_hash, &mut response, false).await
conn.send_command( conn.send_command(
&[ &[
b"UID SEARCH ", b"UID SEARCH",
format!("{}", msg_seq).as_bytes(), format!("{}", msg_seq).as_bytes(),
] ]
.join(&b' '), .join(&b' '),