Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> IMAP clients typically refer to messages by their “sequence number”, the message’s position in a mailbox. The first message has the sequence number “1”, the second “2”, and so on. If a client wants to mark a message as “read”, it will send a command to the server such as “mark message 5 as read”. But what if another client deleted the fourth message in the mailbox? The sequence numbers of all messages after the deleted message will be shifted down by one; the client that sent the “mark message 5 as read” command now refers to a different message than it intended.

> IMAP servers (which include applications such as the Proton Mail Bridge) need to be able to handle this situation. When one client moves messages into or out of a mailbox, the server needs to notify all other clients of the changes so that they can update their own view of the mailbox. And until the clients have received the update, the server needs to remember what each client thinks the mailbox looks like to correctly interpret the client’s commands.

Holy F what a clusterfuck



>> IMAP clients typically refer to messages by their “sequence number” […]

> Holy F what a clusterfuck

Or you could use the unique identifier of the message (UID) instead of the sequence number:

> Messages in IMAP4rev1 are accessed by one of two numbers; the unique identifier or the message sequence number.

* https://www.rfc-editor.org/rfc/rfc3501#section-2.3.1

> A 32-bit value assigned to each message, which when used with the unique identifier validity value (see below) forms a 64-bit value that MUST NOT refer to any other message in the mailbox or any subsequent mailbox with the same name forever.

* https://www.rfc-editor.org/rfc/rfc3501#section-2.3.1.1

The designers of the IMAP protocol aren't as dumb as the above quotation would have you to believe and multi-client operations were thought of.


The UID capability is a newer, hail-mary attempt to fix the issue, and thus it's optional, not all IMAP servers support it


I wrote a popular open source IMAP library in 2009 and even then the UID capability was there and widely adopted. My library relied on it 100%, completely ignoring sequence numbers, and I haven't received a single issue related to this.

It's not that new, it's not a hail mary, and it is very well supported.

* https://packagist.org/packages/tedivm/fetch

* https://github.com/tedious/Fetch


Last time I was writing mail client code was around 2004-2005 and even around that time every large IMAP provider (AOL, Yahoo, …) had UID support.


UID was introduced in IMAPv4 in RFC 1730 in 1994, and it's not optional for IMAPv4 or IMAPv4.1

I last wrote IMAP server-side software in 2000. Even then, I was not aware of any IMAP servers that didn't support UID. It was essential, as most clients expected it to be there.

There might still have been some at that point, but I'd be shocked if anyone is still running any IMAP2 or IMAP3 servers.


Newer as in only ~30 years old? It is likely some obscure IMAP servers doesn't support them but AFAIK all popular ones do.


I've worked on a lot of IMAP stuff and it's been decades since I've seen an implementation that didn't support reference by UID.


Pretty much no client or server uses it that way though. The UID extension is more or less required these days.

But IMAP is pretty twisted in other ways too. IMAP is _not_ a request-response protocol like you might think from other protocols. It looks like it at first because you can say things like (from memory, not the real protocol):

    > cmd1 LIST (here cmd1 is a request/response identifier chosen by the client; the server sends responses prefixed by this identifier)

    < cmd1 LIST RESPONSE...
But you can also multiplex many simultaneous commands like

    > cmd1 LIST folder1
    > cmd2 LIST folder2
    > cmd3 LIST folder3

    < cmd3 LIST folder3 RESPONSE...
    < cmd1 LIST folder1 RESPONSE...
    < cmd2 LIST folder2 RESPONSE...
(note that they can come out of order)

But it gets weirder because you can receive _unsolicited_ responses. Sometimes you want that like

    > cmd1 WATCH folder1 (starts a watch which may have responses but not immediately)
    > (do other stuff)

    < * NEW MESSAGE folder1 DATA... (some time later)
    < * FLAGS CHANGE folder1 DATA... (much more time later)
(the * means that you didn't ask for this so it's not prefixed by an ID that you gave) Here it's sort of solicited or at least something you did on purpose, but there are other more complicated cases where you'll receive data that you never asked for. Again from loose memory but an example might be

    > cmd1 OPEN folder1
    > (do stuff with folder1)

    (much later)
    < * CLOSING folder1 WAS DELETED
That means that your client needs to always be listening for data on the socket and reading it out even if the client is sitting in the background, and that if you send a command you might receive responses to other previous or unsolicited commands before the response to the command you just sent.

It's a cool way to do what it wants to do for GUI clients in particular because IMAP and GUIs have the same event-oriented model. But it makes implementing IMAP as a library a little more difficult because you need bidirectional hooks into the containing application. And you can't easily just have a `if POP3 then... else if IMAP4 then...` abstraction layer without some work to make it happen. Heck even just having a function that returns the list of messages requires a background thread and a little bit of lying about what's happening underneath. Leaving data on the socket unconsumed can get you disconnected by keepalive checks so the background task is required if you want to avoid that (which by observing mail.app it seems to just accept the disconnection and reconnect when it happens). Python has an imap library in the stdlib which "avoids" this by being so low-level that it sidesteps some of the issues by making you implement request/response on top of it, and its model also makes perfect IDLE implementation difficult (maybe impossible? but I didn't put enough time into it to prove this).

Because of the complication many GUI clients don't actually support the liveness built in to the protocol. They just ignore any reponses they didn't ask for, don't use WATCH/IDLE, and do the 15 minute polling cycle as if they were POP3. The protocol has all of the tools to have the liveness of Google Docs but I'm not aware of any GUIs that use it right.


What is difficult about that? Every protocol works this way, nothing new.

The fact that the server may send you messages even they are not responses... of course, otherwise how do you think notifications can work? The only other options is the client to poll the server periodically (how it was done by older clients, and iPhone client...), that is inefficient, consumes resources and you don't get immediate notifications. With an open socket the software stays in the background and gets woken up by the OS when a byte arrives, how is it complex?

> The protocol has all of the tools to have the liveness of Google Docs but I'm not aware of any GUIs that use it right

Never had any problem with Thunderbird. If you have Thunderbird open in the background and the GMail web interface both open at the same time most of the time you see first the notification from Thunderbird than the email in the GMail web interface. It's really fast, to the point that if I send myself a mail from my phone it seems to arrive immediately, and it's quite impressive.


No, every protocol does not work that way. A whole lot of protocols has no concept of multiplexing a connection or server initiated events. Since we're talking about e-mail, neither SMTP or POP3 has either of these complexities (SMTP allows client-initiated pipelining if the client sends the right greeting), but doesn't allow the server to multiplex responses or initiate anything, POP3 allows none of it at all). It's not new now, but it is moderately more complex if you're implementing a client.

I don't think it's quite as complex as suggested above, though. You just want to provide access to the socket in case the client wants to select() etc., and offer an API to send a request, a queue to read responses from that allows you to filter by request, and maybe a wrapper that sends a request and waits for a reply matching that request (while putting everything else onto the queue). Then the client can decide if it wants to pretend the protocol is mostly request/response or take proper advantage.


> What is difficult about that?

It's not independently difficult per se, it's just mismatched to the way that other protocols often work in a way that, in practise, few clients implement well. Most clients and libraries implement it as if it were request-response and as a result either produce wrong answers or are notably less efficient as you note.

> Every protocol works this way, nothing new.

No? POP3 and HTTP as easy examples are simple blocking request-response. Some do either pipelining or multiplexing with the command-tagging, but don't have unsolicited messages. I can think of only a few other comparable protocols with this paradigm but "every" is just not true.

> of course, otherwise how do you think notifications can work?

Yes? This is indeed _why_ it works this way. I don't know what you're correcting me about. It does this to implement notifications and liveness. We're agreeing.

> Never had any problem with Thunderbird

Thunderbird is one of the better implementers, yes. There are messages that it ignores like "folder deleted" but according to Dovecot https://www.dovecot.org/clients/ it implements IDLE correctly which is uncommon among clients. See https://wiki2.dovecot.org/Clients for more client quirks. The dovecot config file https://dovecot.org/doc/dovecot-example.conf has a imap_client_workarounds setting for many of these common bugs including "delay-newmail" which disables several of the unsolicited message types until the server is confident that the client is actually expecting to receive something.

In practise, in real life, clients get this wrong. That's all I'm trying to say.

Hacker News always has the well-actuallies but I'm pretty familiar with this and have implemented both an IMAP server and client. I'm not sure what you're trying to "correct" me about.


> HTTP as easy examples are simple blocking request-response

Well HTTP/1.1, HTTP/2 (and HTTP/3) surely not. You can make more request on a single channel and also the server can send you data you didn't requested (PUSH). Otherwise a modern site with a ton of resources would either take a minute to load or have to open 100 connections to the server.

MQTT is another example of protocol that works this way.

> "every" is just not true

Every protocol that was born after the 90s, let's rephrase it like that.


The issue was not making multiple requests on a single connection, but that IMAP can interleave and reorder responses. It's still not that hard, but it's a change.

HTTP/1.1 does not allow data you didn't request in random places. You need to specifically request a feed of server sent events. In every other instance you know that the response that come back will be replies to a request (in the case of SSE's, the reply will just keep coming as long as the server sends events), and in the same order as the requests. That means that a "dumb" client can just synchronously send requests and read the reply in a string request/response manner without worrying about getting something else back. Neither of those constraints hold for IMAP.


The only other options is the client to poll the server periodically

Another option is to open an idle connection dedicated to this and for a server to stop doing what wasn’t asked for. These legacy protocols always have been “all first thoughts slapped together and then some afterthoughts on top of that”.


A long time ago I wrote a toy IMAP client, and ran into similarly confusing issues about how the protocol is supposed to work:

https://github.com/evmar/go-imap/blob/master/imap/doc.go


> Holy F what a clusterfuck

And that's why when people say "JMAP is pointless", just go back and read some of the email specs!


Until things like Thunderbird support it, its adoption is unfortunately going to remain at the level it is currently at.


Which sucks as it's a massive chicken/egg problem... more clients and hosts need to support it.

Of course, from what I understand is there are JMAP/IMAP gateway options, that should ease availability... assuming these things actually gain any traction ever.


Well email moves slowly for one, and adding another protocol is usually baked into a client and servers approach (imap, pop, smtp, etc) so its not impossible


I'm guessing thunderbird will be the first mainstream gui mua to support jmap.


Stuff like this makes the idea of an "Email 2" protocol being worthwhile. Shocking how over-complicated it is to send text/html between two parties.


People have called things email killers several times. An intelligent approach would be to gradually rebuild parts on top of better foundations and push towards that.

The amount of deadlocks in adopting things like JMAP, IMAP NOTIFY, OAuth2 (+ Dynamic Client Registration) and so on and on is immense. Won't because others don't, too few do, we see no benefit yet etc. it's really hindering.


It's easy to take things like REST for granted. Until you learn about the stuff that came before it, like this!


Most protocols were clear text and session based. Simply connect to a pier and start talking English to the server

Kind of like Siri lol


I remember randomly trying to connect to an FTP server with telnet and being shocked that it actually worked. I was even able to figure out the commands although couldn't figure out how downloads would work (until I read about how FTP works).


I'm wondering why they don't give every mail a UUID. Probably because someone wanted to save a few bytes.


One of the extensions to IMAP is the UID extension, which gives every email something akin to a UUID. It's technically not mandatory, but virtually every email client is going to try to use it if possible, and likely some email clients don't even attempt to use message sequence numbers.

So you don't need to maintain message sequence numbers, except for the few places in the protocol where only the message sequence number is reported (e.g., the unsolicited message deleted alerts). In practice, I would actually expect that trying to use message sequence numbers would be buggier than not using them. But servers still have to support them!


They do, on a per-session basis - but it's an optional feature (that's supported by ~100% of all imap server implementations).


What's the name of this extension?


It's actually in the original IMAP4rev1 RFC: https://www.rfc-editor.org/rfc/rfc3501#section-2.3.1.1

Using UIDs is opt-in, by prefixing `UID ` in front of some commands. There are indeed extensions that add more `UID ` commands to the protocol. I've implemented IMAP clients and never used sequence numbers at all.


UID was present already in RFC 1730 (IMAPv4):

https://www.rfc-editor.org/rfc/rfc1730#section-6.4.9


Yep, just a few bites :) Especially if you're on vacation. IMAP IDs can change, requiring the email client to re-download all emails.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: