IMAP
Tagged commands and selected mailboxes
Read tagged client commands, untagged server data, completion responses, and mailbox selection.
IMAP lets a client send several commands without waiting for each answer. To keep the replies straight, every command starts with a tag, a short label the client picks, like A001. The server repeats the tag on the line that completes that command.
Anything the server sends that isn’t tied to one command starts with *. Those are untagged responses: mailbox data, new message counts, flag changes. A line starting with + is a continuation request, the server asking for more input.
A session in practice
Let’s log in and open the inbox. You can type this yourself after openssl s_client -connect imap.fastmail.com:993 -crlf:
C: A001 LIST "" "*"
S: * LIST (\HasNoChildren) "/" "INBOX"
S: A001 OK LIST completed
C: A002 SELECT INBOX
S: * 42 EXISTS
S: * OK [UIDVALIDITY 93842] UIDs valid
S: A002 OK [READ-WRITE] SELECT completed
The * lines are data. A001 OK and A002 OK close their commands. Everything between a command and its tagged reply belongs to it, unless it’s an unsolicited update.
LIST "" "*" asks for every mailbox. SELECT INBOX opens it read-write. Use EXAMINE INBOX instead when you only want to read, and the server won’t touch flags like \Seen.
Three ways a command ends
OK means it worked. NO means the command was valid but couldn’t be done, like selecting a mailbox that doesn’t exist. BAD means you sent something the server couldn’t parse, or a command that’s illegal in the current state.
If you’re writing a client and see BAD, the bug is on your side.
Mailbox names come from the server
Notice the "/" in the LIST reply. That’s the hierarchy delimiter, the character this server uses between a folder and its subfolder. Some servers use ., so Work.Invoices is a subfolder of Work. A NIL delimiter means the server has no hierarchy at all.
Never hardcode /. Read the delimiter from LIST and split on that.
What SELECT tells you
SELECT returns the state you need to synchronize. 42 EXISTS is the message count. UIDVALIDITY 93842 is the current UID generation of this mailbox, and often you’ll also get UIDNEXT.
Store these values with your local cache of the mailbox. Selecting a mailbox called INBOX next week doesn’t prove it’s the same INBOX: if UIDVALIDITY changed, the server rebuilt it and every UID you saved is meaningless. The synchronization lesson covers what to do then.
Try sending a second tagged command before A001 completes. Match every tagged reply to its command, and separate the untagged data lines from the completion lines.
Lesson completed