- The message for send checks is built after waiting for required
checks that are still starting: a send right after sign-in gave them
no external recipients and no file types.
- A plugin's forward is held too while a required check is down, also
with no plugin listening.
- A plugin the admin forces on that hasn't reached this browser yet (a
new device, a failed plugin sync) counts as starting.
- A check that stopped working says to reload or tell the admin.
- The files that were checked are the ones sent, and recipients a
plugin adds after the check are checked again.
- A file Bulwark couldn't read is marked unreadable; a text file
starting with "MZ" is text, Java and Android apps are told from ZIPs.
Changed assertions: runPluginSendCheck takes a builder and reports
guard-loading or guard-failed instead of guard-down; the composer test
of an auto-disabled guard expects the stopped_working text; the sniff
tests give programs a zero byte, as real ones have, and expect
unreadable on a file that can't be read.
Plain-text bodies render in the page, not in the body iframe, so their
links opened without the link check. Plugins now get each link as the
browser parses it (new URL(href).href) and that same string opens, so a
link check can't judge one host while another opens. With no link check
listening, clicks and middle-clicks stay with the browser as before.
- 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.
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.
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.
- 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.
- 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.
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.
- The private file an export writes in Firefox and Safari is deleted
when the app starts and when someone signs in, not only at the next
export, and at once on sign-out. Close keeps it until its download
had time to finish. Each tab holds its own files under a Web Lock, so
one tab's sweep no longer deletes another tab's running export.
- A browser whose private file system can't write files (Safari before
26) is told the export stays in memory instead of being promised a
download.
- The export's live region announces only the phase and pauses; the
count next to it no longer floods screen readers, and the format
group is named once.
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.
- A message whose attachments are bigger than the server takes is
skipped before anything is read (a 1 GB attachment took several GB in
the worker and could crash it), and a worker that stops answering
ends the import with a message instead of a wait that never ends.
- The items after a broken one in a PST folder are counted as
unreadable in the summary instead of disappearing.
- Sent Items and Deleted Items are known from the PST's own entry IDs,
and the Inbox and the others by their names in more languages, so a
PST imported at the top level fills the user's own folders whatever
its language. The hint about that shows only for the top level.
- Message-ID, In-Reply-To and Content-ID from the PST can't carry line
breaks into the rebuilt headers.
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.
Thunderbird's envelope line is when a message was copied into the folder, so a migrated folder showed years of mail on one day. The envelope date now counts only on Bulwark's own export lines and on lines with a time zone. The mbox writer tests now expect the export's own envelope sender, bulwark-export, in place of MAILER-DAEMON, which marks those lines.
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.