This library is a refactored fork of gleez/smtpd, itself a fork of flashmob/go-guerrilla. It provides an incredibly simple interface to an SMTP server that pushes processed emails to a user-supplied channel, allowing you to make use of those emails in whatever way suits your use case.
The following example illustrates how simple it can be to setup and run an SMTP server that will allow any connecting client to send an email to any domain.
package main
import (
"fmt"
"github.com/StratumSecurity/gosmtpd"
)
// allowAll satisfies the smtpd.PermissionManager interface
type allowAll struct{}
func (a allowAll) IsTrusted(s string) bool {
return true
}
func (a allowAll) IsAllowed(s string) bool {
return true
}
func handleMessages(in <-chan smtpd.Message) {
for {
msg := <-in
fmt.Println("Got message\n", msg, "\n")
}
}
func main() {
messages := make(chan smtpd.Message)
server := smtpd.NewServer(messages, smtpd.ServerConfig{
BindAddress: "127.0.0.1",
BindPort: 25,
})
go handleMessages(messages)
server.Start(allowAll{})
server.Drain()
}
You could then write an email to this running server by doing the following, using Telnet.
$ telnet 127.0.0.1 25
helo 127.0.0.1
mail from:<testing@site.com>
rcpt to:<someone@else.com>
data
Subject: Hello world
This is the body of an email!
.
After pressing enter/return following the single period (.), you'll see the server output the received message struct.
The motivation for creating this library, by refactoring gleez/smtpd was to provide an incredibly simple interface for running a reasonably featureful SMTP server without any of the use case-specific functionality that existing codebases include, like the web interface, Nginx XClient support, or database persistence. The library still provides:
- TLS Support (STARTTLS)
- Support for SMTP Auth and PIPELINING
- Multipart MIME
- Attachments
- UTF8 in subjects and bodies
This is all on top of providing an excellent and simple interface.
const (
DefaultDomain = "local"
DefaultMaxRecipients = 100
DefaultMaxIdleSeconds = 300
DefaultMaxClients = 500
DefaultMaxMsgBytes = 20480000
)
func NewServer(output chan<- Message, cfg ServerConfig) *Server
Creates a new SMTP that will send emails to the provided channel.
type PermissionManager interface {
IsTrusted(string) bool
IsAllowed(string) bool
}
Instead of accepting comma-separated lists of hosts to trust to send email or allow to be sent emails, implementations of PermissionManager have a lot more flexibility.
IsTrusted(string) bool
Called by the SMTP server with a client's IP address (and port number) as a string formatted like
<IP address>:<port>
that should return true if the client should be allowed to send emails using the server, or else false to deny access.
IsAllowed(string) bool
Called by the SMTP server with the domain, such as site.com
, of the email address that a client
wants to send an email to. It should return true if clients are allowed to send to that domain,
or else false to deny access.
type Server struct {
// Private fields omitted
}
func (s *Server) Start(permissionChecker PermissionManager)
Starts the SMTP server with a PermissionManager
that will determine who can send emails and to
whom. If permissionChecker
is nil
, it will default to a localhost-only PermissionManager
that will only allow local clients to send to 127.0.0.1, useful for testing purposes.
func (s *Server) Stop()
Signals to the SMTP server to stop running and accepting client connections.
func (s *Server) Drain()
Causes the caller to block until all active SMTP sessions have finished.
type ServerConfig struct {
BindAddress string
BindPort int
Domain string
MaxRecipients int
MaxIdleSeconds int
MaxClients int
MaxMessageBytes int
PublicKeyFile string
PrivateKeyFile string
}
type Message struct {
Subject string
From *Path
To []*Path
Created time.Time
Attachments []*Attachment
IP string
Content *Content
MIME *MIMEBody
Starred bool
Unread bool
}
type Path struct {
Relays []string
Mailbox string
Domain string
Params string
}
type Content struct {
Headers map[string][]string
TextBody string
HTMLBody string
Size int
Body string
}
type MIMEBody struct {
Parts []*MIMEPart
}
type MIMEPart struct {
Headers map[string][]string
Body string
FileName string
ContentType string
Charset string
MIMEVersion string
TransferEncoding string
Disposition string
Size int
}
type Attachment struct {
Body string
FileName string
ContentType string
Charset string
MIMEVersion string
TransferEncoding string
Size int