diff --git a/CHANGELOG.md b/CHANGELOG.md index cdfaaf577..4ba5ed6a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add `select` command to select threads that match search query +- Add support for mass copying/deleting/flagging/moving of messages +- IMAP: add support for COMPRESS=DEFLATE and others + Extension use can be configured with individual flags such as `use_deflate` +- Rename EXECUTE mode to COMMAND +- add async IMAP backend +- add in-app SMTP support - ui: Show decoded source by default when viewing an Envelope's source - ui: Add search in pagers - Add managesieve REPL binary for managesieve script management diff --git a/Cargo.toml b/Cargo.toml index 066569cff..7e9ca0f88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ quote = "^1.0" proc-macro2 = "1.0.18" [profile.release] -lto = true +lto = "fat" opt-level = "z" debug = false diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index a6283a83e..85c948dea 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -306,10 +306,19 @@ impl MailBackend for ImapType { fn is_online_async(&self) -> ResultFuture<()> { let connection = self.connection.clone(); Ok(Box::pin(async move { - let mut conn = connection.lock().await; - conn.connect().await?; - - Ok(()) + match timeout(std::time::Duration::from_secs(3), connection.lock()).await { + Ok(mut conn) => { + debug!("is_online_async"); + match debug!(timeout(std::time::Duration::from_secs(3), conn.connect()).await) { + Ok(Ok(())) => Ok(()), + Err(err) | Ok(Err(err)) => { + conn.stream = Err(err.clone()); + debug!(conn.connect().await) + } + } + } + Err(err) => Err(err), + } })) } @@ -1627,3 +1636,15 @@ async fn fetch_hlpr( }; Ok(payload) } + +use futures::future::{self, Either}; + +async fn timeout(dur: std::time::Duration, f: impl Future) -> Result { + futures::pin_mut!(f); + match future::select(f, smol::Timer::after(dur)).await { + Either::Left((out, _)) => Ok(out), + Either::Right(_) => { + Err(MeliError::new("Timed out.").set_kind(crate::error::ErrorKind::Network)) + } + } +} diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs index 0a110e927..5e4f906b7 100644 --- a/melib/src/backends/imap/connection.rs +++ b/melib/src/backends/imap/connection.rs @@ -396,9 +396,6 @@ impl ImapStream { last_line_idx += pos + "\r\n".len(); } } - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { - continue; - } Err(e) => { return Err(MeliError::from(e).set_err_kind(crate::error::ErrorKind::Network)); } @@ -634,6 +631,7 @@ impl ImapConnection { if let Err(err) = try_await(async { self.stream.as_mut()?.send_command(command).await }).await { + self.stream = Err(err.clone()); if err.kind.is_network() { self.connect().await?; } @@ -646,6 +644,7 @@ impl ImapConnection { pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> { if let Err(err) = try_await(async { self.stream.as_mut()?.send_literal(data).await }).await { + self.stream = Err(err.clone()); if err.kind.is_network() { self.connect().await?; } @@ -657,6 +656,7 @@ impl ImapConnection { pub async fn send_raw(&mut self, raw: &[u8]) -> Result<()> { if let Err(err) = try_await(async { self.stream.as_mut()?.send_raw(raw).await }).await { + self.stream = Err(err.clone()); if err.kind.is_network() { self.connect().await?; } @@ -862,27 +862,12 @@ async fn read( } *prev_failure = None; } - Err(e) - if e.kind() == std::io::ErrorKind::WouldBlock - || e.kind() == std::io::ErrorKind::Interrupted => - { - debug!(&e); - if let Some(prev_failure) = prev_failure.as_ref() { - if Instant::now().duration_since(*prev_failure) - >= std::time::Duration::new(60 * 5, 0) - { - *err = Some(e.to_string()); - *break_flag = true; - } - } else { - *prev_failure = Some(Instant::now()); - } - } Err(e) => { debug!(&conn.stream); debug!(&e); *err = Some(e.to_string()); *break_flag = true; + *prev_failure = Some(Instant::now()); } } None diff --git a/melib/src/backends/imap/operations.rs b/melib/src/backends/imap/operations.rs index 3ed4685b9..9c30213f7 100644 --- a/melib/src/backends/imap/operations.rs +++ b/melib/src/backends/imap/operations.rs @@ -66,7 +66,8 @@ impl BackendOp for ImapOp { if !exists_in_cache { let mut response = String::with_capacity(8 * 1024); { - let mut conn = connection.lock().await; + let mut conn = + timeout(std::time::Duration::from_secs(3), connection.lock()).await?; conn.examine_mailbox(mailbox_hash, &mut response, false) .await?; conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", uid).as_bytes()) diff --git a/melib/src/collection.rs b/melib/src/collection.rs index bbf0ac5d5..1c9505d29 100644 --- a/melib/src/collection.rs +++ b/melib/src/collection.rs @@ -155,7 +155,7 @@ impl Collection { new_hash: EnvelopeHash, mailbox_hash: MailboxHash, ) -> bool { - if !self.envelopes.write().unwrap().contains_key(&old_hash) { + if !self.envelopes.read().unwrap().contains_key(&old_hash) { return false; } let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap(); diff --git a/melib/src/conf.rs b/melib/src/conf.rs index 706897be1..40b14d16b 100644 --- a/melib/src/conf.rs +++ b/melib/src/conf.rs @@ -75,6 +75,7 @@ impl AccountSettings { #[serde(default)] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MailboxConf { + #[serde(alias = "rename")] pub alias: Option, #[serde(default = "false_val")] pub autoload: bool, diff --git a/melib/src/smtp.rs b/melib/src/smtp.rs index 345dc9b14..9825ebf1f 100644 --- a/melib/src/smtp.rs +++ b/melib/src/smtp.rs @@ -903,9 +903,6 @@ async fn read_lines<'r>( Ok(b) => { ret.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..b]) }); } - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { - panic!("block"); - } Err(e) => { return Err(MeliError::from(e).set_kind(crate::error::ErrorKind::Network)); } diff --git a/src/components/mail/status.rs b/src/components/mail/status.rs index 962e84b25..439857cb4 100644 --- a/src/components/mail/status.rs +++ b/src/components/mail/status.rs @@ -384,7 +384,7 @@ impl StatusPanel { .enumerate() { write_string_to_grid( - &format!("{}: {}", f.path(), f.special_usage()), + &format!("{}: {}", f.special_usage(), f.path()), &mut self.content, self.theme_default.fg, self.theme_default.bg, diff --git a/src/components/mail/view/thread.rs b/src/components/mail/view/thread.rs index 3a875731c..cbbf4130c 100644 --- a/src/components/mail/view/thread.rs +++ b/src/components/mail/view/thread.rs @@ -733,12 +733,10 @@ impl ThreadView { let total_rows = height!(area); let pager_ratio = context.runtime_settings.pager.pager_ratio; - let bottom_entity_rows = (pager_ratio * total_rows) / 100; + let mut bottom_entity_rows = (pager_ratio * total_rows) / 100; if bottom_entity_rows > total_rows { - clear_area(grid, area, crate::conf::value(context, "theme_default")); - context.dirty_areas.push_back(area); - return; + bottom_entity_rows = total_rows.saturating_sub(1); } let mut mid = get_y(upper_left) + total_rows - bottom_entity_rows; @@ -952,6 +950,9 @@ impl Component for ThreadView { self.dirty = false; return; } + if !self.is_dirty() { + return; + } /* If user has selected another mail to view, change to it */ if self.new_expanded_pos != self.expanded_pos { diff --git a/src/components/utilities/widgets.rs b/src/components/utilities/widgets.rs index 5434ee5e9..058eca756 100644 --- a/src/components/utilities/widgets.rs +++ b/src/components/utilities/widgets.rs @@ -1063,7 +1063,7 @@ impl fmt::Display for ProgressSpinner { impl Component for ProgressSpinner { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if self.dirty { - let theme_attr = crate::conf::value(context, "theme_default"); + let theme_attr = crate::conf::value(context, "status.bar"); clear_area(grid, area, theme_attr); let stage = self.stage; self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[self.kind].len());