View Issue Details

IDProjectCategoryView StatusLast Update
0006241SOGoSOPEpublic2026-08-30 23:10
Reporterswallowtail Assigned To 
PrioritynormalSeveritymajorReproducibilityalways
Status newResolutionopen 
PlatformServerOSRed HatOS Version10
Product Version5.12.10 
Summary0006241: fetchVanished: misses an expunged highest UID when using UID FETCH 1:* against Cyrus IMAP 3.8
Description

I updated from Cyrus IMAP 3.0.7 on RHEL 8 to 3.8.3 on RHEL 10 over the weekend. SOGo runs on a separate RHEL 9 server.

Symptom:
SOGo ActiveSync clients retain a deleted Inbox message when the deleted message was the mailbox's highest live UID. Additions continue to synchronize, SOGo webmail shows the correct mailbox, and independent ActiveSync clients (Nine and Samsung Email) fail identically.

Environment:

  • SOGo and sogo-activesync 5.12.10 (30 August 2026 nightly)
  • SOPE 4.9 from the same nightly
  • Cyrus IMAP 3.8.3
  • QRESYNC/CONDSTORE enabled
  • expunge_mode: delayed and autoexpunge: yes

After expunging the mailbox's highest live UID, Cyrus 3.8 resolves * to the highest UID still present. The expunged UID is therefore outside the requested UID set and is not returned as VANISHED. SOGo advances the collection MODSEQ without generating an ActiveSync Delete, leaving the client entry stale.

This differs from Cyrus 3.0.7. Its _parse_sequence() used state->last_uid as the UID * expansion. Cyrus 3.8.3 instead uses index_getuid(state, state->exists) when messages remain, matching the highest surviving UID. The behavior change exposes SOPE's use of an ambiguous upper bound for vanished messages.

Direct reproduction after expunging the highest UID:

UID FETCH 1:* (UID) (CHANGEDSINCE <previous-modseq> VANISHED)

does not return the expunged UID, while both of these do:

UID FETCH 1:<UIDNEXT-1> (UID) (CHANGEDSINCE <previous-modseq> VANISHED)
UID FETCH 1:4294967295 (UID) (CHANGEDSINCE <previous-modseq> VANISHED)

Proposed one-line fix:

diff

  • @"UID FETCH 1:* (UID) (CHANGEDSINCE %llu VANISHED)",
  • @"UID FETCH 1:4294967295 (UID) (CHANGEDSINCE %llu VANISHED)",

The full UID range avoids * changing after the highest UID is expunged.

Validation: we packaged this change in a local fix. A disposable message was confirmed visible in SOGo, Nine and Samsung before deletion. SOGo deleted it; Cyrus reported the allocated UID absent, no live \Deleted records, and an advanced HIGHESTMODSEQ. Both ActiveSync clients then removed the message by normal refresh without resetting the folder or account.

Steps To Reproduce

See above

TagsNo tags attached.

Activities

swallowtail

swallowtail

2026-08-30 23:10

reporter  

sope_patch.txt (875 bytes)   
--- a/sope-mime/NGImap4/NGImap4Client.m
+++ b/sope-mime/NGImap4/NGImap4Client.m
@@ -1254,8 +1254,14 @@ static NSMutableDictionary *namespaces;

   pool = [[NSAutoreleasePool alloc] init];

+  /*
+   * Do not use "*" as the upper UID bound here.  RFC-compliant servers may
+   * resolve it to the highest message still present, which excludes an
+   * expunged message when that message had the mailbox's highest UID.  The
+   * complete legal UID range ensures VANISHED reports that deletion.
+   */
   cmd  = [NSString stringWithFormat:
-                     @"UID FETCH 1:* (UID) (CHANGEDSINCE %llu VANISHED)",
+                     @"UID FETCH 1:4294967295 (UID) (CHANGEDSINCE %llu VANISHED)",
                    (unsigned long long)_modseq];
   fetchres = [self processCommand:cmd];
   result   = [[self->normer normalizeFetchResponse:fetchres] retain];
sope_patch.txt (875 bytes)   

Issue History

Date Modified Username Field Change
2026-08-30 23:10 swallowtail New Issue
2026-08-30 23:10 swallowtail File Added: sope_patch.txt