mirror of
https://github.com/axllent/mailpit.git
synced 2026-03-03 03:47:01 +00:00
Merge branch 'release/v1.28.4'
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -2,6 +2,19 @@
|
||||
|
||||
Notable changes to Mailpit will be documented in this file.
|
||||
|
||||
## [v1.28.4]
|
||||
|
||||
### Chore
|
||||
- Increase allowed SMTP email address length to 1024 chars & return clearer SMTP responses for failures ([#620](https://github.com/axllent/mailpit/issues/620))
|
||||
- Update Go dependencies
|
||||
- Update node dependencies
|
||||
|
||||
### Fix
|
||||
- Ensure SMTP HELO/EHLO command is issued before MAIL FROM as per RFC 5321 ([#621](https://github.com/axllent/mailpit/issues/621))
|
||||
- Prevent nested MAIL command during an active SMTP transaction ([#623](https://github.com/axllent/mailpit/issues/623))
|
||||
- Avoid error on image type assertion in thumbnail generation
|
||||
|
||||
|
||||
## [v1.28.3]
|
||||
|
||||
### Security
|
||||
|
||||
6
go.mod
6
go.mod
@@ -29,7 +29,7 @@ require (
|
||||
golang.org/x/net v0.49.0
|
||||
golang.org/x/text v0.33.0
|
||||
golang.org/x/time v0.14.0
|
||||
modernc.org/sqlite v1.44.1
|
||||
modernc.org/sqlite v1.44.3
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.7.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||
@@ -57,7 +57,7 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/errors v1.2.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.3 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
|
||||
12
go.sum
12
go.sum
@@ -20,8 +20,8 @@ github.com/clipperhouse/displaywidth v0.7.0 h1:QNv1GYsnLX9QBrcWUtMlogpTXuM5FVnBw
|
||||
github.com/clipperhouse/displaywidth v0.7.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.1 h1:RjM8gnVbFbgI67SBekIC7ihFpyXwRPYWXn9BZActHbw=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.1/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -95,8 +95,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 h1:jrYnow5+hy3WRDCBypUFvVKNSPPCdqgSXIE9eJDD8LM=
|
||||
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA=
|
||||
@@ -272,8 +272,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.44.1 h1:qybx/rNpfQipX/t47OxbHmkkJuv2JWifCMH8SVUiDas=
|
||||
modernc.org/sqlite v1.44.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=
|
||||
modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -362,8 +362,9 @@ func (s *session) serve() {
|
||||
// otherwise results in a 5s timeout for each connection
|
||||
defer func(c net.Conn) { _ = c.Close() }(s.conn)
|
||||
|
||||
var gotEHLO bool
|
||||
var from string
|
||||
var gotFrom bool
|
||||
var gotFROM bool
|
||||
var to []string
|
||||
var hasRejectedRecipients bool
|
||||
var buffer bytes.Buffer
|
||||
@@ -397,8 +398,9 @@ loop:
|
||||
s.writef("250 %s greets %s", s.srv.Hostname, s.remoteName)
|
||||
|
||||
// RFC 2821 section 4.1.4 specifies that EHLO has the same effect as RSET, so reset for HELO too.
|
||||
gotEHLO = true
|
||||
from = ""
|
||||
gotFrom = false
|
||||
gotFROM = false
|
||||
to = nil
|
||||
hasRejectedRecipients = false
|
||||
buffer.Reset()
|
||||
@@ -407,8 +409,9 @@ loop:
|
||||
s.writef("%s", s.makeEHLOResponse())
|
||||
|
||||
// RFC 2821 section 4.1.4 specifies that EHLO has the same effect as RSET.
|
||||
gotEHLO = true
|
||||
from = ""
|
||||
gotFrom = false
|
||||
gotFROM = false
|
||||
to = nil
|
||||
hasRejectedRecipients = false
|
||||
buffer.Reset()
|
||||
@@ -421,10 +424,22 @@ loop:
|
||||
s.writef("530 5.7.0 Authentication required")
|
||||
break
|
||||
}
|
||||
if !gotEHLO {
|
||||
s.writef("503 5.5.1 Bad sequence of commands (HELO/EHLO required before MAIL)")
|
||||
break
|
||||
}
|
||||
if to != nil {
|
||||
s.writef("503 5.5.1 Bad sequence of commands (RSET/HELO/EHLO required before MAIL)")
|
||||
break
|
||||
}
|
||||
|
||||
match := extractAndValidateAddress(mailFromRE, args)
|
||||
match, err := extractAndValidateAddress(mailFromRE, args)
|
||||
if match == nil {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid FROM parameter)")
|
||||
if err != nil {
|
||||
s.writef("%s", err.Error())
|
||||
} else {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid FROM parameter)")
|
||||
}
|
||||
} else {
|
||||
// Mailpit Chaos
|
||||
if fail, code := chaos.Config.Sender.Trigger(); fail {
|
||||
@@ -438,7 +453,7 @@ loop:
|
||||
if sizeMatch == nil {
|
||||
// ignore other parameter
|
||||
from = match[1]
|
||||
gotFrom = true
|
||||
gotFROM = true
|
||||
s.writef("250 2.1.0 Ok")
|
||||
} else {
|
||||
// Enforce the maximum message size if one is set.
|
||||
@@ -450,13 +465,13 @@ loop:
|
||||
s.writef("%s", err.Error())
|
||||
} else { // SIZE ok
|
||||
from = match[1]
|
||||
gotFrom = true
|
||||
gotFROM = true
|
||||
s.writef("250 2.1.0 Ok")
|
||||
}
|
||||
}
|
||||
} else { // No parameters after FROM
|
||||
from = match[1]
|
||||
gotFrom = true
|
||||
gotFROM = true
|
||||
s.writef("250 2.1.0 Ok")
|
||||
}
|
||||
}
|
||||
@@ -473,14 +488,18 @@ loop:
|
||||
s.writef("530 5.7.0 Authentication required")
|
||||
break
|
||||
}
|
||||
if !gotFrom {
|
||||
if !gotFROM {
|
||||
s.writef("503 5.5.1 Bad sequence of commands (MAIL required before RCPT)")
|
||||
break
|
||||
}
|
||||
|
||||
match := extractAndValidateAddress(rcptToRE, args)
|
||||
match, err := extractAndValidateAddress(rcptToRE, args)
|
||||
if match == nil {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid TO parameter)")
|
||||
if err != nil {
|
||||
s.writef("%s", err.Error())
|
||||
} else {
|
||||
s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid TO parameter)")
|
||||
}
|
||||
} else {
|
||||
// Mailpit Chaos
|
||||
if fail, code := chaos.Config.Recipient.Trigger(); fail {
|
||||
@@ -516,7 +535,7 @@ loop:
|
||||
break
|
||||
}
|
||||
hasRecipients := len(to) > 0 || hasRejectedRecipients
|
||||
if !gotFrom || !hasRecipients {
|
||||
if !gotFROM || !hasRecipients {
|
||||
s.writef("503 5.5.1 Bad sequence of commands (MAIL & RCPT required before DATA)")
|
||||
break
|
||||
}
|
||||
@@ -594,7 +613,7 @@ loop:
|
||||
|
||||
// Reset for next mail.
|
||||
from = ""
|
||||
gotFrom = false
|
||||
gotFROM = false
|
||||
to = nil
|
||||
hasRejectedRecipients = false
|
||||
buffer.Reset()
|
||||
@@ -608,7 +627,7 @@ loop:
|
||||
}
|
||||
s.writef("250 2.0.0 Ok")
|
||||
from = ""
|
||||
gotFrom = false
|
||||
gotFROM = false
|
||||
to = nil
|
||||
hasRejectedRecipients = false
|
||||
buffer.Reset()
|
||||
@@ -685,7 +704,7 @@ loop:
|
||||
// RFC 3207 specifies that the server must discard any prior knowledge obtained from the client.
|
||||
s.remoteName = ""
|
||||
from = ""
|
||||
gotFrom = false
|
||||
gotFROM = false
|
||||
to = nil
|
||||
hasRejectedRecipients = false
|
||||
buffer.Reset()
|
||||
@@ -707,7 +726,7 @@ loop:
|
||||
}
|
||||
|
||||
// RFC 4954 specifies that AUTH is not permitted during mail transactions.
|
||||
if gotFrom || len(to) > 0 {
|
||||
if gotFROM || len(to) > 0 {
|
||||
s.writef("503 5.5.1 Bad sequence of commands (AUTH not permitted during mail transaction)")
|
||||
break
|
||||
}
|
||||
@@ -1017,31 +1036,33 @@ func (s *session) handleAuthCramMD5() (bool, error) {
|
||||
}
|
||||
|
||||
// Extract and validate email address from a regex match.
|
||||
// This ensures that only RFC 5322 email addresses are accepted (if set).
|
||||
func extractAndValidateAddress(re *regexp.Regexp, args string) []string {
|
||||
// This ensures that only RFC 5322 compliant email addresses are accepted (if set).
|
||||
func extractAndValidateAddress(re *regexp.Regexp, args string) ([]string, error) {
|
||||
match := re.FindStringSubmatch(args)
|
||||
if match == nil || strings.Contains(match[1], " ") {
|
||||
return nil
|
||||
if match == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if strings.Contains(match[1], " ") {
|
||||
return nil, errors.New("553 5.1.3 The address is not a valid RFC 5321 address")
|
||||
}
|
||||
|
||||
// first argument will be the email address, validate it if not empty
|
||||
if match[1] != "" {
|
||||
a, err := mail.ParseAddress(match[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := strings.SplitN(a.Address, "@", 2)
|
||||
|
||||
if len(parts) != 2 {
|
||||
return nil
|
||||
return nil, errors.New("553 5.1.3 The address is not a valid RFC 5321 address")
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1
|
||||
if len(parts[0]) > 64 || len(parts[1]) > 255 || len(a.Address) > 256 {
|
||||
return nil
|
||||
// RFC states that the local part of an email address SHOULD not exceed 64 characters
|
||||
// and the domain part SHOULD not exceed 255 characters, however as per https://github.com/axllent/mailpit/issues/620
|
||||
// it appears that investigated mail servers do not actually implement this limit, but rather enforce
|
||||
// a much larger limit (ie: 1024 characters).
|
||||
if len(a.Address) > 1024 {
|
||||
return nil, errors.New("500 The address is too long")
|
||||
}
|
||||
}
|
||||
|
||||
return match
|
||||
return match, nil
|
||||
}
|
||||
|
||||
@@ -106,15 +106,14 @@ func TestCmdEHLO(t *testing.T) {
|
||||
cmdCode(t, conn, "RCPT TO:<recipient@example.com>", "250")
|
||||
|
||||
// test invalid addresses & header injection
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipient@exampleexampleexampleexampleexampleexampleexample.com>", "501") // too long
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientreciprecipientrecipientrecipientrecipt@exaample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <r@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "250") // valid
|
||||
cmdCode(t, conn, "RCPT TO:<recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipientreciprecipientrecipientrecipientreciptrecipientrecipientrecipient@exampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexample.com>", "500") // too long
|
||||
cmdCode(t, conn, "RCPT TO:<recipientexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@test@example.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@@example.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<recipient\rexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "553")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <>", "501") // empty address not allowed here
|
||||
|
||||
@@ -125,6 +124,41 @@ func TestCmdEHLO(t *testing.T) {
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
func TestCmdMAILBeforeEHLO(t *testing.T) {
|
||||
conn := newConn(t, &Server{})
|
||||
// RFC 5321 §4.1.4 — Order of Commands states (emphasis added):
|
||||
// “The SMTP client MUST issue HELO or EHLO before any other SMTP commands.”
|
||||
cmdCode(t, conn, "MAIL FROM:<sender@example.com>", "503")
|
||||
|
||||
cmdCode(t, conn, "QUIT", "221")
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
func TestCmdMAILAfterRCPT(t *testing.T) {
|
||||
conn := newConn(t, &Server{})
|
||||
|
||||
// Send EHLO, expect greeting
|
||||
cmdCode(t, conn, "EHLO host.example.com", "250")
|
||||
|
||||
// Send MAIL FROM
|
||||
cmdCode(t, conn, "MAIL FROM:<sender@example.com>", "250")
|
||||
|
||||
// Send RCPT TO
|
||||
cmdCode(t, conn, "RCPT TO:<recipient@example.com>", "250")
|
||||
|
||||
// MAIL FROM must not come after RCPT TO in the same transaction
|
||||
cmdCode(t, conn, "MAIL FROM:<sender2@example.com>", "503")
|
||||
|
||||
// RSET to clear the transaction
|
||||
cmdCode(t, conn, "RSET", "250")
|
||||
|
||||
// Now the MAIL FROM should be accepted
|
||||
cmdCode(t, conn, "MAIL FROM:<sender2@example.com>", "250")
|
||||
|
||||
cmdCode(t, conn, "QUIT", "221")
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
func TestCmdRSET(t *testing.T) {
|
||||
conn := newConn(t, &Server{})
|
||||
cmdCode(t, conn, "EHLO host.example.com", "250")
|
||||
@@ -145,7 +179,7 @@ func TestCmdMAIL(t *testing.T) {
|
||||
|
||||
// MAIL with no FROM arg should return 501 syntax error
|
||||
cmdCode(t, conn, "MAIL", "501")
|
||||
// MAIL with empty FROM arg should return 501 syntax error
|
||||
// // MAIL with empty FROM arg should return 501 syntax error
|
||||
cmdCode(t, conn, "MAIL FROM:", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: ", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: ", "501")
|
||||
@@ -160,19 +194,18 @@ func TestCmdMAIL(t *testing.T) {
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com>", "501")
|
||||
|
||||
// test invalid addresses & header injection
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersender@exampleexampleexampleexampleexampleexampleexample.com>", "501") // too long
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersender@exaample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <s@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "250") // valid
|
||||
cmdCode(t, conn, "MAIL FROM:<sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersender@exampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexample.com>", "500") // too long
|
||||
cmdCode(t, conn, "MAIL FROM:<sender\rexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM:<senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM:<senderexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@@example.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@test@example.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com >", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com >", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com >", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com>", "553")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com >", "553")
|
||||
|
||||
// MAIL with valid SIZE parameter should return 250 Ok
|
||||
cmdCode(t, conn, "MAIL FROM:<sender@example.com> SIZE=1000", "250")
|
||||
@@ -241,6 +274,7 @@ func TestCmdRCPT(t *testing.T) {
|
||||
cmdCode(t, conn, "RCPT TO:", "501")
|
||||
cmdCode(t, conn, "RCPT TO: ", "501")
|
||||
cmdCode(t, conn, "RCPT TO: ", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<@route.example user@example.com>", "553")
|
||||
|
||||
// RCPT with valid TO arg should return 250 Ok
|
||||
cmdCode(t, conn, "RCPT TO:<recipient@example.com>", "250")
|
||||
|
||||
835
package-lock.json
generated
835
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -76,13 +76,14 @@ func Thumbnail(w http.ResponseWriter, r *http.Request) {
|
||||
var b bytes.Buffer
|
||||
foo := bufio.NewWriter(&b)
|
||||
|
||||
var dstImageFill *image.NRGBA
|
||||
|
||||
var temp image.Image
|
||||
if img.Bounds().Dx() < thumbWidth || img.Bounds().Dy() < thumbHeight {
|
||||
dstImageFill = imaging.Fit(img, thumbWidth, thumbHeight, imaging.Lanczos).(*image.NRGBA)
|
||||
temp = imaging.Fit(img, thumbWidth, thumbHeight, imaging.Lanczos)
|
||||
} else {
|
||||
dstImageFill = imaging.Fill(img, thumbWidth, thumbHeight, imaging.Center, imaging.Lanczos).(*image.NRGBA)
|
||||
temp = imaging.Fill(img, thumbWidth, thumbHeight, imaging.Center, imaging.Lanczos)
|
||||
}
|
||||
dstImageFill := imaging.Clone(temp)
|
||||
|
||||
// create white image and paste image over the top
|
||||
// preventing black backgrounds for transparent GIF/PNG images
|
||||
dst := imaging.New(thumbWidth, thumbHeight, color.White)
|
||||
|
||||
Reference in New Issue
Block a user