release: 0.4.0: Domain-admin groundwork, send-log auth, packaging fixes

The project's mark is now in use rather than only on file. The README opens
Added:
  - The project's mark is now in use rather than only on file. The README opens
Changed:
  - panel: the sign-in and setup pages are now a column the width of their own
  - panel: the three monitoring pages — Deliveries, Mail queue, System log — are
Fixed:
  - panel: Deliveries now shows the subject as text rather than as its MIME
  - panel: table cells may now break inside a word, so no single long value can
  - panel: the Applications list on a domain page no longer comes apart. It was a

Full detail: CHANGELOG.md (0.4.0).
This commit is contained in:
mix
2026-09-22 15:24:51 +03:00
parent af9f89b628
commit 72b09d60a6
21 changed files with 317 additions and 86 deletions
+26 -1
View File
@@ -13,6 +13,7 @@ package milter
import (
"context"
"log"
"mime"
"net"
"net/textproto"
"strings"
@@ -92,11 +93,35 @@ func (s *session) RcptTo(rcpt string, m *milter.Modifier) (milter.Response, erro
// Header captures the Subject. Only the first Subject header is kept.
func (s *session) Header(name, value string, m *milter.Modifier) (milter.Response, error) {
if s.subject == "" && textproto.CanonicalMIMEHeaderKey(name) == "Subject" {
s.subject = value
s.subject = decodeSubject(value)
}
return milter.RespContinue, nil
}
// subjectMaxRunes caps what the journal keeps of a subject. A Subject header
// may legally run to hundreds of characters; the log only needs enough to
// recognise the message, and the panel shows one row per recipient.
const subjectMaxRunes = 200
// decodeSubject turns the raw Subject header into display text. Anything
// non-ASCII arrives as RFC 2047 encoded-words (=?utf-8?Q?=D0=9F…?=), which the
// panel would otherwise show verbatim: unreadable, and — being one unbreakable
// run — wide enough to push the send-log table out of its card. Go's decoder
// covers the UTF-8 and ASCII charsets senders use in practice; for anything
// else (windows-1251, koi8-r) it fails and the raw header is kept, which is no
// worse than before. Truncation is applied after decoding so the cap counts
// characters of the subject, not bytes of its encoding.
func decodeSubject(v string) string {
if dec, err := (&mime.WordDecoder{}).DecodeHeader(v); err == nil {
v = dec
}
v = strings.TrimSpace(v)
if r := []rune(v); len(r) > subjectMaxRunes {
v = string(r[:subjectMaxRunes]) + "…"
}
return v
}
// Body fires at end-of-message, when the queue-id macro {i} is set and the
// message is about to be committed to the queue. This is where the "queued"
// rows are written. We accept (this milter is done) without ever rejecting.