imap: use anyhow for error handling

There is already free-form Error::Other error type, and SqlError had
incorrect error description (a copy from InTeardown).
This commit is contained in:
Alexander Krotov
2020-08-29 19:44:05 +03:00
committed by link2xt
parent 8f290530fd
commit 0b743c6bc3
2 changed files with 30 additions and 95 deletions

View File

@@ -5,31 +5,11 @@ use async_imap::types::UnsolicitedResponse;
use async_std::prelude::*; use async_std::prelude::*;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use crate::error::{bail, format_err, Result};
use crate::{context::Context, scheduler::InterruptInfo}; use crate::{context::Context, scheduler::InterruptInfo};
use super::select_folder;
use super::session::Session; use super::session::Session;
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("IMAP IDLE protocol failed to init/complete: {0}")]
IdleProtocolFailed(#[from] async_imap::error::Error),
#[error("IMAP IDLE protocol timed out: {0}")]
IdleTimeout(#[from] async_std::future::TimeoutError),
#[error("IMAP server does not have IDLE capability")]
IdleAbilityMissing,
#[error("IMAP select folder error: {0}")]
SelectFolderError(#[from] select_folder::Error),
#[error("Setup handle error: {0}")]
SetupHandleError(#[from] super::Error),
}
impl Imap { impl Imap {
pub fn can_idle(&self) -> bool { pub fn can_idle(&self) -> bool {
self.config.can_idle self.config.can_idle
@@ -43,7 +23,7 @@ impl Imap {
use futures::future::FutureExt; use futures::future::FutureExt;
if !self.can_idle() { if !self.can_idle() {
return Err(Error::IdleAbilityMissing); bail!("IMAP server does not have IDLE capability");
} }
self.setup_handle_if_needed(context).await?; self.setup_handle_if_needed(context).await?;
@@ -72,7 +52,7 @@ impl Imap {
let mut handle = session.idle(); let mut handle = session.idle();
if let Err(err) = handle.init().await { if let Err(err) = handle.init().await {
return Err(Error::IdleProtocolFailed(err)); bail!("IMAP IDLE protocol failed to init/complete: {}", err);
} }
let (idle_wait, interrupt) = handle.wait_with_timeout(timeout); let (idle_wait, interrupt) = handle.wait_with_timeout(timeout);
@@ -115,7 +95,7 @@ impl Imap {
.done() .done()
.timeout(Duration::from_secs(15)) .timeout(Duration::from_secs(15))
.await .await
.map_err(Error::IdleTimeout)??; .map_err(|err| format_err!("IMAP IDLE protocol timed out: {}", err))??;
self.session = Some(Session { inner: session }); self.session = Some(Session { inner: session });
} else { } else {
warn!(context, "Attempted to idle without a session"); warn!(context, "Attempted to idle without a session");

View File

@@ -19,6 +19,7 @@ use crate::context::Context;
use crate::dc_receive_imf::{ use crate::dc_receive_imf::{
dc_receive_imf, from_field_to_contact_id, is_msgrmsg_rfc724_mid_in_list, dc_receive_imf, from_field_to_contact_id, is_msgrmsg_rfc724_mid_in_list,
}; };
use crate::error::{bail, format_err, Result};
use crate::events::EventType; use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::job::{self, Action}; use crate::job::{self, Action};
@@ -42,50 +43,6 @@ use client::Client;
use message::Message; use message::Message;
use session::Session; use session::Session;
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("IMAP Connect without configured params")]
ConnectWithoutConfigure,
#[error("IMAP Connection Failed params: {0}")]
ConnectionFailed(String),
#[error("IMAP No Connection established")]
NoConnection,
#[error("IMAP Could not get OAUTH token")]
OauthError,
#[error("IMAP Could not login as {0}")]
LoginFailed(String),
#[error("IMAP Could not fetch")]
FetchFailed(#[from] async_imap::error::Error),
#[error("IMAP operation attempted while it is torn down")]
InTeardown,
#[error("IMAP operation attempted while it is torn down")]
SqlError(#[from] crate::sql::Error),
#[error("IMAP got error from elsewhere")]
WrappedError(#[from] crate::error::Error),
#[error("IMAP select folder error")]
SelectFolderError(#[from] select_folder::Error),
#[error("Mail parse error")]
MailParseError(#[from] mailparse::MailParseError),
#[error("No mailbox selected, folder: {0}")]
NoMailbox(String),
#[error("IMAP other error: {0}")]
Other(String),
}
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Display, Clone, Copy, PartialEq, Eq)]
pub enum ImapActionResult { pub enum ImapActionResult {
Failed, Failed,
@@ -207,7 +164,7 @@ impl Imap {
async fn setup_handle_if_needed(&mut self, context: &Context) -> Result<()> { async fn setup_handle_if_needed(&mut self, context: &Context) -> Result<()> {
if self.config.lp.server.is_empty() { if self.config.lp.server.is_empty() {
return Err(Error::InTeardown); bail!("IMAP operation attempted while it is torn down");
} }
if self.should_reconnect() { if self.should_reconnect() {
@@ -262,7 +219,7 @@ impl Imap {
}; };
client.authenticate("XOAUTH2", auth).await client.authenticate("XOAUTH2", auth).await
} else { } else {
return Err(Error::OauthError); bail!("IMAP Could not get OAUTH token");
} }
} else { } else {
client.login(imap_user, imap_pw).await client.login(imap_user, imap_pw).await
@@ -283,7 +240,7 @@ impl Imap {
}; };
// IMAP connection failures are reported to users // IMAP connection failures are reported to users
emit_event!(context, EventType::ErrorNetwork(message)); emit_event!(context, EventType::ErrorNetwork(message));
return Err(Error::ConnectionFailed(err.to_string())); bail!("IMAP connection failed: {}", err);
} }
}; };
@@ -329,7 +286,7 @@ impl Imap {
} }
self.trigger_reconnect(); self.trigger_reconnect();
Err(Error::LoginFailed(format!("cannot login as {}", imap_user))) Err(format_err!("IMAP Could not login as {}", imap_user))
} }
} }
} }
@@ -367,7 +324,7 @@ impl Imap {
return Ok(()); return Ok(());
} }
if !context.is_configured().await { if !context.is_configured().await {
return Err(Error::ConnectWithoutConfigure); bail!("IMAP Connect without configured params");
} }
let param = LoginParam::from_database(context, "configured_").await; let param = LoginParam::from_database(context, "configured_").await;
@@ -384,7 +341,7 @@ impl Imap {
{ {
self.ensure_configured_folders(context, true).await self.ensure_configured_folders(context, true).await
} else { } else {
Err(Error::ConnectionFailed(format!("{}", param))) bail!("IMAP Connection Failed params: {}", param);
} }
} }
@@ -479,7 +436,7 @@ impl Imap {
pub async fn fetch(&mut self, context: &Context, watch_folder: &str) -> Result<()> { pub async fn fetch(&mut self, context: &Context, watch_folder: &str) -> Result<()> {
if !context.sql.is_open().await { if !context.sql.is_open().await {
// probably shutdown // probably shutdown
return Err(Error::InTeardown); bail!("IMAP operation attempted while it is torn down");
} }
self.setup_handle_if_needed(context).await?; self.setup_handle_if_needed(context).await?;
@@ -534,13 +491,13 @@ impl Imap {
let session = if let Some(ref mut session) = &mut self.session { let session = if let Some(ref mut session) = &mut self.session {
session session
} else { } else {
return Err(Error::NoConnection); bail!("IMAP No Connection established");
}; };
match session.uid_fetch("1:*", RFC724MID_UID).await { match session.uid_fetch("1:*", RFC724MID_UID).await {
Ok(mut list) => { Ok(mut list) => {
while let Some(fetch) = list.next().await { while let Some(fetch) = list.next().await {
let msg = fetch.map_err(|err| Error::Other(err.to_string()))?; let msg = fetch?;
// Get Message-ID // Get Message-ID
let message_id = get_fetch_headers(&msg) let message_id = get_fetch_headers(&msg)
@@ -553,10 +510,7 @@ impl Imap {
} }
} }
Err(err) => { Err(err) => {
return Err(Error::Other(format!( bail!("Can't resync folder {}: {}", folder, err);
"Can't resync folder {}: {}",
folder, err
)))
} }
} }
@@ -608,13 +562,12 @@ impl Imap {
let mailbox = config let mailbox = config
.selected_mailbox .selected_mailbox
.as_ref() .as_ref()
.ok_or_else(|| Error::NoMailbox(folder.to_string()))?; .ok_or_else(|| format_err!("No mailbox selected, folder: {}", folder))?;
let new_uid_validity = match mailbox.uid_validity { let new_uid_validity = match mailbox.uid_validity {
Some(v) => v, Some(v) => v,
None => { None => {
let s = format!("No UIDVALIDITY for folder {:?}", folder); bail!("No UIDVALIDITY for folder {:?}", folder);
return Err(Error::Other(s));
} }
}; };
@@ -661,22 +614,24 @@ impl Imap {
if let Some(new_last_seen_uid) = new_last_seen_uid { if let Some(new_last_seen_uid) = new_last_seen_uid {
new_last_seen_uid new_last_seen_uid
} else { } else {
return Err(Error::Other("failed to fetch".into())); bail!("failed to fetch");
} }
} }
Err(err) => { Err(err) => {
return Err(Error::FetchFailed(err)); bail!("IMAP Could not fetch: {}", err);
} }
} }
} else { } else {
return Err(Error::NoConnection); bail!("IMAP No Connection established");
} }
} }
}; };
self.set_config_last_seen_uid(context, &folder, new_uid_validity, new_last_seen_uid) self.set_config_last_seen_uid(context, &folder, new_uid_validity, new_last_seen_uid)
.await; .await;
job::schedule_resync(context).await; if uid_validity != 0 || last_seen_uid != 0 {
job::schedule_resync(context).await;
}
info!( info!(
context, context,
"uid/validity change: new {}/{} current {}/{}", "uid/validity change: new {}/{} current {}/{}",
@@ -773,7 +728,7 @@ impl Imap {
uid: u32, uid: u32,
) -> Result<BTreeMap<u32, async_imap::types::Fetch>> { ) -> Result<BTreeMap<u32, async_imap::types::Fetch>> {
if self.session.is_none() { if self.session.is_none() {
return Err(Error::NoConnection); bail!("IMAP No Connection established");
} }
let session = self.session.as_mut().unwrap(); let session = self.session.as_mut().unwrap();
@@ -784,11 +739,11 @@ impl Imap {
let mut list = session let mut list = session
.uid_fetch(set, PREFETCH_FLAGS) .uid_fetch(set, PREFETCH_FLAGS)
.await .await
.map_err(Error::FetchFailed)?; .map_err(|err| format_err!("IMAP Could not fetch: {}", err))?;
let mut msgs = BTreeMap::new(); let mut msgs = BTreeMap::new();
while let Some(fetch) = list.next().await { while let Some(fetch) = list.next().await {
let msg = fetch.map_err(|err| Error::Other(err.to_string()))?; let msg = fetch?;
if let Some(msg_uid) = msg.uid { if let Some(msg_uid) = msg.uid {
msgs.insert(msg_uid, msg); msgs.insert(msg_uid, msg);
} }
@@ -1250,14 +1205,14 @@ impl Imap {
pub async fn configure_folders(&mut self, context: &Context, create_mvbox: bool) -> Result<()> { pub async fn configure_folders(&mut self, context: &Context, create_mvbox: bool) -> Result<()> {
if !self.is_connected() { if !self.is_connected() {
return Err(Error::NoConnection); bail!("IMAP No Connection established");
} }
if let Some(ref mut session) = &mut self.session { if let Some(ref mut session) = &mut self.session {
let mut folders = match session.list(Some(""), Some("*")).await { let mut folders = match session.list(Some(""), Some("*")).await {
Ok(f) => f, Ok(f) => f,
Err(err) => { Err(err) => {
return Err(Error::Other(format!("list_folders failed {:?}", err))); bail!("list_folders failed: {}", err);
} }
}; };
@@ -1268,7 +1223,7 @@ impl Imap {
let mut fallback_folder = get_fallback_folder(&delimiter); let mut fallback_folder = get_fallback_folder(&delimiter);
while let Some(folder) = folders.next().await { while let Some(folder) = folders.next().await {
let folder = folder.map_err(|err| Error::Other(err.to_string()))?; let folder = folder?;
info!(context, "Scanning folder: {:?}", folder); info!(context, "Scanning folder: {:?}", folder);
// Update the delimiter iff there is a different one, but only once. // Update the delimiter iff there is a different one, but only once.
@@ -1553,7 +1508,7 @@ fn prefetch_get_message_id(headers: &[mailparse::MailHeader]) -> Result<String>
if let Some(message_id) = headers.get_header_value(HeaderDef::MessageId) { if let Some(message_id) = headers.get_header_value(HeaderDef::MessageId) {
Ok(crate::mimeparser::parse_message_id(&message_id)?) Ok(crate::mimeparser::parse_message_id(&message_id)?)
} else { } else {
Err(Error::Other("prefetch: No message ID found".to_string())) bail!("prefetch: No message ID found");
} }
} }