Commit Graph
4 Commits
Author SHA1 Message Date
Linus Rath 726e087795 fix: keep offline edits when the app cannot read an account's server record
The app records which server filled each account's caches and checks it
with every new session (a330d77). A read of that record that failed -
a storage error, such as a busy database - answered "none recorded",
so the session counted as the first sight of the account: its queued
offline edits were dropped, with a notice that the mail server had
changed when it had not, and the record was written anew. Had the
server been replaced, that record hid the change from every later
check.

A read that fails now fails the check: nothing is dropped or written,
and the next session checks again. As with any failed check, signing
in goes on.
2026-09-26 05:09:49 +02:00
Linus Rath a330d77d6d fix: keep push, queued edits and iCal feeds safe on a replaced server
The app compares the server behind an account with the one it recorded
and drops what came from a replaced one. The released version recorded
none, so on the first start of this one there is nothing to compare
with, and a server replaced before the update (Stalwart 1.0 is a fresh
install) went unnoticed:

- the push subscription expiry the released version recorded made the
  subscription the new server lacks look revoked, and push was turned
  off for good; it is now forgotten on the first sight of an account, so
  the subscription is re-created
- the queue of offline edits the released version left was replayed
  against the new server, where its ids name other messages, so a
  queued delete deleted another message; it is now discarded on the
  first sight, with the notice a replaced server shows. Nothing proves
  a queued edit belongs to the server: the queue records none, a
  reinstalled Stalwart may accept old JMAP states, and a queued move or
  delete took its message out of the cached list, so there is no copy
  to compare the server's message with

iCal subscriptions recognise their mirror calendar by name, but a sync
still imported the feed into whatever calendar the recorded id named:

- the feed now goes only into a calendar that carries the
  subscription's name, and otherwise into a new mirror; when the name
  can't be read, the sync fails and is tried again
- a rename the server refused renamed the subscription anyway, which
  then no longer recognised its mirror: pruning stopped and removing the
  subscription kept the calendar. Now nothing changes and the error is
  shown
- a replaced server claimed subscriptions made before they recorded
  their account for every account with the same JMAP id, another
  server's account included. They now go only when no other signed-in
  account has that id, or may have it, and a sync that finds their
  mirror records the account on them
2026-09-26 01:43:22 +02:00
Linus Rath f490886a8b fix: keep iCal subscriptions safe when the mail server was replaced
When the server behind an account is replaced (a fresh Stalwart 1.0
install at the address 0.16 ran on), its ids start over. The app
already dropped its caches then, but kept what the user set up with
server ids inside:

- iCal subscriptions kept the ids of their mirror calendars. The next
  sync deleted the "stale" events of whatever calendar now had the id
  and filled it with the feed, and "Remove subscription" destroyed it.
  Now the account's subscriptions keep their feed, name, colour and
  interval but lose their mirror, and the next sync makes a new one as
  adding a subscription does. Renaming or removing an unlinked one
  touches no calendar. Events are only pruned from, and remove only
  destroys, a calendar that still carries the subscription's name.
  Subscriptions now record the signed-in account, so accounts on two
  servers that got the same JMAP id no longer share them.
- the preferred sender identity kept its id, which a new server can
  give to another address (live: identity "b" is usera@ on 0.16.23 and
  team@ on 1.0). It is forgotten for the account's old and new JMAP
  id, which the app now records with the server fingerprint.
- hidden calendars kept their ids. The list is shared by all accounts,
  so it is cleared.

Offline changes the drop discards are now announced once ("3 offline
changes were discarded because the mail server changed"), in all
languages.

Revoking this device from another one turns push off here again: a
subscription gone before its recorded expiry counts as revoked, as it
did before the server check came in. A replaced server forgets the
recorded expiry, so there the subscription is re-created instead.
2026-09-25 23:49:36 +02:00
Linus Rath 97daf76f05 fix: start over when the mail server behind an account was replaced
Stalwart 1.0 has no migration from 0.16, so the way there is a fresh
install at the same address, where ids start over ("a", "b", ...) and
name other objects. The app persists a lot that only means something on
the server it came from: JMAP states and query states, offline bodies
keyed by message id, an outbox that replays stored ids (a `destroy`
included), the calendar cache and push de-dupe lists. It also turned
push off for good when the server no longer had the subscription, and
would have taken another device's subscription for its own once the
reinstalled server handed out the recorded id again.

- Email/changes, Email/queryChanges and Mailbox/changes answers whose
  Stalwart state went below the one asked from count as not computable
  and lead to a full fetch (`stalwartChangeId`, a port of the webmail
  guard). It only reads accounts that list `urn:stalwart:jmap` and
  states in the exact form Stalwart writes, so it never trips while
  states grow or on other servers.
- Every time a session is established, a fingerprint of the server (API
  URL, primary account id, the session's and the primary account's
  capability names) is compared with the one recorded for the account.
  On a change, before anything reads them, the account's folders,
  lists, states and query states, offline bodies, queued and failed
  outbox ops, the calendar and contacts caches and the push de-dupe
  lists are dropped. Preferences, templates, tags and iCal
  subscriptions stay. The first sight of an account only records it.
- The whole outbox goes: every op names a message by its server id.
  Offline edits still waiting are lost, but never a message the user
  wrote, because the outbox holds no sends.
- A subscription the server no longer has counts as expired and is
  re-created, and the recorded one is only ours when its device client
  id matches, on setup and on teardown. Revoking this device from
  another one therefore no longer keeps push off here; turning it off
  on the device does.
2026-09-25 20:57:46 +02:00