Synchronization
These plugins cover how a client learns about changes: push notifications, mod-sequences, quick resynchronization, stable object ids and UID-only sessions.
| Plugin | Capability | RFC |
|---|---|---|
ENABLE | ENABLE | RFC 5161 |
IDLE | IDLE | RFC 2177 |
CONDSTORE | CONDSTORE | RFC 7162 |
QRESYNC | QRESYNC | RFC 7162 |
OBJECTID | OBJECTID | RFC 8474 |
NOTIFY | NOTIFY | RFC 5465 |
UIDONLY | UIDONLY | RFC 9586 |
Changes made by another session, or through the control API, reach a session the same way. See Multiple sessions for when ImapKit reports them.
ENABLE
Adds the ENABLE command. The extensions that can be enabled are those whose plugins are loaded: CONDSTORE, QRESYNC, UIDONLY, UTF8=ACCEPT, IMAP4rev2 and METADATA (or METADATA-SERVER). They can be loaded in any order with ENABLE, and QRESYNC, UIDONLY, UTF8=ACCEPT and IMAP4rev2 load ENABLE themselves.
- Capability names are matched case-insensitively. The
ENABLEDresponse lists only what this command enabled, in the advertised spelling (IMAP4rev2,UTF8=ACCEPT). Unknown names are ignored, as RFC 5161 section 3.1 requires. - ENABLE needs a logged in session, and ImapKit refuses it with
BADonce the session has selected a mailbox, since RFC 5161 section 3.1 says clients MUST NOT issue ENABLE once they SELECT or EXAMINE a mailbox. Servers do not have to check this, ImapKit does to catch the client bug. - UNAUTHENTICATE turns every enabled extension off.
C: A2 ENABLE CONDSTORE
S: * ENABLED CONDSTORE
S: A2 OK ENABLE completed
C: A2 SELECT INBOX
S: ...
S: A2 OK [READ-WRITE] Completed
C: A3 ENABLE CONDSTORE
S: A3 BAD ENABLE is not allowed after SELECT or EXAMINE
IDLE
Adds the IDLE command. After the + idling continuation the server sends changes as soon as they happen: new messages, expunges and flag changes by other sessions or the control API. The client ends IDLE with DONE (case-insensitive); any other line ends it with BAD.
In the transcripts with two sessions, A C: lines are sent by session A and A S: lines are what it receives, the same for session B. Here A has INBOX selected and idles while B appends a message:
A C: A3 IDLE
A S: + idling
B C: B2 APPEND INBOX {20}
B S: + Go ahead
B C: Subject: hi
B C:
B C: hello
A S: * 5 EXISTS
A S: * 1 RECENT
B S: B2 OK APPEND Completed
A C: DONE
A S: A3 OK IDLE terminated
C: A3 IDLE
S: + idling
C: NOOP
S: A3 BAD Invalid Idle continuation
An IDLE that lasts 30 minutes is ended by the server with * BYE IDLE terminated and the connection is closed. RFC 2177 lets a server log out a client that idles longer than that, which is why clients restart IDLE at least every 29 minutes. The timer does not keep the Node.js process alive.
CONDSTORE
Adds mod-sequences (RFC 7162 section 3.1):
- Every message has a MODSEQ value, and every mailbox a HIGHESTMODSEQ. A message in storage can set its own
MODSEQ, others get the next value when the storage is loaded or the message is added. - SELECT and EXAMINE report
[HIGHESTMODSEQ n], and take the(CONDSTORE)parameter. - Changing flags increments the MODSEQ of each changed message. Expunging increments HIGHESTMODSEQ.
FETCH ... (MODSEQ)and theCHANGEDSINCEFETCH modifier.- The
UNCHANGEDSINCESTORE modifier. Messages changed since then are not stored and are listed in[MODIFIED ...]. - The
MODSEQsearch key. The entry name and type are checked but ignored, as the mod-sequence is not stored per flag. STATUS (HIGHESTMODSEQ).- Flag changes by other sessions include MODSEQ once CONDSTORE is enabled in the session.
- SELECT and EXAMINE send
* OK [CLOSED]when they close the selected mailbox (RFC 7162 section 3.2.11).
CONDSTORE is enabled in a session by ENABLE CONDSTORE (with the ENABLE plugin) or by the first CONDSTORE enabling command: SELECT/EXAMINE with (CONDSTORE), STATUS (HIGHESTMODSEQ), FETCH of MODSEQ or with CHANGEDSINCE, STORE with UNCHANGEDSINCE, or a search with the MODSEQ key. ImapKit gives every message changed by one STORE its own MODSEQ.
C: A2 SELECT INBOX (CONDSTORE)
S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] Flags permitted
S: * 4 EXISTS
S: * 0 RECENT
S: * OK [UNSEEN 2] First unseen message
S: * OK [UIDVALIDITY 1] UIDs valid
S: * OK [UIDNEXT 5] Predicted next UID
S: * OK [HIGHESTMODSEQ 5] Highest
S: A2 OK [READ-WRITE] Completed, CONDSTORE is now enabled
C: A3 FETCH 1:* (FLAGS) (CHANGEDSINCE 1)
S: * 1 FETCH (FLAGS (\Seen) MODSEQ (2))
S: * 2 FETCH (FLAGS () MODSEQ (3))
S: * 3 FETCH (FLAGS (\Flagged) MODSEQ (4))
S: * 4 FETCH (FLAGS () MODSEQ (5))
S: A3 OK FETCH Completed
C: A4 STORE 1:2 (UNCHANGEDSINCE 1) +FLAGS (\Answered)
S: A4 OK [MODIFIED 1,2] STORE completed
C: A5 UID SEARCH MODSEQ 1
S: * SEARCH 1 2 3 4 (MODSEQ 5)
S: A5 OK UID SEARCH completed
C: A6 SELECT Archive
S: * OK [CLOSED] Previous mailbox closed
S: ...
S: * OK [HIGHESTMODSEQ 1] Highest
S: A6 OK [READ-WRITE] Completed
Mod-sequence values that break the RFC 7162 grammar (not a number, 0 for CHANGEDSINCE, more than 63 bits) are BAD. Without the CONDSTORE plugin, messages have no MODSEQ and the CONDSTORE syntax is not accepted.
QRESYNC
Loads CONDSTORE and ENABLE. After ENABLE QRESYNC:
SELECTandEXAMINEtake(QRESYNC (uidvalidity modseq [known-uids] [(known-sequence-set known-uid-set)]))and reportVANISHED (EARLIER)for the expunged UIDs and FETCH responses for the messages whose flags changed.UID FETCH ... (CHANGEDSINCE n VANISHED)reports the UIDs expunged since mod-sequence n.- Expunges are reported with
VANISHEDinstead ofEXPUNGE: EXPUNGE, UID EXPUNGE, MOVE, expunges by other sessions and IDLE notifications.
Expunged UIDs are remembered with their mod-sequence. UIDs missing from the initial storage (gaps below UIDNEXT) count as expunged before the server started.
C: A2 SELECT INBOX (QRESYNC (1 1))
S: A2 BAD QRESYNC parameter requires ENABLE QRESYNC
C: A3 ENABLE QRESYNC
S: * ENABLED QRESYNC
S: A3 OK ENABLE completed
C: A4 SELECT INBOX
S: ...
S: * OK [HIGHESTMODSEQ 5] Highest
S: A4 OK [READ-WRITE] Completed
C: A5 STORE 2 +FLAGS (\Deleted)
S: * 2 FETCH (FLAGS (\Deleted) MODSEQ (6) UID 2)
S: A5 OK STORE completed
C: A6 UID EXPUNGE 2
S: * VANISHED 2
S: A6 OK [HIGHESTMODSEQ 7] UID EXPUNGE completed
C: A7 SELECT INBOX (QRESYNC (1 5 1:4))
S: * OK [CLOSED] Previous mailbox closed
S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] Flags permitted
S: * 3 EXISTS
S: * 0 RECENT
S: * OK [UNSEEN 2] First unseen message
S: * OK [UIDVALIDITY 1] UIDs valid
S: * OK [UIDNEXT 5] Predicted next UID
S: * OK [HIGHESTMODSEQ 7] Highest
S: * VANISHED (EARLIER) 2
S: A7 OK [READ-WRITE] Completed
C: A8 UID FETCH 1:* (FLAGS) (CHANGEDSINCE 5 VANISHED)
S: * VANISHED (EARLIER) 2
S: A8 OK UID FETCH Completed
(UID EXPUNGE comes from the UIDPLUS plugin.)
Strict checks, all answered with BAD:
- the QRESYNC SELECT parameter or the
VANISHEDmodifier withoutENABLE QRESYNC VANISHEDwithFETCHinstead ofUID FETCH, or withoutCHANGEDSINCE- a UIDVALIDITY or mod-sequence of
0,*in the UID sets, and sequence match sets that are not in ascending order or not of the same size
OBJECTID
Adds the object identifiers of RFC 8474:
MAILBOXID: a response code of SELECT, EXAMINE and CREATE, and a STATUS item.EMAILIDandTHREADID: FETCH items and SEARCH keys.
Ids are generated (F1, M1, T1 ...) unless the storage sets a MAILBOXID for a mailbox or an EMAILID and THREADID for a message. Storage values must be valid object ids (1 to 255 letters, digits, _ or -), a MAILBOXID can not repeat, and messages with the same EMAILID must have the same THREADID, otherwise the server throws when it is created.
COPY, MOVE and RENAME of INBOX keep the EMAILID and THREADID of a message. Messages are threaded by their Message-ID, In-Reply-To and References headers across all mailboxes, and a message joins the thread of the nearest known parent when it is added.
C: A2 SELECT INBOX
S: ...
S: * OK [MAILBOXID (F7)] Ok
S: A2 OK [READ-WRITE] Completed
C: A3 FETCH 1:4 (EMAILID THREADID)
S: * 1 FETCH (EMAILID (M1) THREADID (T1))
S: * 2 FETCH (EMAILID (M2) THREADID (T1))
S: * 3 FETCH (EMAILID (M3) THREADID (T2))
S: * 4 FETCH (EMAILID (M4) THREADID (T2))
S: A3 OK FETCH Completed
C: A4 STATUS Archive (MAILBOXID)
S: * STATUS Archive (MAILBOXID (F1))
S: A4 OK Status completed
C: A5 CREATE Lists
S: A5 OK [MAILBOXID (F8)] CREATE completed
C: A6 SEARCH THREADID T1
S: * SEARCH 1 2
S: A6 OK SEARCH completed
With X-GM-EXT-1 loaded too, the messages of a THREADID share one X-GM-THRID.
NOTIFY
Adds NOTIFY SET [STATUS] (filter (events)) ... and NOTIFY NONE (RFC 5465).
| Supported | Values |
|---|---|
| Filters | selected, selected-delayed, inboxes (same as personal), personal, subscribed, subtree, mailboxes |
| Events | MessageNew (with fetch attributes for the selected mailbox), MessageExpunge, FlagChange, MailboxName (LIST with OLDNAME for RENAME), SubscriptionChange, and with METADATA loaded MailboxMetadataChange and ServerMetadataChange |
How events are delivered:
- Events are sent as soon as they happen, also between commands. While a command runs they wait for its tagged response, EXPUNGE (or VANISHED) waits longer with
selected-delayedand during FETCH, STORE and SEARCH. For a new message in the selected mailbox an IMAP4rev1 session gets EXISTS, the requested FETCH and then RECENT (RFC 5465 section 5.2 allows the RECENT response). - After the first NOTIFY a session only hears about the events it asked for, also for the selected mailbox. Changes made by the session itself are not reported.
- Other mailboxes are reported with STATUS: MESSAGES and UIDNEXT, UNSEEN when the
\Seencount changed, and HIGHESTMODSEQ when CONDSTORE is enabled. With ACL, only mailboxes with thelandrrights are reported, and granting or revokinglcounts as MailboxName. - Fetch attributes of MessageNew never set
\Seen. server.notifyOverflow([connection])sends* OK [NOTIFICATIONOVERFLOW]and turns NOTIFY off, for testing how a client recovers. Without an argument it applies to every session with NOTIFY.
Session A has INBOX selected. Session B creates a mailbox, then selects INBOX and expunges message 4 (its SELECT and STORE are left out):
A C: A3 NOTIFY SET (selected (MessageNew (UID FLAGS) MessageExpunge)) (personal (MessageNew MessageExpunge MailboxName))
A S: A3 OK NOTIFY completed
B C: B2 CREATE Receipts
B S: B2 OK CREATE completed
A S: * LIST (\HasNoChildren) "/" Receipts
B C: B5 EXPUNGE
B S: * 4 EXPUNGE
B S: B5 OK EXPUNGE Completed
A S: * 4 EXPUNGE
A S: * 3 EXISTS
Strict checks (RFC 5465 sections 3.1, 5 and 6.1):
C: A4 NOTIFY SET (selected (MessageNew MessageExpunge MailboxName))
S: A4 BAD MailboxName can not be used with SELECTED or SELECTED-DELAYED
C: A5 NOTIFY SET (personal (FlagChange))
S: A5 BAD FlagChange and AnnotationChange require MessageNew and MessageExpunge
C: A6 NOTIFY SET (inboxes (MessageNew MessageExpunge AnnotationChange))
S: A6 NO [BADEVENT (MessageNew MessageExpunge FlagChange MailboxName SubscriptionChange)] Unsupported NOTIFY events
Also BAD: MessageNew without MessageExpunge or the other way round, two selected filters, fetch attributes outside the selected filters, empty event or mailbox lists, and NOTIFY SET without event groups. Unknown events, including AnnotationChange (there is no ANNOTATE support), get NO [BADEVENT (...)] listing the supported events. The fetch attributes of the CONTEXT=SEARCH UPDATE option (RFC 5465 section 7) are not supported.
UIDONLY
Loads ENABLE. After ENABLE UIDONLY, the session must use UIDs everywhere (RFC 9586):
FETCH,STORE,SEARCH,COPY,MOVE,SORT,THREADandREPLACEare refused withBAD [UIDREQUIRED], use their UID variants. A synchronizing literal of such a command is refused before it is sent.- Message numbers in the criteria of UID SEARCH, UID SORT, UID THREAD and the ESEARCH command of MULTISEARCH, and the QRESYNC message sequence match data, are refused the same way.
- FETCH responses become
* <uid> UIDFETCH (...). TheUIDitem is only included when UID FETCH asks for it. - Expunges are reported with
VANISHED, and SELECT does not send[UNSEEN n]. EXISTS and RECENT do not change.
Load UIDPLUS as well for UID EXPUNGE and COPYUID.
C: A2 ENABLE UIDONLY
S: * ENABLED UIDONLY
S: A2 OK ENABLE completed
C: A3 SELECT INBOX
S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] Flags permitted
S: * 4 EXISTS
S: * 0 RECENT
S: * OK [UIDVALIDITY 1] UIDs valid
S: * OK [UIDNEXT 5] Predicted next UID
S: A3 OK [READ-WRITE] Completed
C: A4 FETCH 1 (FLAGS)
S: A4 BAD [UIDREQUIRED] FETCH is not allowed once UIDONLY is enabled, use UID FETCH
C: A5 UID FETCH 1:2 (FLAGS)
S: * 1 UIDFETCH (FLAGS (\Seen))
S: * 2 UIDFETCH (FLAGS ())
S: A5 OK UID FETCH Completed
C: A6 UID SEARCH 1:2
S: A6 BAD [UIDREQUIRED] Message numbers are not allowed in the search criteria once UIDONLY is enabled, use UID <sequence set>
C: A7 UID STORE 2 +FLAGS (\Deleted)
S: * 2 UIDFETCH (FLAGS (\Deleted))
S: A7 OK UID STORE completed
C: A8 UID EXPUNGE 2
S: * VANISHED 2
S: A8 OK UID EXPUNGE completed