first commit

This commit is contained in:
Mateusz Gruszczyński committed 2026-11-05 12:52:32 +01:00
commit 19dfc51360
85 files changed
+25504

No files matched your search

+78
View File
@@ -0,0 +1,78 @@
# Development
## Architecture
```
src/
main.rs CLI (serve, admin, config, device, probe)
lib.rs public router (ActiveSync, Autodiscover, /, /healthz)
app.rs application state: settings (ArcSwap), IMAP pool, metrics, lockouts
config.rs settings model (serde, validation, merging, secrets)
db.rs SQLite (sqlx, WAL), migrations in migrations/, housekeeping
server.rs HTTP/1.1 + HTTP/2 listener, optional TLS, graceful shutdown
wbxml/ WBXML encoder/decoder + MS-ASWBXML code pages
eas/ ActiveSync protocol
mod.rs request parsing (plain/base64 query), auth, provisioning, dispatcher
state.rs SyncKey {uuid}N and states (item fingerprints)
folders.rs FolderSync / Create / Update / Delete
sync.rs Sync, GetItemEstimate, MoveItems
ping.rs Ping and the shared wait-for-changes loop (hanging Sync)
provision.rs policies, remote wipe
mailcmd.rs SendMail / SmartReply / SmartForward / MeetingResponse
itemops.rs ItemOperations, GetAttachment
misc.rs Settings, Search, ResolveRecipients
convert/ RFC 5322 / iCalendar / vCard <-> ApplicationData
backend/
imap.rs own async IMAP client (literals, UIDPLUS, MOVE, SPECIAL-USE)
dav.rs CalDAV/CardDAV client
smtp.rs SMTP (lettre) / sendmail
ical.rs iCalendar/vCard parser and serializer
tz.rs EAS <-> IANA time zones, dates
mime.rs MIME building (SmartReply/Forward)
mod.rs session: folder hierarchy, fingerprint listings, operations
autodiscover.rs
admin.rs panel API, sessions, CSRF, SSE, Prometheus
web/ panel (dependency-free HTML/CSS/JS, embedded in the binary)
```
### Synchronisation
The device state of a folder is a map `item id -> fingerprint` (IMAP: flags; DAV: ETag) stored under the key
`{uuid}N`. Sync compares the backend listing with the state and sends the differences (deletes -> changes ->
adds, newest mail first) within `WindowSize`. The new state is stored as `N+1`; the previous one is kept to
handle lost responses (the device replays the old key). Client adds are idempotent thanks to the `ClientId` map.
Ping keeps a folder signature (IMAP `STATUS UIDNEXT/MESSAGES/UNSEEN/HIGHESTMODSEQ`, DAV CTag); a full listing
comparison only happens when the signature changes.
Collection ids that a device uses but that do not exist (Z-Push leftovers) are remembered per device and sent
as FolderHierarchy Delete on the next FolderSync.
## Development environment
```bash
sudo apt install dovecot-imapd # any dovecot >= 2.3
pip install radicale aiosmtpd
./dev.sh backends # Dovecot :11143, Radicale :15232, SMTP sink :12525
./dev.sh seed && ./dev.sh # ActivePush :8081 / panel :9091
./dev.sh e2e # test of every command against live backends
```
Test accounts: `jan`, `anna`, `biuro`, `kasia` `@test.local`, password `Test-Password-123`.
`./dev.sh lint` - rustfmt, clippy `-D warnings`, JS syntax check and WBXML tag name check
(`scripts/check-wbxml-names.py`). `./dev.sh test` - unit tests (WBXML, IMAP tokenizer, UTF-7, calendar/contact/
task conversions, time zones, query parser, CIDR, MIME).
Protocol debugging: add the login to **ActiveSync -> Logging -> Debug users**; decoded WBXML of requests and
responses goes to the log.
Simulating devices: `active-push probe --device-id F2LX8KQ1N72J --device-type iPhone --model "iPhone 15 Pro" ...`.
## Documentation screenshots
`tools/dev/screenshots.js` (Playwright) regenerates `docs/images/` from a running dev instance:
```bash
node tools/dev/screenshots.js http://127.0.0.1:9091 admin 'password'
```