From 2a4e9ae4bc6e8c540688ae0d3da32ea859f68b8a Mon Sep 17 00:00:00 2001 From: Florian Stecker Date: Thu, 13 Jul 2023 22:04:27 +0200 Subject: [PATCH] improve handling of some rare errors --- src/lib.rs | 78 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c353638..332299f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ struct Status { const CONNECTION_LOST_ERRORS: &[ErrorKind] = &[ ErrorKind::Interrupted, + ErrorKind::UnexpectedEOF, // server closes TLS connection without close_notify ErrorKind::WouldBlock, // when a read times out ]; @@ -157,7 +158,8 @@ pub fn run() -> AResult<()> { connection_status.lock().unwrap().connected = false; 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)); continue; @@ -170,7 +172,9 @@ pub fn run() -> AResult<()> { if cli.verbose > 0 { 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)); @@ -235,11 +239,20 @@ pub fn connect_and_idle(cli: &Cli, connected_callback: F, mail } else if tls_client.wants_read() { 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; } - 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] .split(|&x|x == b'\r' || x == b'\n') @@ -257,35 +270,44 @@ pub fn connect_and_idle(cli: &Cli, connected_callback: F, mail } match state { - ImapState::Unauthenticated => if response.starts_with(b"* OK") { - let request = format!("A001 login {} {}\r\n", cli.username, cli.password); - 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")?; - state = ImapState::Inbox; - } else if response.starts_with(b"A001") { - bail!("The server rejected authentication"); - }, - ImapState::Inbox => if response.starts_with(b"A002 OK") { - tls_client.writer().write(b"A003 idle\r\n")?; - state = ImapState::Idling; - connected_callback(); - // notify timer thread that we're live - } else if response.starts_with(b"A002") { - 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(); - } + ImapState::Unauthenticated => + if response.starts_with(b"* OK") { + let request = format!("A001 login {} {}\r\n", cli.username, cli.password); + 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")?; + state = ImapState::Authenticated; + } else if response.starts_with(b"A002 OK") { + tls_client.writer().write(b"A003 fetch 1:* all\r\n")?; + state = ImapState::Inbox; + } else if response.starts_with(b"A001") || response.starts_with(b"A002") { + bail!("The server rejected authentication"); + }, + ImapState::Inbox => + if response.starts_with(b"A003 OK") { + tls_client.writer().write(b"A004 idle\r\n")?; + state = ImapState::Idling; + connected_callback(); // notify timer thread that we're live + } else if response.starts_with(b"A002") { + 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 { // 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 + 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()); } }