Programming With NDC

This part of the documentation for NDC is designed to give you the high-level structure of the toolkit and some basic directions for programming it. If you want to see the actual function calls in the API, it can found elsewhere.

High Level Structure

At the highest level of the toolkit, the objects can be broken into three main semantic groups. These groups are the objects that represent procotol state, objects which access mail message data, and utilities.

IMAP Protocol State Objects

The first group are those objects that represent states of the IMAP protocol. There are four states of the IMAP protocol (see RFC 1730) which are non-authenticated, authenticated, selected, and logout. These represent the different phases of use of the protocol. These states are represented by three NDC objects, Protocol, Connection, and Mailbox. The Protocol object encapsulates the Non-Authenticated state, the Connection object encapsulates the Authenticated state, and the Mailbox object encapsulates the Selected state. (There is no object in NDC which represents the Logout state, but you will get notified if that state is reached if you register an AsynchronousAction object with your Prototcol object as the BYE handler.) On a related note, the Protocol object also effectively represents the state when you are not connected at all to an IMAP server. I call this the "no protocol" state. As you use the these three objects, you will see pattern. You will issue a command to the object, and if succeeds, you can use a getSomeObject() type function to get the object that represents the next state. Here's an example of going from the "no protocol" state all the way to the Authenticated state:

/* declare our variables... I like the name of the object to be the */
/* same as its type, but differ in case */
Protocol protocol;
Connection connection;
Mailbox mailbox;

/* create a protocol object... this object is the "no protcol" */
/* state when it gets created */
protocol=new Protocol();
/* connect to a server on the same machine ... we are now going */
/* to be in the Non-Authenticated state */
protocol.connection("localhost");
/* login to the server ... we'll reach the authenticated state */
/* this is not my real password, so don't bother! */
protocol.LOGIN("iansmith","secret");
/* we're in the new state, but need the object that represents it */
connection=protocol.getConnection();
/* ok, now try to select the system inbox to get the selected state */
connection.SELECT("inbox");
/* made it to selected state (we hope!) and now get the mailbox */
mailbox=connection.getMailbox();

Note that in the interest of clarity of my example I didn't check for the results of the commands which might have (such as LOGIN & SELECT) nor did catch exceptions which might have resulted from these functions. You certainly should do this. Also, this basic structure of issuing a command, checking to see if succeed or failed, then using a "get" function is present in many places in the toolkit, so get used to it now.

Mail Message Data Objects

Returing to our subject of the toolkits basic structure, the second group of interesting objects are those associated with mail message data. These are the Message, the BodyPart, and the MailAddress types. These types are used once you have reached the selected state (i.e. have a Mailbox object) for actually getting at the information contained in mail messages. (By contrast, most of the protocol state types above are used for initiating a connection to a server and/or finding the mailboxes of interest.)

Here's an example piece of code which will retreive the subject line of a message and the text of the same Message, assuming the message has only one part and that part is ascii text. Assume that mailbox is already initialized and is an object of type Mailbox.

Message message;
BodyPart bodypart;
String messageContent;
String subject; ...
/* assume I want message number 25 ... the FETCH() command pulls*/
/* messge into the cache if it is not already there */
mailbox.FETCH(25); /* should check for return code here! */
/* get the message data */
message=mailbox.getMessage(25);
/* lets get the subject line into a string */
subject=message.getSubject();
/* now extract the first (and we assume only) body part from the msg*/
bodypart=message.getBodyPart();
/* the body parts have their own cache, so lets fetch the part */
/* note: if you FETCH() anything already in a cache it has no */
/* effect so don't worry about calling this as much as you want */
bodypart.FETCH();
/* now we know that we have the data, get it is as a string */
messageContent=bodyPart.getData();

Let's continue on with that example a little farther. Suppose you wanted to print on the user's terminal a nicely formatted version of where the mail message came from (the from line). You use the MailAddress object to do this. All of the addressing lines of a mail message (from, to, bcc, cc) are extracted from Message objects as a Vector of MailAddress objects. The common case (although not the only case) is for the from line of a mail message to only have one name on it (messages typically only originate from one person) and in next example, we'll assume that this is the case. Also assume the declarations that we had above are still in force with the same types.

Vector vector;
MailAddress mailaddress;
...
/* assign the vector of mail addresses */
vector=message.getFrom();
/* rip out the first element of the vector (numbered from 0) */
/* note: you must cast the return value here as vector.elementAt() */
/* is defined to just return Object not some subtype of Object*/
/* which is what we want */
mailaddress=(MailAddress)vector.elementAt(0);
/* if they don't have a personal name, just print the email addr */
if (mailaddress.getPersonalName()==null) {
System.out.println(m.getMailboxName()+"@"+m.getHostname());
} else {
/* they supplied a real name, so we'll just use the default */
/* printer that is supplied with the mailaddress object */
/* it formats like this: Ian Smith <iansmith@cc.gatech.edu> */
System.out.println(mailaddress.toString());
}
Again, I should be checking the vectorreturned from getFrom() for being null and for having more than one element.

BodyPart Objects And MIME

The BodyPart object has a special relationship to MIME multimedia and multipart mail. Every part of a mail message in NDC is represented by a BodyPart object; this relationship is one-to-one. To ascertain the type of a BodyPart (i.e is it text? video? audio? image?), you should use the combinartion of getMIMEType() and getMIMESubType(). You will probably also need to look at the value of getEncoding() which will tell you how the text is processed (if at all). The encoding types 7BIT and 8BIT don't need processing as they are straight ASCII and 8 bit ASCII respectively. The type BASE64 does need some processing to decode it, but NDC supplies you with that decoder. Other encoding types are possible and are your problem; the two other encodings defined by MIME standard are BINARY and quoted-printable. Note: BINARY would also imply no more processing is necessary. Support for quoted-printable encoding in NDC is planned for the future.

If you get MIME multipart mail, the multipart container (say, of MIME type "MULITPART" and subtype "MIXED") will be represented as a BodyPart object, as you would expect. This BodyPart will have a linked list of children whose head is accessible as the value of the getFirstChild(). (Note: If you get a null result from getFirstChild() then the BodyPart does not have any children; this is a very useful test!) Each of the children in the linked list will have their successor available by calling getNextSibling(). Thus you had a Message object which consisted of a multipart message with three parts (a text, an image, and an audio clip) you might want a piece of code like this:

/* assume the the variable message is of type Message */
/* and is already initialized to the message of interest */
BodyPart multipart,text,image,audio;
/* get the container */
multipart=message.getBodyPart();
/* traverse into the children */
text=multipart.getFirstChild();
/* successor to text is an image ... */
image=text.getNextSibling();
/* successor to image is an audio */
audio=text.getNextSibling();
/* note, audio.getNextSibling() would give null... */
Also, the MIME type/subtype "MESSAGE/RFC822" is special in NDC. If such a BodyPart is discovered, you can call getEmbeddedMessage() to retreive a complete Message object. Remember, you get a Message from this call, not a BodyPart. Further, the enclosed Message itself might be multipart or of type/subtype MESSAGE/RFC822, so this process could proceed recursively. Be aware that the enclosed message is really part of the mailbox of its encloser, so that closing the Mailbox it is contained in effectively removes your ability to access the enclosed Message. Finally, the enclosed message doesn't have a set of flags like a normal Message nor can it have new flags added to it via STORE().

Utility Objects

There are three utility classes in NDC, MIME, Util, and MailboxName. The first two of these three are simple collections of functions (in "Javaspeak", classes which have only static functions) which perform various useful tasks for programmers. They don't participate in the IMAP protocol. The MailboxName object is a convenience object which is used only with the LIST command on the Connection object.

The MIME object performs functions that are useful in dealing with messages which use the MIME standard. Primarily its function is to decode "base64" encoded email. Base64 is an encoding dictated by the MIME standard for dealing with the problem of sending binary data through email. When base64 is used the binary data is encoded into ascii characters (which won't break the mail systems!) which can then be decoded back into the binary by the receiver. The MIME object includes a base64 decoder. If you use the function decodeBase64() you can just pass it the string you get from doing getData() on a BodyPart (see above) and it will return a String which has the binary data.

Other than base64 encoding, the MIME object can be asked for the filename extension that corresponds to a given MIME type. Generally, this is useful if you are writing MIME data out to disk and want to know what type of file extension to use to indicate the type of the data. Note: If you use this function for writing out MIME data, programs such as Netscape usually will recognize the data in its correct type and launch the correct application to view it.

The other utility type is (obviously) Util. This type has only a few functions that are outside of NDC API developers. (Note: And in future versions the users of the NDC API will not be able to access many of the functions that are now in the Util object which are only of internal use.) The two simplest are the getLine() and makeWidth() functions. The first of these reads a line from the user's terminal and returns it as a String and the second pads or truncates a String to a given width. The other function of interest is the stringInVector() function. This function determines if a given String (second paramter of this function) is a member of Vector of Strings (first parameter of this function). This is most useful in dealing with the flags that are stored on a given message, i.e. testing to see if a message has the "\Seen" or "\Deleted" flag set on it.

Finally, the last utility type is the MailboxName object is used with the LIST command on the Connection object. This object has three "get" functions, getSelectable, getInteresting, and getName. The first two are flags that tell you how a mailbox was marked by the IMAP server in response to a request to list the available mailboxes and third one just gives the mailbox's name. Note: All of these values are more fully explained in RFC 1730. To get a list of mailboxes from the Connection object issue a LIST() command and then use the getMailboxNames() to get a Vector of MailboxName objects.

Coding Standards

I use the javadoc tool to generate the API documentation so if you see comments like this:

/**
* This is some comment
* @param int i some stuff
* @exception foobar some more stuff
*/

these are the ones for javadoc which you can read about from the java pages at Sun.

The user of the toolkit may notice that some of the functions in the toolkit are have names that are all capitals. Functions whose names are all capitals correspond very closely to the client commands in the IMAP prtocol, if not exactly. This is a way giving the user of the API some idea of which IMAP protocol commands are being issued in response to which of his actions.

Students take note: You are expected to code to a standard that is at least at good as my code. Thus, you should be using javadoc and its comments on all of your code.


Ian Smith
Last modified: Sat Jan 20 17:48:13 EST 1996