After selecting a mailbox, clients usually show a list of messages. Usually the list contains message sender, date and subject.
Most clients simply fetch the wanted message data for all messages and then display them in a list. This is very annoying to use when opening a large mailbox for the first time. Suppose your mailbox had 2500 messages and you tried to open the mailbox for the first time with your laptop's mail client using a 8kB/s GPRS connection. FETCH 1:* (ENVELOPE INTERNALDATE) command could easily send over a 1MB reply (my 2520 message INBOX sent 1,1MB), which would take over two minutes to download. Your user most likely just wanted to check if there were any new messages. Is it really necessary to wait for 2 minutes for that?
A well behaving client would first fetch only the messages that are visible in the message list. Only after they're visible the rest of the mailbox list could be downloaded on background. And by background I mean that user should be able to open messages and perform other operations without waiting for the whole mailbox list to finish downloading. This can be done by fetching data in smaller blocks (e.g. 1:100, 101:200, etc. instead of 1:*).
It might even be useful to not download the entire message list at all. Data connections can still cost per downloaded megabyte. This can be easily implemented by having your mailbox list filled with empty or "message not downloaded yet" stubs. When user scrolls the mailbox list and stubs become visible, the messages are downloaded and the stubs are replaced with the real content. The client can of course anticipate this and always have the previous and the next pageful downloaded so user would rarely see the stubs.
What to FETCH?
You should basically fetch only the information you need, but try not to fetch anything that is costly to servers. Below is a list of fields and how costly they usually are for servers:
- UID, FLAGS: Very cheap for all servers
- INTERNALDATE: Quite cheap.
- ENVELOPE: Some servers keep envelopes directly cached (Cyrus), which makes it cheap for them to return. If envelope contains all the necessary information for your client, use it. If you want to fetch some other headers as well, it might be better to just fetch everything with BODY.PEEK[HEADER.FIELDS ()].
- BODY.PEEK[HEADER.FIELDS ()]: Some servers cache specific headers (Dovecot), which makes it cheaper to return them than returning ENVELOPE which requires parsing headers and building the envelope reply.
- BODY, BODYSTRUCTURE: Some servers keep these cached (Cyrus, Dovecot), but others don't and it's very costly for them to calculate it because it requires parsing the whole message.
- RFC822.SIZE: Some servers keeps this cached (Cyrus, Dovecot), but others don't and it can be very costly for them to calculate it if the message's physical size doesn't match the RFC822.SIZE (ie. physically linefeeds are LF, while RFC822.SIZE requires counting linefeeds as CRLFs).
- RFC822.HEADER, BODY.PEEK[HEADER], BODY.PEEK[HEADER.FIELDS.NOT ()]: These require opening the message file and sending (and parsing with HEADER.FIELDS.NOT) the header. Quite costly, but not as much as sending/parsing the entire message.
- BODY, RFC822, RFC822.TEXT: Very bandwidth costly. You should really avoid using these in message list fetches.
Some people want to sort messages by their Date: header, others want to sort messages by when they were received (INTERNALDATE). You should let the user choose this, and if user doesn't care about the received date, don't bother fetching INTERNALDATE.
Many clients wish to display an attachment icon in message list. Instead of fetching the entire message body, you can fetch BODYSTRUCTURE and see if it contains fields that identify an attachment.