feat(send): Phase 2 send path — SMTP, MIME, reply threading, outbound policy
Adds the `send` agent command and everything behind it: - store: Account carries SMTP host/port/security (NULL-safe scan/insert/select); admin `account add` gains --smtp-* flags (applied for RW accounts). - policy: OutboundRule.Check(recipients) → (ok, reason); RO ⇒ ro_mode, whitelist-out blocks the whole send if any recipient fails (no partial send). - mail: Header.References; OutgoingMessage + BuildMIME (plain text + attachments, In-Reply-To/References threading, Bcc envelope-only); SendSMTP (tls/starttls, SASL PLAIN, envelope send) via emersion/go-smtp. - cli: SendCmd gates outbound, resolves --reply-to under the inbound filter (filtered/absent source ⇒ not_found), reads attachments, audits, emits the JSON envelope; repeatable --to/--cc/--bcc/--attach flags wired into the router. Implemented test-first; full suite passes incl -race. Validated live against friday.mxlogin.com: real send to me@stevecliff.com, RO + whitelist-out blocks, and --reply-to threading off a live INBOX message. test-creds.md gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ type Header struct {
|
||||
Subject string
|
||||
Date string
|
||||
MessageID string
|
||||
References []string // parsed Message-IDs (no angle brackets), oldest first
|
||||
HasAttachments bool
|
||||
}
|
||||
|
||||
@@ -51,6 +52,9 @@ func readHeader(mr *mail.Reader, uid uint32) Header {
|
||||
} else {
|
||||
h.MessageID = strings.Trim(hd.Get("Message-Id"), "<> ")
|
||||
}
|
||||
if refs, err := hd.MsgIDList("References"); err == nil && len(refs) > 0 {
|
||||
h.References = refs
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user