<story><title>Gluon, a high-performance IMAP library</title><url>https://proton.me/blog/gluon-imap-library</url></story><parent_chain><item><author>meghan_rain</author><text>&amp;gt; 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.&lt;p&gt;&amp;gt; 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.&lt;p&gt;Holy F what a clusterfuck</text></item></parent_chain><comment><author>throw0101c</author><text>&amp;gt;&amp;gt; &lt;i&gt;IMAP clients typically refer to messages by their “sequence number”&lt;/i&gt; […]&lt;p&gt;&amp;gt; &lt;i&gt;Holy F what a clusterfuck&lt;/i&gt;&lt;p&gt;Or you could use the unique identifier of the message (UID) instead of the sequence number:&lt;p&gt;&amp;gt; &lt;i&gt;Messages in IMAP4rev1 are accessed by one of two numbers; the unique identifier or the message sequence number.&lt;/i&gt;&lt;p&gt;* &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.rfc-editor.org&amp;#x2F;rfc&amp;#x2F;rfc3501#section-2.3.1&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;www.rfc-editor.org&amp;#x2F;rfc&amp;#x2F;rfc3501#section-2.3.1&lt;/a&gt;&lt;p&gt;&amp;gt; &lt;i&gt;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.&lt;/i&gt;&lt;p&gt;* &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.rfc-editor.org&amp;#x2F;rfc&amp;#x2F;rfc3501#section-2.3.1.1&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;www.rfc-editor.org&amp;#x2F;rfc&amp;#x2F;rfc3501#section-2.3.1.1&lt;/a&gt;&lt;p&gt;The designers of the IMAP protocol aren&amp;#x27;t as dumb as the above quotation would have you to believe and multi-client operations were thought of.</text></comment>
<story><title>Gluon, a high-performance IMAP library</title><url>https://proton.me/blog/gluon-imap-library</url></story><parent_chain><item><author>meghan_rain</author><text>&amp;gt; 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.&lt;p&gt;&amp;gt; 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.&lt;p&gt;Holy F what a clusterfuck</text></item></parent_chain><comment><author>ketralnis</author><text>Pretty much no client or server uses it that way though. The UID extension is more or less required these days.&lt;p&gt;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):&lt;p&gt;&lt;pre&gt;&lt;code&gt;    &amp;gt; cmd1 LIST (here cmd1 is a request&amp;#x2F;response identifier chosen by the client; the server sends responses prefixed by this identifier)

    &amp;lt; cmd1 LIST RESPONSE...
&lt;/code&gt;&lt;/pre&gt;
But you can also multiplex many simultaneous commands like&lt;p&gt;&lt;pre&gt;&lt;code&gt;    &amp;gt; cmd1 LIST folder1
    &amp;gt; cmd2 LIST folder2
    &amp;gt; cmd3 LIST folder3

    &amp;lt; cmd3 LIST folder3 RESPONSE...
    &amp;lt; cmd1 LIST folder1 RESPONSE...
    &amp;lt; cmd2 LIST folder2 RESPONSE...
&lt;/code&gt;&lt;/pre&gt;
(note that they can come out of order)&lt;p&gt;But it gets weirder because you can receive _unsolicited_ responses. Sometimes you want that like&lt;p&gt;&lt;pre&gt;&lt;code&gt;    &amp;gt; cmd1 WATCH folder1 (starts a watch which may have responses but not immediately)
    &amp;gt; (do other stuff)

    &amp;lt; * NEW MESSAGE folder1 DATA... (some time later)
    &amp;lt; * FLAGS CHANGE folder1 DATA... (much more time later)
&lt;/code&gt;&lt;/pre&gt;
(the * means that you didn&amp;#x27;t ask for this so it&amp;#x27;s not prefixed by an ID that you gave) Here it&amp;#x27;s sort of solicited or at least something you did on purpose, but there are other more complicated cases where you&amp;#x27;ll receive data that you never asked for. Again from loose memory but an example might be&lt;p&gt;&lt;pre&gt;&lt;code&gt;    &amp;gt; cmd1 OPEN folder1
    &amp;gt; (do stuff with folder1)

    (much later)
    &amp;lt; * CLOSING folder1 WAS DELETED
&lt;/code&gt;&lt;/pre&gt;
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.&lt;p&gt;It&amp;#x27;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&amp;#x27;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&amp;#x27;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 &amp;quot;avoids&amp;quot; this by being so low-level that it sidesteps some of the issues by making you implement request&amp;#x2F;response on top of it, and its model also makes perfect IDLE implementation difficult (maybe impossible? but I didn&amp;#x27;t put enough time into it to prove this).&lt;p&gt;Because of the complication many GUI clients don&amp;#x27;t actually support the liveness built in to the protocol. They just ignore any reponses they didn&amp;#x27;t ask for, don&amp;#x27;t use WATCH&amp;#x2F;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&amp;#x27;m not aware of any GUIs that use it right.</text></comment>