sync(chunk 2b): CalDAV discovery and auth protocol
New :caldav module — MIT, plain JVM, api-depends on :dav. Separate from the vendored MPL tree so the licences stay unmixed, and so "no Android types" is a compile-time guarantee rather than a discipline. Chunk 2 split again: the Android account layer (Keystore, AccountManager, Custom Tabs, account-add UI) is 2c, with different verification and an on-device review. - ServiceDiscovery: the RFC 6764 ladder. SRV priority/weight, TXT path=, non-443 ports, "." targets, well-known then root. - CollectionClassifier: the two filters that are inversions of the obvious rule. An absent or empty supported-calendar-component-set means "supports everything", and classification is a positive test for CALDAV:calendar on an unordered set — excluding schedule-outbox would drop SOGo's main calendar. - CalDavDiscovery: OPTIONS gate, principal, every home set, Depth-1 by name. A failing home set does not fail the account, and every home set failing is reported as an error rather than as an account with no lists. - NextcloudLoginFlow: POST not GET, a User-Agent the user can recognise when revoking, 404-means-pending only, both URLs origin-checked, host mismatch carried rather than refused (reverse proxies are ordinary). - PreemptiveBasicInterceptor, ServerQuirks. dnsjava 3.6.3 (BSD-3) added: Android's DnsResolver is callback-only and cannot do the TXT path lookup, and JNDI's DNS provider does not exist on Android. Behind an interface, so every trap is tested with a fake and no network. :dav gains change 6 — <D:unauthenticated/> is parsed rather than inferred from a null href, which also fires on a merely non-conformant empty element. 52 tests here, 78 in :dav. SYNC.md's live-probed trap table is executable now.
This commit is contained in:
+33
-6
@@ -57,7 +57,8 @@ Nextcloud/Radicale/Baïkal in chunk 5.
|
||||
|---|---|---|---|
|
||||
| 1 | **Mapper** — VTODO ↔ Room | nothing | Unknown properties survive a read-modify-write cycle |
|
||||
| 2a | **Vendored DAV foundation** | nothing | Vendor a tree we can actually maintain, and know which of its defects are real |
|
||||
| 2b | **Auth, discovery, account** | 2a | Never send a credential into an unvalidated redirect chain |
|
||||
| 2b | **Discovery and auth protocol** | 2a | The two collection filters are inversions of the obvious rule |
|
||||
| 2c | **The Android account layer** | 2b | Never send a credential into an unvalidated redirect chain |
|
||||
| 3 | **Engine core** — read, write, conflicts | 1, 2 | A failed resource must not fail the collection |
|
||||
| 4 | **Incremental sync + scheduling** | 3 | Never persist a sync token before the bodies it covers are applied |
|
||||
| 5 | **Hardening, compliance, real servers** | 1–4 | Ship no licence violation, and no retry loop on a dead app password |
|
||||
@@ -200,7 +201,32 @@ runtime dependency are gone, and `:app` links against the module.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2b — auth, discovery, account
|
||||
## Chunk 2b — the discovery and auth protocol
|
||||
|
||||
⚠️ **Split again while building it.** The seam is the platform: everything here
|
||||
is protocol, testable on a JVM against MockWebServer, and none of it touches
|
||||
Android. Keystore, `AccountManager`, Custom Tabs and the account-add UI are a
|
||||
different kind of work with a different kind of verification — including the
|
||||
on-device review — so they are chunk 2c.
|
||||
|
||||
Lands in a new `:caldav` module: MIT, plain JVM, `api`-depending on `:dav`. A
|
||||
separate module from the vendored MPL tree keeps the licences unmixed and makes
|
||||
"no Android types" a compile-time guarantee rather than a discipline.
|
||||
|
||||
**One dependency added:** `dnsjava` 3.6.3 (BSD-3) for the SRV/TXT half of
|
||||
RFC 6764. Android's own `DnsResolver` is callback-only and cannot do the
|
||||
`TXT path=` lookup, and JNDI's DNS provider does not exist on Android at all.
|
||||
It is what DAVx5 uses. Behind a `DnsResolver` interface so the pipeline is
|
||||
testable without a network.
|
||||
|
||||
**Done when:** the trap table is executable — Posteo's SRV-only 8443, GMX's
|
||||
`TXT path=`, null SRV targets, Google's dead-end record, SOGo's triple
|
||||
resourcetype, Nextcloud's trashed calendar, the absent/empty component set, and
|
||||
`<D:unauthenticated/>` as a 200 that means failure.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2c — the Android account layer
|
||||
|
||||
**Goal:** the app can add a CalDAV account and list its VTODO collections. No
|
||||
syncing yet.
|
||||
@@ -466,10 +492,11 @@ attribution screen ships, and a `releaseTest` build syncs on a real device.
|
||||
## Definition of done, every chunk
|
||||
|
||||
1. It builds: `./gradlew :app:assembleDebug`.
|
||||
2. Unit tests pass: `./gradlew :app:testDebugUnitTest :dav:test`. ⚠️ Name
|
||||
`:dav:test` explicitly — `testDebugUnitTest` is an Android-variant task and
|
||||
does not exist on a plain JVM module, so the vendored suite would otherwise be
|
||||
compiled by nobody and run by nobody.
|
||||
2. Unit tests pass:
|
||||
`./gradlew :app:testDebugUnitTest :dav:test :caldav:test`. ⚠️ Name the plain
|
||||
JVM modules explicitly — `testDebugUnitTest` is an Android-variant task and
|
||||
does not exist on them, so their suites would otherwise be compiled by nobody
|
||||
and run by nobody. Add each new module here **and** to CI.
|
||||
3. Lint passes: `./gradlew :app:lintDebug`. Added after chunk 1 shipped a literal
|
||||
byte-order mark that builds and tests both accepted and CI's lint step would
|
||||
have rejected.
|
||||
|
||||
Reference in New Issue
Block a user