Chapter 3. Receiving Mail

Receiving mail is considerably more complex than sending it. SMTP uses only 14 different commands, and a simple email client can be implemented with just five of them. POP3, however, has 12 commands, almost all of which a client must be able to handle; IMAP4 has 24 different commands.

The JavaMail API is designed to retrieve messages from an IMAP or perhaps an NNTP server. That is, it assumes the server can return headers separate from the messages they belong to, search through mailboxes, provide the storage for the messages rather than the client, and so forth. The JavaMail API provides less of what you need for client-oriented mail access protocols, such as POP3, that assume the client stores and manages the mail archive; but it still gives you the tools you need to download the mail from the server. You just have to implement your own storage system on the client. (You can check out the experimental POP3RemoteStore if you want to both store and retrieve.)

We’ll begin with the simpler POP3 protocol, then move on to IMAP. From the perspective of JavaMail, IMAP can be viewed largely as POP plus some commands for manipulating folders. For simple programs that operate only on the INBOX folder, POP and IMAP clients are more or less the same.

There are about 12 steps to reading a remote mailbox (the number of steps can vary a little, since some steps are optional or can be combined with or replaced by others):

  1. Set up the properties you’ll use for the connection.
  2. Construct the Authenticator you’ll use for the connection.
  3. Get a Session object with Session.getInstance().
  4. Use the session’s getStore() method to return a Store.
  5. Connect to the store.
  6. Get the INBOX folder from the store with the getFolder() method.
  7. Open the INBOX folder.
  8. Open the folder you want inside the INBOX folder. Repeat as many times as necessary to reach the folder you’re seeking.
  9. Get the messages from the folder as an array of Message objects.
  10. Iterate through the array of messages, processing each one in turn using the methods of the Message class. For instance, you might print out each message or simply display the sender, subject, and other vital information in a GUI for the user to select from, as in Figure 3-1.
  11. Close the folder.
  12. Close the store.
A GUI for selecting mail messages

Figure 3-1. A GUI for selecting mail messages

Each of these steps is individually quite simple. The first is to set up the properties for the mail session. Properties you might want to set include mail.store.protocol, mail.pop3.user, and mail.pop3.host:

Properties props = new Properties();
props.put("mail.pop3.host", "pop.gmail.com");
props.put("mail.store.protocol", "pop3");
props.put("mail.pop3.user", "erharold");

Alternatively you can provide this information in method calls, in which case an empty Properties object is enough. Personally I prefer using method arguments instead of system properties when possible since it avoids unexpected, hard-to-debug global configuration problems.

Next, you’ll want to create an instance of the javax.mail.Authenticator class (more properly, an instance of a concrete subclass of the abstract Authenticator class) that can ask the user for a password. For now, we’ll simply hardcode those values and pass null instead of an actual Authenticator:

Authenticator a = null;

We’ll fill this piece in later when we discuss authentication.

Next, use these Properties and Authenticator objects to get a Session instance, like this:

Session session = Session.getInstance(props, a);

Ask the session for a store for the provider. Here, we want a provider for POP3:

Store store = session.getStore("pop3");

Finally, you’re ready to connect to the store using the connect() method. You’ll need to provide the host to connect to and the username and password to use:

store.connect("mail.cloud9.net", "elharo", "my_password");

You can pass null for the password to indicate that the previously specified Authenticator should be queried for the password.

Now that the store is connected, you’re ready to open a folder in the store. This step is really more oriented to IMAP than POP, since POP servers don’t keep track of different folders. They simply provide all of a user’s incoming mail as one undifferentiated amalgam. For purposes of the JavaMail API, POP3 providers use the folder name INBOX:

Folder inbox = store.getFolder("INBOX");

The folder is closed when you get it. You can perform some operations on a closed folder including deleting or renaming it, but you can’t get the messages out of a closed folder. First you have to open it. You can open a folder for read access by passing the mnemonic constant Folder.READ_WRITE to the open() method for read access, or Folder.READ_ONLY for read/write access:

inbox.open(Folder.READ_ONLY);

Now you’re ready to retrieve the messages with the getMessages() method, which returns an array containing pointers to all the messages in the folder:

Message[] messages = inbox.getMessages();

This call is lazy. That is, it does not actually download the message headers and content. That will be retrieved later when you ask each message for its data.

The Message class provides many methods for working with individual messages. It has methods to get the various header fields of the message, get the content of the message, reply to the message, and more. We’ll discuss these soon, when we talk about the Message and MimeMessage classes. For now, we’ll do just about the simplest thing imaginable: print each message on System.out using the message’s writeTo() method:

for (int i = 0; i < messages.length; i++) {
  System.out.println("------------ Message " + (i+1)
      + " ------------");
  messages[i].writeTo(System.out);
}

Once you’re done with the messages, close the folder and then close the message store with the aptly named close() methods:

inbox.close(false);
store.close();

The false argument to the folder’s close() method indicates that we do not want the server to actually expunge any deleted messages in the folder. We simply want to break our connection to this folder.

Example 3-1 puts this all together with a basic program that downloads and prints out the contents of a specified POP mailbox. Messages are simply dumped on System.out in the default encoding. The servers, usernames, and so forth are all hardcoded. This quickly demonstrates most of the key points of receiving mail with the JavaMail API. A more advanced program would include an appropriate GUI.

Example 3-1. POP3Client

import javax.mail.*;
import java.util.*;
import java.io.*;

public class POP3Client {

  public static void main(String[] args) {

    Properties props = new Properties();

    String host = "utopia.poly.edu";
    String username = "eharold";
    String password = "mypassword";
    String provider = "pop3";

    try {
      // Connect to the POP3 server
      Session session = Session.getInstance(props);
      Store store = session.getStore(provider);
      store.connect(host, username, password);

      // Open the folder
      Folder inbox = store.getFolder("INBOX");
      if (inbox == null) {
        System.out.println("No INBOX");
        System.exit(1);
      }
      inbox.open(Folder.READ_ONLY);

      // Get the messages from the server
      Message[] messages = inbox.getMessages();
      for (int i = 0; i < messages.length; i++) {
        System.out.println("------------ Message " + (i+1)
            + " ------------");
        messages[i].writeTo(System.out);
      }

      // Close the connection
      // but don't remove the messages from the server
      inbox.close(false);
      store.close();
    } catch (MessagingException | IOException ex) {
      ex.printStackTrace();
    }
  }
}

Here’s some sample output I got when I pointed it at an account I don’t use much:

$ java POP3Client
------------ Message 1 ------------
Received: (from eharold@localhost)
        by utopia.poly.edu (8.8.8/8.8.8) id QAA05728
        for eharold; Mon, 30 Nov 2009 16:14:29 -0500 (EST)
Date: Mon, 30 Nov 2009 16:14:29 -0500 (EST)
From: Elliotte Harold <eharold@utopia.poly.edu>
Message-Id: <200911302114.QAA05728@utopia.poly.edu>
To: eharold@utopia.poly.edu
Subject: test
Content-Type: text
X-UIDL: 87e3f1ba71738c8f772b15e3933241f0
Status: RO

hello you

------------ Message 2 ------------
Received: from russian.cloud9.net (russian.cloud9.net [
.4])
        by utopia.poly.edu (8.8.8/8.8.8) with ESMTP id OAA28428
        for <eharold@utopia.poly.edu>; Tue, 1 Dec 2009 14:05:06 -0500 (
Received: from [168.100.203.234] (macfaq.dialup.cloud9.net [168.100.203
        by russian.cloud9.net (Postfix) with ESMTP id 24B93764F
        for <eharold@utopia.poly.edu>; Tue, 1 Dec 2009 14:02:50 -0500
Mime-Version: 1.0
X-Sender: macfaq@mail.cloud9.net
Message-Id: <v04210100b46b1f97969d@[168.100.203.234]>
Date: Tue, 1 Dec 2009 13:55:40 -0500
To: eharold@utopia.poly.edu
From: Elliotte Rusty Harold <elharo@macfaq.com>
Subject: New system
Content-Type: text/plain; charset="us-ascii" ; format="flowed"
X-UIDL: 01fd5cbcf1768fc6c28f9c8f934534b5

Just thought you'd be happy to know that now that I've got my desk
moved over from my old apartment, I've finally ordered the Windows NT
system I've been promising for months.

About the only change you’d need to make to port this program to IMAP would be setting the provider variable to imap instead of pop3.