-
-
Notifications
You must be signed in to change notification settings - Fork 21
Open
Description
Hi, I used your awesome library to implement a DMARC report viewer with an embedded IMAP client.
One user reported an error while trying to selecting the inbox. I was able to reproduce the issue by running some minimal example code with a test instance of the mail server software called surgemail.
I am not very familiar with the IMAP protocol, but it seems that the server either returns something invalid or there is a bug with the response parsing?
The exchange looks like this:
> * OK IMAP (C) win11vm (Version 8.0e-1)
< A0001
<
< LOGIN "tester" "password"
<
> A0001 OK login completed
< A0002
<
< SELECT "INBOX"
<
> * 1 EXISTS
> * 0 RECENT
> * OK [UNSEEN 1] first unseen message
> * OK [UIDVALIDITY 1754171061] Uid epoch
> * OK [UIDNEXT 2] Predicted next uid
> * FLAGS (\Answered \Flagged \Deleted \Draft \Seen $Forwarded )
> * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Draft \Seen $Forwarded )] Limited
> A0002 OK [READ-WRITE] SELECT completed
thread 'main' panicked at src\main.rs:23:44:
called `Result::unwrap()` on an `Err` value: Io(Custom { kind: Other, error: "Error(Error { input: [42, 32, 70, 76, 65, 71, 83, 32, 40, 92, 65, 110, 115, 119, 101, 114, 101, 100, 32, 92, 70, 108, 97, 103, 103, 101, 100, 32, 92, 68, 101, 108, 101, 116, 101, 100, 32, 92, 68, 114, 97, 102, 116, 32, 92, 83, 101, 101, 110, 32, 36, 70, 111, 114, 119, 97, 114, 100, 101, 100, 32, 41, 13, 10, 42, 32, 79, 75, 32, 91, 80, 69, 82, 77, 65, 78, 69, 78, 84, 70, 76, 65, 71, 83, 32, 40, 92, 65, 110, 115, 119, 101, 114, 101, 100, 32, 92, 70, 108, 97, 103, 103, 101, 100, 32, 92, 68, 101, 108, 101, 116, 101, 100, 32, 92, 68, 114, 97, 102, 116, 32, 92, 83, 101, 101, 110, 32, 36, 70, 111, 114, 119, 97, 114, 100, 101, 100, 32, 41, 93, 32, 76, 105, 109, 105, 116, 101, 100, 13, 10, 65, 48, 48, 48, 50, 32, 79, 75, 32, 91, 82, 69, 65, 68, 45, 87, 82, 73, 84, 69, 93, 32, 83, 69, 76, 69, 67, 84, 32, 99, 111, 109, 112, 108, 101, 116, 101, 100, 13, 10], code: TakeWhile1 }) during parsing of \"* FLAGS (\\\\Answered \\\\Flagged \\\\Deleted \\\\Draft \\\\Seen $Forwarded )\\r\\n* OK [PERMANENTFLAGS (\\\\Answered \\\\Flagged \\\\Deleted \\\\Draft \\\\Seen $Forwarded )] Limited\\r\\nA0002 OK [READ-WRITE] SELECT completed\\r\\n\"" })
This is the code I used to reproduce the problem:
main.rs
use async_imap::Client;
use async_std::net::TcpStream;
use async_std::pin::Pin;
use async_std::task;
use async_std::task::{Context, Poll};
use futures::io::{AsyncRead, AsyncWrite};
use std::fmt;
fn main() {
task::block_on(async {
let host = "127.0.0.1";
let port = 143;
let user = "tester";
let pw = "password";
let mailbox = "INBOX";
let stream = TcpStream::connect((host, port)).await.unwrap();
let proxied = LogProxy::new(stream);
let mut client = Client::new(proxied);
client.read_response().await.unwrap().unwrap();
let mut imap_session = client.login(user, pw).await.unwrap();
imap_session.select(mailbox).await.unwrap();
imap_session.logout().await.unwrap();
});
}
#[derive(Debug)]
struct LogProxy<T: AsyncRead + AsyncWrite + Unpin + fmt::Debug> {
transport: T,
}
impl<T: AsyncRead + AsyncWrite + Unpin + fmt::Debug> AsyncRead for LogProxy<T> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
let ret = AsyncRead::poll_read(Pin::new(&mut self.transport), cx, buf);
if let Poll::Ready(Ok(num)) = ret {
println!("> {}", String::from_utf8_lossy(&buf[0..num]));
}
ret
}
}
impl<T: AsyncRead + AsyncWrite + Unpin + fmt::Debug> AsyncWrite for LogProxy<T> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let ret = AsyncWrite::poll_write(Pin::new(&mut self.transport), cx, buf);
if let Poll::Ready(Ok(num)) = ret {
println!("< {}", String::from_utf8_lossy(&buf[0..num]));
}
ret
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<std::io::Result<()>> {
AsyncWrite::poll_flush(Pin::new(&mut self.transport), cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<std::io::Result<()>> {
AsyncWrite::poll_close(Pin::new(&mut self.transport), cx)
}
}
impl<T: AsyncRead + AsyncWrite + Unpin + fmt::Debug> LogProxy<T> {
pub fn new(transport: T) -> Self {
Self { transport }
}
}
Cargo.toml
[package]
name = "repro"
version = "0.1.0"
edition = "2024"
[dependencies]
async-imap = { version = "0.11", features = ["runtime-async-std"] }
async-std = { version = "1", features = ["std", "attributes"] }
futures = "0.3"
Metadata
Metadata
Assignees
Labels
No labels