From 0f3bf858a3abb14a6300cef965d5d6c2e9c9e35e Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 24 Aug 2020 12:01:28 +0300 Subject: [PATCH] melib/imap: impl UNSELECT via nonexistent mailbox --- melib/src/backends/imap/connection.rs | 72 ++++++++++++++-------- melib/src/backends/imap/protocol_parser.rs | 1 + 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs index 0ba2e2054..7379cea00 100644 --- a/melib/src/backends/imap/connection.rs +++ b/melib/src/backends/imap/connection.rs @@ -589,6 +589,14 @@ impl ImapConnection { ret.push_str(&response); return r.into(); } + ImapResponse::No(ref response_code) + if required_responses.intersects(RequiredResponses::NO_REQUIRED) => + { + debug!( + "Received expected NO response: {:?} {:?}", + response_code, response + ); + } ImapResponse::No(ref response_code) => { //FIXME return error debug!("Received NO response: {:?} {:?}", response_code, response); @@ -731,31 +739,45 @@ impl ImapConnection { pub async fn unselect(&mut self) -> Result<()> { match self.stream.as_mut()?.current_mailbox.take() { - MailboxSelection::Examine(mailbox_hash) | - MailboxSelection::Select(mailbox_hash) =>{ - let mut response = String::with_capacity(8 * 1024); - if self - .uid_store - .capabilities - .lock() - .unwrap() - .iter() - .any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT")) - { - self.send_command(b"UNSELECT").await?; - self.read_response(&mut response, RequiredResponses::empty()) - .await?; - } 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).await?; - self.examine_mailbox(mailbox_hash, &mut response, true).await?; - } - }, - MailboxSelection::None => {}, + MailboxSelection::Examine(_) | + MailboxSelection::Select(_) => { + let mut response = String::with_capacity(8 * 1024); + if self + .uid_store + .capabilities + .lock() + .unwrap() + .iter() + .any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT")) + { + self.send_command(b"UNSELECT").await?; + self.read_response(&mut response, RequiredResponses::empty()) + .await?; + } 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)[..] + */ + let mut nonexistent = "blurdybloop".to_string(); + { + let mailboxes = self.uid_store.mailboxes.lock().await; + while mailboxes.values().any(|m| m.imap_path() == &nonexistent) { + nonexistent.push('p'); + } + } + self.send_command( + format!( + "SELECT \"{}\"", + nonexistent + ) + .as_bytes(), + ) + .await?; + self.read_response(&mut response, RequiredResponses::NO_REQUIRED) + .await?; + } + } + MailboxSelection::None => {}, } Ok(()) } diff --git a/melib/src/backends/imap/protocol_parser.rs b/melib/src/backends/imap/protocol_parser.rs index af7fca19d..444c6df07 100644 --- a/melib/src/backends/imap/protocol_parser.rs +++ b/melib/src/backends/imap/protocol_parser.rs @@ -53,6 +53,7 @@ bitflags! { const EXPUNGE = 0b0001_0000_0000_0000; const SEARCH = 0b0010_0000_0000_0000; const FETCH = 0b0100_0000_0000_0000; + const NO_REQUIRED = 0b1000_0000_0000_0000; const CAPABILITY_REQUIRED = Self::CAPABILITY.bits; const LOGOUT_REQUIRED = Self::BYE.bits; const SELECT_REQUIRED = Self::FLAGS.bits | Self::EXISTS.bits | Self::RECENT.bits | Self::UNSEEN.bits | Self::PERMANENTFLAGS.bits | Self::UIDNEXT.bits | Self::UIDVALIDITY.bits;