Hello Quentin,
thanks for your feedback.
I have created a pull request for the tested one-line SOPE change:
https://github.com/Alinto/sope/pull/73
I have also opened a corresponding Cyrus IMAP issue for the backend-side 
behavior:
https://github.com/cyrusimap/cyrus-imapd/issues/6071
Best regards,
Guido

-------- Original-Nachricht --------
Betreff: Re: [SOGo] EAS with Cyrus: deletions/moves via IMAP or webmail never 
reach ActiveSync clients
Datum: Sonntag, Juni 14, 2026 14:20 CEST
Von: "\"Quentin HIVERT\"" ([email protected]) <[email protected]>
Antwort an: [email protected]

An: [email protected]
Referenzen: <[email protected]>



 
Hello, thanks a lot for this thorough investigation!

Would you like to make a PR for this?

-- 
 




Quentin Hivert



Lead Dev R&D 




[email protected]



Alinto

19, quai Perrache

FR-69002 Lyon 




https://www.alinto.com


Linkedin FR






Le Vendredi, Juin 12, 2026 11:02 CEST, "\"Guido\"" ([email protected]) 
<[email protected]> a écrit:

 
Hello,
I'd like to report a bug in SOGo's ActiveSync implementation that causes 
messages deleted (or moved to another folder) via IMAP or the SOGo web UI to 
remain permanently on EAS clients (tested with iOS/iPadOS Mail). I have fully 
root-caused the issue down to the exact line of code and have a tested one-line 
fix.
Environment:
- SOGo 5.x nightly (Ubuntu 24.04, packages.sogo.nu/nightly/5)
- Backend: Cyrus IMAP 3.8.2 (behind a transparent Dovecot proxy used only for 
XOAUTH2 authentication; all commands are passed through 1:1)
- Backend: Cyrus IMAP 3.8.2 — same problem when connected directly to Cyrus 
(without the Dovecot proxy)
- EAS client: iOS/iPadOS Mail
Symptom:
When a message is expunged from a folder outside of EAS (IMAP client, webmail), 
the deletion is not synced to the ActiveSync device. Crucially, this happens 
exactly when the expunged message holds the HIGHEST UID in the mailbox (i.e. 
the newest message — which on a phone is the one most likely to be deleted). 
Deleting older messages syncs fine, which made the issue look intermittent for 
a long time.
Root cause:
In syncTokenFieldsWithProperties (SoObjects/Mailer/SOGoMailFolder.m), SOGo 
detects external expunges via SOPE's fetchVanished: 
(sope-mime/NGImap4/NGImap4Client.m, ~line 1257), which issues:
   UID FETCH 1:* (UID) (CHANGEDSINCE <modseq> VANISHED)
Cyrus resolves '*' to the highest VISIBLE (non-expunged) UID (imap/index.c, 
_parse_sequence(): maxval = index_getuid(state, state->exists)). In 
index_vanished(), expunged records are then filtered with seqset_ismember() 
against that sequence. If the expunged message had the highest UID, it now lies 
ABOVE the resolved upper bound of '1:*' and is silently excluded — Cyrus 
returns no VANISHED response at all.
SOGo consequently never emits a <Delete> command, but still advances the 
SyncKey past the expunge modseq. The deletion is permanently lost for that 
device; the ghost message stays until a full folder resync.
Wire trace (after deleting the newest message, UID 15626; M0 = HIGHESTMODSEQ 
before deletion):
   C: a005 UID FETCH 1:* (UID) (CHANGEDSINCE 181806 VANISHED)
   S: a005 OK Completed (0.002 sec)            <- no VANISHED response
   C: a004 SELECT "INBOX" (QRESYNC (1146603129 181806))
   S: * VANISHED (EARLIER) 15626               <- Cyrus DOES have the expunge 
history
   C: a004 UID FETCH 1:4294967295 (UID) (CHANGEDSINCE 181806 VANISHED)
   S: * VANISHED (EARLIER) 15626               <- explicit upper bound works
Fix (tested):
In sope-mime/NGImap4/NGImap4Client.m, fetchVanished:, replace the '1:*' with an 
explicit upper bound:
-    @"UID FETCH 1:* (UID) (CHANGEDSINCE %llu VANISHED)"
+    @"UID FETCH 1:4294967295 (UID) (CHANGEDSINCE %llu VANISHED)"
With this single change, deletions AND moves performed via IMAP/webmail are now 
reliably propagated to the EAS device in my setup. Dovecot is unaffected by the 
change (it resolves '*' generously, which is why this bug never shows up on 
Dovecot backends — the combination SOGo+Cyrus is rare).
One could argue Cyrus' interpretation of '*' is the underlying problem (I will 
report it there as well), but since RFC 3501 defines '*' as the highest UID "in 
the mailbox" (which after the expunge no longer covers the vanished UID), 
SOGo/SOPE should be robust against this strictly conformant reading. The 
canonical QRESYNC resync idiom breaks otherwise.
Best regards,
Guido



 

Reply via email to