sync: what the on-device round found

Everything here came from running the account flow against a real
Nextcloud rather than from reading the code.

Discovery
- A typed bare origin now gets the RFC 6764 well-known probe. It was
  returned as the only candidate, so `https://cloud.example.com` — what
  people actually type — was PROPFIND'd against the web UI, answered 405,
  and a working Nextcloud reported as "not a CalDAV server".
- A same-host HTTPS→HTTP redirect is put back on TLS instead of refused
  (`dav` change 7). A Nextcloud behind a TLS-terminating proxy without
  `overwriteprotocol` builds every redirect with http://, including the
  /.well-known/caldav hop discovery depends on. Cross-host still throws.
- Outcomes carry a `Cause` the UI translates, not the server's own words.
  "HTTP 405 Method Not Allowed" told someone entering an address nothing,
  in a language they may not read, from outside strings.xml.
- An IPv6 origin keeps its brackets: `HttpUrl.host` returns "fd00::1", so
  the rebuilt origin did not parse and a homelab address came back as
  "not an address".

Login Flow v2
- The poll response's scheme is coerced, never refused. Nextcloud returns
  the app password exactly once, so throwing there burned a live
  credential and left it dangling in the user's device list. The host
  mismatch already worked this way; the scheme now matches it.

Accounts
- The accounts screen observes Room and the sign-in state instead of
  taking a snapshot, so a sync landing — or a 401 stopping an account —
  reaches a screen that is already open.
- A per-account detail screen, and provider identity (`CalDavProvider`)
  shared with the quirk table so one list drives both the icon and the
  warning.
- The password field masks: floret-kit's `InlineTextField` gained a
  visual transformation, since `KeyboardType.Password` only tells the IME
  to drop suggestions.
This commit is contained in:
2026-09-07 18:41:45 +02:00
parent 28b2423ad9
commit b4a7fcb46e
31 changed files with 1455 additions and 280 deletions
+25
View File
@@ -155,3 +155,28 @@ exists in this version:
Fetch the new tag, diff against `f434c9d`, reapply changes 1–4, run
`./gradlew :dav:test`. If upstream's suite fails, the port is wrong — that is the
entire reason it is vendored alongside the code.
## Change 7 — a same-host HTTPS→HTTP redirect is upgraded, not refused
`DavResource.followRedirects` threw `DavException("Received redirect from HTTPS
to HTTP")` for any downgrade. That is right for a redirect to a *different* host,
which has no innocent reading. It is wrong for the same host, and the same host
is the case that actually occurs.
⚠️ **A Nextcloud behind a TLS-terminating reverse proxy without
`overwriteprotocol` — or without `proxy_set_header X-Forwarded-Proto $scheme` —
builds every redirect with `http://`.** That includes the `/.well-known/caldav`
hop RFC 6764 discovery depends on. The server is entirely functional:
`/remote.php/dav/` answers 401 over HTTPS exactly as it should. But discovery
refuses the downgrade, falls back to a `PROPFIND` on the web root, gets the 405
an ordinary web server returns, and reports "not a CalDAV server" about a working
CalDAV server.
Now: when the redirect target's host matches the current one, the scheme is put
back to `https` and the hop continues. Re-issuing the same host and path over TLS
is *strictly safer* than obeying the redirect as sent, and it preserves the
invariant that matters — credentials never travel in cleartext. A cross-host
downgrade still throws.
Found against a real server, not by reading: `cloud.jeanlucmakiola.de` returns
`301 → http://cloud.jeanlucmakiola.de/remote.php/dav/`.