fix(activesync): stabilize folder UIDs after hierarchy changes#81
Conversation
Derive new EAS folder UIDs deterministically from backend ids so concurrent FolderSync resets produce consistent mappings when contact address books change. Handle stale folder references in MoveItems with INVALID_SRC instead of HTTP 500.
There was a problem hiding this comment.
Pull request overview
This pull request improves ActiveSync resilience during folder hierarchy churn by making EAS folder UIDs deterministic (instead of random) and ensuring MoveItems returns a protocol-level error when folder UIDs are stale, preventing HTTP 500s and helping clients converge after FolderSync resets.
Changes:
- Generate deterministic folder UIDs in
Horde_ActiveSync_Driver_Base::_getFolderUidForBackendId()using acrc32-based derivation. - Catch
Horde_ActiveSync_Exception_FolderGoneduringMoveItemsprocessing and returnSTATUS_INVALID_SRCinstead of failing the request. - Add unit tests covering deterministic UID generation and stale-folder
MoveItemshandling.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| lib/Horde/ActiveSync/Driver/Base.php | Switches folder UID generation from random to deterministic based on backend id. |
| lib/Horde/ActiveSync/Request/MoveItems.php | Ensures MoveItems handles stale folder UIDs by catching FolderGone and returning an error status. |
| test/unit/Horde/ActiveSync/FolderUidDeterministicTest.php | Adds tests validating deterministic UID generation and persisted-map behavior. |
| test/unit/Horde/ActiveSync/MoveItemsFolderGoneTest.php | Adds tests validating MoveItems behavior when source/destination folder UIDs are stale. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
🔍 CI ResultsOverall: ❌ 12/12 lanes failed TL;DR: ❌ Quality issues: PHPUnit: 54 tests failed; PHPStan: 331 unique errors in 8 lanes; PHP-CS-Fixer: 19 files. Summary by PHP Version
Quality Metrics
❌ Failed Lanesphp8.0-dev
php8.0-stable
php8.1-dev
php8.1-stable
php8.2-dev
php8.2-stable
php8.3-dev
php8.3-stable
php8.4-dev
php8.4-stable
php8.5-dev
php8.5-stable
CI powered by horde-components • View full results |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Add a focused backlog entry for persisting or atomically rebuilding the backend-id to EAS-folder-UID map after #81. Note that opaque UID assignment should return once the structural fix removes the empty-map race.
Release version 3.1.0 fix(sync): emit SYNC_MODIFY replies for EAS 16 email modifies fix(activesync): throttle deferred-import keep-alive tokens fix(activesync): address streaming review findings docs(activesync): restructure documentation by audience feat(activesync): defer up-sync imports and emit WBXML keep-alives when streaming docs(activesync): note batch-size vector consolidation for Horde 6 feat(activesync): stream Sync responses incrementally Merge pull request #82 from horde/fix/settings-oof-empty-msgs fix(activesync): guard empty OOF messages in Settings response docs(todo): document folder UID map long-term work docs(activesync): explain crc32 mask in folder UID generation Merge pull request #81 from horde/fix/deterministic-folder-uids-moveitems-gone Potential fix for pull request finding Potential fix for pull request finding Potential fix for pull request finding fix(activesync): stabilize folder UIDs after hierarchy changes Merge pull request #80 from horde/fix/imap-mimepart-null-charset fix(activesync): guard null charset before strcasecmp in getMimePart()
Summary
crc32) instead ofmt_rand(), so concurrent FolderSync resets after hierarchy changes (e.g. moving contacts between Turba address books) assign stable UIDsMoveItemsSTATUS_INVALID_SRCwhen source or destination folder UIDs are no longer in cache, instead of bubblingFolderGoneas HTTP 500Motivation
When the folder hierarchy changes on the server, iOS devices can issue concurrent FolderSync resets and MoveItems against stale folder UIDs. Random per-reset UIDs prevented the device from converging; uncaught
FolderGoneduringMoveItemsinit made recovery worse by returning HTTP 500.Changes
Horde_ActiveSync_Driver_Base::_getFolderUidForBackendId(): deterministic UID viasprintf('%s%08x', $prefix, crc32($prefix . ':' . $id))Horde_ActiveSync_Request_MoveItems: wrapImporter::init()in existing exception handling; catchHorde_ActiveSync_Exception_FolderGoneFolderUidDeterministicTest,MoveItemsFolderGoneTestTest plan
vendor/bin/phpunit --bootstrap vendor/autoload.php test/unit/Horde/ActiveSync/FolderUidDeterministicTest.phpvendor/bin/phpunit --bootstrap vendor/autoload.php test/unit/Horde/ActiveSync/MoveItemsFolderGoneTest.phpMoveItemsno longer produces HTTP 500 in server logs