Skip to content

fix: TrackLink Unicode/emoji URLs, bounce multipart infinite loop, opt-in List-Unsubscribe - #3080

Closed
buddywhitman wants to merge 1 commit into
knadh:masterfrom
buddywhitman:fix/tracklink-unicode-bounce-unsub
Closed

fix: TrackLink Unicode/emoji URLs, bounce multipart infinite loop, opt-in List-Unsubscribe#3080
buddywhitman wants to merge 1 commit into
knadh:masterfrom
buddywhitman:fix/tracklink-unicode-bounce-unsub

Conversation

@buddywhitman

Copy link
Copy Markdown

Fixes three reported bugs with no existing open PRs.

Changes

Fix #3076 — TrackLink regex drops URLs containing emoji or non-ASCII Unicode

The original regex [\p{L}\p{N}_\-\.~!#$&'()*+,/:;=?@\[\]%]* covers Unicode
letters/numbers but not emoji (Unicode category So), so campaign links with
emoji in query params or fragments were silently truncated.

Fix: replace with \S+? (lazy non-whitespace). The @TrackLink suffix anchors
the match, so the lazy quantifier stops at the right boundary and handles any
non-whitespace URL character including emoji.

Fix #3071 — bounce POP processor: infinite loop on broken multipart message

In internal/bounce/mailbox/pop.go, the multipart-parse loop used continue
on non-EOF errors from mr.NextPart(). A broken reader returns the same error
forever, spinning the loop indefinitely.

Fix: break instead of continue on non-EOF errors.

Fix #3063 — List-Unsubscribe header non-functional on opt-in (non-campaign) emails

Opt-in emails use dummyUUID as the campaign UUID in the unsubscribe URL, which
the route rejects — so clicking the header link returned a 404/error. The code
also set List-Unsubscribe-Post (RFC 8058 one-click), which requires the URL to
actually work.

Fix: Remove List-Unsubscribe-Post (one-click requires a working endpoint).
Keep the informational List-Unsubscribe header using the already-resolved
out.UnsubURL rather than re-constructing it with dummyUUID.

Testing

  • go build ./... passes
  • Manually traced each code path against the issue reproduction steps

…bscribe

**knadh#3076@Tracklink corrupts Unicode/emoji URLs**
The RFC3986 character class `[\p{L}\p{N}_-...]` excluded emoji (Unicode
category \p{So}) and other IRI characters. Replace with lazy `\S+?` which
stops at the first `@TrackLink` suffix and handles all non-whitespace URL
characters including Unicode, emoji, and percent-encoded sequences.

**knadh#3071 — Bounce multipart reader loops on malformed messages**
After a non-EOF error from `mr.NextPart()`, the code did `continue`,
re-entering the loop and calling `NextPart()` on the same broken reader
indefinitely. Changed to `break` so we process whatever was successfully
read and skip the rest of the malformed message.

**knadh#3063 — List-Unsubscribe-Post header non-functional on opt-in emails**
Opt-in confirmation emails use `dummyUUID` as the campaign UUID in the
unsubscribe URL; the unsubscribe route rejects this, making RFC 8058
one-click unsubscription silently fail. Removed `List-Unsubscribe-Post`
(the one-click trigger) and the duplicate `unsubURL` computation (using
the already-constructed `out.UnsubURL`). The informational
`List-Unsubscribe` header is retained so email clients still display a
visible unsubscribe link.

Relates to: knadh#3076, knadh#3071, knadh#3063

Signed-off-by: Pulkit Kumar <pulkit.talks@gmail.com>
@blu3id

blu3id commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Hi @buddywhitman,

Thanks for the contributions. Regarding #3063 (the issue I created) I don't think this is a helpful fix. While technically correct removing the List-Unsubscribe-Post header means the List-Unsubscribe is no-longer "broken" per RFC8058 all the major MSPs (Gmail, Microsoft, etc.) expect both a List-Unsubscribe and List-Unsubscribe-Post header to meet their bulk mail requirements for easy unsubscribing.1 This is particularly important given the discussion in the issue regarding the reason for why this header was added to the opt-in emails in the first place. It would have been good to discuss this in the issue (which I thought made it clear that the technical solutions was still up for discussion) before opening a PR.

Footnotes

  1. https://support.google.com/a/answer/81126?hl=en

@blu3id

blu3id commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

On a broader but linked note to the above it isn't great practice to lump multiple issues and their fixes into a single PR as it makes discussion and eventual merging harder for maintainers (FYI which I am not for this project)

Fixes three reported bugs with no existing open PRs.

#3076 has a PR that is already open - #3077 which was opened ~3 days ago
#3071 has a PR that is already open - #3075 which was opened ~3 days ago

@buddywhitman

Copy link
Copy Markdown
Author

Hi @blu3id
Thank you for your feedback! My bad for not discussing the implementation before opening a PR, and thank you for pointing out the newly opened PRs for the other issues. I implemented these fixes a couple of days back, but only created a PR yesterday. Well, regardless, the superseding fixes have superior implementation.

Have some ideas for implementing a fix for your flagged issue, will discuss further in the respective issue thread. Closing this PR for now.

@buddywhitman
buddywhitman deleted the fix/tracklink-unicode-bounce-unsub branch June 9, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants