diff --git a/melib/src/backends.rs b/melib/src/backends.rs index d416c92a..90ec5d43 100644 --- a/melib/src/backends.rs +++ b/melib/src/backends.rs @@ -293,14 +293,22 @@ impl NotifyFn { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct MailBackendCapabilities { pub is_async: bool, pub is_remote: bool, + pub extensions: Option>, pub supports_search: bool, pub supports_tags: bool, } +#[derive(Debug, Copy, Clone)] +pub enum MailBackendExtensionStatus { + Unsupported { comment: Option<&'static str> }, + Supported { comment: Option<&'static str> }, + Enabled { comment: Option<&'static str> }, +} + pub type ResultFuture = Result> + Send + 'static>>>; pub trait MailBackend: ::std::fmt::Debug + Send + Sync { diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index 85c948de..846939ea 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -191,13 +191,70 @@ pub struct ImapType { impl MailBackend for ImapType { fn capabilities(&self) -> MailBackendCapabilities { - const CAPABILITIES: MailBackendCapabilities = MailBackendCapabilities { + let mut extensions = self + .uid_store + .capabilities + .lock() + .unwrap() + .iter() + .map(|c| { + ( + String::from_utf8_lossy(c).into(), + MailBackendExtensionStatus::Unsupported { comment: None }, + ) + }) + .collect::>(); + if let ImapProtocol::IMAP { + extension_use: + ImapExtensionUse { + idle, + #[cfg(feature = "deflate_compression")] + deflate, + }, + } = self.server_conf.protocol + { + for (name, status) in extensions.iter_mut() { + match name.as_str() { + "IDLE" => { + if idle { + *status = MailBackendExtensionStatus::Enabled { comment: None }; + } else { + *status = MailBackendExtensionStatus::Supported { + comment: Some("Disabled by user configuration"), + }; + } + } + "COMPRESS=DEFLATE" => { + if cfg!(feature = "deflate_compression") { + if deflate { + *status = MailBackendExtensionStatus::Enabled { comment: None }; + } else { + *status = MailBackendExtensionStatus::Supported { + comment: Some("Disabled by user configuration"), + }; + } + } else { + *status = MailBackendExtensionStatus::Unsupported { + comment: Some("melib not compiled with DEFLATE."), + }; + } + } + _ => { + if SUPPORTED_CAPABILITIES.contains(&name.as_str()) { + *status = MailBackendExtensionStatus::Enabled { comment: None }; + } + } + } + } + } + extensions.sort_by(|a, b| a.0.cmp(&b.0)); + MailBackendCapabilities { is_async: true, is_remote: true, supports_search: true, + extensions: Some(extensions), supports_tags: true, - }; - CAPABILITIES + } } fn fetch_async( diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs index 5e4f906b..72d5f551 100644 --- a/melib/src/backends/imap/connection.rs +++ b/melib/src/backends/imap/connection.rs @@ -56,7 +56,7 @@ impl Default for ImapExtensionUse { Self { idle: true, #[cfg(feature = "deflate_compression")] - deflate: false, + deflate: true, } } } diff --git a/melib/src/backends/jmap.rs b/melib/src/backends/jmap.rs index b3d3ce7c..a35e8597 100644 --- a/melib/src/backends/jmap.rs +++ b/melib/src/backends/jmap.rs @@ -195,6 +195,7 @@ impl MailBackend for JmapType { is_async: false, is_remote: true, supports_search: true, + extensions: None, supports_tags: true, }; CAPABILITIES diff --git a/melib/src/backends/maildir/backend.rs b/melib/src/backends/maildir/backend.rs index 8bcd161c..5d333049 100644 --- a/melib/src/backends/maildir/backend.rs +++ b/melib/src/backends/maildir/backend.rs @@ -180,6 +180,7 @@ impl MailBackend for MaildirType { is_async: false, is_remote: false, supports_search: false, + extensions: None, supports_tags: false, }; CAPABILITIES diff --git a/melib/src/backends/mbox.rs b/melib/src/backends/mbox.rs index 3b282c0a..016f6a46 100644 --- a/melib/src/backends/mbox.rs +++ b/melib/src/backends/mbox.rs @@ -699,6 +699,7 @@ impl MailBackend for MboxType { is_async: false, is_remote: false, supports_search: false, + extensions: None, supports_tags: false, }; CAPABILITIES diff --git a/melib/src/backends/nntp.rs b/melib/src/backends/nntp.rs index c4745932..a0a8a960 100644 --- a/melib/src/backends/nntp.rs +++ b/melib/src/backends/nntp.rs @@ -81,7 +81,7 @@ impl std::ops::Deref for IsSubscribedFn { &self.0 } } -type Capabilities = HashSet>; +type Capabilities = HashSet; #[derive(Debug)] pub struct UIDStore { @@ -129,13 +129,57 @@ pub struct NntpType { impl MailBackend for NntpType { fn capabilities(&self) -> MailBackendCapabilities { - const CAPABILITIES: MailBackendCapabilities = MailBackendCapabilities { + let mut extensions = self + .uid_store + .capabilities + .lock() + .unwrap() + .iter() + .map(|c| { + ( + c.to_string(), + MailBackendExtensionStatus::Unsupported { comment: None }, + ) + }) + .collect::>(); + let NntpExtensionUse { + #[cfg(feature = "deflate_compression")] + deflate, + } = self.server_conf.extension_use; + { + for (name, status) in extensions.iter_mut() { + match name.as_str() { + "COMPRESS DEFLATE" => { + if cfg!(feature = "deflate_compression") { + if deflate { + *status = MailBackendExtensionStatus::Enabled { comment: None }; + } else { + *status = MailBackendExtensionStatus::Supported { + comment: Some("Disabled by user configuration"), + }; + } + } else { + *status = MailBackendExtensionStatus::Unsupported { + comment: Some("melib not compiled with DEFLATE."), + }; + } + } + _ => { + if SUPPORTED_CAPABILITIES.contains(&name.as_str()) { + *status = MailBackendExtensionStatus::Enabled { comment: None }; + } + } + } + } + } + extensions.sort_by(|a, b| a.0.cmp(&b.0)); + MailBackendCapabilities { is_async: true, is_remote: true, supports_search: false, + extensions: Some(extensions), supports_tags: false, - }; - CAPABILITIES + } } fn fetch_async( @@ -372,7 +416,10 @@ impl NntpType { use_tls, use_starttls, danger_accept_invalid_certs, - extension_use: NntpExtensionUse::default(), + extension_use: NntpExtensionUse { + #[cfg(feature = "deflate_compression")] + deflate: get_conf_val!(s["use_deflate"], true)?, + }, }; let account_hash = { let mut hasher = DefaultHasher::new(); @@ -488,7 +535,7 @@ impl NntpType { .lock() .unwrap() .iter() - .map(|c| String::from_utf8_lossy(c).into()) + .map(|c| c.clone()) .collect::>() } } diff --git a/melib/src/backends/nntp/connection.rs b/melib/src/backends/nntp/connection.rs index 26cfc3ff..2c62b281 100644 --- a/melib/src/backends/nntp/connection.rs +++ b/melib/src/backends/nntp/connection.rs @@ -99,7 +99,7 @@ impl NntpStream { let mut res = String::with_capacity(8 * 1024); let mut ret = NntpStream { stream, - extension_use: NntpExtensionUse::default(), + extension_use: server_conf.extension_use, current_mailbox: MailboxSelection::None, }; @@ -196,11 +196,10 @@ impl NntpStream { &server_conf.server_hostname, res ))); } - let capabilities: HashSet> = - res.lines().skip(1).map(|l| l.as_bytes().to_vec()).collect(); + let capabilities: HashSet = res.lines().skip(1).map(|l| l.to_string()).collect(); #[cfg(feature = "deflate_compression")] #[cfg(feature = "deflate_compression")] - if capabilities.contains(&b"COMPRESS DEFLATE"[..]) && ret.extension_use.deflate { + if capabilities.contains("COMPRESS DEFLATE") && ret.extension_use.deflate { ret.send_command(b"COMPRESS DEFLATE").await?; ret.read_response(&mut res, false).await?; if !res.starts_with("206 ") { diff --git a/melib/src/backends/notmuch.rs b/melib/src/backends/notmuch.rs index f4344234..03bea757 100644 --- a/melib/src/backends/notmuch.rs +++ b/melib/src/backends/notmuch.rs @@ -323,6 +323,7 @@ impl MailBackend for NotmuchDb { is_async: false, is_remote: false, supports_search: true, + extensions: None, supports_tags: true, }; CAPABILITIES diff --git a/src/components/mail/status.rs b/src/components/mail/status.rs index 439857cb..dfe3840d 100644 --- a/src/components/mail/status.rs +++ b/src/components/mail/status.rs @@ -414,7 +414,6 @@ impl Component for AccountStatus { self.dirty = false; let (width, height) = self.content.size(); let a = &context.accounts[self.account_pos]; - let backend_lck = a.backend.read().unwrap(); let (_x, _y) = write_string_to_grid( "(Press Esc to return)", &mut self.content, @@ -450,7 +449,7 @@ impl Component for AccountStatus { ); line += 1; let (_x, _y) = write_string_to_grid( - "Cache backend: ", + "Search backend: ", &mut self.content, self.theme_default.fg, self.theme_default.bg, @@ -537,72 +536,108 @@ impl Component for AccountStatus { } line += 1; - if a.settings.account().format() == "imap" { - let b = (*backend_lck).as_any(); - if let Some(imap_backend) = b.downcast_ref::() { + if let Some(ref extensions) = a.backend_capabilities.extensions { + write_string_to_grid( + "Server Extensions:", + &mut self.content, + self.theme_default.fg, + self.theme_default.bg, + Attr::BOLD, + ((1, line), (width - 1, height - 1)), + None, + ); + let max_name_width = std::cmp::max( + "Server Extensions:".len(), + extensions.iter().map(|(n, _)| n.len()).max().unwrap_or(0), + ); + write_string_to_grid( + "meli support:", + &mut self.content, + self.theme_default.fg, + self.theme_default.bg, + self.theme_default.attrs, + ((max_name_width + 6, line), (width - 1, height - 1)), + None, + ); + line += 1; + for (i, (name, status)) in extensions.into_iter().enumerate() { + let (width, height) = self.content.size(); write_string_to_grid( - "Server Capabilities:", - &mut self.content, - self.theme_default.fg, - self.theme_default.bg, - Attr::BOLD, - ((1, line), (width - 1, height - 1)), - None, - ); - let mut capabilities = imap_backend.capabilities(); - let max_name_width = std::cmp::max( - "Server Capabilities:".len(), - capabilities.iter().map(String::len).max().unwrap_or(0), - ); - write_string_to_grid( - "meli support:", + &name, &mut self.content, self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, - ((max_name_width + 6, line), (width - 1, height - 1)), + ((1, line + i), (width - 1, height - 1)), None, ); - capabilities.sort(); - line += 1; - for (i, cap) in capabilities.into_iter().enumerate() { - let (width, height) = self.content.size(); - write_string_to_grid( - &cap, + + use melib::backends::MailBackendExtensionStatus; + let (width, height) = self.content.size(); + let (x, y) = match status { + MailBackendExtensionStatus::Unsupported { comment: _ } => write_string_to_grid( + "not supported", &mut self.content, - self.theme_default.fg, + Color::Red, self.theme_default.bg, self.theme_default.attrs, - ((1, line + i), (width - 1, height - 1)), + ((max_name_width + 6, line + i), (width - 1, height - 1)), None, - ); - - let (width, height) = self.content.size(); - if melib::backends::imap::SUPPORTED_CAPABILITIES - .iter() - .any(|c| cap.eq_ignore_ascii_case(c)) - { - write_string_to_grid( - "supported", - &mut self.content, - Color::Green, - self.theme_default.bg, - self.theme_default.attrs, - ((max_name_width + 6, line + i), (width - 1, height - 1)), - None, - ); - } else { - write_string_to_grid( - "not supported", - &mut self.content, - Color::Red, - self.theme_default.bg, - self.theme_default.attrs, - ((max_name_width + 6, line + i), (width - 1, height - 1)), - None, - ); + ), + MailBackendExtensionStatus::Supported { comment: _ } => write_string_to_grid( + "supported", + &mut self.content, + Color::Green, + self.theme_default.bg, + self.theme_default.attrs, + ((max_name_width + 6, line + i), (width - 1, height - 1)), + None, + ), + MailBackendExtensionStatus::Enabled { comment: _ } => write_string_to_grid( + "enabled", + &mut self.content, + Color::Green, + self.theme_default.bg, + self.theme_default.attrs, + ((max_name_width + 6, line + i), (width - 1, height - 1)), + None, + ), + }; + match status { + MailBackendExtensionStatus::Unsupported { comment } + | MailBackendExtensionStatus::Supported { comment } + | MailBackendExtensionStatus::Enabled { comment } => { + if let Some(s) = comment { + let (x, y) = write_string_to_grid( + " (", + &mut self.content, + self.theme_default.fg, + self.theme_default.bg, + self.theme_default.attrs, + ((x, y), (width - 1, height - 1)), + None, + ); + let (x, y) = write_string_to_grid( + s, + &mut self.content, + self.theme_default.fg, + self.theme_default.bg, + self.theme_default.attrs, + ((x, y), (width - 1, height - 1)), + None, + ); + write_string_to_grid( + ")", + &mut self.content, + self.theme_default.fg, + self.theme_default.bg, + self.theme_default.attrs, + ((x, y), (width - 1, height - 1)), + None, + ); + } } - } + }; } } diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index a867dda1..e548881b 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -445,6 +445,7 @@ impl Account { } else { self.backend.read().unwrap().mailboxes()? }; + self.backend_capabilities = self.backend.read().unwrap().capabilities(); let mut mailbox_entries: HashMap = HashMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default()); let mut mailboxes_order: Vec = Vec::with_capacity(ref_mailboxes.len()); diff --git a/src/plugins/backend.rs b/src/plugins/backend.rs index 8f3a78d2..7c8d0132 100644 --- a/src/plugins/backend.rs +++ b/src/plugins/backend.rs @@ -70,6 +70,7 @@ impl MailBackend for PluginBackend { is_async: false, is_remote: false, supports_search: false, + extensions: None, supports_tags: false, }; CAPABILITIES