The row's recipient has to be the address Postfix writes in to=<..> or orig_to=<..>, or the delivery line finds no row and the sweep closes it as bounced. Postfix 3.7.11 hands the milter the RCPT argument as the client wrote it (smtpd.c, milter_argv(); extract_addr() leaves it), go-milter v0.4.1 trims "<>" and drops the ESMTP parameters (session.go, case CodeRcpt) - so cleanAddress's cut at the first blank broke a quoted local part (B-G5). Postfix keeps the address in internal form (tok822: quotes off, backslash escapes taken) and logs it external (info_log_addr_form.c, quote_822_local(): quoted again only when not a dot-string); recipientAsLogged does the same, "ab"@x -> ab@x, "a b"@x stays. A recipient given twice in any case is one row: cleanup drops the duplicate, comparing folded (cleanup_out_recipient.c, been_here(), BH_FLAG_FOLD; A-31). A rewritten recipient is matched by orig_to=, which log_adhoc() writes only when it differs (A-9). The store's case-insensitive match stays, now with its reason from the source (D-S20). Stand (2026-10-14, make e2e, exit 0): new recipients_as_postfix_logs_them - RCPT TO "x y"@, "ab"@ and "X Y"@ sink.e2e.test give two rows, "x y"@sink.e2e.test and ab@sink.e2e.test, both sent. go vet and go test ./... pass on the server; a unit test drives the milter through go-milter's own client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+13
-3
@@ -481,9 +481,19 @@ the meantime stands. A deferred row is the sweep's too because Postfix can
|
||||
drop a deferred message without a final line for its recipients: at expiry
|
||||
before `2.5.2` read that line, and with `postsuper -d`, which logs only
|
||||
`removed`. The listing's `#` - a message forced to expire - is read as part
|
||||
of the id's flags (`src/postqueue/showq_compat.c`, `showq_message()`). A delivery line updates only the newest row of its queue id
|
||||
and recipient: Postfix reuses a short queue id once the message has left the
|
||||
queue, and an old row must not be rewritten. `UpdateStatus`'s transaction
|
||||
of the id's flags (`src/postqueue/showq_compat.c`, `showq_message()`).
|
||||
|
||||
**Which row a delivery line is.** The journal milter writes a row's
|
||||
recipient as Postfix will log it: the address the client gave, its quoted
|
||||
local part unquoted and quoted again only where it needs to be (`"ab"@x`
|
||||
becomes `ab@x`, `"a b"@x` stays; Postfix 3.7.11, `tok822_parse.c`,
|
||||
`quote_822_local.c`), case kept, and a recipient given twice - in any case -
|
||||
once, since cleanup keeps one (`cleanup_out_recipient.c`, `been_here()`
|
||||
folding case). A delivery line names it in `orig_to=` when Postfix rewrote
|
||||
the address, in `to=` otherwise (`log_adhoc.c` writes `orig_to=` only then),
|
||||
and is matched without regard to case. It updates only the newest row of its
|
||||
queue id and recipient: Postfix reuses a short queue id once the message has
|
||||
left the queue, and an old row must not be rewritten. `UpdateStatus`'s transaction
|
||||
starts with a write, so a commit by the `dmarc-ingest` process in between
|
||||
cannot fail it with `database is locked`.
|
||||
Now that the log survives the container, reaching this path should mean the
|
||||
|
||||
Reference in New Issue
Block a user