move checking whether a message needs smtputf8 (has utf8 in any of the header sections) to package message

This commit is contained in:
Mechiel Lukkien 2024-12-07 13:05:09 +01:00
parent 3f727cf380
commit 0871bf5219
No known key found for this signature in database
3 changed files with 49 additions and 57 deletions

32
main.go
View file

@ -1,7 +1,6 @@
package main
import (
"bufio"
"bytes"
"context"
"crypto"
@ -3519,35 +3518,10 @@ func cmdMessageParse(c *cmd) {
err = enc.Encode(part)
xcheckf(err, "write")
hasNonASCII := func(r io.Reader) bool {
br := bufio.NewReader(r)
for {
b, err := br.ReadByte()
if err == io.EOF {
break
}
xcheckf(err, "read header")
if b > 0x7f {
return true
}
}
return false
}
var walk func(p *message.Part) bool
walk = func(p *message.Part) bool {
if hasNonASCII(p.HeaderReader()) {
return true
}
for _, pp := range p.Parts {
if walk(&pp) {
return true
}
}
return false
}
if smtputf8 {
fmt.Println("message needs smtputf8:", walk(&part))
needs, err := part.NeedsSMTPUTF8()
xcheckf(err, "checking if message needs smtputf8")
fmt.Println("message needs smtputf8:", needs)
}
}

View file

@ -21,6 +21,7 @@ import (
"net/textproto"
"strings"
"time"
"unicode"
"golang.org/x/text/encoding/ianaindex"
@ -598,6 +599,38 @@ func (p *Part) IsDSN() bool {
(p.Parts[1].MediaSubType == "DELIVERY-STATUS" || p.Parts[1].MediaSubType == "GLOBAL-DELIVERY-STATUS")
}
func hasNonASCII(r io.Reader) (bool, error) {
br := bufio.NewReader(r)
for {
b, err := br.ReadByte()
if err == io.EOF {
break
} else if err != nil {
return false, err
}
if b > unicode.MaxASCII {
return true, nil
}
}
return false, nil
}
// NeedsSMTPUTF8 returns whether the part needs the SMTPUTF8 extension to be
// transported, due to non-ascii in message headers.
func (p *Part) NeedsSMTPUTF8() (bool, error) {
if has, err := hasNonASCII(p.HeaderReader()); err != nil {
return false, fmt.Errorf("reading header: %w", err)
} else if has {
return true, nil
}
for _, pp := range p.Parts {
if has, err := pp.NeedsSMTPUTF8(); err != nil || has {
return has, err
}
}
return false, nil
}
var ErrParamEncoding = errors.New("bad header parameter encoding")
// DispositionFilename tries to parse the disposition header and the "filename"

View file

@ -1940,47 +1940,32 @@ func (c *conn) cmdRcpt(p *parser) {
c.bwritecodeline(smtp.C250Completed, smtp.SeAddr1Other0, "now on the list", nil)
}
// ../rfc/6531:497
func (c *conn) isSMTPUTF8Required(part *message.Part) bool {
hasNonASCII := func(r io.Reader) bool {
br := bufio.NewReader(r)
for {
b, err := br.ReadByte()
if err == io.EOF {
break
}
xcheckf(err, "read header")
if b > unicode.MaxASCII {
return true
}
}
return false
}
var hasNonASCIIPartHeader func(p *message.Part) bool
hasNonASCIIPartHeader = func(p *message.Part) bool {
if hasNonASCII(p.HeaderReader()) {
func hasNonASCII(s string) bool {
for _, c := range []byte(s) {
if c > unicode.MaxASCII {
return true
}
for _, pp := range p.Parts {
if hasNonASCIIPartHeader(&pp) {
return true
}
}
return false
}
return false
}
// ../rfc/6531:497
func (c *conn) isSMTPUTF8Required(part *message.Part) bool {
// Check "MAIL FROM".
if hasNonASCII(strings.NewReader(string(c.mailFrom.Localpart))) {
if hasNonASCII(string(c.mailFrom.Localpart)) {
return true
}
// Check all "RCPT TO".
for _, rcpt := range c.recipients {
if hasNonASCII(strings.NewReader(string(rcpt.Addr.Localpart))) {
if hasNonASCII(string(rcpt.Addr.Localpart)) {
return true
}
}
// Check header in all message parts.
return hasNonASCIIPartHeader(part)
smtputf8, err := part.NeedsSMTPUTF8()
xcheckf(err, "checking if smtputf8 is required")
return smtputf8
}
// ../rfc/5321:1992 ../rfc/5321:1098