This is a forked version of go-smtp, changed API to function options. Added NewDefaultBackend and DefaultSession for easier usage. Client has a own package. Added XForward support
- ESMTP client & server implementing RFC 5321
- Support for SMTP AUTH and PIPELINING
- UTF-8 support for subject and message
- LMTP support
- XFORWARD support
- Since v1.1.2: Keep \r\n in Data Reader (textproto DotReader replaces it to \n)
package main
import (
"io"
"io/ioutil"
"log"
"time"
"github.com/mschneider82/go-smtp"
)
// A Session is returned after successful login.
type Session struct{
smtp.DefaultSession
}
func (s *Session) Data(r io.Reader, d smtp.DataContext) error {
if b, err := ioutil.ReadAll(r); err != nil {
return err
} else {
log.Println("Data:", string(b))
}
return nil
}
type sessionFactory struct{}
func (s *sessionFactory) New() smtp.Session {
return &Session{}
}
func main() {
err := smtp.NewServer(
smtp.NewDefaultBackend(&sessionFactory{}),
smtp.Addr(":1025"),
smtp.Domain("localhost"),
smtp.WriteTimeout(10*time.Second),
smtp.ReadTimeout(10*time.Second),
smtp.MaxMessageBytes(1024*1024),
smtp.MaxRecipients(50),
smtp.AllowInsecureAuth(),
smtp.DisableAuth(),
).ListenAndServe()
if err != nil {
log.Fatal(err)
}
}
You can use the server manually with telnet
:
$ telnet localhost 1025
EHLO localhost
AUTH PLAIN
AHVzZXJuYW1lAHBhc3N3b3Jk
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
DATA
Hey <3
.
package main
import (
"context"
"io"
"io/ioutil"
"log"
"time"
"fmt"
"github.com/mschneider82/go-smtp"
)
// Session is returned for every connection
type Session struct{
smtp.DefaultSession
}
func (s *Session) Data(r io.Reader, dataContext smtp.DataContext) error {
mailBytes, err := ioutil.ReadAll(r)
if err != nil {
return err
}
globalCtx := context.Background()
for i, rcpt := range s.Rcpts {
rcptCtx, _ := context.WithTimeout(globalCtx, 2*time.Second)
// we have to assing i and rcpt new to access them in the go() routine
rcpt := rcpt
i := i
dataContext.StartDelivery(rcptCtx, rcpt)
go func() {
// normaly we would deliver the mailBytes to our Maildir/HTTP backend
// in this case we just do a sleep
time.Sleep(time.Duration(2+i) * time.Second)
fmt.Println(string(mailBytes))
// Lets finish with OK (if the request wasn't canceled because of the ctx timeout)
dataContext.SetStatus(rcpt, &smtp.SMTPError{
Code: 250,
EnhancedCode: smtp.EnhancedCode{2, 0, 0},
Message: "Finished",
})
}()
}
// we always return nil in LMTP because every rcpt return code was set with dataContext.SetStatus()
return nil
}
func main() {
err := smtp.NewServer(
smtp.NewDefaultBackend(&Session{}),
smtp.Addr(":1025"),
smtp.Domain("localhost"),
smtp.WriteTimeout(10*time.Second),
smtp.ReadTimeout(10*time.Second),
smtp.MaxMessageBytes(1024*1024),
smtp.MaxRecipients(50),
smtp.AllowInsecureAuth(),
smtp.DisableAuth(),
smtp.LMTP(),
).ListenAndServe()
if err != nil {
log.Fatal(err)
}
}
You can use the server manually with telnet
:
$ telnet localhost 1025
LHLO localhost
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
RCPT TO:<[email protected]>
RCPT TO:<[email protected]>
DATA
Hey <3
.
250 2.0.0 <[email protected]> Finished
420 4.4.7 <[email protected]> Error: timeout reached
420 4.4.7 <[email protected]> Error: timeout reached
MIT