About Anthony Dahanne

Anthony Dahanne is a Java software developer for 8 years, his favorite topics are Android, building tools, Continuous Integration and, of course, core Java development. Working for Terracotta, he currently implements the REST management interface for EhCache.

Sending a mail in Java (and Android) with Apache Commons Net SMTP : STARTTLS, SSL

Recently working on an Android experiment, I wanted to send emails using a SMTP server, using authentication and encryption, from an android app.

Well, I found out that javax.mail on Android is not a really good option, since it depends on awt classes (legacy I guess) ; some people have tried to adapt it so that you don’t require the whole awt package, but I had little success with that; not mentioning those people have refactored javax.mail for Android few years ago themselves, without any maintenance.

Another option that came to my mind is re using Apache Commons Net : since the community added an SMTPSClient and an AuthenticatingSMTPClient to the original SMTP client (and applied a little patch of mine for SSL and authentication), you can embed this library in your Android app (no transitive dependencies needed) to send mail using authentication over a secured layer. (this post actually inspired me, but it is using an old version of Apache Commons Net, using 3.3 you don’t need to do that anymore)

SMTP Authentication and STARTTLS with Commons Net

Usually the port used for this matter is 25 or the alternate 587 port : you connect to the SMTP server on a  plain connection, you ask for the available commands, if STARTTLS is supported, you use it and the rest of the communication is encrypted.

Let’s take the gmail example, since smtp.gmail.com supports authentication and STARTTLS

public void sendEmail() throws Exception {  
    String hostname = "smtp.gmail.com";
    int port = 587;
 
    String password = "gmailpassword";
    String login = "account@gmail.com";
 
    String from = login;
 
    String subject = "subject" ;
    String text = "message";
 
    AuthenticatingSMTPClient client = new AuthenticatingSMTPClient();
    try {
      String to = "recipient@email.com";
      // optionally set a timeout to have a faster feedback on errors
      client.setDefaultTimeout(10 * 1000);
      // you connect to the SMTP server
      client.connect(hostname, port);
      // you say ehlo  and you specify the host you are connecting from, could be anything
      client.ehlo("localhost");
      // if your host accepts STARTTLS, we're good everything will be encrypted, otherwise we're done here
      if (client.execTLS()) {
 
        client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password);
        checkReply(client);
 
        client.setSender(from);
        checkReply(client);
 
        client.addRecipient(to);
        checkReply(client);
 
        Writer writer = client.sendMessageData();
 
        if (writer != null) {
          SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject);
          writer.write(header.toString());
          writer.write(text);
          writer.close();
          if(!client.completePendingCommand()) {// failure
            throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
          }
        } else {
          throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
        }
      } else {
        throw new Exception("STARTTLS was not accepted "+ client.getReply() + client.getReplyString());
      }
    } catch (Exception e) {
        throw e;
    } finally {
        client.logout();
        client.disconnect();
    }
  }
 
  private static void checkReply(SMTPClient sc) throws Exception {
    if (SMTPReply.isNegativeTransient(sc.getReplyCode())) {
      throw new Exception("Transient SMTP error " + sc.getReply() + sc.getReplyString());
    } else if (SMTPReply.isNegativePermanent(sc.getReplyCode())) {
      throw new Exception("Permanent SMTP error " + sc.getReply() + sc.getReplyString());
    }

Nothing much to add here, of course the exception handling could be optimized if you used your own exception classes.

SMTP Authentication and SSL with Commons Net

Some SMTP servers are configured to only accept “a to z SSL” : you have to secure the communication right before issuing any commands to the server; usually the port used is 465.

Let’s take the LaPoste.net example (free email accounts offered by the french post) :

public void sendEmail() throws Exception {  
    String hostname = "smtp.laposte.net";
    int port = 465;
 
    String password = "password";
    String login = "firstname.lastname";
 
    String from = login + "@laposte.net";
 
    String subject = "subject" ;
    String text = "message";
 
    // this is the important part : you tell your client to connect using SSL right away
    AuthenticatingSMTPClient client = new AuthenticatingSMTPClient("TLS",true);
    try {
      String to = "anthony.dahanne@gmail.com";
      // optionally set a timeout to have a faster feedback on errors
      client.setDefaultTimeout(10 * 1000);
      client.connect(hostname, port);
      client.ehlo("localhost");
      client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password);
      checkReply(client);
 
      client.setSender(from);
      checkReply(client);
 
      client.addRecipient(to);
      checkReply(client);
 
      Writer writer = client.sendMessageData();
 
      if (writer != null) {
        SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject);
        writer.write(header.toString());
        writer.write(text);
        writer.close();
        if(!client.completePendingCommand()) {// failure
          throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
        }
      } else {
        throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
      }
    } catch (Exception e) {
        throw e;
    } finally {
        client.logout();
        client.disconnect();
    }

I did not repeat the checkReply() method here, since it is the same for both code snippets; you will have noticed that using SSL right away means you don’t have to check for execTls() response (in fact it won’t work if you do so).

Wrapping up

That’s about it; if you want to make those examples work in your environment, you can add the apache commons net 3.3 jar to your classpath

If you’re using Maven add the dependency  :

<dependency>
    <groupid>commons-net</groupid>
    <artifactid>commons-net</artifactid>
    <version>3.3</version>
</dependency>

If you’re using Gradle for your Android project, you can also use the following build.gradle file :

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.4.2'
    }
}
apply plugin: 'android'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile fileTree(dir: 'libs', include: '*.jar'), 'commons-net:commons-net:3.3'
}
 
android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"
 
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
 
        instrumentTest.setRoot('tests')
    }
}

Enjoy !
 

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


− one = 0



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books