Go 1.27.1, and the code it can say more simply

Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers; omitzero is not applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-09-16 22:56:29 -07:00
co-authored by Hanzo Dev
parent 0aad59738b
commit 483277a9cc
30 changed files with 174 additions and 219 deletions
+6 -9
View File
@@ -21,7 +21,6 @@ import (
"github.com/hanzoai/mail/config"
"github.com/hanzoai/mail/dns"
"github.com/hanzoai/mail/junk"
"github.com/hanzoai/mail/mlog"
"github.com/hanzoai/mail/mox-"
"github.com/hanzoai/mail/mtasts"
@@ -111,14 +110,12 @@ func MakeAccountConfig(addr smtp.Address) config.Account {
},
RejectsMailbox: "Rejects",
JunkFilter: &config.JunkFilter{
Threshold: 0.95,
Params: junk.Params{
Onegrams: true,
MaxPower: .01,
TopWords: 10,
IgnoreWords: .1,
RareWords: 2,
},
Threshold: 0.95,
Onegrams: true,
MaxPower: .01,
TopWords: 10,
IgnoreWords: .1,
RareWords: 2,
},
NoCustomPassword: true,
}
+3 -3
View File
@@ -354,9 +354,9 @@ Accounts:
m1 := store.Message{
MailboxID: sent.ID,
MailboxOrigID: sent.ID,
Flags: store.Flags{Seen: true, Junk: true},
Size: int64(len(prefix1) + len(msg1)),
MsgPrefix: []byte(prefix1),
Seen: true, Junk: true,
Size: int64(len(prefix1) + len(msg1)),
MsgPrefix: []byte(prefix1),
}
mf1 := tempfile()
xcheckf(err, "creating temp file for delivery")
+1 -1
View File
@@ -1,6 +1,6 @@
module github.com/hanzoai/mail
go 1.25.0
go 1.27.1
require (
github.com/mjl-/adns v0.0.0-20250321173553-ab04b05bdfea
+1 -1
View File
@@ -136,7 +136,7 @@ type Opts struct {
// Debug-8.
func New(conn net.Conn, opts *Opts) (client *Conn, rerr error) {
c := Conn{
Proto: Proto{conn: conn},
conn: conn,
}
var clog *slog.Logger
+9 -8
View File
@@ -886,15 +886,16 @@ func (p *Proto) xnilString() string {
}
}
//go:fix inline
func ptr[T any](v T) *T {
return &v
return new(v)
}
func (p *Proto) xnilptrString() *string {
if p.peek('"') {
return ptr(p.xquoted())
return new(p.xquoted())
} else if p.peek('{') {
return ptr(string(p.xliteral()))
return new(string(p.xliteral()))
} else {
p.xtake("nil")
return nil
@@ -1118,11 +1119,11 @@ func (p *Proto) xbodyExtMpart() (ext *BodyExtensionMpart) {
if !p.space() {
return
}
ext.Language = ptr(p.xbodyFldLang())
ext.Language = new(p.xbodyFldLang())
if !p.space() {
return
}
ext.Location = ptr(p.xbodyFldLoc())
ext.Location = new(p.xbodyFldLoc())
for p.space() {
ext.More = append(ext.More, p.xbodyExtension())
}
@@ -1141,11 +1142,11 @@ func (p *Proto) xbodyExt1Part() (ext *BodyExtension1Part) {
if !p.space() {
return
}
ext.Language = ptr(p.xbodyFldLang())
ext.Language = new(p.xbodyFldLang())
if !p.space() {
return
}
ext.Location = ptr(p.xbodyFldLoc())
ext.Location = new(p.xbodyFldLoc())
for p.space() {
ext.More = append(ext.More, p.xbodyExtension())
}
@@ -1182,7 +1183,7 @@ func (p *Proto) xbodyFldDsp() (*string, [][2]string) {
p.xspace()
param := p.xbodyFldParam()
p.xtake(")")
return ptr(disposition), param
return new(disposition), param
}
// ../rfc/9051:6391 ../rfc/3501:4616
+4 -3
View File
@@ -18,7 +18,8 @@ func tcompare(t *testing.T, a, b any) {
}
}
func uint32ptr(v uint32) *uint32 { return &v }
//go:fix inline
func uint32ptr(v uint32) *uint32 { return new(v) }
func TestParse(t *testing.T) {
code, err := ParseCode("COPYUID 1 1:3 2:4")
@@ -26,8 +27,8 @@ func TestParse(t *testing.T) {
tcompare(t, code,
CodeCopyUID{
DestUIDValidity: 1,
From: []NumRange{{First: 1, Last: uint32ptr(3)}},
To: []NumRange{{First: 2, Last: uint32ptr(4)}},
From: []NumRange{{First: 1, Last: new(uint32(3))}},
To: []NumRange{{First: 2, Last: new(uint32(4))}},
},
)
+1 -1
View File
@@ -293,7 +293,7 @@ func testCondstoreQresync(t *testing.T, qresync, uidonly bool) {
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 1, All: esearchall0("1"), ModSeq: clientModseq})
tc.transactf("ok", "Search Return (Count) 1:* Modseq 0")
tc.xesearch(imapclient.UntaggedEsearch{Count: uint32ptr(6), ModSeq: clientModseq})
tc.xesearch(imapclient.UntaggedEsearch{Count: new(uint32(6)), ModSeq: clientModseq})
tc.transactf("ok", "Search Return (Min Max) 1:* Modseq 0")
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 6, ModSeq: clientModseq})
+21 -21
View File
@@ -59,10 +59,10 @@ func testFetch(t *testing.T, uidonly bool) {
bodystructure1 := bodyxstructure1
bodystructure1.RespAttr = "BODYSTRUCTURE"
bodyext1 := imapclient.BodyExtension1Part{
Disposition: ptr((*string)(nil)),
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new((*string)(nil)),
DispositionParams: new([][2]string(nil)),
Language: new([]string(nil)),
Location: new((*string)(nil)),
}
bodystructbody1.Ext = &bodyext1
bodystructure1.Body = bodystructbody1
@@ -312,19 +312,19 @@ func testFetch(t *testing.T, uidonly bool) {
Bodies: []any{
imapclient.BodyTypeBasic{MediaType: "AUDIO", MediaSubtype: "BASIC", BodyFields: imapclient.BodyFields{CTE: "BASE64", Octets: 22}, Ext: &bodyext1},
imapclient.BodyTypeBasic{MediaType: "IMAGE", MediaSubtype: "JPEG", BodyFields: imapclient.BodyFields{CTE: "BASE64"}, Ext: &imapclient.BodyExtension1Part{
Disposition: ptr(ptr("inline")),
DispositionParams: ptr([][2]string{{"filename", "image.jpg"}}),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new(new("inline")),
DispositionParams: new([][2]string{{"filename", "image.jpg"}}),
Language: new([]string(nil)),
Location: new((*string)(nil)),
}},
},
MediaSubtype: "PARALLEL",
Ext: &imapclient.BodyExtensionMpart{
Params: [][2]string{{"BOUNDARY", "unique-boundary-2"}},
Disposition: ptr((*string)(nil)), // Present but nil.
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new((*string)(nil)), // Present but nil.
DispositionParams: new([][2]string(nil)),
Language: new([]string(nil)),
Location: new((*string)(nil)),
},
},
imapclient.BodyTypeText{MediaType: "TEXT", MediaSubtype: "ENRICHED", BodyFields: imapclient.BodyFields{Octets: 145}, Lines: 5, Ext: &bodyext1},
@@ -343,21 +343,21 @@ func testFetch(t *testing.T, uidonly bool) {
MediaType: "TEXT", MediaSubtype: "PLAIN", BodyFields: imapclient.BodyFields{Params: [][2]string{{"CHARSET", "ISO-8859-1"}}, CTE: "QUOTED-PRINTABLE", Octets: 51}, Lines: 1, Ext: &bodyext1},
Lines: 7,
Ext: &imapclient.BodyExtension1Part{
MD5: ptr("MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY="),
Disposition: ptr((*string)(nil)),
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string{"en", "de"}),
Location: ptr(ptr("http://localhost")),
MD5: new("MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY="),
Disposition: new((*string)(nil)),
DispositionParams: new([][2]string(nil)),
Language: new([]string{"en", "de"}),
Location: new(new("http://localhost")),
},
},
},
MediaSubtype: "MIXED",
Ext: &imapclient.BodyExtensionMpart{
Params: [][2]string{{"BOUNDARY", "unique-boundary-1"}},
Disposition: ptr((*string)(nil)), // Present but nil.
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new((*string)(nil)), // Present but nil.
DispositionParams: new([][2]string(nil)),
Language: new([]string(nil)),
Location: new((*string)(nil)),
},
},
}
+3 -3
View File
@@ -68,7 +68,7 @@ func testMove(t *testing.T, uidonly bool) {
tc.transactf("ok", "move 1:* Trash")
tc.xuntagged(
imapclient.UntaggedResult{Status: "OK", Code: imapclient.CodeCopyUID{DestUIDValidity: 1, From: []imapclient.NumRange{{First: 3, Last: uint32ptr(4)}}, To: []imapclient.NumRange{{First: 1, Last: uint32ptr(2)}}}, Text: "moved"},
imapclient.UntaggedResult{Status: "OK", Code: imapclient.CodeCopyUID{DestUIDValidity: 1, From: []imapclient.NumRange{{First: 3, Last: new(uint32(4))}}, To: []imapclient.NumRange{{First: 1, Last: new(uint32(2))}}}, Text: "moved"},
imapclient.UntaggedExpunge(1),
imapclient.UntaggedExpunge(1),
)
@@ -92,12 +92,12 @@ func testMove(t *testing.T, uidonly bool) {
tc.transactf("ok", "uid move 6:5 Trash")
if uidonly {
tc.xuntagged(
imapclient.UntaggedResult{Status: "OK", Code: imapclient.CodeCopyUID{DestUIDValidity: 1, From: []imapclient.NumRange{{First: 5, Last: uint32ptr(6)}}, To: []imapclient.NumRange{{First: 3, Last: uint32ptr(4)}}}, Text: "moved"},
imapclient.UntaggedResult{Status: "OK", Code: imapclient.CodeCopyUID{DestUIDValidity: 1, From: []imapclient.NumRange{{First: 5, Last: new(uint32(6))}}, To: []imapclient.NumRange{{First: 3, Last: new(uint32(4))}}}, Text: "moved"},
imapclient.UntaggedVanished{UIDs: xparseNumSet("5:6")},
)
} else {
tc.xuntagged(
imapclient.UntaggedResult{Status: "OK", Code: imapclient.CodeCopyUID{DestUIDValidity: 1, From: []imapclient.NumRange{{First: 5, Last: uint32ptr(6)}}, To: []imapclient.NumRange{{First: 3, Last: uint32ptr(4)}}}, Text: "moved"},
imapclient.UntaggedResult{Status: "OK", Code: imapclient.CodeCopyUID{DestUIDValidity: 1, From: []imapclient.NumRange{{First: 5, Last: new(uint32(6))}}, To: []imapclient.NumRange{{First: 3, Last: new(uint32(4))}}}, Text: "moved"},
imapclient.UntaggedExpunge(1),
imapclient.UntaggedExpunge(1),
)
+13 -13
View File
@@ -109,10 +109,10 @@ func testNotify(t *testing.T, uidonly bool) {
},
Lines: 1,
Ext: &imapclient.BodyExtension1Part{
Disposition: ptr((*string)(nil)),
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new((*string)(nil)),
DispositionParams: new([][2]string(nil)),
Language: new([]string(nil)),
Location: new((*string)(nil)),
},
},
imapclient.BodyTypeText{
@@ -124,24 +124,24 @@ func testNotify(t *testing.T, uidonly bool) {
},
Lines: 1,
Ext: &imapclient.BodyExtension1Part{
Disposition: ptr((*string)(nil)),
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new((*string)(nil)),
DispositionParams: new([][2]string(nil)),
Language: new([]string(nil)),
Location: new((*string)(nil)),
},
},
},
MediaSubtype: "ALTERNATIVE",
Ext: &imapclient.BodyExtensionMpart{
Params: [][2]string{{"BOUNDARY", "x"}},
Disposition: ptr((*string)(nil)), // Present but nil.
DispositionParams: ptr([][2]string(nil)),
Language: ptr([]string(nil)),
Location: ptr((*string)(nil)),
Disposition: new((*string)(nil)), // Present but nil.
DispositionParams: new([][2]string(nil)),
Language: new([]string(nil)),
Location: new((*string)(nil)),
},
},
},
imapclient.FetchPreview{Preview: ptr("this is plain text.")},
imapclient.FetchPreview{Preview: new("this is plain text.")},
imapclient.FetchModSeq(modseq),
),
)
+16 -15
View File
@@ -34,8 +34,9 @@ this is html.
--x--
`, "\n", "\r\n")
//go:fix inline
func uint32ptr(v uint32) *uint32 {
return &v
return new(v)
}
func (tc *testconn) xsearch(nums ...uint32) {
@@ -316,7 +317,7 @@ func testSearch(t *testing.T, uidonly bool) {
tc.xesearch(esearchall("1:3")) // Without any options, "ALL" is implicit.
tc.transactf("ok", "search return (min max count all) all")
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 3, Count: uint32ptr(3), All: esearchall0("1:3")})
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 3, Count: new(uint32(3)), All: esearchall0("1:3")})
tc.transactf("ok", "search return (min) all")
tc.xesearch(imapclient.UntaggedEsearch{Min: 1})
@@ -352,25 +353,25 @@ func testSearch(t *testing.T, uidonly bool) {
tc.xesearch(imapclient.UntaggedEsearch{})
tc.transactf("ok", "search return (min max all count) not all")
tc.xesearch(imapclient.UntaggedEsearch{Count: uint32ptr(0)})
tc.xesearch(imapclient.UntaggedEsearch{Count: new(uint32(0))})
tc.transactf("ok", "search return (min max count all) 1,3")
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 3, Count: uint32ptr(2), All: esearchall0("1,3")})
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 3, Count: new(uint32(2)), All: esearchall0("1,3")})
tc.transactf("ok", "search return (min max count all) UID 5,7")
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 3, Count: uint32ptr(2), All: esearchall0("1,3")})
tc.xesearch(imapclient.UntaggedEsearch{Min: 1, Max: 3, Count: new(uint32(2)), All: esearchall0("1,3")})
}
tc.transactf("ok", "UID search return (min max count all) all")
tc.xesearch(imapclient.UntaggedEsearch{UID: true, Min: 5, Max: 7, Count: uint32ptr(3), All: esearchall0("5:7")})
tc.xesearch(imapclient.UntaggedEsearch{UID: true, Min: 5, Max: 7, Count: new(uint32(3)), All: esearchall0("5:7")})
if !uidonly {
tc.transactf("ok", "uid search return (min max count all) 1,3")
tc.xesearch(imapclient.UntaggedEsearch{UID: true, Min: 5, Max: 7, Count: uint32ptr(2), All: esearchall0("5,7")})
tc.xesearch(imapclient.UntaggedEsearch{UID: true, Min: 5, Max: 7, Count: new(uint32(2)), All: esearchall0("5,7")})
}
tc.transactf("ok", "uid search return (min max count all) UID 5,7")
tc.xesearch(imapclient.UntaggedEsearch{UID: true, Min: 5, Max: 7, Count: uint32ptr(2), All: esearchall0("5,7")})
tc.xesearch(imapclient.UntaggedEsearch{UID: true, Min: 5, Max: 7, Count: new(uint32(2)), All: esearchall0("5,7")})
if !uidonly {
tc.transactf("no", `search return () charset unknown text "mox"`)
@@ -584,8 +585,8 @@ func testSearchMulti(t *testing.T, selected, uidonly bool) {
tc.cmdf("Tag1", `Esearch In (Mailboxes (INBOX Archive Archive)) Return (Min Max Count All) All`)
tc.response("ok")
tc.xuntagged(
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Min: 5, Max: 7, Count: uint32ptr(3), All: esearchall0("5:7")},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Archive", UIDValidity: 1, UID: true, Min: 1, Max: 1, Count: uint32ptr(1), All: esearchall0("1")},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Min: 5, Max: 7, Count: new(uint32(3)), All: esearchall0("5:7")},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Archive", UIDValidity: 1, UID: true, Min: 1, Max: 1, Count: new(uint32(1)), All: esearchall0("1")},
)
// No response if none of the mailboxes exist.
@@ -746,15 +747,15 @@ func testSearchMulti(t *testing.T, selected, uidonly bool) {
tc.response("ok")
if selected {
tc.xuntagged(
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Count: uint32ptr(3)},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Archive", UIDValidity: 1, UID: true, Count: uint32ptr(2)},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Count: new(uint32(3))},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Archive", UIDValidity: 1, UID: true, Count: new(uint32(2))},
imapclient.UntaggedExists(4),
tc.untaggedFetch(4, 8, imapclient.FetchFlags(nil)),
)
} else {
tc.xuntagged(
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Count: uint32ptr(4)},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Archive", UIDValidity: 1, UID: true, Count: uint32ptr(2)},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Count: new(uint32(4))},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Archive", UIDValidity: 1, UID: true, Count: new(uint32(2))},
)
}
@@ -845,7 +846,7 @@ func testSearchMulti(t *testing.T, selected, uidonly bool) {
tc.cmdf("Tag1", `Esearch In (Selected) Return (Save Min Max Count) All`)
tc.response("ok")
tc.xuntagged(
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Min: 5, Max: 8, Count: uint32ptr(4)},
imapclient.UntaggedEsearch{Tag: "Tag1", Mailbox: "Inbox", UIDValidity: 1, UID: true, Min: 5, Max: 8, Count: new(uint32(4))},
)
tc.cmdf("Tag1", `Esearch In (Selected) Return () $`)
+3 -6
View File
@@ -1231,9 +1231,6 @@ func (c *conn) command() {
panic(x)
}
var sxerr syntaxError
var uerr userError
var serr serverError
if isClosed(err) {
c.log.Infox("imap command ioerror", err, logFields...)
result = "ioerror"
@@ -1241,7 +1238,7 @@ func (c *conn) command() {
debug.PrintStack()
}
panic(err)
} else if errors.As(err, &sxerr) {
} else if sxerr, ok := errors.AsType[syntaxError](err); ok {
result = "badsyntax"
if c.ncmds == 0 {
// Other side is likely speaking something else than IMAP, send error message and
@@ -1269,12 +1266,12 @@ func (c *conn) command() {
c.xflush()
panic(fmt.Errorf("aborting connection after syntax error for command with non-sync literal: %w", errProtocol))
}
} else if errors.As(err, &serr) {
} else if _, ok := errors.AsType[serverError](err); ok {
result = "servererror"
c.log.Errorx("imap command server error", err, logFields...)
debug.PrintStack()
c.xbwriteresultf("%s NO %s %v", tag, cmd, err)
} else if errors.As(err, &uerr) {
} else if uerr, ok := errors.AsType[userError](err); ok {
result = "usererror"
c.log.Debugx("imap command user error", err, logFields...)
if uerr.code != "" {
+2 -1
View File
@@ -42,8 +42,9 @@ func init() {
mox.Context = ctxbg
}
//go:fix inline
func ptr[T any](v T) *T {
return &v
return new(v)
}
func tocrlf(s string) string {
+6 -9
View File
@@ -29,7 +29,6 @@ import (
"github.com/hanzoai/mail/config"
"github.com/hanzoai/mail/dkim"
"github.com/hanzoai/mail/dns"
"github.com/hanzoai/mail/junk"
"github.com/hanzoai/mail/mlog"
"github.com/hanzoai/mail/mox-"
"github.com/hanzoai/mail/moxvar"
@@ -409,14 +408,12 @@ func writeLocalConfig(log mlog.Log, dir, ip string) (rerr error) {
acc.AutomaticJunkFlags.JunkMailboxRegexp = "^(junk|spam)"
acc.AutomaticJunkFlags.NeutralMailboxRegexp = "^(inbox|neutral|postmaster|dmarc|tlsrpt|rejects)"
acc.JunkFilter = &config.JunkFilter{
Threshold: 0.95,
Params: junk.Params{
Onegrams: true,
MaxPower: .01,
TopWords: 10,
IgnoreWords: .1,
RareWords: 2,
},
Threshold: 0.95,
Onegrams: true,
MaxPower: .01,
TopWords: 10,
IgnoreWords: .1,
RareWords: 2,
}
dkimKeyBuf, err := admin.MakeDKIMEd25519Key(dns.Domain{ASCII: "localserve"}, dns.Domain{ASCII: "localhost"})
+1 -2
View File
@@ -3478,8 +3478,7 @@ func cmdWebapi(c *cmd) {
t := reflect.TypeFor[webapi.Methods]()
methods := map[string]reflect.Type{}
var ml []string
for i := range t.NumMethod() {
mt := t.Method(i)
for mt := range t.Methods() {
methods[mt.Name] = mt.Type
ml = append(ml, mt.Name)
}
+1 -2
View File
@@ -328,8 +328,7 @@ func Get(ctx context.Context, elog *slog.Logger, resolver dns.Resolver, domain d
}
func policyFetchFailureDetails(err error) tlsrpt.FailureDetails {
var verificationErr *tls.CertificateVerificationError
if errors.As(err, &verificationErr) {
if verificationErr, ok := errors.AsType[*tls.CertificateVerificationError](err); ok {
resultType, reasonCode := tlsrpt.TLSFailureDetails(verificationErr)
// Result: ../rfc/8460:601
reason := string(resultType)
+2 -4
View File
@@ -288,8 +288,7 @@ func deliverDirect(qlog mlog.Log, resolver dns.Resolver, dialer smtpclient.Diale
remoteMTA = dsn.NameIP{Name: h.XString(false), IP: remoteIP}
if result.err != nil {
lastErr = result.err
var cerr smtpclient.Error
if errors.As(result.err, &cerr) {
if cerr, ok := errors.AsType[smtpclient.Error](result.err); ok {
if cerr.Secode == smtp.SePol7MissingReqTLS30 {
nmissingRequireTLS++
}
@@ -551,8 +550,7 @@ func deliverHost(log mlog.Log, resolver dns.Resolver, dialer smtpclient.Dialer,
hostResult = tlsrpt.MakeResult(tlsrpt.NoPolicyFound, tlsaBaseDomain)
} else if err != nil {
fd := tlsrpt.Details(tlsrpt.ResultTLSAInvalid, "")
var errCode adns.ErrorCode
if errors.As(err, &errCode) {
if errCode, ok := errors.AsType[adns.ErrorCode](err); ok {
fd.FailureReasonCode = fmt.Sprintf("extended-dns-error-%d-%s", errCode, strings.ReplaceAll(errCode.String(), " ", "-"))
if errCode.IsAuthentication() {
// Result: ../rfc/8460:580
+1 -2
View File
@@ -70,13 +70,12 @@ func failMsgsTx(qlog mlog.Log, tx *bstore.Tx, msgs []*Msg, dialedIPs map[string]
m0 := msgs[0]
var smtpLines []string
var cerr smtpclient.Error
var permanent bool
var errmsg = err.Error()
var code int
var secodeOpt string
var event webhook.OutgoingEvent
if errors.As(err, &cerr) {
if cerr, ok := errors.AsType[smtpclient.Error](err); ok {
if cerr.Line != "" {
smtpLines = append([]string{cerr.Line}, cerr.MoreLines...)
}
+1 -2
View File
@@ -54,8 +54,7 @@ func (p *parser) recover(rerr *error) {
if !ok {
panic(x)
}
var xerr Error
if errors.As(err, &xerr) {
if _, ok := errors.AsType[Error](err); ok {
*rerr = err
return
}
+1 -1
View File
@@ -326,7 +326,7 @@ Only implemented on unix systems, not Windows.
m := store.Message{
Received: time.Now(),
Flags: store.Flags{Flagged: true},
Flagged: true,
}
n, err := fmt.Fprintf(f, "Date: %s\r\nSubject: mox %s available\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: 8-bit\r\n\r\nHi!\r\n\r\nVersion %s of mox is available, this install is at %s.\r\n\r\nChanges:\r\n\r\n%s\r\n\r\nPlease report any issues at https://github.com/hanzoai/mail, thanks!\r\n\r\nCheers,\r\nmox\r\n", time.Now().Format(message.RFC5322Z), latest, latest, current, strings.ReplaceAll(cl.String(), "\n", "\r\n"))
if err != nil {
+2 -4
View File
@@ -94,10 +94,8 @@ func TestReputation(t *testing.T) {
DKIMDomains: dkimDomains,
Flags: store.Flags{
Junk: junk,
Notjunk: !junk,
},
Junk: junk,
Notjunk: !junk,
}
return m
}
+1 -2
View File
@@ -1054,8 +1054,7 @@ func command(c *conn) {
panic(err)
}
var serr smtpError
if errors.As(err, &serr) {
if serr, ok := errors.AsType[smtpError](err); ok {
c.xwritecodeline(serr.code, serr.secode, fmt.Sprintf("%s (%s)", serr.errmsg, mox.ReceivedID(c.cid)), serr.err)
if serr.printStack {
c.log.Errorx("smtp error", serr.err, slog.Int("code", serr.code), slog.String("secode", serr.secode))
+3 -3
View File
@@ -725,8 +725,8 @@ func TestSpam(t *testing.T) {
MsgFromOrgDomain: "example.org",
MsgFromValidated: true,
MsgFromValidation: store.ValidationStrict,
Flags: store.Flags{Seen: true, Junk: true},
Size: int64(len(deliverMessage)),
Seen: true, Junk: true,
Size: int64(len(deliverMessage)),
}
for range 3 {
nm := m
@@ -963,7 +963,7 @@ func TestDMARCSent(t *testing.T) {
MailFrom: "[email protected]",
RcptToLocalpart: smtp.Localpart("mjl"),
RcptToDomain: "mox.example",
Flags: store.Flags{Seen: true},
Seen: true,
Size: int64(len(deliverMessage)),
}
// We need at least 50 ham messages for the junk filter to become significant. We
+6 -12
View File
@@ -355,14 +355,8 @@ var invalidReasons = map[x509.InvalidReason]string{
// Errors from crypto/tls, including local and remote alerts, from crypto/x509,
// and generic i/o and timeout errors are recognized.
func TLSFailureDetails(err error) (ResultType, string) {
var invalidErr x509.CertificateInvalidError
var hostErr x509.HostnameError
var unknownAuthErr x509.UnknownAuthorityError
var rootsErr x509.SystemRootsError
var verifyErr *tls.CertificateVerificationError
var netErr *net.OpError
var recordHdrErr tls.RecordHeaderError
if errors.As(err, &invalidErr) {
if invalidErr, ok := errors.AsType[x509.CertificateInvalidError](err); ok {
if invalidErr.Reason == x509.Expired {
// Result: ../rfc/8460:546
return ResultCertificateExpired, ""
@@ -373,16 +367,16 @@ func TLSFailureDetails(err error) (ResultType, string) {
}
// Result: ../rfc/8460:549
return ResultCertificateNotTrusted, s
} else if errors.As(err, &hostErr) {
} else if _, ok := errors.AsType[x509.HostnameError](err); ok {
// Result: ../rfc/8460:541
return ResultCertificateHostMismatch, ""
} else if errors.As(err, &unknownAuthErr) {
} else if _, ok := errors.AsType[x509.UnknownAuthorityError](err); ok {
// Result: ../rfc/8460:549
return ResultCertificateNotTrusted, ""
} else if errors.As(err, &rootsErr) {
} else if _, ok := errors.AsType[x509.SystemRootsError](err); ok {
// Result: ../rfc/8460:549
return ResultCertificateNotTrusted, "no-system-roots"
} else if errors.As(err, &verifyErr) {
} else if _, ok := errors.AsType[*tls.CertificateVerificationError](err); ok {
// We don't know a more specific error. ../rfc/8460:610
// Result: ../rfc/8460:567
return ResultValidationFailure, "unknown-go-certificate-verification-error"
@@ -398,7 +392,7 @@ func TLSFailureDetails(err error) (ResultType, string) {
}
}
return ResultValidationFailure, reasonCode
} else if errors.As(err, &recordHdrErr) {
} else if _, ok := errors.AsType[tls.RecordHeaderError](err); ok {
// Like for AlertError, not a lot of details, but better than nothing.
// Result: ../rfc/8460:567
return ResultValidationFailure, "tls-record-header-error"
+5 -8
View File
@@ -33,7 +33,6 @@ import (
"github.com/hanzoai/mail/config"
"github.com/hanzoai/mail/dns"
"github.com/hanzoai/mail/junk"
"github.com/hanzoai/mail/mlog"
"github.com/hanzoai/mail/mox-"
"github.com/hanzoai/mail/queue"
@@ -497,13 +496,11 @@ func TestAccount(t *testing.T) {
api.JunkFilterSave(ctx, nil)
jf := config.JunkFilter{
Threshold: 0.95,
Params: junk.Params{
Twograms: true,
MaxPower: 0.1,
TopWords: 10,
IgnoreWords: 0.1,
},
Threshold: 0.95,
Twograms: true,
MaxPower: 0.1,
TopWords: 10,
IgnoreWords: 0.1,
}
api.JunkFilterSave(ctx, &jf)
+3 -3
View File
@@ -1089,9 +1089,9 @@ func (s server) Send(ctx context.Context, req webapi.SendRequest) (resp webapi.S
ModSeq: modseq,
MailboxID: sentmb.ID,
MailboxOrigID: sentmb.ID,
Flags: store.Flags{Notjunk: true, Seen: true},
Size: int64(len(msgPrefix)) + xc.Size,
MsgPrefix: []byte(msgPrefix),
Notjunk: true, Seen: true,
Size: int64(len(msgPrefix)) + xc.Size,
MsgPrefix: []byte(msgPrefix),
}
err = acc.MessageAdd(log, tx, &sentmb, &sentm, dataFile, store.AddOpts{})
+27 -41
View File
@@ -238,12 +238,10 @@ func TestServer(t *testing.T) {
var sb strings.Builder
mp := multipart.NewWriter(&sb)
fdSendReq := webapi.SendRequest{
Message: webapi.Message{
To: []webapi.NameAddress{{Address: "mø[email protected]"}},
// Let server assign date, message-id.
Subject: "test",
Text: "hi",
},
To: []webapi.NameAddress{{Address: "mø[email protected]"}},
// Let server assign date, message-id.
Subject: "test",
Text: "hi",
// Don't let server add its own user-agent.
Headers: [][2]string{{"User-Agent", "test"}},
}
@@ -297,63 +295,51 @@ func TestServer(t *testing.T) {
// Trigger various error conditions.
_, err = client.Send(ctxbg, webapi.SendRequest{
Message: webapi.Message{
To: []webapi.NameAddress{{Address: "[email protected]"}},
Subject: "test",
},
To: []webapi.NameAddress{{Address: "[email protected]"}},
Subject: "test",
})
terrcode(t, err, "missingBody")
_, err = client.Send(ctxbg, webapi.SendRequest{
Message: webapi.Message{
From: []webapi.NameAddress{{Address: "other@mox.example"}},
To: []webapi.NameAddress{{Address: "[email protected]"}},
Subject: "test",
Text: "hi",
},
From: []webapi.NameAddress{{Address: "[email protected]"}},
To: []webapi.NameAddress{{Address: "mjl@mox.example"}},
Subject: "test",
Text: "hi",
})
terrcode(t, err, "badFrom")
_, err = client.Send(ctxbg, webapi.SendRequest{
Message: webapi.Message{
From: []webapi.NameAddress{{Address: "m[email protected]"}, {Address: "mox@mox.example"}},
To: []webapi.NameAddress{{Address: "[email protected]"}},
Subject: "test",
Text: "hi",
},
From: []webapi.NameAddress{{Address: "[email protected]"}, {Address: "[email protected]"}},
To: []webapi.NameAddress{{Address: "mjl@mox.example"}},
Subject: "test",
Text: "hi",
})
terrcode(t, err, "multipleFrom")
_, err = client.Send(ctxbg, webapi.SendRequest{Message: webapi.Message{Subject: "test", Text: "hi"}})
_, err = client.Send(ctxbg, webapi.SendRequest{Subject: "test", Text: "hi"})
terrcode(t, err, "noRecipients")
_, err = client.Send(ctxbg, webapi.SendRequest{
Message: webapi.Message{
MessageID: "missingltgt@localhost",
To: []webapi.NameAddress{{Address: "mø[email protected]"}},
Subject: "test",
Text: "hi",
},
MessageID: "missingltgt@localhost",
To: []webapi.NameAddress{{Address: "mø[email protected]"}},
Subject: "test",
Text: "hi",
})
terrcode(t, err, "malformedMessageID")
_, err = client.Send(ctxbg, webapi.SendRequest{
Message: webapi.Message{
MessageID: "missingltgt@localhost",
To: []webapi.NameAddress{{Address: "mø[email protected]"}},
Subject: "test",
Text: "hi",
},
MessageID: "missingltgt@localhost",
To: []webapi.NameAddress{{Address: "mø[email protected]"}},
Subject: "test",
Text: "hi",
})
terrcode(t, err, "malformedMessageID")
_, err = client.Send(ctxbg, webapi.SendRequest{
Message: webapi.Message{
From: []webapi.NameAddress{{Address: "mjl@disabled.example"}},
To: []webapi.NameAddress{{Address: "[email protected]"}},
Subject: "test",
Text: "hi",
},
From: []webapi.NameAddress{{Address: "[email protected]"}},
To: []webapi.NameAddress{{Address: "mjl@mox.example"}},
Subject: "test",
Text: "hi",
})
terrcode(t, err, "domainDisabled")
+4 -4
View File
@@ -457,7 +457,7 @@ func (w Webmail) MessageCompose(ctx context.Context, m ComposeMessage, mailboxID
ModSeq: modseq,
MailboxID: mb.ID,
MailboxOrigID: mb.ID,
Flags: store.Flags{Notjunk: true},
Notjunk: true,
Size: xc.Size,
}
@@ -1123,9 +1123,9 @@ func (w Webmail) MessageSubmit(ctx context.Context, m SubmitMessage) {
ModSeq: modseq,
MailboxID: sentmb.ID,
MailboxOrigID: sentmb.ID,
Flags: store.Flags{Notjunk: true, Seen: true},
Size: int64(len(msgPrefix)) + xc.Size,
MsgPrefix: []byte(msgPrefix),
Notjunk: true, Seen: true,
Size: int64(len(msgPrefix)) + xc.Size,
MsgPrefix: []byte(msgPrefix),
}
err = acc.MessageAdd(log, tx, &sentmb, &sentm, dataFile, store.AddOpts{})
+10 -10
View File
@@ -221,16 +221,16 @@ func TestAPI(t *testing.T) {
return nil
})
tcheck(t, err, "get mailboxes")
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: archive.ID, SpecialUse: store.SpecialUse{Draft: true}}) // Already set.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{Draft: true}}) // New draft mailbox.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{Sent: true}})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{Archive: true}})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{Trash: true}})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{Junk: true}})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{}}) // None
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{Draft: true, Sent: true, Archive: true, Trash: true, Junk: true}}) // All
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{}}) // None again.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: sent.ID, SpecialUse: store.SpecialUse{Sent: true}}) // Sent, for sending mail later.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: archive.ID, Draft: true}) // Already set.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, Draft: true}) // New draft mailbox.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, Sent: true})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, Archive: true})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, Trash: true})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, Junk: true})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{}}) // None
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, Draft: true, Sent: true, Archive: true, Trash: true, Junk: true}) // All
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: testbox1.ID, SpecialUse: store.SpecialUse{}}) // None again.
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: sent.ID, Sent: true}) // Sent, for sending mail later.
tneedError(t, func() { api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: 0}) })
// MailboxesMarkRead
+17 -25
View File
@@ -385,18 +385,18 @@ func TestView(t *testing.T) {
var chmbrename ChangeMailboxRename
getChanges(&chmbrename)
tcompare(t, chmbrename, ChangeMailboxRename{
ChangeRenameMailbox: store.ChangeRenameMailbox{MailboxID: chmbadd.Mailbox.ID, OldName: "Newbox", NewName: "Newbox2", Flags: nil, ModSeq: 13},
MailboxID: chmbadd.Mailbox.ID, OldName: "Newbox", NewName: "Newbox2", Flags: nil, ModSeq: 13,
})
// ChangeMailboxSpecialUse
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: chmbadd.Mailbox.ID, SpecialUse: store.SpecialUse{Archive: true}})
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: chmbadd.Mailbox.ID, Archive: true})
var chmbspecialuseOld, chmbspecialuseNew ChangeMailboxSpecialUse
getChanges(&chmbspecialuseOld, &chmbspecialuseNew)
tcompare(t, chmbspecialuseOld, ChangeMailboxSpecialUse{
ChangeMailboxSpecialUse: store.ChangeMailboxSpecialUse{MailboxID: archive.ID, MailboxName: "Archive", SpecialUse: store.SpecialUse{}, ModSeq: 14},
MailboxID: archive.ID, MailboxName: "Archive", SpecialUse: store.SpecialUse{}, ModSeq: 14,
})
tcompare(t, chmbspecialuseNew, ChangeMailboxSpecialUse{
ChangeMailboxSpecialUse: store.ChangeMailboxSpecialUse{MailboxID: chmbadd.Mailbox.ID, MailboxName: "Newbox2", SpecialUse: store.SpecialUse{Archive: true}, ModSeq: 14},
MailboxID: chmbadd.Mailbox.ID, MailboxName: "Newbox2", SpecialUse: store.SpecialUse{Archive: true}, ModSeq: 14,
})
// ChangeMailboxRemove
@@ -404,7 +404,7 @@ func TestView(t *testing.T) {
var chmbremove ChangeMailboxRemove
getChanges(&chmbremove)
tcompare(t, chmbremove, ChangeMailboxRemove{
ChangeRemoveMailbox: store.ChangeRemoveMailbox{MailboxID: chmbadd.Mailbox.ID, Name: "Newbox2", ModSeq: 15},
MailboxID: chmbadd.Mailbox.ID, Name: "Newbox2", ModSeq: 15,
})
// ChangeMsgAdd
@@ -417,11 +417,9 @@ func TestView(t *testing.T) {
tcompare(t, chmsgadd.MessageItems[0].Message.ID, inboxNew.ID)
chmbcounts.Size = 0
tcompare(t, chmbcounts, ChangeMailboxCounts{
ChangeMailboxCounts: store.ChangeMailboxCounts{
MailboxID: inbox.ID,
MailboxName: inbox.Name,
MailboxCounts: store.MailboxCounts{Total: 4, Unread: 3, Unseen: 3},
},
MailboxID: inbox.ID,
MailboxName: inbox.Name,
Total: 4, Unread: 3, Unseen: 3,
})
// ChangeMsgFlags
@@ -431,19 +429,15 @@ func TestView(t *testing.T) {
getChanges(&chmsgflags, &chmbcounts, &chmbkeywords)
tcompare(t, chmsgadd.ChangeAddUID.MailboxID, inbox.ID)
tcompare(t, chmbkeywords, ChangeMailboxKeywords{
ChangeMailboxKeywords: store.ChangeMailboxKeywords{
MailboxID: inbox.ID,
MailboxName: inbox.Name,
Keywords: []string{`aaa`, `changelabel`, `testlabel`},
},
MailboxID: inbox.ID,
MailboxName: inbox.Name,
Keywords: []string{`aaa`, `changelabel`, `testlabel`},
})
chmbcounts.Size = 0
tcompare(t, chmbcounts, ChangeMailboxCounts{
ChangeMailboxCounts: store.ChangeMailboxCounts{
MailboxID: inbox.ID,
MailboxName: inbox.Name,
MailboxCounts: store.MailboxCounts{Total: 4, Unread: 2, Unseen: 2},
},
MailboxID: inbox.ID,
MailboxName: inbox.Name,
Total: 4, Unread: 2, Unseen: 2,
})
// ChangeMsgRemove
@@ -454,11 +448,9 @@ func TestView(t *testing.T) {
tcompare(t, chmsgremove.ChangeRemoveUIDs.UIDs, []store.UID{inboxMinimal.m.UID, inboxNew.m.UID})
chmbcounts.Size = 0
tcompare(t, chmbcounts, ChangeMailboxCounts{
ChangeMailboxCounts: store.ChangeMailboxCounts{
MailboxID: inbox.ID,
MailboxName: inbox.Name,
MailboxCounts: store.MailboxCounts{Total: 2, Unread: 1, Unseen: 1},
},
MailboxID: inbox.ID,
MailboxName: inbox.Name,
Total: 2, Unread: 1, Unseen: 1,
})
// ChangeMsgThread