Commit Graph
1233 Commits
Author SHA1 Message Date
Linus Rath 6e931a731c fix: keep the server-side device policies on when a login's session or the policy can't be read 2026-11-01 09:44:03 +01:00
Linus Rath b58dc1d66b fix: apply the device policy and retention line of the account whose folder is open, and keep Copy to account from moving mail out of a restricted account 2026-11-01 09:42:19 +01:00
Linus Rath 61733d839f fix: show an announcement that arrives after its start, once in the Pro shell, and keep it closed when only its times move
- The banner judged its start and end by the time it was mounted, so an
  announcement fetched later than its start never showed in that tab.
- The Pro shell shows it once above its panes; email and folder tabs had
  none and split panes showed it twice.
- Screen readers get it from a live region that fills a moment after it
  is in the page; text present when a live region appears isn't read out
  reliably. The tests that checked the strip's role now check that region.
- Moving the start or end no longer shows a closed announcement again;
  the lib test that expected a new end time to reset it now expects the
  opposite.
2026-11-01 09:39:36 +01:00
Linus Rath e80f5ade81 feat: let admins turn off downloads, export, printing and the install prompt, and add a watermark
Organisation > Devices now sets policy.json `organisation.devices`, each
switch off by default and scoped per domain or group:

- No downloads: attachments and files only preview (reader, conversation
  view, Pro tab, list chips, .eml previews, the Files app). Download,
  Download all, Open in new tab, Save to Files, drag-out, the browser's
  PDF viewer (pdf.js instead) and media download controls go away.
- No export: no .eml export, View source, message drag-out, or folder and
  account export; a running export stops and its file is deleted when
  the switch turns on. Import stays.
- No printing: no Print buttons in mail, calendar and contacts; Ctrl+P
  prints a note; the reader frame prints blank.
- No install prompt: no card, and the browser's own banner is held back.
- Watermark: the user's address and the date over the reader,
  previews and every printout (mail, calendar, contacts, plugins).

A message follows its own account's policy; what isn't about one message
follows the strictest signed-in account. Every user path hides its
button, and the anchor and new-tab helpers refuse as a backstop
(lib/download-guard.ts), never the blob readers. The plugin APIs
`ui.downloadFile` and `ui.print` refuse too, and the office editor frame
loses `allow-downloads`.

Two assertions in components/mail/__tests__/blob-routing.test.tsx now
expect `{ kind: 'export' }` as the fifth argument of downloadBlob for an
.eml export: the export says what it is, so the backstop checks "No
export" rather than "No downloads" for it.
2026-11-01 08:52:34 +01:00
Linus Rath 140db2cf8f feat: turn printing and export off in the office editor under the device policies 2026-11-01 08:40:03 +01:00
Linus Rath adbec718ef feat: let admins keep sender and subject out of push notifications, enforced by the server
The push preview route now reads the device policy of the login it
serves (its scopes worked out from the login's own JMAP session, every
non-personal account counted as a group) and under
`pushWithoutContent` asks the mail server for the message's ids only,
so sender, subject and text never reach the notification. The service
worker shows "New mail" for such an answer. A policy that can't be read
counts as on.

The three existing push preview route tests now mock the config manager
with the default policy: without it the route can't read a policy in the
test environment and holds the text back, which those tests didn't mean
to cover. No assertion changed.
2026-11-01 08:40:02 +01:00
Linus Rath 47784cc07a feat: offer admin out-of-office templates and show the absence dates in the user's time zone 2026-11-01 08:02:39 +01:00
Linus Rath b99003956a feat: show an admin announcement across the apps 2026-11-01 07:51:46 +01:00
Linus Rath 2a952f4b72 Merge branch 'feat/enterprise-pack-c'
# Conflicts:
#	lib/sign-out-cleanup.ts
2026-11-01 07:39:30 +01:00
Linus Rath ebb33619a0 Merge branch 'feat/enterprise-pack-a' 2026-11-01 07:36:59 +01:00
Linus Rath 1bb2a85de4 fix: translate the turned-off link note and tidy the label and phishing texts
The VS-NfD warning test now expects "BSI-approved products", the corrected wording.
2026-11-01 07:14:10 +01:00
Linus Rath 13c57414a2 fix: report phishing per account from every view and keep a reported message's links off everywhere
- Pro tabs stamp a message of another login or a shared account with
  where it came from, so the report and the move use its account.
- A selection across accounts names every address its reports go to,
  and Undo says how many messages came back.
- A message the list doesn't hold runs the plugins' spam hooks too, and
  every open view shows a reported message as reported at once.
- A reported message hides its unsubscribe banner, and links are off in
  its attachment previews.
- Search results and All mail don't offer reporting the user's own sent
  mail; the Mountain View selection bar offers it.
- The focus goes back to the button that opened the menu.
- ARF names the delivered-to address (or leaves it out), says binary for
  bare line breaks and long lines, and refuses a message bigger than the
  server's upload limit before downloading it.

The phishing tests that compared one address now compare the list of
addresses, and the ARF builder test passes the encoding, not a flag.
2026-11-01 07:07:30 +01:00
Linus Rath cbd23db2fe fix: give mail import and export their own settings section and name the upload limit as the server does
- Settings > Backup & reset gets a Mail files section for Import mail
  and Export all mail, instead of sitting under settings backup.
- The pause message names Stalwart's 50000000-byte quota as 50 MB, not
  48 MB, and the dialog and settings texts mention .ost files.
- The old import's leftovers go: expandImportableEmails and
  EML_IMPORT_ACCEPT in lib/eml-import.ts, and the strings
  email_viewer.import_email, mailbox_context_menu.import_email and
  notifications.import_email_success/_error in all 30 locales.

lib/__tests__/eml-import.test.ts loses the tests of the removed
functions; its #891 checks that an .eml and an .eml in a ZIP keep their
Date header now run against the import's own sources.
2026-11-01 07:05:02 +01:00
Linus Rath f3440d127b fix: share free/busy only with an address the directory names as a group 2026-11-01 06:57:24 +01:00
Linus Rath 85deb006a8 fix: keep plugin raw sends from dropping or forging the classification label
Raw sends (api.jmap.sendRaw and submitRaw) now always lose label headers
a plugin wrote while the organisation uses labels. A crypto takeover is
matched to its raw send by a labelToken the composer puts in the
onComposeSend request (plugins that don't pass it back are matched by
identity, and refused when two takeovers are open), not by comparing
subjects. A raw send outside a takeover gets the default label, or is
refused when a label is required. The extra header is folded on the raw
path. The takeover code moves to lib/outgoing/classification-raw.ts;
the old test that let a raw send outside a takeover through unlabelled
now expects the forged header to be removed.
2026-11-01 06:52:44 +01:00
Linus Rath 870881c57e fix: fetch a company calendar once for everyone at a time and pause after a failure 2026-11-01 06:51:17 +01:00
Linus Rath 3ef16f602e fix: keep a message's label through Undo send and take over labels only from internal senders
A sent message brought back by Undo, Cancel and edit or edit-as-new
reopened with the default label, because only drafts carried the
X-Bulwark-Label marker; it now falls back to the SIO-Label it went out
with. A reply, quick reply or plugin forward takes over the original's
label only when an internal sender set it and never one below the
default, since anyone outside can write the header. The tests written
for the earlier rule (any received label counts) now pass an internal
sender, which is why their originals changed.
2026-11-01 06:48:01 +01:00
Linus Rath 88a5c3e5d5 fix: take company calendar events away when the feed is gone or the account signs out 2026-11-01 06:44:57 +01:00
Linus Rath cbae82b2f0 fix: keep the out-of-office event in step when the reply changes on another device
The event counted as Bulwark's only while its times matched the record
in this browser's settings, so a reply switched off on a phone, by a
delegate, in Lite or with settings sync off left colleagues seeing a
busy event for a cancelled absence. It now also counts as Bulwark's
when it sits on the reply's dates from before the change, and it
carries the keyword bulwark-out-of-office so a device without a record
takes it over when the reply is loaded. Each account gets its own
pending sync.
2026-11-01 06:42:37 +01:00
Linus Rath c89c1cb36c fix: fetch company calendars for the active account when several are signed in 2026-11-01 06:37:50 +01:00
Linus Rath 8d48f98261 fix: keep failed messages for the next import run and stop an import that can't go on
A message that failed for a passing reason (a dropped connection, a
server error, an expired token) used to count as done, so continuing
the import skipped it for good, and a slow upload followed by a cancel
imported the messages after it twice.

- The checkpoint keeps every message that is done, also past a gap,
  and the next run of the same import tries only the rest again.
- Ten failures in a row, a full mailbox (overQuota) or a refusal
  (forbidden) stop the import with a message instead of going through
  the whole file.
- An upload without an answer pauses the import instead of failing the
  message: 30 seconds, then until the upload window ends.
- Email/import, the export's Blob/get and folder listing pass the
  client's own 429 pause by, and a 429 on them doesn't pause the rest
  of the app.
- Signing out stops the job of that account only, writes no checkpoint
  and imports nothing more afterwards; another account's sign-out
  leaves the job alone.
- One tab imports a file at a time (Web Locks), and Import waits until
  the dialog knows whether an earlier run can be continued.

The import dialog tests now wait for the Import button to be enabled
before clicking it, since it stays off during that lookup.
2026-11-01 06:37:08 +01:00
Linus Rath 0fcdc2ece4 fix: reach a company calendar on a private address only when its host names the intranet 2026-11-01 06:35:56 +01:00
Linus Rath 9c60140e49 fix: write the SIO-Label header without a doubled space after the colon 2026-11-01 06:01:20 +01:00
Linus Rath 36160f9fbd fix: answer for a group mailbox as the group when the group address is one of your identities #634 2026-11-01 06:00:43 +01:00
Linus Rath 3bd8e83d60 feat: show company calendars set by the admin 2026-11-01 05:41:34 +01:00
Linus Rath 1c7f81bcc6 feat: pick a classification label in the composer and stamp it on sent mail 2026-11-01 05:33:12 +01:00
Linus Rath 15f0bd198c feat: export a folder or the whole account as mbox or ZIP 2026-11-01 05:28:58 +01:00
Linus Rath a8f9d81337 feat: block the calendar while the out-of-office reply is on 2026-11-01 05:19:07 +01:00
Linus Rath 877198a4bf feat: answer invitations in a calendar shared with you as its owner #634 2026-11-01 05:05:12 +01:00
Linus Rath 6262e58ef9 feat: offer the ARF report format for phishing reports 2026-11-01 05:02:32 +01:00
Linus Rath 50f95f4463 feat: report a message as phishing and move it to Junk 2026-11-01 04:58:01 +01:00
Linus Rath ab2fc6454d feat: let admins share every user's free/busy with a group 2026-11-01 04:51:30 +01:00
Linus Rath 93f02e9615 feat: suggest meeting times that are free for every attendee #1164
The participant availability test that expected "Free/busy isn't
available on this server" when Stalwart refuses the directory listing
now expects the attendee list: Stalwart 1.0.1 still answers
getAvailability for the principals the user knows with directory
queries off, so the list stays and names who couldn't be looked up.
The 1.0 build that refuses the lookups too keeps its own case.
2026-11-01 04:47:05 +01:00
Linus Rath d2a83a4ee9 fix: stop proxying locale rewrites back to the server when it listens on 127.0.0.1 #1487 2026-11-01 02:41:42 +01:00
Linus Rath d781e8762e Merge branch 'feat/multi-instance' 2026-11-01 01:43:15 +01:00
Linus Rath 23b50f8268 test: read the wrapped mkdir in the Dockerfile as one command 2026-11-01 00:47:30 +01:00
Linus Rath c0c1a8f0fb test: give the config route test the logger calls config-manager now makes 2026-11-01 00:46:42 +01:00
Linus Rath ecdd2b54fe chore: name the app Bulwark Webmail in exported files, outgoing requests and the manifest 2026-11-01 00:41:21 +01:00
Linus Rath 355e57ca6c Merge branch 'feat/multi-instance' into feat/multi-instance-ext 2026-11-01 00:28:15 +01:00
Linus Rath 3c34eda260 fix: cap the cache of Stalwart admin checks so it can't grow without bound 2026-11-01 00:25:35 +01:00
Linus Rath a03f76ec85 feat: send X-Powered-By: bulwarkmail.org on every response 2026-11-01 00:21:32 +01:00
Linus Rath 2078b0a13f Merge branch 'feat/multi-instance-auth' into feat/multi-instance 2026-11-01 00:13:17 +01:00
Linus Rath 89f337c0ae Merge branch 'feat/multi-instance-admin' into feat/multi-instance 2026-11-01 00:12:24 +01:00
Linus Rath 8127640acf fix: write an office editor sign-out made during a store outage as soon as the store is back #1066 2026-11-01 00:10:09 +01:00
Linus Rath 40578f039d test: give each replica in the setup token tests a state dir of its own #1066 2026-11-01 00:09:49 +01:00
Linus Rath 47281e5156 fix: answer 503 when an impersonation link's use can't be recorded and keep used links as expiring keys #1066
The marker-write test from d28ee361c now expects the 503 path (a rejected promise) instead of false: refusing with "Token already used" sent admins looking for a replay that never happened, and the jti is now remembered in memory only after it was recorded, so a retry works once the store or state dir is back.
2026-11-01 00:07:41 +01:00
Linus Rath ff08fe50fe fix: say when an admin sign-out or password change couldn't be stored instead of answering ok or wrong password #1066 2026-11-01 00:05:15 +01:00
Linus Rath df1f5cd6a4 fix: give a pair step-up attempt back only to the windows it was counted in #1066 2026-11-01 00:05:09 +01:00
Linus Rath 0ed5fb148c fix: never write telemetry defaults over a state file that can't be read #1066 2026-11-01 00:03:34 +01:00
Linus Rath 10abf7273c test: check that pairing, the policy Bearer check and the SSO hand-off answer 503 while the shared store is down #1066 2026-11-01 00:03:20 +01:00