imap: recognize EXPUNGE events
This commit is contained in:
parent
bfbaf3d617
commit
d7444a5b19
5 changed files with 100 additions and 10 deletions
|
@ -127,6 +127,7 @@ pub struct UIDStore {
|
||||||
uidvalidity: Arc<Mutex<HashMap<MailboxHash, UID>>>,
|
uidvalidity: Arc<Mutex<HashMap<MailboxHash, UID>>>,
|
||||||
hash_index: Arc<Mutex<HashMap<EnvelopeHash, (UID, MailboxHash)>>>,
|
hash_index: Arc<Mutex<HashMap<EnvelopeHash, (UID, MailboxHash)>>>,
|
||||||
uid_index: Arc<Mutex<HashMap<(MailboxHash, UID), EnvelopeHash>>>,
|
uid_index: Arc<Mutex<HashMap<(MailboxHash, UID), EnvelopeHash>>>,
|
||||||
|
msn_index: Arc<Mutex<HashMap<MailboxHash, Vec<UID>>>>,
|
||||||
|
|
||||||
byte_cache: Arc<Mutex<HashMap<UID, EnvelopeCache>>>,
|
byte_cache: Arc<Mutex<HashMap<UID, EnvelopeCache>>>,
|
||||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
|
@ -145,6 +146,7 @@ impl Default for UIDStore {
|
||||||
uidvalidity: Default::default(),
|
uidvalidity: Default::default(),
|
||||||
hash_index: Default::default(),
|
hash_index: Default::default(),
|
||||||
uid_index: Default::default(),
|
uid_index: Default::default(),
|
||||||
|
msn_index: Default::default(),
|
||||||
byte_cache: Default::default(),
|
byte_cache: Default::default(),
|
||||||
mailboxes: Arc::new(RwLock::new(Default::default())),
|
mailboxes: Arc::new(RwLock::new(Default::default())),
|
||||||
tag_index: 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());
|
debug!("responses len is {}", v.len());
|
||||||
for UidFetchResponse {
|
for UidFetchResponse {
|
||||||
uid,
|
uid,
|
||||||
|
message_sequence_number,
|
||||||
flags,
|
flags,
|
||||||
envelope,
|
envelope,
|
||||||
..
|
..
|
||||||
|
@ -438,6 +441,13 @@ impl MailBackend for ImapType {
|
||||||
env.labels_mut().push(hash);
|
env.labels_mut().push(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uid_store
|
||||||
|
.msn_index
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.entry(mailbox_hash)
|
||||||
|
.or_default()
|
||||||
|
.insert(message_sequence_number - 1, uid);
|
||||||
uid_store
|
uid_store
|
||||||
.hash_index
|
.hash_index
|
||||||
.lock()
|
.lock()
|
||||||
|
|
|
@ -595,6 +595,30 @@ impl ImapConnection {
|
||||||
self.uid_store.refresh_events.lock().unwrap().push(ev);
|
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 {
|
pub struct ImapBlockingConnection {
|
||||||
|
|
|
@ -421,6 +421,7 @@ pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UidFetchResponse<'a> {
|
pub struct UidFetchResponse<'a> {
|
||||||
pub uid: UID,
|
pub uid: UID,
|
||||||
|
pub message_sequence_number: usize,
|
||||||
pub flags: Option<(Flag, Vec<String>)>,
|
pub flags: Option<(Flag, Vec<String>)>,
|
||||||
pub body: Option<&'a [u8]>,
|
pub body: Option<&'a [u8]>,
|
||||||
pub envelope: Option<Envelope>,
|
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;
|
i += 1;
|
||||||
bounds!();
|
bounds!();
|
||||||
}
|
}
|
||||||
|
@ -479,13 +491,6 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
|
||||||
eat_whitespace!();
|
eat_whitespace!();
|
||||||
should_start_with!(input[i..], "FETCH (");
|
should_start_with!(input[i..], "FETCH (");
|
||||||
i += "FETCH (".len();
|
i += "FETCH (".len();
|
||||||
|
|
||||||
let mut ret = UidFetchResponse {
|
|
||||||
uid: 0,
|
|
||||||
flags: None,
|
|
||||||
body: None,
|
|
||||||
envelope: None,
|
|
||||||
};
|
|
||||||
let mut has_attachments = false;
|
let mut has_attachments = false;
|
||||||
while i < input.len() {
|
while i < input.len() {
|
||||||
eat_whitespace!(break);
|
eat_whitespace!(break);
|
||||||
|
|
|
@ -74,7 +74,32 @@ impl ImapConnection {
|
||||||
(std::time::Instant::now(), Err(reason.into()));
|
(std::time::Instant::now(), Err(reason.into()));
|
||||||
}
|
}
|
||||||
UntaggedResponse::Expunge(n) => {
|
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) => {
|
UntaggedResponse::Exists(n) => {
|
||||||
/* UID FETCH ALL UID, cross-ref, then FETCH difference headers
|
/* UID FETCH ALL UID, cross-ref, then FETCH difference headers
|
||||||
|
|
|
@ -400,11 +400,37 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(Expunge(n))) => {
|
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
|
work_context
|
||||||
.set_status
|
.set_status
|
||||||
.send((thread_id, format!("got `{} EXPUNGED` notification", n)))
|
.send((thread_id, format!("got `{} EXPUNGED` notification", n)))
|
||||||
.unwrap();
|
.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))) => {
|
Ok(Some(Exists(n))) => {
|
||||||
let mut conn = super::try_lock(&main_conn, Some(std::time::Duration::new(10, 0)))?;
|
let mut conn = super::try_lock(&main_conn, Some(std::time::Duration::new(10, 0)))?;
|
||||||
|
|
Loading…
Add table
Reference in a new issue