Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions api/v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ func createAPIv2(conf *config.Config, r *pat.Router) *APIv2 {
r.Path(conf.WebPath + "/api/v2/outgoing-smtp").Methods("GET").HandlerFunc(apiv2.listOutgoingSMTP)
r.Path(conf.WebPath + "/api/v2/outgoing-smtp").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)

r.Path(conf.WebPath + "/api/v2/blacklist").Methods("GET").HandlerFunc(apiv2.blacklist_list)
r.Path(conf.WebPath + "/api/v2/blacklist").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)

r.Path(conf.WebPath + "/api/v2/blacklist/{email}").Methods("POST").HandlerFunc(apiv2.blacklist_add)
r.Path(conf.WebPath + "/api/v2/blacklist/{email}").Methods("DELETE").HandlerFunc(apiv2.blacklist_remove)
r.Path(conf.WebPath + "/api/v2/blacklist/{email}").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions)

r.Path(conf.WebPath + "/api/v2/websocket").Methods("GET").HandlerFunc(apiv2.websocket)

go func() {
Expand Down Expand Up @@ -256,3 +263,64 @@ func (apiv2 *APIv2) broadcast(msg *data.Message) {

apiv2.wsHub.Broadcast(msg)
}

func (apiv2 *APIv2) blacklist_list(w http.ResponseWriter, req *http.Request) {
log.Println("[APIv2] GET /api/v2/blacklist")

apiv2.defaultOptions(w, req)

emails := make([]string, 0, len(apiv2.config.Blacklist))
for email := range apiv2.config.Blacklist {
emails = append(emails, email)
}

bytes, err := json.Marshal(emails)
if err != nil {
log.Printf("Error marshaling blacklist: %s", err)
w.WriteHeader(500)
return
}

w.Header().Add("Content-Type", "application/json")
w.Write(bytes)
}

func (apiv2 *APIv2) blacklist_add(w http.ResponseWriter, req *http.Request) {
email := req.URL.Query().Get(":email")
log.Printf("[APIv2] POST /api/v2/blacklist/%s", email)

apiv2.defaultOptions(w, req)

if email == "" {
w.WriteHeader(400)
w.Write([]byte("Email address is required"))
return
}

apiv2.config.Blacklist[email] = true
log.Printf("Added %s to blacklist", email)

w.Header().Add("Content-Type", "application/json")
w.WriteHeader(200)
w.Write([]byte(`{"success": true}`))
}

func (apiv2 *APIv2) blacklist_remove(w http.ResponseWriter, req *http.Request) {
email := req.URL.Query().Get(":email")
log.Printf("[APIv2] DELETE /api/v2/blacklist/%s", email)

apiv2.defaultOptions(w, req)

if email == "" {
w.WriteHeader(400)
w.Write([]byte("Email address is required"))
return
}

delete(apiv2.config.Blacklist, email)
log.Printf("Removed %s from blacklist", email)

w.Header().Add("Content-Type", "application/json")
w.WriteHeader(200)
w.Write([]byte(`{"success": true}`))
}
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func DefaultConfig() *Config {
WebPath: "",
MessageChan: make(chan *data.Message),
OutgoingSMTP: make(map[string]*OutgoingSMTP),
Blacklist: make(map[string]bool),
}
}

Expand All @@ -49,6 +50,7 @@ type Config struct {
OutgoingSMTPFile string
OutgoingSMTP map[string]*OutgoingSMTP
WebPath string
Blacklist map[string]bool
}

// OutgoingSMTP is an outgoing SMTP server config
Expand Down
22 changes: 16 additions & 6 deletions smtp/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/ian-kent/linkio"
"github.com/mailhog/MailHog-Server/config"
"github.com/mailhog/MailHog-Server/monkey"
"github.com/mailhog/data"
"github.com/mailhog/smtp"
Expand All @@ -24,31 +25,32 @@ type Session struct {
isTLS bool
line string
link *linkio.Link
blacklist map[string]bool

reader io.Reader
writer io.Writer
monkey monkey.ChaosMonkey
}

// Accept starts a new SMTP session using io.ReadWriteCloser
func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Storage, messageChan chan *data.Message, hostname string, monkey monkey.ChaosMonkey) {
func Accept(remoteAddress string, conn io.ReadWriteCloser, cfg *config.Config) {
defer conn.Close()

proto := smtp.NewProtocol()
proto.Hostname = hostname
proto.Hostname = cfg.Hostname
var link *linkio.Link
reader := io.Reader(conn)
writer := io.Writer(conn)
if monkey != nil {
linkSpeed := monkey.LinkSpeed()
if cfg.Monkey != nil {
linkSpeed := cfg.Monkey.LinkSpeed()
if linkSpeed != nil {
link = linkio.NewLink(*linkSpeed * linkio.BytePerSecond)
reader = link.NewLinkReader(io.Reader(conn))
writer = link.NewLinkWriter(io.Writer(conn))
}
}

session := &Session{conn, proto, storage, messageChan, remoteAddress, false, "", link, reader, writer, monkey}
session := &Session{conn, proto, cfg.Storage, cfg.MessageChan, remoteAddress, false, "", link, cfg.Blacklist, reader, writer, cfg.Monkey}
proto.LogHandler = session.logf
proto.MessageReceivedHandler = session.acceptMessage
proto.ValidateSenderHandler = session.validateSender
Expand All @@ -59,7 +61,7 @@ func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Stora
session.logf("Starting session")
session.Write(proto.Start())
for session.Read() == true {
if monkey != nil && monkey.Disconnect != nil && monkey.Disconnect() {
if cfg.Monkey != nil && cfg.Monkey.Disconnect != nil && cfg.Monkey.Disconnect() {
session.conn.Close()
break
}
Expand All @@ -79,6 +81,10 @@ func (c *Session) validateAuthentication(mechanism string, args ...string) (erro
}

func (c *Session) validateRecipient(to string) bool {
if c.blacklist != nil && c.blacklist[to] {
c.logf("Rejecting email to blacklisted address: %s", to)
return false
}
if c.monkey != nil {
ok := c.monkey.ValidRCPT(to)
if !ok {
Expand All @@ -89,6 +95,10 @@ func (c *Session) validateRecipient(to string) bool {
}

func (c *Session) validateSender(from string) bool {
if c.blacklist != nil && c.blacklist[from] {
c.logf("Rejecting email from blacklisted address: %s", from)
return false
}
if c.monkey != nil {
ok := c.monkey.ValidMAIL(from)
if !ok {
Expand Down
5 changes: 1 addition & 4 deletions smtp/smtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ func Listen(cfg *config.Config, exitCh chan int) *net.TCPListener {
go Accept(
conn.(*net.TCPConn).RemoteAddr().String(),
io.ReadWriteCloser(conn),
cfg.Storage,
cfg.MessageChan,
cfg.Hostname,
cfg.Monkey,
cfg,
)
}
}