RFC 2180

IMAP4 Multi-Accessed Mailbox Practice

πŸ“… published July 1997, from ietf

RFC 2180: IMAP4 Multi-Accessed Mailbox Practice πŸŽ€

Network Working Group 🌸 this is the group that wrote it.

Request for Comments: 2180 ✨

Author: M. Gahrns, Microsoft πŸ’–

Category: Informational 🧚 this one is informational, not a standard.

Date: July 1997 πŸ“…

Obsoletes / Updates: the header doesn't list anything it obsoletes or updates πŸ¦‹

Status of this Memo ✨

This memo provides information for the Internet community. It does not specify an Internet standard of any kind. Distribution of this memo is unlimited πŸ’…

1. Abstract 🌈

Okay so [RFC-2060] is IMAP4, and it's a rich client/server protocol that lets a client access and manipulate email messages on a server πŸ“¬ Inside that protocol there's room for different servers to give different results for the same client/server interaction. If a protocol didn't allow for that, it would often be unduly restrictive, and nobody wants that πŸ™„

For example, say several clients are using a mailbox and one of them tries to delete it. An IMAP4 server could handle that in a few different ways, depending on its architecture or just what its makers prefer πŸ€·β€β™€οΈ

Here's the catch: with all that flexibility comes more responsibility for the client 🫢 A client can't be built around how one specific IMAP server happens to act. It has to be built around what the protocol actually allows. That's the main thing this doc wants you to remember, bestie πŸ’œ

By writing down common IMAP4 server practice for when several clients access one mailbox at the same time, we're hoping for the widest possible interoperation between IMAP4 clients and servers. It's giving teamwork 🀝

The behavior in this document comes from what some existing servers already do, plus behavior the IMAP mailing list consensus agreed was reasonable. It's believed to be [RFC-2060] compliant βœ… But this document is not meant to define IMAP4 compliance, and it's not an exhaustive list of valid IMAP4 behavior either. [RFC-2060] is always the place to check for IMAP4 compliance, especially for server behavior that isn't covered here πŸ“– no shortcuts, babe.

2. Conventions used in this document πŸŽ€

In the examples, "C1:", "C2:" and "C3:" mark lines sent by three different clients (client #1, client #2 and client #3) that are connected to a server. "S1:", "S2:" and "S3:" mark the lines the server sends to client #1, client #2 and client #3 respectively πŸ’Œ

A shared mailbox is a mailbox that can be used by multiple users πŸ‘―β€β™€οΈ

A multi-accessed mailbox is a mailbox that has multiple clients accessing it at the same time πŸ¦‹

A client is said to have accessed a mailbox after a successful SELECT or EXAMINE command πŸ”‘

Okay so the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are the ones that mean business in this doc, and they get read the way [RFC-2119] describes πŸ’…

3. Deletion/Renaming of a multi-accessed mailbox 🌷

If an external agent or multiple clients are accessing a mailbox, you have to be careful when deleting or renaming it 😬 These are some strategies an IMAP server may choose when that happens.

3.1. The server MAY fail the DELETE/RENAME command of a multi-accessed mailbox 🚫

Sometimes this behavior isn't practical. If a lot of clients are accessing a shared mailbox, the window where no client has it accessed might be tiny or totally nonexistent. That basically makes the mailbox undeletable or unrenamable 😭

Example:

<Client #1 and Client #2 have mailbox FOO accessed. Client #1 tries to DELETE the mailbox and is refused>

C1: A001 DELETE FOO
S1: A001 NO Mailbox FOO is in use by another user.

3.2. The server MAY allow the DELETE command of a multi-accessed mailbox, but keep the information in the mailbox available for those clients that currently have access to the mailbox. πŸ‘»

Once every client is done accessing the mailbox, it's permanently removed ✨ Clients that didn't already have access can't see the 'ghosted' mailbox. It won't show up in a later LIST or LSUB response, and it won't work as a mailbox argument for any other IMAP command until the reference count of clients accessing it hits 0 πŸ’€

This isn't always the move, though. Say someone made a mailbox full of offensive or sensitive stuff. You might want it deleted and all access to what's inside removed right away, instead of letting people keep access until their client closes the mailbox πŸ™ˆ

Also, this behavior can stop the same mailbox name from being 'recycled' until every client finishes with the original mailbox πŸ”

Example:

<Client #1 and Client #2 have mailbox FOO selected. Client #1 DELETEs mailbox FOO>

C1: A001 DELETE FOO
S1: A001 OK Mailbox FOO is deleted.

<Client #2 is still able to operate on the deleted mailbox>

C2: B001 STORE 1 +FLAGS (\Seen)
S2: * 1 FETCH FLAGS (\Seen)
S2: B001 OK STORE completed

<Client #3 which did not have access to the mailbox prior to the deletion by client #1 does not have access to the mailbox>

C3: C001 STATUS FOO (MESSAGES)
S3: C001 NO Mailbox does not exist

<Nor is client #3 able to create a mailbox with the name FOO, while the reference count is non zero>

C3: C002 CREATE FOO
S3: C002 NO Mailbox FOO is still in use. Try again later.

<Client #2 closes its access to the mailbox, no other clients have access to the mailbox FOO and reference count becomes 0>

C2: B002 CLOSE
S2: B002 OK CLOSE Completed

<Now that the reference count on FOO has reached 0, the mailbox name can be recycled>

C3: C003 CREATE FOO
S3: C003 OK CREATE Completed

3.3. The server MAY allow the DELETE/RENAME of a multi-accessed mailbox, but disconnect all other clients who have the mailbox accessed by sending a untagged BYE response. πŸ‘‹

A server might often pick disconnecting clients for the DELETE case, but it could go with a "friendlier" approach for RENAME πŸ₯Ί

Example:

<Client #1 and Client #2 have mailbox FOO accessed. Client #1 DELETEs the mailbox FOO>

C1: A002 DELETE FOO
S1: A002 OK DELETE completed.

<Server disconnects all other users of the mailbox>

S2: * BYE Mailbox FOO has been deleted.

3.4. The server MAY allow the RENAME of a multi-accessed mailbox by simply changing the name attribute on the mailbox. ✏️

Other clients with access to the mailbox can keep going with commands like FETCH that don't reference the mailbox name πŸƒβ€β™€οΈ They'd only find out about the rename the next time they use the old mailbox name. Some servers MAY choose to include the [NEWNAME] response code in their tagged NO response to a command that used the old mailbox name πŸ’‘ It's a hint that the operation can succeed if the client tries again with the new name.

Example:

<Client #1 and Client #2 have mailbox FOO accessed. Client #1 RENAMEs the mailbox.>

C1: A001 RENAME FOO BAR
S1: A001 OK RENAME completed.

<Client #2 is still able to do operations that do not reference the mailbox name>

C2: B001 FETCH 2:4 (FLAGS)
S2: * 2 FETCH . . .
S2: * 3 FETCH . . .
S2: * 4 FETCH . . .
S2: B001 OK FETCH completed

<Client #2 is not able to do operations that reference the mailbox name>

C2: B002 APPEND FOO {300} C2: Date: Mon, 7 Feb 1994
21:52:25 0800 (PST) C2: . . .  S2: B002 NO [NEWNAME FOO
BAR] Mailbox has been renamed

4. Expunging of messages on a multi-accessed mailbox πŸ—‘οΈ

If an external agent or multiple clients are accessing a mailbox, you have to be careful with EXPUNGE of messages 🫣 Other clients using the mailbox might be in the middle of a command that depends on message sequence numbers. An EXPUNGE response can't be sent while the server is responding to a FETCH, STORE or SEARCH command, so there's no way to notify the client of the EXPUNGE right away. That can get ambiguous if a client runs FETCH, STORE or SEARCH on a message that's already been EXPUNGED πŸ˜΅β€πŸ’«

4.1. Fetching of expunged messages πŸ“©

These are some strategies an IMAP server may choose for handling a FETCH command on expunged messages.

Here's the scenario we're working with:

  • Client #1 and Client #2 have mailbox FOO selected.
  • There are 7 messages in the mailbox.
  • Messages 4:7 are marked for deletion.
  • Client #1 issues an EXPUNGE, to expunge messages 4:7

4.1.1. The server MAY allow the EXPUNGE of a multi-accessed mailbox but keep the messages available to satisfy subsequent FETCH commands until it is able to send an EXPUNGE response to each client. πŸ‘€

Keeping 'ghosted' messages around isn't always ideal. If a message had offensive or sensitive info in it, you might want all access to it gone instantly, even if another client is in the middle of reading it πŸ™…β€β™€οΈ

Example: (Building upon the scenario outlined in 4.1.)

<Client #2 is still able to access the expunged messages because the server has kept a 'ghosted' copy of the messages until it is able to notify client #2 of the EXPUNGE>

C2: B001 FETCH 4:7 RFC822
S2: * 4 FETCH RFC822 . . . (RFC822 info returned)
S2: * 5 FETCH RFC822 . . . (RFC822 info returned)
S2: * 6 FETCH RFC822 . . . (RFC822 info returned)
S2: * 7 FETCH RFC822 . . . (RFC822 info returned)
S2: B001 OK FETCH Completed

<Client #2 issues a command where it can get notified of the EXPUNGE>

C2: B002 NOOP
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 3 EXISTS
S2: B002 OK NOOP Complete

<Client #2 no longer has access to the expunged messages>

C2: B003 FETCH 4:7 RFC822
S2: B003 NO Messages 4:7 are no longer available.

4.1.2. The server MAY allow the EXPUNGE of a multi-accessed mailbox, and on subsequent FETCH commands return FETCH responses only for non-expunged messages and a tagged NO πŸ’…

okay so here the server MAY let an EXPUNGE go through on a shared mailbox, and then any FETCH after that only gets back data for the messages that still exist, plus a tagged NO πŸ™ˆ

once you get that tagged NO on a FETCH, bestie, you SHOULD send a NOOP so the server can tell you about any pending EXPUNGE responses ✨ then you can either retry the FETCH, or look at the EXPUNGE from the NOOP and the FETCH response and realize the FETCH failed because of pending expunges. no cap, that's the move 🧚

Example: (building on the scenario from 4.1.)

Here Client #2 tries to FETCH a mix of expunged and non-expunged messages. It only gets FETCH responses for the messages that are still there, plus a tagged NO.

C2: B001 FETCH 3:5 ENVELOPE
S2: * 3 FETCH ENVELOPE . . . (ENVELOPE info returned)
S2: B001 NO Some of the requested messages no longer exist

Now that Client #2 got a tagged NO on that FETCH, it sends a NOOP to find out about the pending EXPUNGE responses 🌸

C2: B002 NOOP
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 3 EXISTS
S2: B002 OK NOOP Completed.

Here's the payoff: the client got a FETCH response for message 3, and an EXPUNGE response saying messages 4:7 are gone. So it doesn't need to re-issue the FETCH πŸ‘‘

4.1.3. The server MAY allow the EXPUNGE of a multi-accessed mailbox, and on subsequent FETCH commands return the usual FETCH responses for non-expunged messages, "NIL FETCH Responses" for expunged messages, and a tagged OK response. πŸŽ€

this one is a different flavor. the server MAY still allow the EXPUNGE, but now any FETCH gets normal data for the messages that exist, "NIL FETCH Responses" for the expunged ones, and a tagged OK at the end πŸ¦‹

if every single message in the FETCH has been expunged, the server SHOULD return only a tagged NO. in that case the client SHOULD send a NOOP so it hears about any pending EXPUNGE responses. after that it can either retry the FETCH, or look at the EXPUNGE from the NOOP and realize the FETCH failed because of pending expunges πŸ’–

"NIL FETCH responses" are just a way of showing empty data, matching whatever FETCH argument was asked for πŸ“

Example:

* 1 FETCH (ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL))
* 1 FETCH (FLAGS ())
* 1 FETCH (INTERNALDATE "00-Jan-0000 00:00:00 +0000")
* 1 FETCH (RFC822 "")
* 1 FETCH (RFC822.HEADER "")
* 1 FETCH (RFC822.TEXT "")
* 1 FETCH (RFC822.SIZE 0)
* 1 FETCH (BODY ("TEXT" "PLAIN" NIL NIL NIL "7BIT" 0 0)
* 1 FETCH (BODYSTRUCTURE ("TEXT" "PLAIN" NIL NIL NIL "7BIT" 0 0)
* 1 FETCH (BODY[<section>] "")
* 1 FETCH (BODY[<section>]<partial> "")

ngl this is where it gets a little messy 😭 a client can't always tell a "NIL FETCH response" caused by an expunge apart from one where the data is actually NIL. like, a * 5 FETCH (FLAGS ()) could mean message 5 has no flags set, or that message 5 was expunged. so if it's unclear, the client SHOULD send something like a NOOP to force the EXPUNGE responses out and clear up the confusion πŸ”₯

Example: (building on the scenario from 4.1.)

Client #2 tries to access a mix of expunged and non-expunged messages. The non-expunged ones come back with normal data, and the expunged ones come back as "NIL FETCH responses."

C2: B002 FETCH 3:5 ENVELOPE
S2: * 3 FETCH ENVELOPE . . . (ENVELOPE info returned)
S2: * 4 FETCH ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL
      NIL NIL)
S2: * 5 FETCH ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL
      NIL NIL)
S2: B002 OK FETCH Completed

Now Client #2 tries to FETCH only expunged messages, and gets a tagged NO back πŸ₯€

C2: B002 FETCH 4:7 ENVELOPE
S2: B002 NO Messages 4:7 have been expunged.

4.1.4. To avoid the situation altogether, the server MAY fail the EXPUNGE of a multi-accessed mailbox πŸ™…β€β™€οΈ

sometimes this just isn't practical πŸ₯Ί like, if tons of clients are hanging out in a shared mailbox, there might be a tiny window (or no window at all) where nobody has it accessed. so the server basically can't ever expunge anything, and the message ends up being unexpungeable.

4.2. Storing of expunged messages 🧁

here are some strategies an IMAP server might use when a STORE command hits expunged messages.

4.2.1. If the ".SILENT" suffix is used, and the STORE completed successfully for all the non-expunged messages, the server SHOULD return a tagged OK. ✨

Example: (building on the scenario from 4.1.)

Client #2 tries to silently STORE flags on expunged and non-expunged messages. The server sets the flags on the non-expunged ones and returns OK.

C2: B001 STORE 1:7 +FLAGS.SILENT (\SEEN)
S2: B001 OK

4.2.2. If the ".SILENT" suffix is not used, and only expunged messages are referenced, the server SHOULD return only a tagged NO. πŸ’”

Example: (building on the scenario from 4.1.)

Client #2 tries to STORE flags only on expunged messages.

C2: B001 STORE 5:7 +FLAGS (\SEEN)
S2: B001 NO  Messages have been expunged

4.2.3. If the ".SILENT" suffix is not used, and a mixture of expunged and non-expunged messages are referenced, the server MAY set the flags and return a FETCH response for the non-expunged messages along with a tagged NO. πŸ¦„

after getting a tagged NO STORE response, the client SHOULD send a NOOP so it hears about any pending EXPUNGE responses. then it can retry the failed STORE, or look at the EXPUNGE responses from the NOOP and the FETCH responses from the STORE and realize the STORE failed because of pending expunges πŸ’…

Example: (building on the scenario from 4.1.)

Client #2 tries to STORE flags on a mix of expunged and non-expunged messages.

C2: B001 STORE 1:7 +FLAGS (\SEEN)
S2: * FETCH 1 FLAGS (\SEEN)
S2: * FETCH 2 FLAGS (\SEEN)
S2: * FETCH 3 FLAGS (\SEEN)
S2: B001 NO Some of the messages no longer exist.

C2: B002 NOOP
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 3 EXISTS
S2: B002 OK NOOP Completed.

Got it? The client has FETCH responses for messages 1:3, and an EXPUNGE response saying messages 4:7 are gone, so it doesn't need to re-issue the STORE 🫢

4.2.4. If the ".SILENT" suffix is not used, and a mixture of expunged and non-expunged messages are referenced, the server MAY return an untagged NO and not set any flags. πŸ™ˆ

after a tagged NO STORE response, the client SHOULD send a NOOP so it hears about pending EXPUNGE responses. then it would re-issue the STORE after updating its message list based on whatever EXPUNGE responses came back 🌈

if a huge number of clients are sharing a mailbox, there might be a tiny window (or none at all) with no pending expunges. that basically means a client can't set the flags on every message in one go 😩

Example: (building on the scenario from 4.1.)

Client #2 tries to STORE flags on a mix of expunged and non-expunged messages.

C2: B001 STORE 1:7 +FLAGS (\SEEN)
S2: B001 NO  Some of the messages no longer exist.

Client #2 sends a NOOP to hear about the EXPUNGED messages.

C2: B002 NOOP
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 4 EXPUNGE
S2: * 3 EXISTS
S2: B002 OK NOOP Completed.

Client #2 updates its message list, then re-issues the STORE on only the messages that haven't been expunged.

C2: B003 STORE 1:3 +FLAGS (\SEEN)
S2: * FETCH 1 FLAGS (\SEEN)
S2: * FETCH 2 FLAGS (\SEEN)
S2: * FETCH 3 FLAGS (\SEEN)
S2: B003 OK STORE Completed

4.3. Searching of expunged messages πŸ”

a server MAY simply not return a search response for messages that were expunged and that it hasn't been able to tell the client about. if a client was expecting a particular message in the search results and it didn't show up, the client SHOULD send a NOOP to see whether another client expunged it πŸ•΅οΈβ€β™€οΈ

4.4 Copying of expunged messages πŸ“¦

COPY is the only IMAP4 sequence number command that's safe to allow an EXPUNGE response on. the reason is that a client isn't allowed to chain several COPY commands together. it has to wait and confirm the copy worked before sending another one 🧚

4.4.1. The server MAY disallow the COPY of messages in a multi-access mailbox that contains expunged messages. 🚫

Pending EXPUNGE response(s) MUST be returned to the COPY command. πŸ₯Ή

Example:

C: A001 COPY 2,4,6,8 FRED
S: * 4 EXPUNGE
S: A001 NO COPY rejected, because some of the requested messages were expunged

Note: None of the above messages are copied, because if a COPY command is unsuccessful, the server MUST restore the destination mailbox to its state before the COPY attempt. πŸ’€

4.4.2. The server MAY allow the COPY of messages in a multi-access mailbox that contains expunged messages. πŸ’œ

Pending EXPUNGE response(s) MUST be returned to the COPY command. The messages that get copied are the ones matching the sequence numbers from before any EXPUNGE response 🌸

Example:

C: A001 COPY 2,4,6,8 FRED
S: * 3 EXPUNGE
S: A001 OK COPY completed

In this example, the messages copied to FRED are messages 2,4,6,8 as they were at the start of the COPY command. Those same messages are numbered 2,3,5,7 by the end of it. The EXPUNGE response can't happen until after the COPY command has identified its messages, because of the "no expunge while no commands in progress" rule πŸŽ€

Example:

C: A001 COPY 2,4,6,8 FRED
S: * 4 EXPUNGE
S: A001 OK COPY completed

In this one, message 4 got copied before it was expunged, so it MUST appear in the destination mailbox FRED 🫢

5. Security Considerations πŸ”

This document describes the behavior of servers that use the IMAP4 protocol, so it has the same security considerations as described in [RFC-2060].

Some of the server behavior described here doesn't allow for immediate deletion of information when a mailbox is being accessed by multiple clients. this could matter if you're dealing with sensitive information and you'd much rather have immediate deletion 😱

6. References πŸ“š

[RFC-2060], Crispin, M., "Internet Message Access Protocol - Version 4rev1", RFC 2060, University of Washington, December 1996.

[RFC-2119], Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", RFC 2119, Harvard University, March 1997.

7. Acknowledgments 🌟

This document came out of discussions on the IMAP4 mailing list and is meant to reflect that group's consensus. Special shoutout to Raymond Cheng, Mark Crispin, Jim Evans, Erik Forsberg, Steve Hole, Mark Keasling, Barry Leiba, Syd Logan, John Mani, Pat Moran, Larry Osterman, Chris Newman, Bart Schaefer, Vladimir Vulovic, and Jack De Winter, who were active in the discussion or made suggestions to this document πŸ‘‘

8. Author's Address πŸ’–

okay so the very last bit of this doc is literally just where to find the author, Mike Gahrns, at Microsoft 🧚 here's his info, bestie:

Mike Gahrns
Microsoft
One Microsoft Way
Redmond, WA, 98072

Phone: (206) 936-9833
EMail: mikega@microsoft.com

that's the whole RFC, no cap πŸŽ€βœ¨

πŸ“¦ where this page comes from