imap: recognize EXPUNGE events

async
Manos Pitsidianakis 2020-06-23 17:25:42 +03:00
parent bfbaf3d617
commit d7444a5b19
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 100 additions and 10 deletions

View File

@ -127,6 +127,7 @@ pub struct UIDStore {
uidvalidity: Arc<Mutex<HashMap<MailboxHash, UID>>>,
hash_index: Arc<Mutex<HashMap<EnvelopeHash, (UID, MailboxHash)>>>,
uid_index: Arc<Mutex<HashMap<(MailboxHash, UID), EnvelopeHash>>>,
msn_index: Arc<Mutex<HashMap<MailboxHash, Vec<UID>>>>,
byte_cache: Arc<Mutex<HashMap<UID, EnvelopeCache>>>,
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
@ -145,6 +146,7 @@ impl Default for UIDStore {
uidvalidity: Default::default(),
hash_index: Default::default(),
uid_index: Default::default(),
msn_index: Default::default(),
byte_cache: Default::default(),
mailboxes: Arc::new(RwLock::new(Default::default())),
tag_index: Arc::new(RwLock::new(Default::default())),
@ -414,6 +416,7 @@ impl MailBackend for ImapType {
debug!("responses len is {}", v.len());
for UidFetchResponse {
uid,
message_sequence_number,
flags,
envelope,
..
@ -438,6 +441,13 @@ impl MailBackend for ImapType {
env.labels_mut().push(hash);
}
}
uid_store
.msn_index
.lock()
.unwrap()
.entry(mailbox_hash)
.or_default()
.insert(message_sequence_number - 1, uid);
uid_store
.hash_index
.lock()

View File

@ -595,6 +595,30 @@ impl ImapConnection {
self.uid_store.refresh_events.lock().unwrap().push(ev);
}
}
pub fn create_uid_msn_cache(&mut self, mailbox_hash: MailboxHash, low: usize) -> Result<()> {
debug_assert!(low > 0);
let mut response = String::new();
if self
.current_mailbox
.map(|h| h != mailbox_hash)
.unwrap_or(true)
{
self.examine_mailbox(mailbox_hash, &mut response)?;
}
self.send_command(format!("UID SEARCH {}:*", low).as_bytes())?;
self.read_response(&mut response, RequiredResponses::SEARCH)?;
debug!("uid search response {:?}", &response);
let mut msn_index_lck = self.uid_store.msn_index.lock().unwrap();
let msn_index = msn_index_lck.entry(mailbox_hash).or_default();
let _ = msn_index.drain(low - 1..);
msn_index.extend(
debug!(protocol_parser::search_results(response.as_bytes()))?
.1
.into_iter(),
);
Ok(())
}
}
pub struct ImapBlockingConnection {

View File

@ -421,6 +421,7 @@ pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
#[derive(Debug)]
pub struct UidFetchResponse<'a> {
pub uid: UID,
pub message_sequence_number: usize,
pub flags: Option<(Flag, Vec<String>)>,
pub body: Option<&'a [u8]>,
pub envelope: Option<Envelope>,
@ -471,7 +472,18 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
};
}
while (input.as_bytes()[i] as char).is_numeric() {
let mut ret = UidFetchResponse {
uid: 0,
message_sequence_number: 0,
flags: None,
body: None,
envelope: None,
};
while input.as_bytes()[i].is_ascii_digit() {
let b: u8 = input.as_bytes()[i] - 0x30;
ret.message_sequence_number *= 10;
ret.message_sequence_number += b as usize;
i += 1;
bounds!();
}
@ -479,13 +491,6 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
eat_whitespace!();
should_start_with!(input[i..], "FETCH (");
i += "FETCH (".len();
let mut ret = UidFetchResponse {
uid: 0,
flags: None,
body: None,
envelope: None,
};
let mut has_attachments = false;
while i < input.len() {
eat_whitespace!(break);

View File

@ -74,7 +74,32 @@ impl ImapConnection {
(std::time::Instant::now(), Err(reason.into()));
}
UntaggedResponse::Expunge(n) => {
debug!("expunge {}", n);
let deleted_uid = self
.uid_store
.msn_index
.lock()
.unwrap()
.entry(mailbox_hash)
.or_default()
.remove(n);
debug!("expunge {}, UID = {}", n, deleted_uid);
let deleted_hash: crate::email::EnvelopeHash = self
.uid_store
.uid_index
.lock()
.unwrap()
.remove(&(mailbox_hash, deleted_uid))
.unwrap();
self.uid_store
.hash_index
.lock()
.unwrap()
.remove(&deleted_hash);
self.add_refresh_event(RefreshEvent {
account_hash: self.uid_store.account_hash,
mailbox_hash,
kind: Remove(deleted_hash),
});
}
UntaggedResponse::Exists(n) => {
/* UID FETCH ALL UID, cross-ref, then FETCH difference headers

View File

@ -400,11 +400,37 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
}
}
Ok(Some(Expunge(n))) => {
// The EXPUNGE response reports that the specified message sequence
// number has been permanently removed from the mailbox. The message
// sequence number for each successive message in the mailbox is
// immediately decremented by 1, and this decrement is reflected in
// message sequence numbers in subsequent responses (including other
// untagged EXPUNGE responses).
let mut conn = super::try_lock(&main_conn, Some(std::time::Duration::new(10, 0)))?;
work_context
.set_status
.send((thread_id, format!("got `{} EXPUNGED` notification", n)))
.unwrap();
debug!("expunge {}", n);
let deleted_uid = uid_store
.msn_index
.lock()
.unwrap()
.entry(mailbox_hash)
.or_default()
.remove(n);
debug!("expunge {}, UID = {}", n, deleted_uid);
let deleted_hash: EnvelopeHash = uid_store
.uid_index
.lock()
.unwrap()
.remove(&(mailbox_hash, deleted_uid))
.unwrap();
uid_store.hash_index.lock().unwrap().remove(&deleted_hash);
conn.add_refresh_event(RefreshEvent {
account_hash: uid_store.account_hash,
mailbox_hash,
kind: Remove(deleted_hash),
});
}
Ok(Some(Exists(n))) => {
let mut conn = super::try_lock(&main_conn, Some(std::time::Duration::new(10, 0)))?;