resultify fetch and simplify fake_idle

This commit is contained in:
holger krekel
2019-11-19 20:28:44 +01:00
parent fa01884350
commit a30fa710ad
4 changed files with 195 additions and 224 deletions

View File

@@ -7,8 +7,6 @@ use crate::constants::*;
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::e2ee; use crate::e2ee;
use crate::error::*;
use crate::imap::*;
use crate::job::*; use crate::job::*;
use crate::login_param::LoginParam; use crate::login_param::LoginParam;
use crate::oauth2::*; use crate::oauth2::*;
@@ -583,26 +581,6 @@ fn try_smtp_one_param(context: &Context, param: &LoginParam) -> Option<bool> {
} }
} }
/// Connects to the configured account
pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> Result<()> {
if async_std::task::block_on(async move { imap.is_connected().await }) {
return Ok(());
}
if !context.sql.get_raw_config_bool(context, "configured") {
return Err(Error::ConnectWithoutConfigure);
}
let param = LoginParam::from_database(context, "configured_");
// the trailing underscore is correct
if imap.connect(context, &param) {
return Ok(());
}
return Err(Error::ImapConnectionFailed(
format!("{}", param).to_string(),
));
}
/******************************************************************************* /*******************************************************************************
* Configure a Context * Configure a Context
******************************************************************************/ ******************************************************************************/

View File

@@ -38,6 +38,10 @@ pub enum Error {
WatchFolderNotFound(String), WatchFolderNotFound(String),
#[fail(display = "Connection Failed params: {}", _0)] #[fail(display = "Connection Failed params: {}", _0)]
ImapConnectionFailed(String), ImapConnectionFailed(String),
#[fail(display = "Could not get OAUTH token")]
ImapOauthError,
#[fail(display = "Could not login as {}", _0)]
ImapLoginFailed(String),
#[fail(display = "Cannot idle")] #[fail(display = "Cannot idle")]
ImapMissesIdle, ImapMissesIdle,
#[fail(display = "Imap IDLE protocol failed to init/complete")] #[fail(display = "Imap IDLE protocol failed to init/complete")]
@@ -48,6 +52,8 @@ pub enum Error {
ConnectWithoutConfigure, ConnectWithoutConfigure,
#[fail(display = "imap operation attempted while imap is torn down")] #[fail(display = "imap operation attempted while imap is torn down")]
ImapInTeardown, ImapInTeardown,
#[fail(display = "No IMAP Connection established")]
ImapNoConnection,
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -10,11 +10,10 @@ use async_std::prelude::*;
use async_std::sync::{Arc, Mutex, RwLock}; use async_std::sync::{Arc, Mutex, RwLock};
use async_std::task; use async_std::task;
use crate::configure::dc_connect_to_configured_imap;
use crate::constants::*; use crate::constants::*;
use crate::context::Context; use crate::context::Context;
use crate::dc_receive_imf::dc_receive_imf; use crate::dc_receive_imf::dc_receive_imf;
use crate::error::Error; use crate::error::{Error, Result};
use crate::events::Event; use crate::events::Event;
use crate::imap_client::*; use crate::imap_client::*;
use crate::job::{job_add, Action}; use crate::job::{job_add, Action};
@@ -35,12 +34,6 @@ pub enum ImapActionResult {
Success, Success,
} }
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)]
pub enum IdlePollMode {
Often,
Never,
}
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)"; const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])"; const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
const SELECT_ALL: &str = "1:*"; const SELECT_ALL: &str = "1:*";
@@ -95,7 +88,6 @@ struct ImapConfig {
pub can_idle: bool, pub can_idle: bool,
pub has_xlist: bool, pub has_xlist: bool,
pub imap_delimiter: char, pub imap_delimiter: char,
pub watch_folder: Option<String>,
} }
impl Default for ImapConfig { impl Default for ImapConfig {
@@ -114,7 +106,6 @@ impl Default for ImapConfig {
can_idle: false, can_idle: false,
has_xlist: false, has_xlist: false,
imap_delimiter: '.', imap_delimiter: '.',
watch_folder: None,
} }
} }
} }
@@ -143,17 +134,17 @@ impl Imap {
self.should_reconnect.store(true, Ordering::Relaxed) self.should_reconnect.store(true, Ordering::Relaxed)
} }
fn setup_handle_if_needed(&self, context: &Context) -> bool { fn setup_handle_if_needed(&self, context: &Context) -> Result<()> {
task::block_on(async move { task::block_on(async move {
if self.config.read().await.imap_server.is_empty() { if self.config.read().await.imap_server.is_empty() {
return false; return Err(Error::ImapInTeardown);
} }
if self.should_reconnect() { if self.should_reconnect() {
self.unsetup_handle(context).await; self.unsetup_handle(context).await;
self.should_reconnect.store(false, Ordering::Relaxed); self.should_reconnect.store(false, Ordering::Relaxed);
} else if self.is_connected().await { } else if self.is_connected().await {
return true; return Ok(());
} }
let server_flags = self.config.read().await.server_flags as i32; let server_flags = self.config.read().await.server_flags as i32;
@@ -181,13 +172,12 @@ impl Imap {
let imap_server: &str = config.imap_server.as_ref(); let imap_server: &str = config.imap_server.as_ref();
let imap_port = config.imap_port; let imap_port = config.imap_port;
let res = Client::connect_secure( Client::connect_secure(
(imap_server, imap_port), (imap_server, imap_port),
imap_server, imap_server,
config.certificate_checks, config.certificate_checks,
) )
.await; .await
res
}; };
let login_res = match connection_res { let login_res = match connection_res {
@@ -209,7 +199,7 @@ impl Imap {
let res = client.authenticate("XOAUTH2", &auth).await; let res = client.authenticate("XOAUTH2", &auth).await;
res res
} else { } else {
return false; return Err(Error::ImapOauthError);
} }
} else { } else {
let res = client.login(imap_user, imap_pw).await; let res = client.login(imap_user, imap_pw).await;
@@ -217,18 +207,19 @@ impl Imap {
} }
} }
Err(err) => { Err(err) => {
let config = self.config.read().await; let message = {
let imap_server: &str = config.imap_server.as_ref(); let config = self.config.read().await;
let imap_port = config.imap_port; let imap_server: &str = config.imap_server.as_ref();
let message = context.stock_string_repl_str2( let imap_port = config.imap_port;
StockMessage::ServerResponse, context.stock_string_repl_str2(
format!("{}:{}", imap_server, imap_port), StockMessage::ServerResponse,
format!("{}", err), format!("{}:{}", imap_server, imap_port),
); format!("{}", err),
)
};
// IMAP connection failures are reported to users
emit_event!(context, Event::ErrorNetwork(message)); emit_event!(context, Event::ErrorNetwork(message));
return Err(Error::ImapConnectionFailed(format!("{}", err)));
return false;
} }
}; };
@@ -237,7 +228,7 @@ impl Imap {
match login_res { match login_res {
Ok(session) => { Ok(session) => {
*self.session.lock().await = Some(session); *self.session.lock().await = Some(session);
true Ok(())
} }
Err((err, _)) => { Err((err, _)) => {
let imap_user = self.config.read().await.imap_user.to_owned(); let imap_user = self.config.read().await.imap_user.to_owned();
@@ -248,9 +239,11 @@ impl Imap {
context, context,
Event::ErrorNetwork(format!("{} ({})", message, err)) Event::ErrorNetwork(format!("{} ({})", message, err))
); );
self.unsetup_handle(context).await; self.trigger_reconnect();
Err(Error::ImapLoginFailed(format!(
false "cannot login as {}",
imap_user
)))
} }
} }
}) })
@@ -266,11 +259,12 @@ impl Imap {
warn!(context, "failed to close connection: {:?}", err); warn!(context, "failed to close connection: {:?}", err);
} }
} }
*self.connected.lock().await = false;
info!(context, "IMAP unsetup_handle step 3 (clearing config)."); info!(context, "IMAP unsetup_handle step 3 (clearing config).");
self.config.write().await.selected_folder = None; self.config.write().await.selected_folder = None;
self.config.write().await.selected_mailbox = None; self.config.write().await.selected_mailbox = None;
info!(context, "IMAP unsetup_handle step 4 (disconnected)."); info!(context, "IMAP unsetup_handle step 4 (disconnected)");
} }
async fn free_connect_params(&self) { async fn free_connect_params(&self) {
@@ -284,8 +278,28 @@ impl Imap {
cfg.can_idle = false; cfg.can_idle = false;
cfg.has_xlist = false; cfg.has_xlist = false;
}
cfg.watch_folder = None; /// Connects to configured account
pub fn connect_configured(&self, context: &Context) -> Result<()> {
if async_std::task::block_on(async move {
self.is_connected().await && !self.should_reconnect()
}) {
return Ok(());
}
if !context.sql.get_raw_config_bool(context, "configured") {
return Err(Error::ConnectWithoutConfigure);
}
let param = LoginParam::from_database(context, "configured_");
// the trailing underscore is correct
if self.connect(context, &param) {
return Ok(());
}
return Err(Error::ImapConnectionFailed(
format!("{}", param).to_string(),
));
} }
pub fn connect(&self, context: &Context, lp: &LoginParam) -> bool { pub fn connect(&self, context: &Context, lp: &LoginParam) -> bool {
@@ -294,10 +308,6 @@ impl Imap {
return false; return false;
} }
if self.is_connected().await {
return true;
}
{ {
let addr = &lp.addr; let addr = &lp.addr;
let imap_server = &lp.mail_server; let imap_server = &lp.mail_server;
@@ -316,7 +326,8 @@ impl Imap {
config.server_flags = server_flags; config.server_flags = server_flags;
} }
if !self.setup_handle_if_needed(context) { if let Err(err) = self.setup_handle_if_needed(context) {
warn!(context, "failed to setup imap handle: {}", err);
self.free_connect_params().await; self.free_connect_params().await;
return false; return false;
} }
@@ -366,44 +377,26 @@ impl Imap {
pub fn disconnect(&self, context: &Context) { pub fn disconnect(&self, context: &Context) {
task::block_on(async move { task::block_on(async move {
if self.is_connected().await { self.unsetup_handle(context).await;
self.unsetup_handle(context).await; self.free_connect_params().await;
self.free_connect_params().await;
*self.connected.lock().await = false;
}
}); });
} }
pub fn set_watch_folder(&self, watch_folder: String) { pub fn fetch(&self, context: &Context, watch_folder: &str) -> Result<()> {
task::block_on(async move {
self.config.write().await.watch_folder = Some(watch_folder);
});
}
pub fn fetch(&self, context: &Context) -> bool {
task::block_on(async move { task::block_on(async move {
if !context.sql.is_open() { if !context.sql.is_open() {
// probably shutdown // probably shutdown
return false; return Err(Error::ImapInTeardown);
} }
while self
self.setup_handle_if_needed(context); .fetch_from_single_folder(context, &watch_folder)
.await?
let watch_folder = self.config.read().await.watch_folder.to_owned(); {
// During the fetch commands new messages may arrive. So we fetch until we do not
if let Some(ref watch_folder) = watch_folder { // get any more. If IDLE is called directly after, there is only a small chance that
// as during the fetch commands, new messages may arrive, we fetch until we do not
// get any more. if IDLE is called directly after, there is only a small chance that
// messages are missed and delayed until the next IDLE call // messages are missed and delayed until the next IDLE call
loop {
if self.fetch_from_single_folder(context, watch_folder).await == 0 {
break;
}
}
true
} else {
false
} }
Ok(())
}) })
} }
@@ -506,15 +499,14 @@ impl Imap {
} }
} }
async fn fetch_from_single_folder<S: AsRef<str>>(&self, context: &Context, folder: S) -> usize { async fn fetch_from_single_folder<S: AsRef<str>>(
&self,
context: &Context,
folder: S,
) -> Result<bool> {
match self.select_folder(context, Some(&folder)).await { match self.select_folder(context, Some(&folder)).await {
ImapActionResult::Failed | ImapActionResult::RetryLater => { ImapActionResult::Failed | ImapActionResult::RetryLater => {
warn!( bail!("Cannot select folder {:?} for fetching.", folder.as_ref());
context,
"Cannot select folder \"{}\" for fetching.",
folder.as_ref()
);
return 0;
} }
ImapActionResult::Success | ImapActionResult::AlreadyDone => {} ImapActionResult::Success | ImapActionResult::AlreadyDone => {}
} }
@@ -525,23 +517,20 @@ impl Imap {
let config = self.config.read().await; let config = self.config.read().await;
let mailbox = config.selected_mailbox.as_ref().expect("just selected"); let mailbox = config.selected_mailbox.as_ref().expect("just selected");
if mailbox.uid_validity.is_none() { ensure!(
error!( mailbox.uid_validity.is_some(),
context, "Cannot get UIDVALIDITY for folder {:?}",
"Cannot get UIDVALIDITY for folder \"{}\".", folder.as_ref()
folder.as_ref(), );
);
return 0; let new_uid_validity = mailbox.uid_validity.unwrap();
} if new_uid_validity != uid_validity {
if mailbox.uid_validity.unwrap() != uid_validity {
// First time this folder is selected or UIDVALIDITY has changed. // First time this folder is selected or UIDVALIDITY has changed.
// Init lastseenuid and save it to config. // Init lastseenuid and save it to config.
info!( info!(
context, context,
"uid_validity={} local uid_validity={} lastseenuid={}", "new_uid_validity={} current local uid_validity={} lastseenuid={}",
mailbox.uid_validity.unwrap(), new_uid_validity,
uid_validity, uid_validity,
last_seen_uid last_seen_uid
); );
@@ -553,13 +542,8 @@ impl Imap {
// id we do not do this here, we'll miss the first message // id we do not do this here, we'll miss the first message
// as we will get in here again and fetch from lastseenuid+1 then // as we will get in here again and fetch from lastseenuid+1 then
self.set_config_last_seen_uid( self.set_config_last_seen_uid(context, &folder, new_uid_validity, 0);
context, return Ok(false);
&folder,
mailbox.uid_validity.unwrap_or_default(),
0,
);
return 0;
} }
let list = if let Some(ref mut session) = &mut *self.session.lock().await { let list = if let Some(ref mut session) = &mut *self.session.lock().await {
@@ -567,19 +551,12 @@ impl Imap {
let set = format!("{}", mailbox.exists); let set = format!("{}", mailbox.exists);
match session.fetch(set, PREFETCH_FLAGS).await { match session.fetch(set, PREFETCH_FLAGS).await {
Ok(list) => list, Ok(list) => list,
Err(_err) => { Err(err) => {
self.trigger_reconnect(); bail!("fetch failed: {}", err);
info!(
context,
"No result returned for folder \"{}\".",
folder.as_ref()
);
return 0;
} }
} }
} else { } else {
return 0; return Err(Error::ImapNoConnection);
}; };
last_seen_uid = list[0].uid.unwrap_or_else(|| 0); last_seen_uid = list[0].uid.unwrap_or_else(|| 0);
@@ -589,7 +566,7 @@ impl Imap {
last_seen_uid -= 1; last_seen_uid -= 1;
} }
uid_validity = mailbox.uid_validity.unwrap_or_default(); uid_validity = new_uid_validity;
self.set_config_last_seen_uid(context, &folder, uid_validity, last_seen_uid); self.set_config_last_seen_uid(context, &folder, uid_validity, last_seen_uid);
info!( info!(
context, context,
@@ -611,12 +588,11 @@ impl Imap {
match session.uid_fetch(set, PREFETCH_FLAGS).await { match session.uid_fetch(set, PREFETCH_FLAGS).await {
Ok(list) => list, Ok(list) => list,
Err(err) => { Err(err) => {
warn!(context, "failed to fetch uids: {}", err); bail!("uid_fetch failed: {}", err);
return 0;
} }
} }
} else { } else {
return 0; return Err(Error::ImapNoConnection);
}; };
// go through all mails in folder (this is typically _fast_ as we already have the whole list) // go through all mails in folder (this is typically _fast_ as we already have the whole list)
@@ -677,7 +653,7 @@ impl Imap {
); );
} }
read_cnt Ok(read_cnt > 0)
} }
fn set_config_last_seen_uid<S: AsRef<str>>( fn set_config_last_seen_uid<S: AsRef<str>>(
@@ -764,26 +740,19 @@ impl Imap {
1 1
} }
pub fn idle(&self, context: &Context) -> Result<(), Error> { pub fn idle(&self, context: &Context, watch_folder: Option<String>) -> Result<()> {
task::block_on(async move { task::block_on(async move {
if !self.config.read().await.can_idle { if !self.config.read().await.can_idle {
return Err(Error::ImapMissesIdle); return Err(Error::ImapMissesIdle);
} }
self.setup_handle_if_needed(context); self.setup_handle_if_needed(context)?;
let watch_folder = self.config.read().await.watch_folder.clone(); match self.select_folder(context, watch_folder.clone()).await {
if watch_folder.is_none() {
return Err(Error::ImapInTeardown);
}
match self.select_folder(context, watch_folder.as_ref()).await {
ImapActionResult::Success | ImapActionResult::AlreadyDone => {} ImapActionResult::Success | ImapActionResult::AlreadyDone => {}
ImapActionResult::Failed | ImapActionResult::RetryLater => { ImapActionResult::Failed | ImapActionResult::RetryLater => {
return Err(Error::ImapSelectFailed(format!( return Err(Error::ImapSelectFailed(format!("{:?}", watch_folder)));
"{:?}",
watch_folder.as_ref()
)));
} }
} }
@@ -811,16 +780,16 @@ impl Imap {
info!(context, "Idle entering wait-on-remote state"); info!(context, "Idle entering wait-on-remote state");
match idle_wait.await { match idle_wait.await {
IdleResponse::NewData(_) => { IdleResponse::NewData(_) => {
info!(context, "Idle finished with 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 .
IdleResponse::Timeout => { IdleResponse::Timeout => {
warn!(context, "Idle wait timed out"); info!(context, "Idle-wait timeout or interruption");
return Err(Error::ImapIdleProtocolFailed(
"timeout".to_string(),
));
} }
IdleResponse::ManualInterrupt => { IdleResponse::ManualInterrupt => {
warn!(context, "Idle wait was interrupted"); info!(context, "Idle wait was interrupted");
} }
} }
} }
@@ -853,8 +822,20 @@ impl Imap {
info!(context, "Idle wait was skipped"); info!(context, "Idle wait was skipped");
} else { } else {
info!(context, "Idle entering wait-on-remote state"); info!(context, "Idle entering wait-on-remote state");
let res = idle_wait.await; match idle_wait.await {
info!(context, "Idle finished wait-on-remote: {:?}", res); 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 .
IdleResponse::Timeout => {
info!(context, "Idle-wait timeout or interruption");
}
IdleResponse::ManualInterrupt => {
info!(context, "Idle wait was interrupted");
}
}
} }
match handle.done().await { match handle.done().await {
Ok(session) => { Ok(session) => {
@@ -876,7 +857,7 @@ impl Imap {
}) })
} }
pub(crate) fn fake_idle(&self, context: &Context, poll_mode: IdlePollMode) { pub(crate) fn fake_idle(&self, context: &Context, watch_folder: Option<String>) {
// Idle using polling. // Idle using polling.
task::block_on(async move { task::block_on(async move {
let fake_idle_start_time = SystemTime::now(); let fake_idle_start_time = SystemTime::now();
@@ -885,45 +866,43 @@ impl Imap {
let interrupt = stop_token::StopSource::new(); let interrupt = stop_token::StopSource::new();
// we use 1000 minutes if we are told to not try network // check every minute if there are new messages
// which can happen because the watch_folder is not defined // TODO: grow sleep durations / make them more flexible
// but clients are still calling us in a loop. let interval = async_std::stream::interval(Duration::from_secs(60));
// if we are to use network, we check every minute if there
// is new mail -- TODO: make this more flexible
let secs = match poll_mode {
IdlePollMode::Never => 60000,
IdlePollMode::Often => 60,
};
let interval = async_std::stream::interval(Duration::from_secs(secs));
let mut interrupt_interval = interrupt.stop_token().stop_stream(interval); let mut interrupt_interval = interrupt.stop_token().stop_stream(interval);
*self.interrupt.lock().await = Some(interrupt); *self.interrupt.lock().await = Some(interrupt);
// loop until we are interrupted or if we fetched something // loop until we are interrupted or if we fetched something
while let Some(_) = interrupt_interval.next().await { while let Some(_) = interrupt_interval.next().await {
if poll_mode == IdlePollMode::Never { // 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) {
warn!(context, "fake_idle: could not connect: {}", err);
continue; continue;
} }
if !self.is_connected().await { if self.config.read().await.can_idle {
// try to connect with proper login params // we only fake-idled because network was gone during IDLE, probably
// (setup_handle_if_needed might not know about them if we break;
// never successfully connected)
match dc_connect_to_configured_imap(context, &self) {
Ok(()) => {}
Err(err) => {
warn!(context, "fake_idle: could not connect: {}", err);
continue;
}
}
} }
info!(context, "fake_idle is connected");
// we are connected, let's see if fetching messages results // we are connected, let's see if fetching messages results
// in anything. If so, we behave as if IDLE had data but // in anything. If so, we behave as if IDLE had data but
// will have already fetched the messages so perform_*_fetch // will have already fetched the messages so perform_*_fetch
// will not find any new. // will not find any new.
let watch_folder = self.config.read().await.watch_folder.clone(); if let Some(ref watch_folder) = watch_folder {
if let Some(watch_folder) = watch_folder { match self.fetch_from_single_folder(context, watch_folder).await {
if 0 != self.fetch_from_single_folder(context, watch_folder).await { Ok(res) => {
break; info!(context, "fetch_from_single_folder returned {:?}", res);
if res {
break;
}
}
Err(err) => {
error!(context, "could not fetch from folder: {}", err);
self.trigger_reconnect()
}
} }
} }
} }
@@ -1434,7 +1413,7 @@ fn precheck_imf(context: &Context, rfc724_mid: &str, server_folder: &str, server
} }
} }
fn prefetch_get_message_id(prefetch_msg: &Fetch) -> Result<String, Error> { fn prefetch_get_message_id(prefetch_msg: &Fetch) -> Result<String> {
let message_id = prefetch_msg.envelope().unwrap().message_id.unwrap(); let message_id = prefetch_msg.envelope().unwrap().message_id.unwrap();
wrapmime::parse_message_id(&message_id) wrapmime::parse_message_id(&message_id)
} }

View File

@@ -1,9 +1,8 @@
use std::sync::{Arc, Condvar, Mutex}; use std::sync::{Arc, Condvar, Mutex};
use crate::configure::*;
use crate::context::Context; use crate::context::Context;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::imap::{IdlePollMode, Imap}; use crate::imap::Imap;
#[derive(Debug)] #[derive(Debug)]
pub struct JobThread { pub struct JobThread {
@@ -89,20 +88,32 @@ impl JobThread {
let prefix = format!("{}-fetch", self.name); let prefix = format!("{}-fetch", self.name);
match self.connect_to_imap(context) { match self.connect_to_imap(context) {
Ok(()) => { Ok(()) => {
let start = std::time::Instant::now(); if let Some(watch_folder) = self.get_watch_folder(context) {
info!(context, "{} started...", prefix); let start = std::time::Instant::now();
self.imap.fetch(context); info!(context, "{} started...", prefix);
let res = self.imap.fetch(context, &watch_folder);
info!(
context,
"{} done in {:.3} ms.",
prefix,
start.elapsed().as_millis(),
);
if self.imap.should_reconnect() { if let Err(err) = res {
info!(context, "{} aborted, starting over...", prefix); warn!(context, "fetch failed: {}, reconnect & retry", err);
self.imap.fetch(context); self.imap.trigger_reconnect();
match self.connect_to_imap(context) {
Ok(()) => {
if let Err(err) = self.imap.fetch(context, &watch_folder) {
error!(context, "fetch failed: {}", err);
}
}
Err(err) => {
error!(context, "connect failed: {}", err);
}
}
}
} }
info!(
context,
"{} done in {:.3} ms.",
prefix,
start.elapsed().as_millis(),
);
} }
Err(err) => { Err(err) => {
warn!( warn!(
@@ -116,25 +127,23 @@ impl JobThread {
self.state.0.lock().unwrap().using_handle = false; self.state.0.lock().unwrap().using_handle = false;
} }
pub fn connect_to_imap(&self, context: &Context) -> Result<()> { fn get_watch_folder(&self, context: &Context) -> Option<String> {
if async_std::task::block_on(async move { self.imap.is_connected().await }) { match context.sql.get_raw_config(context, self.folder_config_name) {
return Ok(()); Some(name) => Some(name),
}
let watch_folder_name = match context.sql.get_raw_config(context, self.folder_config_name) {
Some(name) => name,
None => { None => {
if self.folder_config_name == "configured_inbox_folder" { if self.folder_config_name == "configured_inbox_folder" {
// operating on an old database? // initialized with old version, so has not set configured_inbox_folder
"INBOX".to_string() Some("INBOX".to_string())
} else { } else {
return Err(Error::WatchFolderNotFound( None
self.folder_config_name.to_string(),
));
} }
} }
}; }
}
pub fn connect_to_imap(&self, context: &Context) -> Result<()> {
self.imap.connect_configured(context)?;
dc_connect_to_configured_imap(context, &self.imap)?;
if context if context
.sql .sql
.get_raw_config_int(context, "folders_configured") .get_raw_config_int(context, "folders_configured")
@@ -143,7 +152,6 @@ impl JobThread {
{ {
self.imap.configure_folders(context, 0x1); self.imap.configure_folders(context, 0x1);
} }
self.imap.set_watch_folder(watch_folder_name);
Ok(()) Ok(())
} }
@@ -185,36 +193,36 @@ impl JobThread {
} }
let prefix = format!("{}-IDLE", self.name); let prefix = format!("{}-IDLE", self.name);
let poll_mode = match self.connect_to_imap(context) { let do_fake_idle = match self.connect_to_imap(context) {
Ok(()) => { Ok(()) => {
info!(context, "{} started...", prefix); info!(context, "{} started...", prefix);
let res = self.imap.idle(context); let watch_folder = self.get_watch_folder(context);
let res = self.imap.idle(context, watch_folder);
info!(context, "{} ended...", prefix); info!(context, "{} ended...", prefix);
match res { match res {
Ok(()) => None, Ok(()) => false,
Err(Error::ImapConnectionFailed(err)) Err(Error::ImapMissesIdle) => true, // we have to do fake_idle
| Err(Error::ImapIdleProtocolFailed(err)) => {
self.imap.trigger_reconnect();
warn!(context, "{} failed: {}, reconnecting", prefix, err);
Some(IdlePollMode::Often)
}
Err(Error::ImapInTeardown) => {
warn!(context, "{} aborting as imap is in teardown", prefix);
None
}
Err(err) => { Err(err) => {
warn!(context, "{} failed fundamentally: {}", prefix, err); warn!(context, "{} failed: {} -> reconnecting", prefix, err);
Some(IdlePollMode::Never) // something is borked, let's start afresh on the next occassion
self.imap.disconnect(context);
false
} }
} }
} }
Err(err) => { Err(err) => {
info!(context, "{}-IDLE connection fail: {:?}", self.name, err); info!(context, "{}-IDLE connection fail: {:?}", self.name, err);
Some(IdlePollMode::Often) // if the connection fails, use fake_idle to retry periodically
// fake_idle() will be woken up by interrupt_idle() as
// well so will act on maybe_network events
//
true
} }
}; };
if let Some(poll_mode) = poll_mode { if do_fake_idle {
self.imap.fake_idle(context, poll_mode); let watch_folder = self.get_watch_folder(context);
self.imap.fake_idle(context, watch_folder);
} }
self.state.0.lock().unwrap().using_handle = false; self.state.0.lock().unwrap().using_handle = false;