Display watch thread errors to user

Show a proper notification with the error message to the user instead of
just logging it on debug-tracing.
jmap
Manos Pitsidianakis 2019-11-23 17:50:22 +02:00
parent b8e4a35963
commit eecec551c1
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 91 additions and 15 deletions

View File

@ -240,11 +240,45 @@ impl ImapStream {
.as_bytes(),
)?;
let mut res = String::with_capacity(8 * 1024);
ret.read_lines(&mut res, &String::new())?;
let capabilities = protocol_parser::capabilities(res.as_bytes()).to_full_result()?;
let capabilities = FnvHashSet::from_iter(capabilities.into_iter().map(|s| s.to_vec()));
let tag_start = format!("M{} ", (ret.cmd_id - 1));
loop {
ret.read_lines(&mut res, &String::new())?;
if res.starts_with("* OK") {
if let Some(pos) = res.as_bytes().find(b"\r\n") {
let pos = pos + "\r\n".len();
res.replace_range(..pos, "");
}
}
Ok((capabilities, ret))
if res.starts_with(tag_start.as_str()) {
if !res[tag_start.len()..].trim().starts_with("OK ") {
return Err(MeliError::new(format!(
"Could not connect. Server replied with '{}'",
res[tag_start.len()..].trim()
)));
}
break;
}
}
let capabilities: std::result::Result<Vec<&[u8]>, _> =
protocol_parser::capabilities(res.as_bytes()).to_full_result();
if capabilities.is_err() {
/* sending CAPABILITY after LOGIN automatically is an RFC recommendation, so check
* for lazy servers */
drop(capabilities);
ret.send_command(b"CAPABILITY")?;
ret.read_response(&mut res).unwrap();
let capabilities = protocol_parser::capabilities(res.as_bytes()).to_full_result()?;
let capabilities = FnvHashSet::from_iter(capabilities.into_iter().map(|s| s.to_vec()));
Ok((capabilities, ret))
} else {
let capabilities = capabilities?;
let capabilities = FnvHashSet::from_iter(capabilities.into_iter().map(|s| s.to_vec()));
Ok((capabilities, ret))
}
}
}
@ -369,6 +403,7 @@ pub struct ImapBlockingConnection {
result: Vec<u8>,
prev_res_length: usize,
pub conn: ImapConnection,
err: Option<String>,
}
impl From<ImapConnection> for ImapBlockingConnection {
@ -398,6 +433,7 @@ impl From<ImapConnection> for ImapBlockingConnection {
conn,
prev_res_length: 0,
result: Vec::with_capacity(8 * 1024),
err: None,
}
}
}
@ -406,6 +442,10 @@ impl ImapBlockingConnection {
pub fn into_conn(self) -> ImapConnection {
self.conn
}
pub fn err(&self) -> Option<&str> {
self.err.as_ref().map(String::as_str)
}
}
impl Iterator for ImapBlockingConnection {
@ -418,6 +458,7 @@ impl Iterator for ImapBlockingConnection {
ref mut result,
ref mut conn,
ref mut buf,
ref mut err,
} = self;
loop {
if conn.stream.is_err() {
@ -436,7 +477,8 @@ impl Iterator for ImapBlockingConnection {
}
Err(e) => {
debug!(&conn.stream);
debug!(e);
debug!(&e);
*err = Some(e.to_string());
return None;
}
}

View File

@ -108,13 +108,28 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
std::thread::sleep(std::time::Duration::from_millis(100));
}
let thread_id: std::thread::ThreadId = std::thread::current().id();
let folder: ImapFolder = folders
let folder: ImapFolder = match folders
.read()
.unwrap()
.values()
.find(|f| f.parent.is_none() && f.path().eq_ignore_ascii_case("INBOX"))
.map(std::clone::Clone::clone)
.unwrap();
{
Some(folder) => folder,
None => {
let err = MeliError::new("INBOX mailbox not found in local folder index. meli may have not parsed the IMAP folders correctly");
debug!("failure: {}", err.to_string());
work_context
.set_status
.send((thread_id, err.to_string()))
.unwrap();
sender.send(RefreshEvent {
hash: 0,
kind: RefreshEventKind::Failure(err.clone()),
});
return Err(err);
}
};
let folder_hash = folder.hash();
let mut response = String::with_capacity(8 * 1024);
exit_on_error!(
@ -428,16 +443,21 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
.send((thread_id, "IDLEing".to_string()))
.unwrap();
}
debug!("IDLE exited");
debug!("IDLE connection dropped");
let err: &str = iter.err().unwrap_or("Unknown reason.");
work_context
.set_status
.send((thread_id, "IDLE exited".to_string()))
.send((thread_id, "IDLE connection dropped".to_string()))
.unwrap();
work_context.finished.send(thread_id).unwrap();
sender.send(RefreshEvent {
hash: folder_hash,
kind: RefreshEventKind::Failure(MeliError::new("IDLE exited".to_string())),
kind: RefreshEventKind::Failure(MeliError::new(format!(
"IDLE connection dropped: {}",
&err
))),
});
Err(MeliError::new("IDLE exited".to_string()))
Err(MeliError::new(format!("IDLE connection dropped: {}", err)))
}
fn examine_updates(

View File

@ -638,6 +638,14 @@ impl Account {
}
RefreshEventKind::Failure(e) => {
debug!("RefreshEvent Failure: {}", e.to_string());
context
.1
.send(ThreadEvent::UIEvent(UIEvent::Notification(
Some(format!("{} watcher exited with error", &self.name)),
e.to_string(),
Some(crate::types::NotificationType::ERROR),
)))
.expect("Could not send event on main channel");
self.watch(context);
}
}

View File

@ -317,10 +317,16 @@ impl State {
self.rcv_event(notification);
}
} else {
debug!(
"BUG: mailbox with hash {} not found in mailbox_hashes.",
hash
);
if let melib::backends::RefreshEventKind::Failure(e) = event.kind() {
self.context
.sender
.send(ThreadEvent::UIEvent(UIEvent::Notification(
Some("watcher thread exited with error".to_string()),
e.to_string(),
Some(crate::types::NotificationType::ERROR),
)))
.expect("Could not send event on main channel");
}
}
}