Circle of Confusion

...I once was lost...
                          Limboville 居士

Wednesday, March 12, 2008

 

Send Email with Javamail and Google Gmail SMTP Server

I need to send email directly from my computer using Javamail. With a Google account, you can send email with their 'smtp.gmail.com' server. It's quite straight forward to use Javamail to interface to google's smtp server. The one hicup is it uses SSL for communication and require the StARTTLS command for secured communication. The following gives me some clues:

http://forum.java.sun.com/thread.jspa?threadID=486031&messageID=2275282

First, Javamail version >1.4 is required as support for STARTTLS is added after that. Get the current version here: http://java.sun.com/products/javamail/downloads/index.html. Note: the download link need Javascript to work! Unzip the downloaded file. There is a mail.jar file inside. That is the actual Javamail. Install Javamail in the local maven repository:

mvn install:install-file -DgroupId=javax.mail \
-DartifactId=mail -Dversion=1.4.1 -Dpackaging=jar \
-Dfile=mail.jar

And declare dependency in pom.xml:

<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
</dependency>

Javamail has Activation dependency. This is taken care by Java6 and newer. Older Java version must have the Activation jar install also. In the unzipped javamail-1.4.1 folder, there is a file SSLNOTES.txt that documents the STARTTLS support and SSL socket.

Test code that send an email via Google:
package com.hddigitalworks;

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class HelloToMe {

public static void main(String... args) throws Exception {

String[] recipients = {"someone@somewhere.com", "someoneelse@somewhereelse.com"};
String subject = "重要消息 From Master Mo";
String body = "This is a very important message\n" +
"這是一則非常重要消息。\n" +
"Это будет очень важное сообщение.";
sendViaGMail(recipients, subject, body);
}



public static void sendViaGMail(String recipients[],
String subject,
String body) throws MessagingException {

boolean debug = true;

Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.user", "you@here.com");
props.put("mail.smtp.auth", "true");
if (debug) {
props.put("mail.debug", "true");
}
props.put("mail.smtp.port", "465");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");

// your google account name (without the '@gmail.com' part!)
final String username = "google-sign-in";
final String password = "your-password";
Authenticator auth = new Authenticator() {
@Override protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};

Session session = Session.getDefaultInstance(props, auth);
session.setDebug(debug);

MimeMessage email = new MimeMessage(session);

email.setSubject(subject, "utf-8");
email.setText(body, "utf-8");

InternetAddress[] addressTo = new InternetAddress[recipients.length];
for (int i = 0; i < recipients.length; i++) {
addressTo[i] = new InternetAddress(recipients[i]);
}
email.setRecipients(Message.RecipientType.TO, addressTo);

Transport.send(email);

}
}

The important bits are in the last few Session properties values. Those sets up the SSL socket factory for the session to issue the 'STARTTLS' for Google. I originally thought I only need this:

        props.put("mail.smtp.starttls.enable", "true");

My code hangs. I have to add this

        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

to make it work. Somehow Javamail is unable to get the SSLSocketFactory from the JSSE configuration machinery. Hmmm.

There is no need to setup Google's server certificate. Their cert is signed by the ThawtePremiumServerCA and that is already in the JRE's 'cacert' keystore. To install some unknown cert, use the keytool's -import command. The default password of 'cacert' is 'changeit'.

My test message contains Chinese characters in both the subject and body. To make both support non-ascii character, Both MimeMessage#setSubject() and MimeMessage#setText() calls need to specify the "utf-8" parameter. Without specifying utf-8, the Chinese characters becomes 'babel' on the receiving end. Utf-8 should be the default, but it's not.

Comments: Post a Comment

Subscribe to Post Comments [Atom]





<< Home

Archives

August 2005   September 2005   October 2005   December 2005   January 2006   February 2006   March 2006   April 2006   December 2006   January 2008   February 2008   March 2008   October 2009  

This page is powered by Blogger. Isn't yours?

Subscribe to Comments [Atom]