POP3 is a popular protocol for accessing a mailbox full of email messages. While small devices have moved their mail reading apps to IMAP and proprietary protocols, POP3 remains the preferred protocol for moving email messages between big servers where a no-frills, download-and-delete system is preferred.
A problem with this protocol is embodied in this question: “How often should we poll for new messages?” There’s a non-trivial overhead to connecting. Poll too quickly and you overload the system. Poll too far apart and messages take too long to arrive.
Over my head!
To recap, let’s take a look at what needs to happen every time a POP3 client wants to check for new messages.
- The client and server handshake TCP as the underlying connection.
- The client and server handshake TLS for security.
- The client authenticates itself to the server.
- The client finally gets to ask if there are any new messages.
“Oh, no new messages? Okay, I’ll go through all that again in five seconds.”
POP3 doesn’t have a way to avoid this continual opening and closing of connections, but it does have a mechanism to add extensions to the protocol. All it needs is for someone to write the new extension down and to develop a working prototype. Which I have done.
billpg industries POP3 service
On my github account, you’ll find a prototype POP3 server that implements this extension. Download it, compile it, run it. Go nuts. The service is written using the “listener” model. You set it up to listen for incomming connections and it talks the protocol until you shut it down. The library deals with the complexities while requests for messages are passed onto your provider code.
You don’t need to write that provider code if you only want to try it out. I’ve written a basic Windows app where you can type in new messages into a form, ready for the client to connect and download them. If Linux is more your thing or you prefer your test apps to work autonomously, I’ve also written a command-line app that sits there randomly populating a mailbox with new messages, waiting for a client to come along and get them.
To Sleep, and Goodnight…
Now for the new extension itself. It comes in two parts, the SLEE command put a connection to sleep and the WAKE command brings it back.
If a server normally locks a mailbox while a connection is open, then SLEE should release that lock. In fact, SLEE is defined to do everything that QUIT does, except actually close down the connection. Crucially, this includes committing any messages deleted with DELE.
During a sleeping state, you’re no longer attached to the mailbox. None of the normal commands work. You can only NOOP to keep the underlying connection alive, QUIT to shut it down or WAKE to reconnect with your mailbox.
If the server responds to WAKE with a +OK response, a new session has begun. The refreshed connection needs to be viewed as if it is a new connection, as if the client had QUIT and reconnected. The numeric message IDs from before will now be invalid and so the client will need to send a new STAT or UIDL command to update them.
In order to save the client additional effort, the server should include a new response code in the +OK response.
- [ACTIVITY/NEW] indicates there is are new messages in the mailbox that were not accessible in the earlier session on this connection.
- [ACTIVITY/NONE] indicates there are no new messages this time, but it does serve as an indication that this server has actively checked and that it is not necessary for the client to send a command to check.
- (No “ACTIVITY” response indicates the server is not performing this test and the client will need to send a STAT or UIDL command to retrieve this information.)
A server might give an error response to a WAKE command, which may include a brief error message. In this situation where a connection can’t be refreshed for whatever reason, a client might chose to close the underlying connection and open a new one.
The error might include the response code [IN-USE] to indicate that someone else is connected to the mailbox, or [AUTH] to indicate that the credentials presented earlier are no longer acceptable.
Note that the SLEE command is required to include a commit of DELE commands made. If the client does not want the server to commit message deletes, it should send an RSET command first to clear those out.
How would a client use this?
To help unpack this protocol extension, here is description of how the process would work in practice with a server that implements this extension.
The client connects to the server for the first time. It sends a CAPA request and the response includes SLEE-WAKE, which means this connection may be pooled later on.
S: +OK Welcome to my POP3 service! C: CAPA S: +OK Capabilities follow... S: UIDL S: RESP-CODES S: SLEE-WAKE S: .
The client successfully logs in and performs a UIDL which reveals three messages ready to be downloaded. It successfully RETRs and DELEs each message, one by one.
C: USER firstname.lastname@example.org S: +OK Send password. C: PASS passw0rd S: +OK Logged in. You have three messages. C: UIDL (Remainder of normal POP3 session redacted for brevity.)
Having successfully downloaded and flagged those three messages for deletion, the client now sends a SLEE command to commit those three DELE commands sent earlier. The response acknowledges that the deleted messages have finally gone and the connection has entered a sleeping state.
C: SLEE S: +OK Deleted 3 messages. Sleeping.
The client can now put the opened connection in a pool of opened connections until needed later. It is not a problem if the underlying connection is closed without ceremony in this state, but it may be prudent for the pool manager to periodically send a NOOP command to keep the connection alive and to detect if any connections have since been dropped.
C: NOOP S: +OK Noop to you too!
Time passes and the client wishes to poll the mailbox for new messages. It looks in the pool for an opened connection to this mailbox and takes the one opened earlier. It sends a WAKE command to refresh the mailbox.
C: WAKE S: +OK [ACTIVITY/NONE] No new messages.
Because the server supports the ACTIVITY response code and the client recognized it, the client immediately knows that there is nothing left to do. The client immediately sends a second SLEE command to put right back into the sleeping state.
C: SLEE S: +OK Deleted 0 messages. Sleeping.
(Incidentally, the client is free to ignore the “ACTIVITY” response code and instead send a STAT or UIDL command to make its own conclusion. It will need to do this anyway if the server does not include such a response code.)
More time passes and the client is ready to poll the mailbox again. As before, it finds a suitable opened connection in the pool and it sends another WAKE command.
C: WAKE S: +OK [ACTIVITY/NEW] You've got mail.
Having observed the notification of new mail, the client sends a new sequence of normal POP3 commands.
This was all within one connection. All the additional resources needed to repeatedly open and close TCP and TLS are no longer needed.
I’ll be doing the job of turning all this informal text into a formal RFC on my github project.
📷 Keyboard Painting Prototype by Rodolphe Courtier.