CS161 Programming Assignment 1: IMAP Login Client

Chris Erway, Ronald Tse.
{cce, rhtse}@cs.brown.edu
Last updated: Wednesday, September 14, 2005

Preliminaries

Questions: cs161tas@cs.brown.edu

Help sessions (held in the fishbowl, 2nd floor):
    Chris - Thursday, 12-2pm
    Ron - Wednesday, 6-8pm

Due date: Monday, September 19th, 10am.

Introduction

Your first programming task is to implement a simple, threaded IMAP client. You're probably familiar with IMAP, which allows clients to access messages and folders stored remotely on a mail server. We'll be using IMAP to learn how to design and implement servers that can effectively juggle network and disk I/O; as the semester progresses, you'll build such an IMAP server and a flexible client to test it.

The complete protocol description for IMAP version 4, revision 1 is described in RFC 3501 (and a slightly prettier version is provided for your convenience), but you needn't worry about it just yet. For this assignment you'll take the first step towards building your client, implementing only four basic commands: LOGIN, LOGOUT, CAPABILITY, and NOOP.

All you need to know about IMAP

IMAP is a line-oriented protocol: clients send one-line commands (though quoting is possible), and servers return one or more lines in response. Lines are ended with CRLFs ("\r\n"), though most servers support plain LFs ("\n") too. Commands and other token strings are case-insensitive.

Each client command begins with a tag, which is an alphanumeric string identifying the command, typically something short and easy to increment (e.g. "0001", "tag1") but potentially almost anything (e.g. "asdf", "THISISALONGTAG"). The server similarly uses the tag to identify its response to the client command. A tagged server response indicates the completion result of the command: either "OK", "NO" or "BAD". The remaining part of the tagged server response (e.g. "LOGIN completed") is "human-readable text" provided for informational purposes.

The server also sends untagged responses, which begin with the token "*" instead of a tag. This is how the server gives you information beyond simply the failure or success of your command -- lists of available mailboxes, how many new messages are in your inbox, the content of those messages, etc. Luckily, you don't have to implement any commands dealing with actual messages yet, so you can largely ignore this information; if you're curious about what's coming up, you should get started reading the RFC.

Here's an example session typical of what you'll deal with in this assignment. Use of "C:" and "S:" indicate lines sent by the client and server respectively. You can also go ahead and try out this exchange with telnet by connecting to anonymous.cs.brown.edu, port 143.

(Connection to server opened by client)
S: * OK IMAP4rev1 Service Ready
C: a001 CAPABILITY
S: * CAPABILITY IMAP4REV1 STARTTLS IDLE BINARY UNSELECT SCAN SORT
S: a001 OK CAPABILITY completed
C: a002 login joe j0epasswd
S: a002 OK LOGIN completed
C: a003 NOOP
S: a003 OK NOOP completed
C: a004 LGOUT
S: a004 BAD Error in IMAP command received by server.
C: a005 LOGOUT
S: * BYE IMAP4rev1 Server logging out
S: a005 OK LOGOUT completed
(Server and client then close the connection)

As illustrated above, of the four commands, only LOGIN takes arguments (the username and password), separated by spaces. It is also the only command for which you may receive a "NO" response, indicating the username or password was rejected. This is different from a "BAD" response, indicating the server didn't understand your command or arguments, which may occur for any command.

The rest of the commands are fairly straightforward. CAPABILITY's untagged response lists the server's capabilities -- for example, STARTTLS indicates the server is SSL-ready. NOOP does nothing (though when a mailbox is selected, NOOP will often reveal the existence of a new message via untagged server responses) and always succeeds. LOGOUT triggers a BYE untagged response containing a human-readable string wishing the client farewell, followed by tagged success and server disconnect.

Assignment Specification

In this assignment, you'll be implementing an IMAP client in Java that understands the commands CAPABILITY, LOGIN, LOGOUT, and NOOP. Your client will have to know how to connect to a server, run a specified sequence of commands, and print their responses as they come in. If one of the commands fails ("NO" or "BAD" response), your client should recognize this and end that connection.

The tricky part is that we'd like to connect to multiple servers in parallel. That is, provided a list of many different servers and respective command sequences, we'd like to connect to all of them at once, so that if one of the connections take too long, the others will not be delayed. Since we're just getting started, you'll be using regular Java threads; after this assignment we'll use asynchronous I/O.

Your client must accept, as its sole argument, a filename of a file we'll use to test your client. The file will describe a list of servers and associated commands to run, and will be in the following format:

anonymous.cs.brown.edu 143
CAPABILITY
LOGIN user pass
LOGOUT

server1.cs.brown.edu 143
CAPABILITY
NOOP
LOGIN user badpass

server2.cs.brown.edu 1043
LOGIN user pass
CAPABILITY
NOOP
NOOP
LOGOUT

As shown, each server connection is described by a group of lines, separated by blank lines. The first line in each group contains the hostname and port to connect to, followed by the commands (with arguments for LOGIN). Your client should read this file, then open up connections to the listed servers in parallel, running the commands and printing their replies in an intelligible way.

You should resist the urge to simply spit out lines read from the file directly to the server, without tokenizing and understanding them -- in the future, as you add support for more commands, your clients will need to be easily extensible.

Using Java NIO

As mentioned in class on Monday, you are encouraged to use Java NIO SocketChannel and Buffer objects. SocketChannel.read/write both take Buffers as argument. There are many types of buffers, including CharBuffers, ByteBuffers, etc. Some basics:

Testing your code

Most IMAP servers these days -- for example, the CS department's -- report the LOGINDISABLED capability, meaning that they only accept encrypted passwords, rather than the plaintext ones that the LOGIN command uses. To help you test your clients, we've set up an IMAP server on anonymous.cs.brown.edu (port 143, only accessible from within the CS department) that will work fine with the LOGIN command. The username and password you should use are the same as in the IMAP example transcript above ("joe").

If you're disconnected from the Internet or working off the Brown campus, you can also try running this tiny Perl IMAP server script we've written. It should respond appropriately to the four commands you'll be implementing. The username/password combinations it accepts are listed at the beginning of the source. This tiny client is also running at anonymous.cs.brown.edu, port 1043.

Submitting your code

Please include a README file explaining briefly which files contain what code, as well as how to run your code. To submit your assignment, place your source files in a directory, and run the following script while in that directory:

/course/cs161/bin/cs161_handin login

This will cause all the files in the current directory to be submitted. Please contact the TAs if you have problems submitting. For your own sanity, don't leave your submission until the minute before the deadline.

Grading scheme

We'll be testing your client against several popular IMAP servers, as well as a custom server written by us to better test your client on timing, robustness, etc. Your code will be graded on the following factors (and their weights).

Documentation and code legibility: 20%
Functionality: 50%
Robustness: 30%