piątek, 29 października 2010

SMTP in Java with javax.mail

JavaMail API (aka javax.mail, available here) is a great library for sending/receiving mails straight from a Java code. Web is crammed with basic examples how to use it. It's however much more difficult to find an example with secure SMTP working with "real" server, like GMail.
In fact, there are two standardized types of secure mail sending:
  • ESMTP protocol, working with or without encryption. Requires "pure" (non-SSL) sockets to be used. With "starttls" command, communication can be upgraded to full SSL. RFC-defined port is 587.

  • SMTP protocol over the SSL transport. Requires SSL sockets to be used from the beginning. RFC-defined port is 465.

In order to configure JavaMail for ESMTP, following code is needed:

Properties properties = new Properties();
properties.put("mail.smtp.starttls.enable", "true")
final javax.mail.Session session = javax.mail.Session.getInstance(properties);

while for SMTP over SSL, use following:

Properties properties = new Properties();
properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
final javax.mail.Session session = javax.mail.Session.getInstance(properties);

That's basically it. I tested following properties with GMail account, using both "SMTP over SSL" and "ESMTP with STARTTLS" options and it worked smoothly.
Following some most commonly experienced errors/exceptions with short descriptions:
  1. com.sun.mail.smtp.SMTPSendFailedException: 554 5.7.1 Sender address rejected.
    In case of many providers (like "o2.pl") means that only account owner (like "user@o2.pl") is allowed as the message sender.

  2. javax.mail.MessagingException: Could not connect to SMTP host: smtp.live.com, port: 465; nested exception is: java.net.ConnectException: Connection timed out
    Basically thrown when requested port isn't open. In the mail world usually means that server doesn't offer configured secure protocol (for example Hotmail doesn't offer pure SSL connection)

  3. com.sun.mail.smtp.SMTPSendFailedException: 553 From address not verified
    Means that sender address needs to be verified (enabled) in account's configuration. Can be experienced with for example Yahoo! free accounts.

  4. javax.mail.AuthenticationFailedException: 535-5.7.1 Username and Password not accepted.
    Send by server if user or password used for authentication failed. Usually means that server requires full user name (user@server.com) to be used.

If above solution fails (or experienced exception is different than listed), you can always enable JavaMail debug with following property:

properties.put("mail.smtp.debug", "true");

There is also nice shell tool, very useful for checking secure SMTP account's features, called "smtp-cli". You can grab it here.

4 komentarze:

Kapelusznik pisze...

Real pain begins when we want to use SSL with server, whose certificate is not trusted.
It's not the case with javamail only, it's of design of Java.
There's nice utility class hidden deep inside HttpClient code, that allows to do that.

Benny Bottema pisze...

This is simpel and all but completely skips the whole problem of attachments, sending HTML, embedding images having alternative text/html versions.

That's where this approach falls apart and you will have to dive into loads of RFC's.

Just go with Simple Java Mail in that case, which adds a thin layer to the javax.mail API to automagically do all this for you.

Benny Bottema pisze...
Ten komentarz został usunięty przez autora.
Benny Bottema pisze...

@Kapelusznik, Simple Java Mail allows you to fine tune when SSL certificates should be trusted or ignored.

// trust all hosts for SSL connections, don't validate keys
mailer.trustAllSSLHosts(true)
// white list hosts for SSL connections, don't validate keys for these
mailer.trustSSLHosts("a", "b", "c", ...);