diff --git a/Cargo.lock b/Cargo.lock index 6d627ed2d..629834220 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,7 +553,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -587,6 +587,16 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ctor" version = "0.1.12" @@ -1626,7 +1636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2609,17 +2619,12 @@ dependencies = [ [[package]] name = "smallvec" -version = "0.6.13" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "smallvec" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "sourcefile" version = "0.1.4" @@ -3631,7 +3636,6 @@ dependencies = [ "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slice-deque 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffddf594f5f597f63533d897427a570dbaa9feabaaa06595b74b71b7014507d7" "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" diff --git a/Cargo.toml b/Cargo.toml index 8a6f40747..95c31472b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ num-derive = "0.2.5" num-traits = "0.2.6" native-tls = "0.2.3" lettre = { git = "https://github.com/deltachat/lettre", branch = "master" } -# imap = { git = "https://github.com/deltachat/rust-imap", branch = "master" } async-imap = { git = "https://github.com/dignifiedquire/async-imap", branch = "master" } async-tls = { git = "https://github.com/async-rs/async-tls", branch = "master" } async-std = { git = "https://github.com/async-rs/async-std", branch = "master", features = ["unstable"] } @@ -52,6 +51,7 @@ bitflags = "1.1.0" jetscii = "0.4.4" debug_stub_derive = "0.3.0" sanitize-filename = "0.2.1" +stop-token = { git = "https://github.com/async-rs/stop-token" } [dev-dependencies] tempfile = "3.0" diff --git a/src/imap.rs b/src/imap.rs index 4135271f3..898c53017 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -3,10 +3,7 @@ use async_std::prelude::*; use async_std::sync::{Arc, Mutex, RwLock}; use async_std::task; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Condvar, -}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, SystemTime}; use crate::configure::dc_connect_to_configured_imap; @@ -41,10 +38,10 @@ const SELECT_ALL: &str = "1:*"; #[derive(Debug)] pub struct Imap { config: Arc>, - watch: Arc<(Mutex, Condvar)>, session: Arc>>, connected: Arc>, + interrupt: Arc>>, should_reconnect: AtomicBool, } @@ -92,13 +89,11 @@ enum IdleHandle { Insecure(async_imap::extensions::idle::Handle), } -impl IdleHandle {} - impl Client { pub async fn connect_secure>( addr: A, domain: S, - certificate_checks: CertificateChecks, + _certificate_checks: CertificateChecks, ) -> async_imap::error::Result { let stream = net::TcpStream::connect(addr).await?; let tls = async_tls::TlsConnector::new(); @@ -138,7 +133,7 @@ impl Client { pub async fn secure>( self, domain: S, - certificate_checks: CertificateChecks, + _certificate_checks: CertificateChecks, ) -> async_imap::error::Result { match self { Client::Insecure(client) => { @@ -314,7 +309,7 @@ impl Session { Ok(res) } - pub async fn idle(self) -> IdleHandle { + pub fn idle(self) -> IdleHandle { match self { Session::Secure(i) => { let h = i.idle(); @@ -423,7 +418,7 @@ impl Imap { Imap { session: Arc::new(Mutex::new(None)), config: Arc::new(RwLock::new(ImapConfig::default())), - watch: Arc::new((Mutex::new(false), Condvar::new())), + interrupt: Arc::new(Mutex::new(None)), connected: Arc::new(Mutex::new(false)), should_reconnect: AtomicBool::new(false), } @@ -1069,7 +1064,50 @@ impl Imap { return; } - // TODO: IMPLEMENT ME + let session = self.session.lock().await.take(); + let timeout = Duration::from_secs(23 * 60); + if let Some(session) = session { + match session.idle() { + IdleHandle::Secure(mut handle) => { + if let Err(err) = handle.init().await { + warn!(context, "Failed to establish IDLE connection: {:?}", err); + return; + } + let (idle_wait, interrupt) = handle.wait_with_timeout(timeout); + *self.interrupt.lock().await = Some(interrupt); + + let res = idle_wait.await; + info!(context, "Idle finished: {:?}", res); + match handle.done().await { + Ok(session) => { + *self.session.lock().await = Some(Session::Secure(session)); + } + Err(err) => { + warn!(context, "Failed to close IMAP IDLE connection: {:?}", err); + } + } + } + IdleHandle::Insecure(mut handle) => { + if let Err(err) = handle.init().await { + warn!(context, "Failed to establish IDLE connection: {:?}", err); + return; + } + let (idle_wait, interrupt) = handle.wait_with_timeout(timeout); + *self.interrupt.lock().await = Some(interrupt); + + let res = idle_wait.await; + info!(context, "Idle finished: {:?}", res); + match handle.done().await { + Ok(session) => { + *self.session.lock().await = Some(Session::Insecure(session)); + } + Err(err) => { + warn!(context, "Failed to close IMAP IDLE connection: {:?}", err); + } + } + } + } + } }); } @@ -1077,72 +1115,57 @@ impl Imap { // Idle using timeouts. This is also needed if we're not yet configured - // in this case, we're waiting for a configure job let fake_idle_start_time = SystemTime::now(); - let mut wait_long = false; info!(context, "IMAP-fake-IDLEing..."); - let mut do_fake_idle = true; - while do_fake_idle { - // wait a moment: every 5 seconds in the first 3 minutes after a new message, after that every 60 seconds. - let seconds_to_wait = if fake_idle_start_time.elapsed().unwrap_or_default() - < Duration::new(3 * 60, 0) - && !wait_long - { - Duration::new(5, 0) - } else { - Duration::new(60, 0) - }; + task::block_on(async move { + let interrupt = stop_token::StopSource::new(); - // TODO: implement me better + // TODO: More flexible interval + let interval = async_std::stream::interval(Duration::from_secs(10)); + let mut interrupt_interval = interrupt.stop_token().stop_stream(interval); + *self.interrupt.lock().await = Some(interrupt); - // let &(ref lock, ref cvar) = &*self.watch.clone(); - // let mut watch = lock.lock().await; + while let Some(_) = interrupt_interval.next().await { + // check if we want to finish fake-idling. + if !self.is_connected().await { + // try to connect with proper login params + // (setup_handle_if_needed might not know about them if we + // never successfully connected) + if dc_connect_to_configured_imap(context, &self) != 0 { + self.interrupt.lock().await.take(); + } + } + // we are connected, let's see if fetching messages results + // in anything. If so, we behave as if IDLE had data but + // will have already fetched the messages so perform_*_fetch + // will not find any new. - // loop { - // let res = cvar.wait_timeout(watch, seconds_to_wait).await; - // watch = res.0; - // if *watch { - // do_fake_idle = false; - // } - // if *watch || res.1.timed_out() { - // break; - // } - // } + let watch_folder = self.config.read().await.watch_folder.clone(); + if let Some(watch_folder) = watch_folder { + if 0 != self.fetch_from_single_folder(context, watch_folder).await { + self.interrupt.lock().await.take(); + break; + } + } + } + }); - // *watch = false; - - // if !do_fake_idle { - // return; - // } - - // // check if we want to finish fake-idling. - // if !self.is_connected() { - // // try to connect with proper login params - // // (setup_handle_if_needed might not know about them if we - // // never successfully connected) - // if dc_connect_to_configured_imap(context, &self) != 0 { - // return; - // } - // // we cannot connect, wait long next time (currently 60 secs, see above) - // wait_long = true; - // continue; - // } - // // we are connected, let's see if fetching messages results - // // in anything. If so, we behave as if IDLE had data but - // // will have already fetched the messages so perform_*_fetch - // // will not find any new. - - // let watch_folder = self.config.read().await.watch_folder.clone(); - // if let Some(watch_folder) = watch_folder { - // if 0 != self.fetch_from_single_folder(context, watch_folder) { - // do_fake_idle = false; - // } - // } - } + info!( + context, + "IMAP-fake-IDLE done after {:.4}s", + SystemTime::now() + .duration_since(fake_idle_start_time) + .unwrap() + .as_millis() as f64 + / 1000., + ); } pub fn interrupt_idle(&self) { - // TODO: implement me + task::block_on(async move { + let _ = self.interrupt.lock().await.take(); + }); } pub fn mv(