Преглед изворни кода

imap: recognize EXPUNGE events

Manos Pitsidianakis пре 5 година
родитељ
комит
d7444a5b19

+ 10 - 0
melib/src/backends/imap.rs

@@ -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()

+ 24 - 0
melib/src/backends/imap/connection.rs

@@ -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 {

+ 13 - 8
melib/src/backends/imap/protocol_parser.rs

@@ -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);

+ 26 - 1
melib/src/backends/imap/untagged.rs

@@ -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

+ 27 - 1
melib/src/backends/imap/watch.rs

@@ -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)))?;