Repository navigation
v0.3.2
Image: ghcr.io/karatektus/pl_mail:0.3.2
Security
Security: outsiders can no longer change your calendar. Reported by @jhuber in #34, who found everything in this section by reading the code. Two holes, neither of which exposed data or ran code, both of which let an outsider put misleading entries on a calendar unnoticed.
- A meeting could be moved or cancelled by anyone who knew its identifier. Everyone invited to a meeting has that identifier, and so does anyone a copy was forwarded to. A cancellation or an update from any of them was applied, and the changed meeting still showed as accepted. A change is now applied only when it comes from the address that sent the invitation, or from the organiser the event names. From anyone else nothing changes, and a card above the message says so and offers Apply the change. The same now protects meetings mirrored from a connected calendar.
- A booking in a mail's markup was put on the calendar on the strength of an unchecked From line. A stranger could add a "flight", and a forged airline address with a real booking number could cancel the real one. A booking is now added unasked only when your mail server vouches for the sender; otherwise it is offered on a card with Add to calendar. For IMAP accounts the server's verdict is believed when its name is on the same domain as the IMAP host.
- Mail in Spam or the bin is no longer read for events. A message moved back out is read then.
Mail styles no longer leak into replies and prints. Inline styles are kept because in mail they are the design, and in the reading pane that is harmless: the message is in a sandboxed frame. The quote in the reply composer and the print page are not framed, and a mail using position: fixed could cover the composer with content of its own. Such declarations are now removed, and both places clip what is inside them. Also from #34.
The default TRUSTED_PROXIES is documented for what it is. It trusts every private address so a reverse proxy works unconfigured, which also lets another machine on the LAN forge its address and step around the per-address limits on login and booking. The per-account login limit is unaffected. The default is unchanged — narrowing it blindly would break sign-in behind a proxy — and Reverse proxy now says how and when to narrow it. Also from #34.
Reminders no longer reach Google readable. A calendar reminder pushed to an Android device through Firebase carried the event's title as plain JSON, although the code said it was encrypted — that was only true of browser push. Anything with content in it is now sealed to a key the app registers, the same way a browser's push is, and a device that has registered none is not sent it at all. State changes carry no content and are unchanged. The Android app needs the version that registers a key to show these reminders; older versions never showed them. Also from #34.
Nothing already on a calendar is removed or changed by upgrading. One new nullable column, event_source_link.hold_reason. See Invitations and events from mail.
Fixed: a fresh install could only be opened on the machine it runs on. The stock compose.yaml served https://localhost and nothing else. Opened from another machine by address or hostname, the browser reported an SSL protocol error, which unlike a certificate warning cannot be clicked through, so a headless server could not be set up at all without first setting SERVER_NAME. The default now answers on every name and address with the same self-signed certificate, and the browser shows its usual warning. Live updates follow as well: the hub address is taken from the public address chosen during setup, where the stock file used to pin it to localhost. An install that sets SERVER_NAME or MERCURE_PUBLIC_URL itself is unchanged. See Docker → Starting it.
Mark as spam button. It sits beside Delete: among a row's buttons, in an open conversation's toolbar and above a selection. On one conversation it opens a short menu — move it to Spam, or move it and also create a filter that sends everything from this sender, or from the sender's whole domain, to Spam from now on. The domain filter asks once more before it acts, and for a mail provider's domain such as gmail.com it is behind More options first. The filter is an ordinary one under Settings → Filters. Undo takes back the move and a filter the same click created. Until now the only ways to say "this is spam" were Move to and dragging onto the Spam row. See Mail → Mark as spam.
Filters can match a sender exactly. Two new conditions sit beside "From contains": From is exactly, which matches the sender's whole address, and Sender's domain is, which matches everything after the @. "From contains" looks for the text anywhere in the address or the display name, so a filter for a@x.de also caught ba@x.de, and anybody could match it by putting that address in their display name. The spam button's filters use the new conditions. Existing filters are unchanged. JMAP clients get the same two as fromAddress and fromDomain in Email/query.
Message templates. Settings → Templates is a new section: write a message once, file it in a folder, and insert it from a new button in the compose window. The folder tree opens with one folder per mail account, and the account you are writing from has its templates listed first. A template can hold variables — the recipient's first name, full name and address, your own name and address, a signature, and dates with an offset and a format of their own ("14 days ago", written out long or as dd.MM.yyyy). Dates, sender and signature are filled in when the template is inserted. Recipient variables wait for a recipient: they stay in the message as placeholders until someone is in To, and Send asks first if one is still open. The compose window can also save the message on screen as a new template. Two new tables, mail_template and template_folder; nothing existing changes. Removing a mail account keeps its templates and moves them to the top level. See Templates.
Templates are available to JMAP clients. A new capability, urn:plmail:params:jmap:templates, with Template/get, Template/set, TemplateFolder/get, TemplateFolder/set and Template/render. The first four read and write the same templates and folders as Settings → Templates, through the same sanitiser and the same ownership rules. Template/render returns a template ready to insert for the account being written from: dates, sender and signature filled in, the recipient too when the client supplies one, and a list of the recipient variables that are still open. The Session lists the variables and date formats an editor may offer. The apps do not use any of this yet. See Client development → Templates.
A demo mailbox starts with templates. A visitor to a public demo gets six templates along with the seeded mail and calendar: a quote follow-up, an invoice reminder, an out-of-office and others, filed at the top level, under the demo account and in a folder inside it. They use every variable between them. app:test:seed-demo seeds the same six. Nothing changes on a normal install.
Fixed: an emoji picked in a reply you had not clicked into yet did not appear. A reply opens with the cursor on its empty first line, and whatever a toolbar button inserted there — an emoji, an inline image, and now a template — went somewhere that is never drawn: the button seemed to do nothing, and the reply stayed empty. Typing was never affected, and neither was a message you had clicked into first. The insertion now lands on the line.
Usage figures for public demos. In demo mode the admin area has a new section, Demo visitors: how many demo sessions were started in the last hour, 24 hours, 7 days and 30 days, how many different visitors started them, the total so far and how many have a mailbox right now. No address is stored. What is kept per session is a keyed hash of the visitor's address with its last part cut off, and that hash is deleted after 30 days. The demo's privacy notice says so. None of this exists on a normal install: the section is not in the navigation, its address answers 404 and nothing is recorded. See Demo mode.
Fixed messages growing without end. Some mail sizes its outer block as "one screen tall". Inside plMail a message sits in a frame that is made as tall as its content, so the block grew with the frame, the frame grew to fit the block, and the message kept getting longer, pushing everything below it away. Sizes given in screen heights are now ignored inside a message, and a message whose content merely follows the frame is no longer resized again.
Inbox as one list, without tabs. Under Settings → General → Categories, Inbox tabs can be switched to One list: no Primary, Social, Promotions and the rest, just the inbox, newest first. Per person, and tabs stay on unless you change it. Mail is still filed in the background, so switching back shows sorted tabs at once; if the assistant is what sorts your mail it keeps doing so, and the card says that when the tabs are off. "Select all in this view" then means the whole inbox. Apps can read the choice from the JMAP session as inboxTabs. #29.
A round of first-impression fixes. All reported by @pmdevelopment in #37.
- A selection survives the pager. Conversations ticked on one page of a list stay selected on the next, the count and the actions follow, and one action covers all of them. Going to a different list starts again with nothing selected, and None in the select menu clears every page.
- A whole folder can be selected from the select menu. "All 195 in this view" was only offered after every row on the page had been ticked. Unticking a row afterwards now narrows the selection back to what is ticked; before, the action still ran on the whole view.
- Fixed: moving mail into a folder could fill the log with "duplicate key" errors. A sync that was running while mail was moved tried to store the moved messages a second time, and each one was logged as a message that could not be built. Nothing was lost or duplicated. The sync now notices that the folder already holds them.
- A paired device and a mail app are given the public address. The pairing code and the JMAP address under Settings → App passwords carried whatever address the browser was on, so a phone paired at home got a LAN address. Both now use the address from Admin → Public address, and fall back to the browser's only when none is set.
- Hiding quoted text gives the space back. The message kept the height it had with the quote shown.
- Your own sent mail shows its images without asking. Only mail in Sent: a message in the inbox that merely claims to be from you is still blocked, and so is anything in Spam.
- Placeholder text is lighter than what you type. In several themes an empty field and a filled one looked the same.
- The actions on a connected integration are buttons, lined up, where they were four small words on different baselines.
- Save signature, and four buttons in the composer, stay readable under the pointer. They went white on white in light themes.
- The Spam badge is muted. Still a count of unread, no longer in the accent colour.
- German: "vor 12s" in the admin area, where it said "12s her".
Fixed: copying an error's reference showed an empty box instead of a tick. The copy itself worked. Reported by @pmdevelopment in #33.
Before you upgrade
- One migration, applied on boot. It adds an empty table,
demo_visit, which only a demo instance ever writes to. - And one column,
user.category_tabs, true for everybody, so no inbox changes by upgrading.