greenmail-mail-test / greenmail

Official master for the Greenmail project
http://greenmail-mail-test.github.io/greenmail/
Apache License 2.0
611 stars 181 forks source link

Rarely occurring IllegalStateException: Can not handle IMAP connection #343

Open ceisele-r opened 3 years ago

ceisele-r commented 3 years ago

Rarely I am facing an exception that occurs when using greenmail:

Exception in thread "imap:mydomain.com:3143<-/127.0.0.1:44240" java.lang.IllegalStateException: Can not handle IMAP connection
    at com.icegreen.greenmail.imap.ImapHandler.run(ImapHandler.java:75)
    at com.icegreen.greenmail.server.AbstractServer$1.run(AbstractServer.java:156)
    at java.lang.Thread.run(Thread.java:748)
 Caused by: java.lang.IllegalStateException: Need to do this properly...
    at com.icegreen.greenmail.imap.ImapSessionFolder.getExpunged(ImapSessionFolder.java:93)
    at com.icegreen.greenmail.imap.ImapSessionImpl.unsolicitedResponses(ImapSessionImpl.java:81)
    at com.icegreen.greenmail.imap.commands.SearchCommand.doProcess(SearchCommand.java:89)
    at com.icegreen.greenmail.imap.commands.UidCommand.doProcess(UidCommand.java:41)
    at com.icegreen.greenmail.imap.commands.CommandTemplate.process(CommandTemplate.java:57)
    at com.icegreen.greenmail.imap.ImapRequestHandler.doProcessRequest(ImapRequestHandler.java:96)
    at com.icegreen.greenmail.imap.ImapRequestHandler.handleRequest(ImapRequestHandler.java:51)
    at com.icegreen.greenmail.imap.ImapHandler.run(ImapHandler.java:71)
    ... 2 more

According to the message in the Exception and the line in the code ( https://github.com/greenmail-mail-test/greenmail/blob/master/greenmail-core/src/main/java/com/icegreen/greenmail/imap/ImapSessionFolder.java#L93 ), this seems to be a greenmail-internal issue, right?

Is there anything I can do to prevent this to happen or do you have plans to fix the TODO in the code causing this?

HerrMuellerluedenscheid commented 5 months ago

Hi,

This error happens whenever I try reading mails with this (drafty) rust implementation:

use imap;
use native_tls;

use dotenvy::dotenv;
use std::env;

use crate::mail;

struct ImapClient {
    session: imap::Session<native_tls::TlsStream<std::net::TcpStream>>,
}

impl ImapClient {
    fn new() -> ImapClient {
        let mut session = mail::read::mailer();
        ImapClient { session }
    }

    fn logout(mut self) {
        self.session.logout().unwrap();
    }
}

pub fn mailer() -> imap::Session<native_tls::TlsStream<std::net::TcpStream>> {
    dotenvy::dotenv().ok();
    dotenv().expect(".env file not found");
    let user = env::var("USER").expect("USER not defined");
    let password =
        env::var("PASSWORD").expect("PASSWORD not defined");
    let imap_host = env::var("IMAP").expect("IMAP not defined");
    println!(
        "connecting to imap server: {imap_host}, with username: {}",
        user.clone()
    );
    let tls = native_tls::TlsConnector::builder().build().unwrap();

    // we pass in the domain twice to check that the server's TLS
    // certificate is valid for the domain we're connecting to.
    let client = imap::connect((imap_host.clone(), 3143), imap_host, &tls).unwrap();

    // the client we have here is unauthenticated.
    // to do anything useful with the e-mails, we need to log in
    let mut imap_session = client.login(user, password).map_err(|e| e.0).unwrap();

    // we want to fetch the first email in the INBOX mailbox
    imap_session.select("INBOX").unwrap();
    imap_session
}

// pub fn read_mail(mut session: imap::Session<native_tls::TlsStream<std::net::TcpStream>>) {
fn read_mail(client: ImapClient) {
    let mut session = client.session;
    let messages = session.fetch("1", "RFC822").unwrap();
    let message = messages.iter().next().unwrap();

    // extract the message's body
    let body = message.body().expect("message did not have a body!");
    let body = std::str::from_utf8(body)
        .expect("message was not valid utf-8")
        .to_string();

    println!("body: {}", body);
    // session.logout().unwrap();
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_read_mail() {
        let mut client = ImapClient::new();
        read_mail(client);
    }
}

Equally when sendin mails

marcelmay commented 5 months ago

Thanks alot, @HerrMuellerluedenscheid !

Can you maybe provide a Cargo manifest, too?

HerrMuellerluedenscheid commented 5 months ago

I setup a little project that includes a docker-compose with greenmail and two tests for reading and sending mail. Checkout the readme https://github.com/HerrMuellerluedenscheid/greenmail-rust

marcelmay commented 5 months ago

Thanks alot, @HerrMuellerluedenscheid !

Got the setup running by changing .env ports to secure imap/smtp. But I'll have to switch back and change the client to non-TLS to avoid untrusted cert issue for reproducing.

marcelmay commented 5 months ago

@HerrMuellerluedenscheid ,

there was an already fixed bug ( #633 in just released 2.1.0-alpha-4)

Running w/o TLS (to avoid untrusted cert issue) and reading from to-user works: Fork: https://github.com/marcelmay/greenmail-rust

Sending email:

 cargo test test_send_mail -- --nocapture                                     
    Finished test [unoptimized + debuginfo] target(s) in 0.08s
warning: the following packages contain code that will be rejected by a future version of Rust: imap-proto v0.10.2
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
     Running unittests src/main.rs (target/debug/deps/greenmail_reproduce-7bd3c65c432287df)

running 1 test
connecting to smtp server: localhost:3025, with username: greenmail
Email sent successfully!
test send::tests::test_send_mail ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.32s

Reading sent mail (specifying to-user):

EMAIL_USER=to@localhost cargo test test_read_mail -- --nocapture
    Finished test [unoptimized + debuginfo] target(s) in 0.08s
warning: the following packages contain code that will be rejected by a future version of Rust: imap-proto v0.10.2
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
     Running unittests src/main.rs (target/debug/deps/greenmail_reproduce-7bd3c65c432287df)

running 1 test
connecting to imap server: localhost:3993, with username: to@localhost
body: Return-Path: <from@localhost>
Received: from 172.19.0.1 (HELO hex); Sun Jan 28 19:59:48 GMT 2024
From: from@localhost
Reply-To: from@localhost
To: to@localhost
Subject: foo subject
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Date: Sun, 28 Jan 2024 19:59:47 +0000

bar body
test read::tests::test_read_mail ... ok
HerrMuellerluedenscheid commented 5 months ago

Thanks for the analysis and help @marcelmay !