diff --git a/melib/src/backends/imap/operations.rs b/melib/src/backends/imap/operations.rs index 9157049b..3ed4685b 100644 --- a/melib/src/backends/imap/operations.rs +++ b/melib/src/backends/imap/operations.rs @@ -129,7 +129,7 @@ impl BackendOp for ImapOp { response.len(), response.lines().collect::>().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 { diff --git a/melib/src/backends/imap/protocol_parser.rs b/melib/src/backends/imap/protocol_parser.rs index e3d904bf..b0f969a6 100644 --- a/melib/src/backends/imap/protocol_parser.rs +++ b/melib/src/backends/imap/protocol_parser.rs @@ -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))>> { - many0(|input| -> IResult<&[u8], (usize, (Flag, Vec))> { - 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))>> { + many0(uid_fetch_flags_response)(input) +} + +pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], (usize, (Flag, Vec))> { + 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)), + UIDFetch(UID, (Flag, Vec)), Bye { reason: String, }, } pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option> { + 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 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)); diff --git a/melib/src/backends/imap/untagged.rs b/melib/src/backends/imap/untagged.rs index 3063e1e1..6887701a 100644 --- a/melib/src/backends/imap/untagged.rs +++ b/melib/src/backends/imap/untagged.rs @@ -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' '), diff --git a/melib/src/backends/imap/watch.rs b/melib/src/backends/imap/watch.rs index 266ff0d8..d452f24f 100644 --- a/melib/src/backends/imap/watch.rs +++ b/melib/src/backends/imap/watch.rs @@ -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' '),