Forward a message as an attachment #197

Closed
opened 2026-09-02 04:57:25 +00:00 by jcoffey-dev · 0 comments
Owner

Forwarding quoted the original into a new message. That is right for passing on something to be read, and wrong for passing on something to be looked at — quoting rewrites the body, drops the headers and re-parents the attachments, so a bounce, a phishing report, or anything else where the message itself is the evidence arrived altered.

What it does

Sends the message whole, as a message/rfc822 part.

It costs no upload at all. A message's own blobId is its RFC822 blob and already lives in the account, so this goes through the same by-reference path as attach-from-Files — a 40 MB message attaches as fast as a small one.

Available in three places: the message's ⋮ menu, the list's right-click menu, and the overflow on the reply strip at the foot of a thread (the one a thumb finds on a phone). All three verified in the running app against the mock.

Two fixes on the way, both exposed rather than introduced

The .eml filename rule threw away most of the world's subjects. It was subject.replace(/[^\w.-]+/g, "_"), and \w without the u flag is ASCII — so every character of a Russian, Japanese or Chinese subject failed the class and the message downloaded as a row of underscores. ihasmail ships in nine languages besides English, so the subjects it handled worst were most of them.

What is actually unsafe in a filename is a much shorter list than "not ASCII": path separators, the names Windows reserves, the control range. The rule now keeps letters from any script and drops only those, and it lives in one place that the .eml download uses too. Written as a code-point test rather than a character class, because the escaping in one of those is its own small trap.

The composer's attachment chip never truncated. .att-name set overflow: hidden; text-overflow: ellipsis on a <span> — neither does anything on an inline element — so the name ran full width and the size flowed inline after it, wrapping mid-unit. Only long names showed it, which is every .eml named from a subject. Both spans are now block.

Note for review

The by-reference path inherits addFromFiles's size check, which is measured against maxSizeUpload even though nothing is uploaded. I left that alone and commented it rather than special-casing here: it applies to every by-reference attachment, so if it is wrong it is wrong in one place and should be fixed there. Worth a look, since FEATURES.md describes attach-from-Files as working "however large".

Testing

18 new unit tests — 10 on the filename rule (Unicode subjects, reserved characters, control characters, leading/trailing dots, length cap) and 8 on the forward itself (attaches by reference with no upload, names from the original subject not the Fwd: one, does not double a Fwd: prefix, marks the original $forwarded, starts no reply thread, addresses nobody, does not also quote the body).

Suite green: web 621, server 110, typecheck clean.

Merged 2026-09-01 as coffey-labs/ihasmail@ccc9f73a71

Rebuilt from: git history, session transcript.

Forwarding quoted the original into a new message. That is right for passing on something to be **read**, and wrong for passing on something to be **looked at** — quoting rewrites the body, drops the headers and re-parents the attachments, so a bounce, a phishing report, or anything else where the message itself is the evidence arrived altered. ## What it does Sends the message whole, as a `message/rfc822` part. It costs **no upload at all**. A message's own `blobId` *is* its RFC822 blob and already lives in the account, so this goes through the same by-reference path as attach-from-Files — a 40 MB message attaches as fast as a small one. Available in three places: the message's ⋮ menu, the list's right-click menu, and the overflow on the reply strip at the foot of a thread (the one a thumb finds on a phone). All three verified in the running app against the mock. ## Two fixes on the way, both exposed rather than introduced **The `.eml` filename rule threw away most of the world's subjects.** It was `subject.replace(/[^\w.-]+/g, "_")`, and `\w` without the `u` flag is ASCII — so every character of a Russian, Japanese or Chinese subject failed the class and the message downloaded as a row of underscores. ihasmail ships in nine languages besides English, so the subjects it handled worst were most of them. What is actually unsafe in a filename is a much shorter list than "not ASCII": path separators, the names Windows reserves, the control range. The rule now keeps letters from any script and drops only those, and it lives in one place that the `.eml` download uses too. Written as a code-point test rather than a character class, because the escaping in one of those is its own small trap. **The composer's attachment chip never truncated.** `.att-name` set `overflow: hidden; text-overflow: ellipsis` on a `<span>` — neither does anything on an inline element — so the name ran full width and the size flowed inline after it, wrapping mid-unit. Only long names showed it, which is every `.eml` named from a subject. Both spans are now block. ## Note for review The by-reference path inherits `addFromFiles`'s size check, which is measured against `maxSizeUpload` even though nothing is uploaded. I left that alone and commented it rather than special-casing here: it applies to every by-reference attachment, so if it is wrong it is wrong in one place and should be fixed there. Worth a look, since FEATURES.md describes attach-from-Files as working "however large". ## Testing 18 new unit tests — 10 on the filename rule (Unicode subjects, reserved characters, control characters, leading/trailing dots, length cap) and 8 on the forward itself (attaches by reference with no upload, names from the original subject not the `Fwd:` one, does not double a `Fwd:` prefix, marks the original `$forwarded`, starts no reply thread, addresses nobody, does not also quote the body). Suite green: web 621, server 110, typecheck clean. **Merged** 2026-09-01 as coffey-labs/ihasmail@ccc9f73a71f4 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.