Commit Graph
901 Commits
Author SHA1 Message Date
Linus Rath 868805ec9a fix: keep ringing for a second mail in the same account #1141
The quiet window ignored which account rang, so a second, different
message in the same account within 30 seconds arrived without sound, and
every single-login user lost the alert too. The window now records the
account of the last audible alert and silences only other accounts,
which is the burst across logins the change is meant for.
2026-10-01 11:02:39 +02:00
Linus Rath fa45ff997f Merge pull request #1141 from lucamzanon/feat/push-quiet-window
feat(push): ring once for a burst of mail across accounts
2026-10-01 11:02:33 +02:00
Linus Rath 6bce3323a9 fix: keep previews that only start like a style sheet #1136
The leading-CSS strip emptied or cut ordinary previews:
"@media team, the brief is attached", "@Page can you check?" and
"@import the CSV ..." became empty, and "Reminder: your appointment
{date: 2026-10-01} is confirmed" became "is confirmed".

At-rule names now match in lower case only, only `@import` and
`@charset` end at a semicolon, an at-rule word with no block after it
is prose, and a selector colon must not be followed by a space.

The conversation view's collapsed cards, contact activity, the pro
folder tab and the list's row-height estimate now use `cleanPreview`
too, so they agree with the list rows. The test's invisible characters
are written as escapes.
2026-10-01 11:02:26 +02:00
Linus Rath 1a186f8962 Merge pull request #1136 from lucamzanon/fix/preview-leading-css
fix: skip a style sheet at the start of a message preview
2026-10-01 11:01:28 +02:00
Linus Rath 5597eadc28 fix: give a server its own sign-in button only when it has a button label #1135
A server entry's OAuth client ID already has a meaning while OAuth is
off globally: it is the registered client that password and TOTP logins
use with Stalwart's require_client_registration. The PR made every such
entry offer OAuth on its own, so existing installs would suddenly run
discovery and show SSO buttons on the login page, and multi-IdP setups
would get one extra button per server.

A server now gets its own button only with both a client ID and a button
label, which no existing config has. The rule lives in `offersOwnOAuth`
with a test. Also:
- the OAuth-only login form uses the selected server's label
- the public config validator checks `buttonLabel` is a string
- the admin hint and `.env.example` say the label enables the button
2026-10-01 11:01:17 +02:00
Linus Rath d2855695a6 Merge pull request #1135 from lucamzanon/feat/per-server-oauth-sign-in
feat(login): let a server in the list sign in with its own OAuth client
2026-10-01 11:00:26 +02:00
lucamzanon cd39805566 feat(push): ring once for a burst of mail across accounts
Each account has its own notification, so one message reaching several
of the user's accounts rang once per account. Only the first alert in a
30 s window makes a sound now; the others still appear or update,
silently. The window runs from the last audible alert, so a steady
trickle still rings every so often.
2026-10-01 04:40:08 +02:00
lucamzanon 717f18fc46 feat(unified): say how many accounts the unified mailbox holds while logins reconnect
After a load the logins other than the one on screen reconnect in the
background, and until they are all back the unified counts cover only
some of them. With many logins that lasts seconds, and a partial total
looks exactly like the whole one.

While that restore runs, the "All accounts" header shows how many logins
are already in the scope, e.g. "4/10", with a translated label for screen
readers. It goes away when the scope is complete or the restore is over -
a login that failed to reconnect does not keep it up.

`restoringAccounts` in the auth store marks the background restore; the
count itself is a pure function with its own tests.
2026-10-01 04:38:04 +02:00
Linus Rath 2cbf73ed08 test: expect session URLs hosted on another origin to be kept 2026-10-01 00:36:00 +02:00
Linus Rath 29c74c3b77 feat: sign in with an access token 2026-10-01 00:24:47 +02:00
lucamzanon 2d521d19b2 fix: skip a style sheet at the start of a message preview
Some servers build a message's preview from its HTML body without skipping
the <style> element, so a marketing mail's preview - in the list and in its
push notification - can read "@media screen and (min-width:600px){.hide{
display:none!important;…" and never reach the text.

Drop whole leading rules from the preview before showing it; a preview that
ends inside the style sheet was nothing but CSS and becomes empty, so the
usual "no preview" text shows instead. A rule only counts as CSS when its
block holds declarations or further rules, so prose with a brace in it
("the meeting is at {time} tomorrow") is left alone.
2026-09-30 21:35:20 +02:00
lucamzanon 1fac44832a feat(auth): let one server in the list sign in with OAuth on its own
A server entry that names its own OAuth client now offers the OAuth
button even while OAuth is off globally, so a Gmail bridge can sign in
with Google next to a server that uses passwords. The entry can also
name the button ("Sign in with Google"); the generic SSO label stays
the default.
2026-09-30 21:28:06 +02:00
Linus Rath 2d2b0753be Merge pull request #1132 from lucamzanon/perf/multi-account-startup
perf: stop the startup request storm with many logins
2026-09-30 20:33:32 +02:00
Linus Rath cd14807133 Merge pull request #1112 from theLucius7/fix/config-load-recovery-20260927
fix: recover from transient application configuration failures
2026-09-30 20:32:44 +02:00
Linus Rath 692be6aa9d Merge pull request #1095 from lucletoffe/fix/calendar-location-links
fix(calendar): surface the meeting link and open the location in maps
2026-09-30 20:32:44 +02:00
Linus Rath 7b61e55529 chore: write nul separators as \0 so git diffs these files as text
Three files carried a literal NUL byte inside a string: the admin
check's cache key in `app/api/admin/auth/route.ts`, the global search
hit key in `lib/global-search/types.ts` and two TNEF fixtures in
`lib/__tests__/tnef.test.ts`. Git treats any file containing a NUL as
binary, so it showed no diffs for them, never normalised their line
endings, and reported every pull request that touched the admin route
as a conflict even when a text merge was clean.

The escape produces the same string at runtime. The admin route was
stored with CRLF while git saw it as binary; it is now stored with LF
like the rest of the tree, so its diff covers the whole file (the only
content change is on line 70).
2026-09-30 19:34:37 +02:00
Linus Rath 4db88255e8 fix: stop a sixth sso account from overwriting the first account's sign-in
`/api/auth/sso/complete` only accepted cookie slots 0-4 and quietly
used slot 0 for anything higher. Since the account limit rose to
`MAX_ACCOUNT_SLOTS` (50) on HTTP/2, adding a sixth account through
server-side SSO wrote its refresh token over the first account's cookie,
while the new account was registered on slot 5 with no cookie behind it.

The route and the OAuth callback's pending-slot check now use the same
bound as every other auth route.
2026-09-30 19:31:03 +02:00
lucamzanon 2d5756bff7 perf(push): reconcile push bindings instead of rebuilding them
The push effect re-runs every time a login connects, which while a browser
with many logins restores means once per account. Each run closed every
client's push and set it up again: the live streams were torn down and
reopened and every slow poll re-primed its baseline with a full state read
(Mailbox, Email, Calendar, CalendarEvent, SieveScript) - quadratic in the
number of accounts. With ten logins that was 21 stream openings and about
180 state-poll methods in the first seconds.

Reconcile instead: bind the clients that are new, unbind the ones that are
gone, and on an account switch re-bind only the two logins whose role
changed, handing the live stream to the newly active one when it has
none. Handlers are read through a ref at event time, so a binding never has
to be re-created because a callback changed identity.

The reconciliation is a pure function (lib/push-bindings.ts) with its own
tests; the effect only supplies the callbacks.
2026-09-30 19:03:34 +02:00
Linus Rath 1d90fe17f4 fix: preview audio, video and icon attachments sent with a catch-all type 2026-09-30 15:08:31 +02:00
Linus Rath d0a1d0183b Merge pull request #1127 from dealerweb/fix/preview-declared-type
fix: preview attachments whose declared type the browser cannot show
2026-09-30 14:57:33 +02:00
Linus Rath 98cada387c Merge branch 'main' of https://github.com/bulwarkmail/webmail 2026-09-30 14:50:52 +02:00
Linus Rath 3051f58a1b fix: keep the rules menu working for group members and list mail on stalwart 2026-09-30 14:48:26 +02:00
Linus Rath b663f0e774 fix: treat an empty PLUGIN_SIGNING_KEY_FILE as unset #996 2026-09-30 14:24:29 +02:00
Linus Rath 70dee66966 Merge pull request #996 from mulatta/feat/external-plugin-signing-key
feat: support external plugin signing keys
2026-09-30 14:23:45 +02:00
Linus Rath f51ce0e448 fix: read impersonation secrets from *_FILE when the variable is empty #897 2026-09-30 14:23:09 +02:00
Linus Rath 9c1dcd309c Merge pull request #897 from Cameo007/main
add file-based env variables for LibreTranslate and Stalwart Impersonation & handle file-based vars from JSON configs in ConfigManager
2026-09-30 14:22:01 +02:00
Linus Rath ff0d923bd1 fix: let COOKIE_SECURE decide the admin cookie's Secure flag #1063 2026-09-30 14:21:46 +02:00
Linus Rath 9985c86c97 Merge pull request #1063 from lucamzanon/fix/admin-cookie-secure-by-request
fix: derive the admin cookie's Secure flag from the request
2026-09-30 14:21:26 +02:00
Linus Rath fa789a6d9f Merge pull request #748 from lucletoffe/fix/calendar-blank-cn-and-description
fix(calendar): stop writing blank participant names and empty descriptions
2026-09-30 14:21:20 +02:00
dealerweb 1512898cdb fix: preview attachments whose declared type the browser cannot show
The preview hands the attachment's blob to the browser's own viewer,
which picks the viewer from the blob's type and downloads anything it
cannot show - under the blob's UUID. It only corrected the exact
application/octet-stream declaration from the file name. Other
declarations kept the preview from working: IBM i (AS/400) systems send
every attachment as application/x-as400attachment, some senders use
application/x-pdf or force-download, and the octet-stream check was
case-sensitive.

Move the decision into previewBlobType(): a PDF, image, audio or video
preview takes the first type its viewer can show - declared, then
served, then the one the file name implies. Other previews keep the
declared type unless it is generic, and the name never yields a
script-capable type.
2026-09-30 14:20:20 +02:00
Linus Rath 1531dae7eb feat: create filter rules from the message right-click menu 2026-09-30 13:52:41 +02:00
Linus Rath fee0e7c9e7 fix: show default oauth scopes in the admin dashboard #1126 2026-09-30 12:17:25 +02:00
lucamzanon 2b83bce8c3 Merge remote-tracking branch 'origin/main' into fix/admin-cookie-secure-by-request
# Conflicts:
#	app/api/admin/auth/route.ts
2026-09-30 08:54:14 +02:00
Linus Rath a24d6626b5 fix: stop reading ids in delivery reports as verification codes 2026-09-30 00:14:08 +02:00
Linus Rath ac34c9a6ac test: pass inboxOnly in web-push vapid tests 2026-09-30 00:00:38 +02:00
Linus Rath 040cea30c5 fix: keep calendars visible after a failed calendar fetch 2026-09-29 23:59:03 +02:00
Linus Rath 9d140fa762 Merge pull request #1052 from lucamzanon/feat/account-row-colours
feat: colour unified rows by account and give every tag a hue of its own
2026-09-29 23:30:24 +02:00
Linus Rath f499915cf1 Merge pull request #1121 from shukiv/fix/web-push-vapid-key-mismatch
fix(push): resubscribe on VAPID key mismatch, and survive an unreadable subscription
2026-09-29 23:24:55 +02:00
Linus Rath ddc0b03055 fix: reuse cached access token on lite session restore #552 2026-09-29 21:25:02 +02:00
Linus Rath affa999cd0 fix: report recipients the server rejects at send #1123 2026-09-29 20:49:40 +02:00
Linus Rath be0494cb5c fix: move mail to trash when emptying an ordinary folder 2026-09-29 20:35:09 +02:00
Linus Rath 324fb48312 fix: keep an opened mail in place in unread-first lists until another is opened 2026-09-29 20:28:39 +02:00
Linus Rath 3bccca715c feat: show the folder hierarchy in the search folder dropdown 2026-09-29 20:15:14 +02:00
Linus Rath 3904af2cbb feat: search spam and trash themselves while they are open 2026-09-29 20:09:26 +02:00
Linus Rath 763732fdc9 feat: leave spam and trash out of search by default 2026-09-29 19:52:39 +02:00
Linus Rath aed0d27320 fix: stop the mail list flashing a reload right after the page opens 2026-09-27 18:21:24 +02:00
Linus Rath be3c4b0d99 feat: draw calendar events as solid blocks and toasts as plain cards 2026-09-27 12:45:19 +02:00
Linus Rath 3ed484df35 feat: offer the code from sign-in mail as a copy chip in the list and reader 2026-09-27 11:19:48 +02:00
Lucius7nya d923b92034 fix: recover from transient application configuration failures
Retry interrupted or invalid runtime configuration requests with bounded backoff and per-attempt timeouts. Validate the complete response before caching it, and prevent stale HTTP-cached configuration.

Cover request and body timeouts, shared hook consumers, retry bounds, the server response contract, and both Lite configuration paths.
2026-09-27 15:41:21 +08:00
Linus Rath 377c283d86 fix: say up front when the mobile app cannot use an http mail server
The app refuses a JMAP server over plain http (loopback aside), yet the
desktop issued a code, reported "Signed in on your phone" once it was
redeemed, and the phone failed. Linking now stops before the step-up
with an explanation.
2026-09-27 04:53:49 +02:00