forwardemail / forwardemail/forwardemail.net
Add a CardDAV contacts-aware Sieve test condition (e.g. address :contacts "From")
- Dominant language
- JavaScript
- Stars
- 1.7k
- Forks
- 203
- PR merge metrics
- No merged PRs in 30d
Description
Customer-suggested feature (spam triage use case, see support thread 2026-09-09). Request is for a Sieve test condition that checks whether a message's From address matches a saved CardDAV contact for that mailbox, so scripts can tag (not block) mail from non-contacts, e.g. addheader "X-Not-A-Contact" "true", for later review. Explicitly not looking for auto allow/deny, since plenty of legitimate mail (banks, government, retailers) will never be a personal contact.
Confirmed nothing like this exists today: grep -n "contacts" helpers/sieve/*.js -i returns zero matches across engine.js, extensions.js, filter-handler.js, integration.js, validator.js, security.js, managesieve-server.js, parser-generated.js, store.js, script-checker.js. No :contacts match-type, no contact test anywhere in the registered test inventory (engine.js, evaluateTest switch, lines ~637-739).
Feasibility: this is a scoped addition, not a rebuild or third-party fork. The Sieve engine is fully in-house (custom PEG grammar in helpers/sieve/grammar.pegjs, compiled to parser-generated.js, executed by a switch-dispatch interpreter in engine.js), so no vendored Sieve library constraint applies. Contacts are already stored per-alias (app/models/contacts.js, emails[].value field, isolated per-alias via the per-alias SQLite database in helpers/get-database.js), and the alias identity needed to query that alias's contacts is already threaded into the engine's execution context today (filter-handler.js processMessage(aliasId, message, context), populated from session.user.alias_id in helpers/parse-payload.js). The engine also already supports async test evaluation (see DuplicateTest, engine.js:713), so an async contacts lookup fits the existing pattern.
Scope of work: grammar change in grammar.pegjs (+ regenerate parser-generated.js) for a new test/match-type, a new case in engine.js's evaluateTest switch that queries Contacts by the alias's address book and matches against the From header, and updating the supported-capabilities lists (helpers/sieve/index.js, engine.js, filter-handler.js, managesieve-server.js, script-checker.js, validator.js, extensions.js) if exposed as a named extension/capability.
Contributor guide
Research direction
Start with helpers/sieve/grammar.pegjs and the evaluateTest switch in helpers/sieve/engine.js, then trace alias context through filter-handler.js and helpers/parse-payload.js to the per-alias contacts model. Review the existing DuplicateTest async pattern and the capability lists named in the issue. Done means the new condition parses, checks the From address against that alias's contacts, and is consistently advertised where appropriate.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, sqlite
- Domain
- backend, databases
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100