Adds a minimal ManageSieve client so the existing "Email filters" UI
works for IMAP accounts, not just JMAP. SieveRepository becomes a
dispatcher that routes to JMAP or ManageSieve based on account.type.
Account model + DB schema v15 grow manageSieveHost/Port/Ssl fields
(default 4190 / TLS, host falls back to imapHost when blank). The Add
and Edit account screens expose them inside a collapsed ExpansionTile
to keep the form short for users who accept defaults.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Added lint rule to analysis_options.yaml and ran dart fix --apply to convert
125 relative imports in 33 files to package:sharedinbox/... style.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add live-connection Sieve script management for JMAP accounts via
RFC 9661: list, create, edit, delete, and activate scripts.
Accessible via "Email filters" in the account popup menu.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds downloadBlob() to JmapClient (RFC 8620 §6 downloadUrl template)
and routes downloadAttachment() to it for JMAP accounts. IMAP path
is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When account.verbose is true, raw IMAP/JMAP protocol traffic is captured
via a Zone, redacted of credentials (LOGIN password, AUTHENTICATE tokens),
and stored in the sync log entry for display in the sync log screen.
- DB schema v13: adds verbose column to accounts, protocolLog to sync_logs
- IMAP: Zone print-capture feeds ImapClient isLogEnabled output
- JMAP: JmapClient.call() writes request/response bodies to zone buffer
- Sync log screen: shows a monospace "Protocol log" block when present
- Edit account screen: adds verbose toggle with warning subtitle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: flutter test ran all 3 integration test files in parallel
against the same Stalwart instance. Concurrent SMTP/IMAP from
email_repository_imap_test and concurrent_sync_test caused SMTP rate
limiting (4th send hung for ~27s) and flushPendingChanges race failures.
Fixes:
- stalwart-dev/test.sh: add --concurrency=1 so test files run serially
- concurrent_sync_test: reduce timeout 2 min → 30 s (tests now pass in ~2s)
- imap_client_factory + test helpers: set defaultResponseTimeout=20s on
ImapClient so individual IMAP commands never block indefinitely
- jmap_client: reduce HTTP call timeout 30 s → 10 s (local server; keeps
stacked-timeout total well below any reasonable per-test limit)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Check notUpdated/notDestroyed per-item errors in Email/set; throw
JmapSetItemException for permanent failures (notFound, forbidden) so
they are discarded immediately rather than retried
- Add _maxChangeAttempts=5 constant; _recordChangeError() evicts the
pending-change row when attempts reach the limit, preventing unbounded
queue growth from transient errors
- Both IMAP and JMAP flush paths now use _recordChangeError() consistently
- Document server-wins conflict-resolution policy in DB-SYNC.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sendEmail dispatches on account type: IMAP keeps SMTP+APPEND path,
JMAP chains Email/set create + EmailSubmission/set in one API call
- Sent mailbox looked up by role='sent' from local DB so sent mail lands
in the right folder
- JmapClient gains uploadUrl/eventSourceUrl/capabilities from session,
supportsSubmission getter, withSubmission flag on call(), and uploadBlob()
for attachment upload before send
- Mailboxes table gains nullable role column (schema v8); _upsertJmapMailboxes
persists role from JMAP Mailbox/get response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Include bodyValues/textBody/htmlBody/attachments in every Email/get call
during syncEmails; _upsertJmapEmails writes to email_bodies so first open
is instant even for freshly synced messages
- Extract _parseJmapBody helper shared by sync path and on-demand fetch
- Add JmapStateMismatchException; _applyPendingChangeJmap passes ifInState
and returns newState; on stateMismatch the local checkpoint is cleared so
the next cycle does a full re-sync before retrying the mutation
- Update DB-SYNC.md to reflect what has been implemented
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parses the JMAP Session object (RFC 8620 §2): fetches GET {jmapUrl},
extracts apiUrl and primary accountId, and wraps API calls via
call(methodCalls) which POSTs to apiUrl with Basic Auth.
Handles relative apiUrl, primaryAccounts fallback, and top-level
JMAP error responses. Covered by unit tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>