diff --git a/melib/src/backends/imap/cache.rs b/melib/src/backends/imap/cache.rs index 60be5513..b72515a8 100644 --- a/melib/src/backends/imap/cache.rs +++ b/melib/src/backends/imap/cache.rs @@ -50,6 +50,7 @@ impl core::fmt::Display for ModSequence { #[derive(Debug)] pub struct CachedEnvelope { pub inner: Envelope, + pub uid: UID, pub mailbox_hash: MailboxHash, pub modsequence: Option, } @@ -206,6 +207,7 @@ mod sqlite3_m { env.hash(), CachedEnvelope { inner: env, + uid, mailbox_hash, modsequence: modseq, }, @@ -279,6 +281,104 @@ mod sqlite3_m { tx.commit()?; Ok(()) } + + pub fn update( + &mut self, + mailbox_hash: MailboxHash, + refresh_events: &[(UID, RefreshEvent)], + ) -> Result<()> { + debug!( + "update with refresh_events mailbox_hash {} len {}", + mailbox_hash, + refresh_events.len() + ); + if self.mailbox_state(mailbox_hash)?.is_none() { + debug!(self.mailbox_state(mailbox_hash)?.is_none()); + let uidvalidity = self + .uid_store + .uidvalidity + .lock() + .unwrap() + .get(&mailbox_hash) + .cloned(); + let highestmodseq = self + .uid_store + .highestmodseqs + .lock() + .unwrap() + .get(&mailbox_hash) + .cloned(); + debug!(&uidvalidity); + debug!(&highestmodseq); + if let Some(uidvalidity) = uidvalidity { + debug!(self.clear( + mailbox_hash, + uidvalidity, + highestmodseq.and_then(|v| v.ok()), + ))?; + } + } + let Self { + ref mut connection, + ref uid_store, + } = self; + let tx = connection.transaction()?; + let mut hash_index_lck = uid_store.hash_index.lock().unwrap(); + for (uid, event) in refresh_events { + match debug!(&event.kind) { + RefreshEventKind::Remove(env_hash) => { + hash_index_lck.remove(&env_hash); + tx.execute( + "DELETE FROM envelopes WHERE mailbox_hash = ?1 AND uid = ?2;", + sqlite3::params![mailbox_hash as i64, *uid as i64], + ) + .chain_err_summary(|| { + format!( + "Could not remove envelope {} uid {} from mailbox {} account {}", + env_hash, *uid, mailbox_hash, uid_store.account_name + ) + })?; + } + RefreshEventKind::NewFlags(env_hash, (flags, tags)) => { + let mut stmt = tx.prepare( + "SELECT envelope FROM envelopes WHERE mailbox_hash = ?1 AND uid = ?2;", + )?; + + let mut ret: Vec = stmt + .query_map(sqlite3::params![mailbox_hash as i64, *uid as i64], |row| { + Ok(row.get(0)?) + })? + .collect::>()?; + if let Some(mut env) = ret.pop() { + env.set_flags(*flags); + env.labels_mut().clear(); + env.labels_mut().extend(tags.iter().map(|t| tag_hash!(t))); + tx.execute( + "UPDATE envelopes SET envelope = ?1 WHERE mailbox_hash = ?2 AND uid = ?3;", + sqlite3::params![&env, mailbox_hash as i64, *uid as i64], + ) + .chain_err_summary(|| { + format!( + "Could not update envelope {} uid {} from mailbox {} account {}", + env_hash, *uid, mailbox_hash, uid_store.account_name + ) + })?; + uid_store + .envelopes + .lock() + .unwrap() + .entry(*env_hash) + .and_modify(|entry| { + entry.inner = env; + }); + } + } + _ => {} + } + } + tx.commit()?; + Ok(()) + } } } diff --git a/melib/src/backends/imap/cache/sync.rs b/melib/src/backends/imap/cache/sync.rs index 518bbe45..0e218f96 100644 --- a/melib/src/backends/imap/cache/sync.rs +++ b/melib/src/backends/imap/cache/sync.rs @@ -128,7 +128,7 @@ impl ImapConnection { //rfc4549_Synchronization_Operations_for_Disconnected_IMAP4_Clients pub async fn resync_basic( &mut self, - cache_handle: CacheHandle, + mut cache_handle: CacheHandle, mailbox_hash: MailboxHash, ) -> Result>> { let mut payload = vec![]; @@ -319,34 +319,41 @@ impl ImapConnection { .labels_mut() .extend(tags.iter().map(|t| tag_hash!(t))); }); - refresh_events.push(RefreshEvent { - mailbox_hash, - account_hash: self.uid_store.account_hash, - kind: RefreshEventKind::NewFlags(env_hash, (flags, tags)), - }); + refresh_events.push(( + uid, + RefreshEvent { + mailbox_hash, + account_hash: self.uid_store.account_hash, + kind: RefreshEventKind::NewFlags(env_hash, (flags, tags)), + }, + )); } } - for env_hash in valid_envs.difference( - &env_lck - .iter() - .filter_map(|(h, cenv)| { - if cenv.mailbox_hash == mailbox_hash { - Some(*h) - } else { - None - } - }) - .collect(), - ) { + for env_hash in env_lck + .iter() + .filter_map(|(h, cenv)| { + if cenv.mailbox_hash == mailbox_hash { + Some(*h) + } else { + None + } + }) + .collect::>() + .difference(&valid_envs) + { + refresh_events.push(( + env_lck[env_hash].uid, + RefreshEvent { + mailbox_hash, + account_hash: self.uid_store.account_hash, + kind: RefreshEventKind::Remove(*env_hash), + }, + )); env_lck.remove(env_hash); - refresh_events.push(RefreshEvent { - mailbox_hash, - account_hash: self.uid_store.account_hash, - kind: RefreshEventKind::Remove(*env_hash), - }); } drop(env_lck); - for ev in refresh_events { + cache_handle.update(mailbox_hash, &refresh_events)?; + for (_uid, ev) in refresh_events { self.add_refresh_event(ev); } Ok(Some(payload.into_iter().map(|(_, env)| env).collect())) @@ -356,7 +363,7 @@ impl ImapConnection { //Section 6.1 pub async fn resync_condstore( &mut self, - cache_handle: CacheHandle, + mut cache_handle: CacheHandle, mailbox_hash: MailboxHash, ) -> Result>> { let mut payload = vec![]; @@ -598,11 +605,14 @@ impl ImapConnection { .labels_mut() .extend(tags.iter().map(|t| tag_hash!(t))); }); - refresh_events.push(RefreshEvent { - mailbox_hash, - account_hash: self.uid_store.account_hash, - kind: RefreshEventKind::NewFlags(env_hash, (flags, tags)), - }); + refresh_events.push(( + uid, + RefreshEvent { + mailbox_hash, + account_hash: self.uid_store.account_hash, + kind: RefreshEventKind::NewFlags(env_hash, (flags, tags)), + }, + )); } } self.uid_store @@ -622,9 +632,9 @@ impl ImapConnection { for uid in v { valid_envs.insert(generate_envelope_hash(&mailbox_path, &uid)); } - let mut env_lck = self.uid_store.envelopes.lock().unwrap(); - for env_hash in valid_envs.difference( - &env_lck + { + let mut env_lck = self.uid_store.envelopes.lock().unwrap(); + for env_hash in env_lck .iter() .filter_map(|(h, cenv)| { if cenv.mailbox_hash == mailbox_hash { @@ -633,17 +643,23 @@ impl ImapConnection { None } }) - .collect(), - ) { - env_lck.remove(env_hash); - refresh_events.push(RefreshEvent { - mailbox_hash, - account_hash: self.uid_store.account_hash, - kind: RefreshEventKind::Remove(*env_hash), - }); + .collect::>() + .difference(&valid_envs) + { + refresh_events.push(( + env_lck[env_hash].uid, + RefreshEvent { + mailbox_hash, + account_hash: self.uid_store.account_hash, + kind: RefreshEventKind::Remove(*env_hash), + }, + )); + env_lck.remove(env_hash); + } + drop(env_lck); } - drop(env_lck); - for ev in refresh_events { + cache_handle.update(mailbox_hash, &refresh_events)?; + for (_uid, ev) in refresh_events { self.add_refresh_event(ev); } Ok(Some(payload.into_iter().map(|(_, env)| env).collect())) diff --git a/melib/src/backends/imap/untagged.rs b/melib/src/backends/imap/untagged.rs index 3de81b20..a4b99838 100644 --- a/melib/src/backends/imap/untagged.rs +++ b/melib/src/backends/imap/untagged.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -use super::{ImapConnection, MailboxSelection}; +use super::{ImapConnection, MailboxSelection, UID}; use crate::backends::imap::protocol_parser::{ generate_envelope_hash, FetchResponse, ImapLineSplit, RequiredResponses, UntaggedResponse, }; @@ -58,6 +58,7 @@ impl ImapConnection { let mailbox = std::clone::Clone::clone(&self.uid_store.mailboxes.lock().await[&mailbox_hash]); + let mut cache_handle = super::cache::CacheHandle::get(self.uid_store.clone())?; let mut response = String::with_capacity(8 * 1024); let untagged_response = match super::protocol_parser::untagged_responses(line).map(|(_, v, _)| v) { @@ -81,23 +82,38 @@ impl ImapConnection { .or_default() .remove(n); debug!("expunge {}, UID = {}", n, deleted_uid); - let deleted_hash: crate::email::EnvelopeHash = self + let deleted_hash: crate::email::EnvelopeHash = match self .uid_store .uid_index .lock() .unwrap() .remove(&(mailbox_hash, deleted_uid)) - .unwrap(); + { + Some(v) => v, + None => return Ok(true), + }; 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), - }); + let mut event: [(UID, RefreshEvent); 1] = [( + deleted_uid, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Remove(deleted_hash), + }, + )]; + cache_handle.update(mailbox_hash, &event)?; + self.add_refresh_event(std::mem::replace( + &mut event[0].1, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Rescan, + }, + )); } UntaggedResponse::Exists(n) => { /* UID FETCH ALL UID, cross-ref, then FETCH difference headers @@ -166,11 +182,23 @@ impl ImapConnection { mailbox.unseen.lock().unwrap().insert_new(env.hash()); } mailbox.exists.lock().unwrap().insert_new(env.hash()); - self.add_refresh_event(RefreshEvent { - account_hash: self.uid_store.account_hash, - mailbox_hash, - kind: Create(Box::new(env)), - }); + let mut event: [(UID, RefreshEvent); 1] = [( + uid, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Create(Box::new(env)), + }, + )]; + cache_handle.update(mailbox_hash, &event)?; + self.add_refresh_event(std::mem::replace( + &mut event[0].1, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Rescan, + }, + )); } } } @@ -256,11 +284,23 @@ impl ImapConnection { } mailbox.exists.lock().unwrap().insert_new(env.hash()); - self.add_refresh_event(RefreshEvent { - account_hash: self.uid_store.account_hash, - mailbox_hash, - kind: Create(Box::new(env)), - }); + let mut event: [(UID, RefreshEvent); 1] = [( + uid, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Create(Box::new(env)), + }, + )]; + cache_handle.update(mailbox_hash, &event)?; + self.add_refresh_event(std::mem::replace( + &mut event[0].1, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Rescan, + }, + )); } } } @@ -334,9 +374,13 @@ impl ImapConnection { } }; 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); + let env_hash = self + .uid_store + .uid_index + .lock() + .unwrap() + .get(&(mailbox_hash, uid)) + .copied(); if let Some(env_hash) = env_hash { if !flags.0.intersects(crate::email::Flag::SEEN) { mailbox.unseen.lock().unwrap().insert_new(env_hash); @@ -357,12 +401,23 @@ impl ImapConnection { .unwrap() .insert(env_hash, modseq); } - - self.add_refresh_event(RefreshEvent { - account_hash: self.uid_store.account_hash, - mailbox_hash, - kind: NewFlags(env_hash, flags), - }); + let mut event: [(UID, RefreshEvent); 1] = [( + uid, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: NewFlags(env_hash, flags), + }, + )]; + cache_handle.update(mailbox_hash, &event)?; + self.add_refresh_event(std::mem::replace( + &mut event[0].1, + RefreshEvent { + account_hash: self.uid_store.account_hash, + mailbox_hash, + kind: Rescan, + }, + )); }; } }