From f8b84a192c73c596d52c7088878aa9edd15032c9 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 6 Jul 2020 11:32:03 +0300 Subject: [PATCH] imap: add current_mailbox enum MailboxSelection Add enum to track the currently selected Mailbox in the IMAP connection --- melib/src/backends/imap.rs | 10 +-- melib/src/backends/imap/connection.rs | 87 +++++++++++++++++---------- melib/src/backends/imap/operations.rs | 8 +-- melib/src/backends/imap/untagged.rs | 10 ++- melib/src/backends/imap/watch.rs | 8 +-- 5 files changed, 74 insertions(+), 49 deletions(-) diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index 126767387..0ffd85903 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -296,7 +296,7 @@ impl MailBackend for ImapType { /* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only * returns READ-ONLY for both cases) */ - conn.select_mailbox(mailbox_hash, &mut response) + conn.select_mailbox(mailbox_hash, &mut response, true) .chain_err_summary(|| { format!("Could not select mailbox {}", mailbox_path) })?; @@ -360,7 +360,7 @@ impl MailBackend for ImapType { return Ok(()); } /* reselecting the same mailbox with EXAMINE prevents expunging it */ - conn.examine_mailbox(mailbox_hash, &mut response)?; + conn.examine_mailbox(mailbox_hash, &mut response, true)?; if examine_response.uidnext == 0 { /* UIDNEXT shouldn't be 0, since exists != 0 at this point */ conn.send_command( @@ -782,7 +782,9 @@ impl MailBackend for ImapType { let mut response = String::with_capacity(8 * 1024); { let mut conn_lck = try_lock(&self.connection, None)?; - if !mailboxes[&mailbox_hash].no_select && conn_lck.current_mailbox == Some(mailbox_hash) + if !mailboxes[&mailbox_hash].no_select + && (conn_lck.current_mailbox == MailboxSelection::Examine(mailbox_hash) + || conn_lck.current_mailbox == MailboxSelection::Select(mailbox_hash)) { /* make sure mailbox is not selected before it gets deleted, otherwise * connection gets dropped by server */ @@ -998,7 +1000,7 @@ impl MailBackend for ImapType { let mut response = String::with_capacity(8 * 1024); let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; - conn.examine_mailbox(mailbox_hash, &mut response)?; + conn.examine_mailbox(mailbox_hash, &mut response, false)?; conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query_str).as_bytes())?; conn.read_response(&mut response, RequiredResponses::SEARCH)?; debug!(&response); diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs index bfcdf09d3..2d0a4039b 100644 --- a/melib/src/backends/imap/connection.rs +++ b/melib/src/backends/imap/connection.rs @@ -49,13 +49,25 @@ pub struct ImapStream { protocol: ImapProtocol, } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum MailboxSelection { + None, + Select(MailboxHash), + Examine(MailboxHash), +} + +impl MailboxSelection { + pub fn take(&mut self) -> Self { + std::mem::replace(self, MailboxSelection::None) + } +} #[derive(Debug)] pub struct ImapConnection { pub stream: Result, pub server_conf: ImapServerConf, pub capabilities: Capabilities, pub uid_store: Arc, - pub current_mailbox: Option, + pub current_mailbox: MailboxSelection, } impl Drop for ImapStream { @@ -395,7 +407,7 @@ impl ImapConnection { server_conf: server_conf.clone(), capabilities: Capabilities::default(), uid_store, - current_mailbox: None, + current_mailbox: MailboxSelection::None, } } @@ -531,7 +543,15 @@ impl ImapConnection { Err(MeliError::new("Connection timed out")) } - pub fn select_mailbox(&mut self, mailbox_hash: MailboxHash, ret: &mut String) -> Result<()> { + pub fn select_mailbox( + &mut self, + mailbox_hash: MailboxHash, + ret: &mut String, + force: bool, + ) -> Result<()> { + if !force && self.current_mailbox == MailboxSelection::Select(mailbox_hash) { + return Ok(()); + } self.send_command( format!( "SELECT \"{}\"", @@ -541,11 +561,19 @@ impl ImapConnection { )?; self.read_response(ret, RequiredResponses::SELECT_REQUIRED)?; debug!("select response {}", ret); - self.current_mailbox = Some(mailbox_hash); + self.current_mailbox = MailboxSelection::Select(mailbox_hash); Ok(()) } - pub fn examine_mailbox(&mut self, mailbox_hash: MailboxHash, ret: &mut String) -> Result<()> { + pub fn examine_mailbox( + &mut self, + mailbox_hash: MailboxHash, + ret: &mut String, + force: bool, + ) -> Result<()> { + if !force && self.current_mailbox == MailboxSelection::Examine(mailbox_hash) { + return Ok(()); + } self.send_command( format!( "EXAMINE \"{}\"", @@ -555,29 +583,32 @@ impl ImapConnection { )?; self.read_response(ret, RequiredResponses::EXAMINE_REQUIRED)?; debug!("examine response {}", ret); - self.current_mailbox = Some(mailbox_hash); + self.current_mailbox = MailboxSelection::Examine(mailbox_hash); Ok(()) } pub fn unselect(&mut self) -> Result<()> { - if let Some(mailbox_hash) = self.current_mailbox.take() { - let mut response = String::with_capacity(8 * 1024); - if self - .capabilities - .iter() - .any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT")) - { - self.send_command(b"UNSELECT")?; - self.read_response(&mut response, RequiredResponses::empty())?; - } else { - /* `RFC3691 - UNSELECT Command` states: "[..] IMAP4 provides this - * functionality (via a SELECT command with a nonexistent mailbox name or - * reselecting the same mailbox with EXAMINE command)[..] - */ - - self.select_mailbox(mailbox_hash, &mut response)?; - self.examine_mailbox(mailbox_hash, &mut response)?; - } + match self.current_mailbox.take() { + MailboxSelection::Examine(mailbox_hash) | MailboxSelection::Select(mailbox_hash) => { + let mut response = String::with_capacity(8 * 1024); + if self + .capabilities + .iter() + .any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT")) + { + self.send_command(b"UNSELECT")?; + self.read_response(&mut response, RequiredResponses::empty())?; + } else { + /* `RFC3691 - UNSELECT Command` states: "[..] IMAP4 provides this + * functionality (via a SELECT command with a nonexistent mailbox name or + * reselecting the same mailbox with EXAMINE command)[..] + */ + + self.select_mailbox(mailbox_hash, &mut response, true)?; + self.examine_mailbox(mailbox_hash, &mut response, true)?; + } + }, + MailboxSelection::None => {}, } Ok(()) } @@ -596,13 +627,7 @@ impl ImapConnection { 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.examine_mailbox(mailbox_hash, &mut response, false)?; self.send_command(format!("UID SEARCH {}:*", low).as_bytes())?; self.read_response(&mut response, RequiredResponses::SEARCH)?; debug!("uid search response {:?}", &response); diff --git a/melib/src/backends/imap/operations.rs b/melib/src/backends/imap/operations.rs index c102f8601..a344dca61 100644 --- a/melib/src/backends/imap/operations.rs +++ b/melib/src/backends/imap/operations.rs @@ -81,7 +81,7 @@ impl BackendOp for ImapOp { { let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; - conn.examine_mailbox(self.mailbox_hash, &mut response)?; + conn.examine_mailbox(self.mailbox_hash, &mut response, false)?; conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?; conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?; } @@ -131,7 +131,7 @@ impl BackendOp for ImapOp { &self.connection, Some(std::time::Duration::new(2, 0)) )); - or_return_default!(conn.examine_mailbox(self.mailbox_hash, &mut response)); + or_return_default!(conn.examine_mailbox(self.mailbox_hash, &mut response, false)); or_return_default!( conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes()) ); @@ -170,7 +170,7 @@ impl BackendOp for ImapOp { let mut response = String::with_capacity(8 * 1024); let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; - conn.select_mailbox(self.mailbox_hash, &mut response)?; + conn.select_mailbox(self.mailbox_hash, &mut response, false)?; debug!(&response); conn.send_command( format!( @@ -205,7 +205,7 @@ impl BackendOp for ImapOp { fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> { let mut response = String::with_capacity(8 * 1024); let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; - conn.select_mailbox(self.mailbox_hash, &mut response)?; + conn.select_mailbox(self.mailbox_hash, &mut response, false)?; conn.send_command( format!( "UID STORE {} {}FLAGS.SILENT ({})", diff --git a/melib/src/backends/imap/untagged.rs b/melib/src/backends/imap/untagged.rs index 9acae88e4..3b251fad0 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; +use super::{ImapConnection, MailboxSelection}; use crate::backends::imap::protocol_parser::{ ImapLineSplit, RequiredResponses, UidFetchResponse, UntaggedResponse, }; @@ -51,11 +51,9 @@ impl ImapConnection { } else { Ok(()) }?;)+ }; } - //FIXME - let mailbox_hash = if let Some(mailbox_hash) = self.current_mailbox { - mailbox_hash - } else { - return Ok(false); + let mailbox_hash = match self.current_mailbox { + MailboxSelection::Select(h) | MailboxSelection::Examine(h) => h, + MailboxSelection::None => return Ok(false), }; let mailbox = std::clone::Clone::clone(&self.uid_store.mailboxes.read().unwrap()[&mailbox_hash]); diff --git a/melib/src/backends/imap/watch.rs b/melib/src/backends/imap/watch.rs index 9452de373..6ee139b14 100644 --- a/melib/src/backends/imap/watch.rs +++ b/melib/src/backends/imap/watch.rs @@ -281,7 +281,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> { mailbox_hash, work_context, thread_id, - conn.examine_mailbox(mailbox_hash, &mut response) + conn.examine_mailbox(mailbox_hash, &mut response, false) conn.send_command(b"UID SEARCH RECENT") conn.read_response(&mut response, RequiredResponses::SEARCH) ); @@ -459,7 +459,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> { mailbox_hash, work_context, thread_id, - conn.examine_mailbox(mailbox_hash, &mut response) + conn.examine_mailbox(mailbox_hash, &mut response, false) conn.send_command( &[ b"FETCH", @@ -567,7 +567,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> { mailbox_hash, work_context, thread_id, - conn.examine_mailbox(mailbox_hash, &mut response) + conn.examine_mailbox(mailbox_hash, &mut response, false) conn.send_command( &[ b"UID SEARCH ", @@ -648,7 +648,7 @@ pub fn examine_updates( mailbox_hash, work_context, thread_id, - conn.examine_mailbox(mailbox_hash, &mut response) + conn.examine_mailbox(mailbox_hash, &mut response, true) ); *uid_store.is_online.lock().unwrap() = (Instant::now(), Ok(())); let uidvalidity;