diff --git a/src/imap/idle.rs b/src/imap/idle.rs index a029f74e3..dfd2d70cc 100644 --- a/src/imap/idle.rs +++ b/src/imap/idle.rs @@ -48,11 +48,10 @@ impl Imap { self.select_folder(context, watch_folder.clone()).await?; - let session = self.session.take(); let timeout = Duration::from_secs(23 * 60); let mut info = Default::default(); - if let Some(session) = session { + if let Some(session) = self.session.take() { let mut handle = session.idle(); if let Err(err) = handle.init().await { return Err(Error::IdleProtocolFailed(err)); @@ -65,68 +64,43 @@ impl Imap { Interrupt(InterruptInfo), } - if self.skip_next_idle_wait { - // interrupt_idle has happened before we - // provided self.interrupt - self.skip_next_idle_wait = false; - drop(idle_wait); + info!(context, "Idle entering wait-on-remote state"); + let fut = idle_wait.map(|ev| ev.map(Event::IdleResponse)).race(async { + let probe_network = self.idle_interrupt.recv().await; + + // cancel imap idle connection properly drop(interrupt); - info!(context, "Idle wait was skipped"); - } else { - info!(context, "Idle entering wait-on-remote state"); - let fut = idle_wait.map(|ev| ev.map(Event::IdleResponse)).race( - self.idle_interrupt.recv().map(|probe_network| { - Ok(Event::Interrupt(probe_network.unwrap_or_default())) - }), - ); + Ok(Event::Interrupt(probe_network.unwrap_or_default())) + }); - match fut.await { - Ok(Event::IdleResponse(IdleResponse::NewData(_))) => { - info!(context, "Idle has NewData"); - } - // TODO: idle_wait does not distinguish manual interrupts - // from Timeouts if we would know it's a Timeout we could bail - // directly and reconnect . - Ok(Event::IdleResponse(IdleResponse::Timeout)) => { - info!(context, "Idle-wait timeout or interruption"); - } - Ok(Event::IdleResponse(IdleResponse::ManualInterrupt)) => { - info!(context, "Idle wait was interrupted"); - } - Ok(Event::Interrupt(i)) => { - info = i; - info!(context, "Idle wait was interrupted"); - } - Err(err) => { - warn!(context, "Idle wait errored: {:?}", err); - } + match fut.await { + Ok(Event::IdleResponse(IdleResponse::NewData(_))) => { + info!(context, "Idle has NewData"); + } + Ok(Event::IdleResponse(IdleResponse::Timeout)) => { + info!(context, "Idle-wait timeout or interruption"); + } + Ok(Event::IdleResponse(IdleResponse::ManualInterrupt)) => { + info!(context, "Idle wait was interrupted"); + } + Ok(Event::Interrupt(i)) => { + info = i; + info!(context, "Idle wait was interrupted"); + } + Err(err) => { + warn!(context, "Idle wait errored: {:?}", err); } } - // if we can't properly terminate the idle - // protocol let's break the connection. - let res = handle + let session = handle .done() .timeout(Duration::from_secs(15)) .await - .map_err(|err| { - self.trigger_reconnect(); - Error::IdleTimeout(err) - })?; - - match res { - Ok(session) => { - self.session = Some(Session { inner: session }); - } - Err(err) => { - // if we cannot terminate IDLE it probably - // means that we waited long (with idle_wait) - // but the network went away/changed - self.trigger_reconnect(); - return Err(Error::IdleProtocolFailed(err)); - } - } + .map_err(Error::IdleTimeout)??; + self.session = Some(Session { inner: session }); + } else { + warn!(context, "Attempted to idle without a session"); } Ok(info) @@ -148,73 +122,66 @@ impl Imap { return self.idle_interrupt.recv().await.unwrap_or_default(); } - let mut info: InterruptInfo = Default::default(); - if self.skip_next_idle_wait { - // interrupt_idle has happened before we - // provided self.interrupt - self.skip_next_idle_wait = false; - info!(context, "fake-idle wait was skipped"); - } else { - // check every minute if there are new messages - // TODO: grow sleep durations / make them more flexible - let mut interval = async_std::stream::interval(Duration::from_secs(60)); + // check every minute if there are new messages + // TODO: grow sleep durations / make them more flexible + let mut interval = async_std::stream::interval(Duration::from_secs(60)); - enum Event { - Tick, - Interrupt(InterruptInfo), - } - // loop until we are interrupted or if we fetched something - info = - loop { - use futures::future::FutureExt; - match interval - .next() - .map(|_| Event::Tick) - .race(self.idle_interrupt.recv().map(|probe_network| { - Event::Interrupt(probe_network.unwrap_or_default()) - })) - .await - { - Event::Tick => { - // try to connect with proper login params - // (setup_handle_if_needed might not know about them if we - // never successfully connected) - if let Err(err) = self.connect_configured(context).await { - warn!(context, "fake_idle: could not connect: {}", err); - continue; - } - if self.config.can_idle { - // we only fake-idled because network was gone during IDLE, probably - break InterruptInfo::new(false, None); - } - info!(context, "fake_idle is connected"); - // 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. + enum Event { + Tick, + Interrupt(InterruptInfo), + } + // loop until we are interrupted or if we fetched something + let info = loop { + use futures::future::FutureExt; + match interval + .next() + .map(|_| Event::Tick) + .race( + self.idle_interrupt + .recv() + .map(|probe_network| Event::Interrupt(probe_network.unwrap_or_default())), + ) + .await + { + Event::Tick => { + // try to connect with proper login params + // (setup_handle_if_needed might not know about them if we + // never successfully connected) + if let Err(err) = self.connect_configured(context).await { + warn!(context, "fake_idle: could not connect: {}", err); + continue; + } + if self.config.can_idle { + // we only fake-idled because network was gone during IDLE, probably + break InterruptInfo::new(false, None); + } + info!(context, "fake_idle is connected"); + // 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. - if let Some(ref watch_folder) = watch_folder { - match self.fetch_new_messages(context, watch_folder).await { - Ok(res) => { - info!(context, "fetch_new_messages returned {:?}", res); - if res { - break InterruptInfo::new(false, None); - } - } - Err(err) => { - error!(context, "could not fetch from folder: {}", err); - self.trigger_reconnect() - } + if let Some(ref watch_folder) = watch_folder { + match self.fetch_new_messages(context, watch_folder).await { + Ok(res) => { + info!(context, "fetch_new_messages returned {:?}", res); + if res { + break InterruptInfo::new(false, None); } } - } - Event::Interrupt(info) => { - // Interrupt - break info; + Err(err) => { + error!(context, "could not fetch from folder: {}", err); + self.trigger_reconnect() + } } } - }; - } + } + Event::Interrupt(info) => { + // Interrupt + break info; + } + } + }; info!( context, diff --git a/src/imap/mod.rs b/src/imap/mod.rs index 22529c5d4..bac253058 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -117,7 +117,6 @@ pub struct Imap { session: Option, connected: bool, interrupt: Option, - skip_next_idle_wait: bool, should_reconnect: bool, } @@ -191,7 +190,6 @@ impl Imap { session: Default::default(), connected: Default::default(), interrupt: Default::default(), - skip_next_idle_wait: Default::default(), should_reconnect: Default::default(), } }