journal milter: recipients as Postfix logs them, once each
test / test (push) Canceled after 0s

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:
mix
2026-10-14 20:30:47 +03:00
co-authored by Claude Opus 5.5
parent 0ed03b2094
commit c8f669714e
11 changed files with 302 additions and 58 deletions
+7 -4
View File
@@ -23,6 +23,7 @@ const smtpsAddr = "127.0.0.1:20465"
type sendAttempt struct {
authLogin, authPassword string
from, to string
moreTo []string // further RCPT TO, after to
headerFrom string
subject, body string
}
@@ -133,10 +134,12 @@ func attemptSend(a sendAttempt) sendResult {
_ = c.Quit()
return res
}
if err := c.Rcpt(a.to); err != nil {
res.rcptErr = err
_ = c.Quit()
return res
for _, to := range append([]string{a.to}, a.moreTo...) {
if err := c.Rcpt(to); err != nil {
res.rcptErr = err
_ = c.Quit()
return res
}
}
w, err := c.Data()
if err != nil {