imap: add current_mailbox enum MailboxSelection

Add enum to track the currently selected Mailbox in the IMAP connection
async
Manos Pitsidianakis 2020-07-06 11:32:03 +03:00
parent ca7bbd9de4
commit f8b84a192c
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 74 additions and 49 deletions

View File

@ -296,7 +296,7 @@ impl MailBackend for ImapType {
/* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only /* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only
* returns READ-ONLY for both cases) */ * returns READ-ONLY for both cases) */
conn.select_mailbox(mailbox_hash, &mut response) conn.select_mailbox(mailbox_hash, &mut response, true)
.chain_err_summary(|| { .chain_err_summary(|| {
format!("Could not select mailbox {}", mailbox_path) format!("Could not select mailbox {}", mailbox_path)
})?; })?;
@ -360,7 +360,7 @@ impl MailBackend for ImapType {
return Ok(()); return Ok(());
} }
/* reselecting the same mailbox with EXAMINE prevents expunging it */ /* 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 { if examine_response.uidnext == 0 {
/* UIDNEXT shouldn't be 0, since exists != 0 at this point */ /* UIDNEXT shouldn't be 0, since exists != 0 at this point */
conn.send_command( conn.send_command(
@ -782,7 +782,9 @@ impl MailBackend for ImapType {
let mut response = String::with_capacity(8 * 1024); let mut response = String::with_capacity(8 * 1024);
{ {
let mut conn_lck = try_lock(&self.connection, None)?; 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 /* make sure mailbox is not selected before it gets deleted, otherwise
* connection gets dropped by server */ * connection gets dropped by server */
@ -998,7 +1000,7 @@ impl MailBackend for ImapType {
let mut response = String::with_capacity(8 * 1024); let mut response = String::with_capacity(8 * 1024);
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; 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.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query_str).as_bytes())?;
conn.read_response(&mut response, RequiredResponses::SEARCH)?; conn.read_response(&mut response, RequiredResponses::SEARCH)?;
debug!(&response); debug!(&response);

View File

@ -49,13 +49,25 @@ pub struct ImapStream {
protocol: ImapProtocol, 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)] #[derive(Debug)]
pub struct ImapConnection { pub struct ImapConnection {
pub stream: Result<ImapStream>, pub stream: Result<ImapStream>,
pub server_conf: ImapServerConf, pub server_conf: ImapServerConf,
pub capabilities: Capabilities, pub capabilities: Capabilities,
pub uid_store: Arc<UIDStore>, pub uid_store: Arc<UIDStore>,
pub current_mailbox: Option<MailboxHash>, pub current_mailbox: MailboxSelection,
} }
impl Drop for ImapStream { impl Drop for ImapStream {
@ -395,7 +407,7 @@ impl ImapConnection {
server_conf: server_conf.clone(), server_conf: server_conf.clone(),
capabilities: Capabilities::default(), capabilities: Capabilities::default(),
uid_store, uid_store,
current_mailbox: None, current_mailbox: MailboxSelection::None,
} }
} }
@ -531,7 +543,15 @@ impl ImapConnection {
Err(MeliError::new("Connection timed out")) 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( self.send_command(
format!( format!(
"SELECT \"{}\"", "SELECT \"{}\"",
@ -541,11 +561,19 @@ impl ImapConnection {
)?; )?;
self.read_response(ret, RequiredResponses::SELECT_REQUIRED)?; self.read_response(ret, RequiredResponses::SELECT_REQUIRED)?;
debug!("select response {}", ret); debug!("select response {}", ret);
self.current_mailbox = Some(mailbox_hash); self.current_mailbox = MailboxSelection::Select(mailbox_hash);
Ok(()) 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( self.send_command(
format!( format!(
"EXAMINE \"{}\"", "EXAMINE \"{}\"",
@ -555,29 +583,32 @@ impl ImapConnection {
)?; )?;
self.read_response(ret, RequiredResponses::EXAMINE_REQUIRED)?; self.read_response(ret, RequiredResponses::EXAMINE_REQUIRED)?;
debug!("examine response {}", ret); debug!("examine response {}", ret);
self.current_mailbox = Some(mailbox_hash); self.current_mailbox = MailboxSelection::Examine(mailbox_hash);
Ok(()) Ok(())
} }
pub fn unselect(&mut self) -> Result<()> { pub fn unselect(&mut self) -> Result<()> {
if let Some(mailbox_hash) = self.current_mailbox.take() { match self.current_mailbox.take() {
let mut response = String::with_capacity(8 * 1024); MailboxSelection::Examine(mailbox_hash) | MailboxSelection::Select(mailbox_hash) => {
if self let mut response = String::with_capacity(8 * 1024);
.capabilities if self
.iter() .capabilities
.any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT")) .iter()
{ .any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT"))
self.send_command(b"UNSELECT")?; {
self.read_response(&mut response, RequiredResponses::empty())?; self.send_command(b"UNSELECT")?;
} else { self.read_response(&mut response, RequiredResponses::empty())?;
/* `RFC3691 - UNSELECT Command` states: "[..] IMAP4 provides this } else {
* functionality (via a SELECT command with a nonexistent mailbox name or /* `RFC3691 - UNSELECT Command` states: "[..] IMAP4 provides this
* reselecting the same mailbox with EXAMINE command)[..] * 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)?; self.select_mailbox(mailbox_hash, &mut response, true)?;
} self.examine_mailbox(mailbox_hash, &mut response, true)?;
}
},
MailboxSelection::None => {},
} }
Ok(()) Ok(())
} }
@ -596,13 +627,7 @@ impl ImapConnection {
pub fn create_uid_msn_cache(&mut self, mailbox_hash: MailboxHash, low: usize) -> Result<()> { pub fn create_uid_msn_cache(&mut self, mailbox_hash: MailboxHash, low: usize) -> Result<()> {
debug_assert!(low > 0); debug_assert!(low > 0);
let mut response = String::new(); let mut response = String::new();
if self self.examine_mailbox(mailbox_hash, &mut response, false)?;
.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.send_command(format!("UID SEARCH {}:*", low).as_bytes())?;
self.read_response(&mut response, RequiredResponses::SEARCH)?; self.read_response(&mut response, RequiredResponses::SEARCH)?;
debug!("uid search response {:?}", &response); debug!("uid search response {:?}", &response);

View File

@ -81,7 +81,7 @@ impl BackendOp for ImapOp {
{ {
let mut conn = let mut conn =
try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; 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.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?; conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?;
} }
@ -131,7 +131,7 @@ impl BackendOp for ImapOp {
&self.connection, &self.connection,
Some(std::time::Duration::new(2, 0)) 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!( or_return_default!(
conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes()) 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 response = String::with_capacity(8 * 1024);
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; 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); debug!(&response);
conn.send_command( conn.send_command(
format!( format!(
@ -205,7 +205,7 @@ impl BackendOp for ImapOp {
fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> { fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> {
let mut response = String::with_capacity(8 * 1024); let mut response = String::with_capacity(8 * 1024);
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; 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( conn.send_command(
format!( format!(
"UID STORE {} {}FLAGS.SILENT ({})", "UID STORE {} {}FLAGS.SILENT ({})",

View File

@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
use super::ImapConnection; use super::{ImapConnection, MailboxSelection};
use crate::backends::imap::protocol_parser::{ use crate::backends::imap::protocol_parser::{
ImapLineSplit, RequiredResponses, UidFetchResponse, UntaggedResponse, ImapLineSplit, RequiredResponses, UidFetchResponse, UntaggedResponse,
}; };
@ -51,11 +51,9 @@ impl ImapConnection {
} else { Ok(()) }?;)+ } else { Ok(()) }?;)+
}; };
} }
//FIXME let mailbox_hash = match self.current_mailbox {
let mailbox_hash = if let Some(mailbox_hash) = self.current_mailbox { MailboxSelection::Select(h) | MailboxSelection::Examine(h) => h,
mailbox_hash MailboxSelection::None => return Ok(false),
} else {
return Ok(false);
}; };
let mailbox = let mailbox =
std::clone::Clone::clone(&self.uid_store.mailboxes.read().unwrap()[&mailbox_hash]); std::clone::Clone::clone(&self.uid_store.mailboxes.read().unwrap()[&mailbox_hash]);

View File

@ -281,7 +281,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
mailbox_hash, mailbox_hash,
work_context, work_context,
thread_id, 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.send_command(b"UID SEARCH RECENT")
conn.read_response(&mut response, RequiredResponses::SEARCH) conn.read_response(&mut response, RequiredResponses::SEARCH)
); );
@ -459,7 +459,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
mailbox_hash, mailbox_hash,
work_context, work_context,
thread_id, thread_id,
conn.examine_mailbox(mailbox_hash, &mut response) conn.examine_mailbox(mailbox_hash, &mut response, false)
conn.send_command( conn.send_command(
&[ &[
b"FETCH", b"FETCH",
@ -567,7 +567,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
mailbox_hash, mailbox_hash,
work_context, work_context,
thread_id, thread_id,
conn.examine_mailbox(mailbox_hash, &mut response) conn.examine_mailbox(mailbox_hash, &mut response, false)
conn.send_command( conn.send_command(
&[ &[
b"UID SEARCH ", b"UID SEARCH ",
@ -648,7 +648,7 @@ pub fn examine_updates(
mailbox_hash, mailbox_hash,
work_context, work_context,
thread_id, 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(())); *uid_store.is_online.lock().unwrap() = (Instant::now(), Ok(()));
let uidvalidity; let uidvalidity;