mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-10 19:10:57 +00:00
imap: Add support for untagged FETCH (FLAG.. messages
IDLE connection can get untagged "* FETCH (FLAGS ({flag_list))" messages if any client has changed flags. Support this refresh event.
This commit is contained in:
parent
c1a64d6c33
commit
ca51077f53
@ -210,6 +210,7 @@ pub enum RefreshEventKind {
|
||||
Rename(EnvelopeHash, EnvelopeHash),
|
||||
Create(Box<Envelope>),
|
||||
Remove(EnvelopeHash),
|
||||
NewFlags(EnvelopeHash, (Flag, Vec<String>)),
|
||||
Rescan,
|
||||
Failure(MeliError),
|
||||
}
|
||||
|
@ -648,6 +648,7 @@ pub enum UntaggedResponse {
|
||||
/// messages).
|
||||
/// ```
|
||||
Recent(usize),
|
||||
Fetch(usize, (Flag, Vec<String>)),
|
||||
}
|
||||
|
||||
named!(
|
||||
@ -664,6 +665,8 @@ named!(
|
||||
b"EXPUNGE" => Some(Expunge(num)),
|
||||
b"EXISTS" => Some(Exists(num)),
|
||||
b"RECENT" => Some(Recent(num)),
|
||||
_ if tag.starts_with(b"FETCH ") => flags(
|
||||
unsafe { std::str::from_utf8_unchecked(&tag[b"FETCH (FLAGS (".len()..]) }).map(|flags| Fetch(num, flags)).to_full_result().map_err(|err| debug!("untagged_response malformed fetch: {}", unsafe { std::str::from_utf8_unchecked(tag) })).ok(),
|
||||
_ => {
|
||||
debug!("unknown untagged_response: {}", unsafe { std::str::from_utf8_unchecked(tag) });
|
||||
None
|
||||
@ -675,8 +678,8 @@ named!(
|
||||
|
||||
named!(
|
||||
pub search_results<Vec<usize>>,
|
||||
alt_complete!(do_parse!( tag!("* SEARCH")
|
||||
>> list: separated_nonempty_list_complete!(tag!(" "), map_res!(is_not!("\r\n"), |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }))
|
||||
alt_complete!(do_parse!( tag!("* SEARCH ")
|
||||
>> list: separated_nonempty_list_complete!(tag!(b" "), map_res!(is_not!(" \r\n"), |s: &[u8]| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }))
|
||||
>> tag!("\r\n")
|
||||
>> ({ list })) |
|
||||
do_parse!(tag!("* SEARCH\r\n") >> ({ Vec::new() })))
|
||||
@ -691,6 +694,23 @@ named!(
|
||||
do_parse!(tag!("* SEARCH\r\n") >> ({ &b""[0..] })))
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_imap_search() {
|
||||
assert_eq!(search_results(b"* SEARCH\r\n").to_full_result(), Ok(vec![]));
|
||||
assert_eq!(
|
||||
search_results(b"* SEARCH 1\r\n").to_full_result(),
|
||||
Ok(vec![1])
|
||||
);
|
||||
assert_eq!(
|
||||
search_results(b"* SEARCH 1 2 3 4\r\n").to_full_result(),
|
||||
Ok(vec![1, 2, 3, 4])
|
||||
);
|
||||
assert_eq!(
|
||||
search_results_raw(b"* SEARCH 1 2 3 4\r\n").to_full_result(),
|
||||
Ok(&b"1 2 3 4"[..])
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct SelectResponse {
|
||||
pub exists: usize,
|
||||
|
@ -492,6 +492,47 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||
*prev_exists = n;
|
||||
}
|
||||
}
|
||||
Ok(Some(Fetch(msg_seq, flags))) => {
|
||||
/* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq
|
||||
* and send update
|
||||
*/
|
||||
let mut conn = main_conn.lock().unwrap();
|
||||
debug!("fetch {} {:?}", msg_seq, flags);
|
||||
exit_on_error!(
|
||||
sender,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(b"EXAMINE INBOX")
|
||||
conn.read_response(&mut response)
|
||||
conn.send_command(
|
||||
&[
|
||||
b"UID SEARCH ",
|
||||
format!("{}", msg_seq).as_bytes(),
|
||||
]
|
||||
.join(&b' '),
|
||||
)
|
||||
conn.read_response(&mut response)
|
||||
);
|
||||
match search_results(response.split_rn().next().unwrap_or("").as_bytes())
|
||||
.to_full_result()
|
||||
{
|
||||
Ok(mut v) => {
|
||||
if let Some(uid) = v.pop() {
|
||||
if let Some(env_hash) = uid_store.uid_index.lock().unwrap().get(&uid) {
|
||||
sender.send(RefreshEvent {
|
||||
hash: mailbox_hash,
|
||||
kind: NewFlags(*env_hash, flags),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(&response);
|
||||
debug!(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None) | Err(_) => {}
|
||||
}
|
||||
work_context
|
||||
|
@ -453,6 +453,34 @@ impl Account {
|
||||
self.collection.update(old_hash, *envelope, mailbox_hash);
|
||||
return Some(EnvelopeUpdate(old_hash));
|
||||
}
|
||||
RefreshEventKind::NewFlags(env_hash, (flags, tags)) => {
|
||||
let mut envelopes = self.collection.envelopes.write().unwrap();
|
||||
envelopes.entry(env_hash).and_modify(|entry| {
|
||||
for f in tags {
|
||||
let hash = tag_hash!(f);
|
||||
if !entry.labels().contains(&hash) {
|
||||
entry.labels_mut().push(hash);
|
||||
}
|
||||
}
|
||||
entry.set_flags(flags);
|
||||
});
|
||||
#[cfg(feature = "sqlite3")]
|
||||
{
|
||||
if let Err(err) = crate::sqlite3::remove(env_hash).and_then(|_| {
|
||||
crate::sqlite3::insert(&envelopes[&env_hash], &self.backend, &self.name)
|
||||
}) {
|
||||
melib::log(
|
||||
format!(
|
||||
"Failed to update envelope {} in cache: {}",
|
||||
envelopes[&env_hash].message_id_display(),
|
||||
err.to_string()
|
||||
),
|
||||
melib::ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
return Some(EnvelopeUpdate(env_hash));
|
||||
}
|
||||
RefreshEventKind::Rename(old_hash, new_hash) => {
|
||||
debug!("rename {} to {}", old_hash, new_hash);
|
||||
self.collection.rename(old_hash, new_hash, mailbox_hash);
|
||||
|
Loading…
Reference in New Issue
Block a user