improve handling of some rare errors

This commit is contained in:
Florian Stecker 2023-07-13 22:04:27 +02:00
parent 002393b18e
commit 2a4e9ae4bc
1 changed files with 50 additions and 28 deletions

View File

@ -56,6 +56,7 @@ struct Status {
const CONNECTION_LOST_ERRORS: &[ErrorKind] = &[ const CONNECTION_LOST_ERRORS: &[ErrorKind] = &[
ErrorKind::Interrupted, ErrorKind::Interrupted,
ErrorKind::UnexpectedEOF, // server closes TLS connection without close_notify
ErrorKind::WouldBlock, // when a read times out ErrorKind::WouldBlock, // when a read times out
]; ];
@ -157,7 +158,8 @@ pub fn run() -> AResult<()> {
connection_status.lock().unwrap().connected = false; connection_status.lock().unwrap().connected = false;
time_to_reconnect = 1; time_to_reconnect = 1;
println!("Connection lost, reconnecting in {time_to_reconnect} seconds"); let error_kind = io_err.kind();
println!("Connection lost ({error_kind}), reconnecting in {time_to_reconnect} seconds");
thread::sleep(Duration::from_secs(time_to_reconnect)); thread::sleep(Duration::from_secs(time_to_reconnect));
continue; continue;
@ -170,7 +172,9 @@ pub fn run() -> AResult<()> {
if cli.verbose > 0 { if cli.verbose > 0 {
println!("Error: {:?}", err); println!("Error: {:?}", err);
} }
println!("Cannot connect currently, retrying in {time_to_reconnect} seconds");
let error_kind = io_err.kind();
println!("Cannot connect currently ({error_kind}), retrying in {time_to_reconnect} seconds");
thread::sleep(Duration::from_secs(time_to_reconnect)); thread::sleep(Duration::from_secs(time_to_reconnect));
@ -235,11 +239,20 @@ pub fn connect_and_idle<F: Fn(), G: Fn()>(cli: &Cli, connected_callback: F, mail
} else if tls_client.wants_read() { } else if tls_client.wants_read() {
let _i = tls_client.read_tls(&mut socket)?; let _i = tls_client.read_tls(&mut socket)?;
if tls_client.process_new_packets()?.plaintext_bytes_to_read() == 0 { let bytes_to_read = tls_client.process_new_packets()?.plaintext_bytes_to_read();
if bytes_to_read == 0 {
continue; continue;
} }
let len = tls_client.reader().read(&mut buffer)?; println!("Bytes to read: {bytes_to_read}");
let mut len = tls_client.reader().read(&mut buffer)?;
while len == buffer.len() {
println!("Buffer full, read again");
len = tls_client.reader().read(&mut buffer)?;
}
let responses = buffer[0..len] let responses = buffer[0..len]
.split(|&x|x == b'\r' || x == b'\n') .split(|&x|x == b'\r' || x == b'\n')
@ -257,35 +270,44 @@ pub fn connect_and_idle<F: Fn(), G: Fn()>(cli: &Cli, connected_callback: F, mail
} }
match state { match state {
ImapState::Unauthenticated => if response.starts_with(b"* OK") { ImapState::Unauthenticated =>
let request = format!("A001 login {} {}\r\n", cli.username, cli.password); if response.starts_with(b"* OK") {
tls_client.writer().write(request.as_bytes())?; let request = format!("A001 login {} {}\r\n", cli.username, cli.password);
state = ImapState::Authenticated; tls_client.writer().write(request.as_bytes())?;
}, state = ImapState::Authenticated;
ImapState::Authenticated => if response.starts_with(b"A001 OK") { },
tls_client.writer().write(b"A002 select inbox\r\n")?; ImapState::Authenticated =>
state = ImapState::Inbox; if response.starts_with(b"A001 OK") {
} else if response.starts_with(b"A001") { tls_client.writer().write(b"A002 select inbox\r\n")?;
bail!("The server rejected authentication"); state = ImapState::Authenticated;
}, } else if response.starts_with(b"A002 OK") {
ImapState::Inbox => if response.starts_with(b"A002 OK") { tls_client.writer().write(b"A003 fetch 1:* all\r\n")?;
tls_client.writer().write(b"A003 idle\r\n")?; state = ImapState::Inbox;
state = ImapState::Idling; } else if response.starts_with(b"A001") || response.starts_with(b"A002") {
connected_callback(); bail!("The server rejected authentication");
// notify timer thread that we're live },
} else if response.starts_with(b"A002") { ImapState::Inbox =>
bail!("Selecting inbox failed"); if response.starts_with(b"A003 OK") {
}, tls_client.writer().write(b"A004 idle\r\n")?;
ImapState::Idling => if response.starts_with(b"+ idling") { state = ImapState::Idling;
println!("Connected and idling ..."); connected_callback(); // notify timer thread that we're live
} else if response.starts_with(b"*") && response.ends_with(b"EXISTS") { } else if response.starts_with(b"A002") {
mail_callback(); bail!("Selecting inbox failed");
} },
ImapState::Idling =>
if response.starts_with(b"+ idling") {
println!("Connected and idling ...");
} else if response.starts_with(b"*") && response.ends_with(b"EXISTS") {
mail_callback();
}
} }
} }
} else { } else {
// if wants_read() and wants_write() are both false, this usually means the connection was closed // if wants_read() and wants_write() are both false, this usually means the connection was closed
// so just return "Interrupted" and reconnect after a few seconds // so just return "Interrupted" and reconnect after a few seconds
if cli.verbose > 0 {
println!("wants_read() and wants_write() are both false");
}
return Err(io::Error::new(ErrorKind::Interrupted, "Connection was closed by server").into()); return Err(io::Error::new(ErrorKind::Interrupted, "Connection was closed by server").into());
} }
} }