mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2024-11-27 16:24:04 +00:00
Fix storage interface and fix dependency mess
This commit is contained in:
parent
2b58b571cb
commit
fc25fce4d2
8 changed files with 56 additions and 44 deletions
|
@ -1,5 +1,9 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ian-kent/Go-MailHog/mailhog/storage"
|
||||||
|
)
|
||||||
|
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
SMTPBindAddr: "0.0.0.0:1025",
|
SMTPBindAddr: "0.0.0.0:1025",
|
||||||
|
@ -19,6 +23,6 @@ type Config struct {
|
||||||
MongoDb string
|
MongoDb string
|
||||||
MongoColl string
|
MongoColl string
|
||||||
MessageChan chan interface{}
|
MessageChan chan interface{}
|
||||||
Storage interface{}
|
Storage storage.Storage
|
||||||
Assets func(asset string) ([]byte, error)
|
Assets func(asset string) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,22 @@ type Protocol struct {
|
||||||
message *data.SMTPMessage
|
message *data.SMTPMessage
|
||||||
hostname string
|
hostname string
|
||||||
|
|
||||||
LogHandler func(message string, args ...interface{})
|
// LogHandler is called for each log message. If nil, log messages will
|
||||||
MessageReceivedHandler func(*data.Message) (string, error)
|
// be output using fmt.Printf instead.
|
||||||
ValidateSenderHandler func(from string) bool
|
LogHandler func(message string, args ...interface{})
|
||||||
ValidateRecipientHandler func(to string) bool
|
// MessageReceivedHandler is called for each message accepted by the
|
||||||
|
// SMTP protocol. It must return a MessageID or error. If nil, messages
|
||||||
|
// will be rejected with an error.
|
||||||
|
MessageReceivedHandler func(*data.Message) (string, error)
|
||||||
|
// ValidateSenderHandler should return true if the sender is valid,
|
||||||
|
// otherwise false. If nil, all senders will be accepted.
|
||||||
|
ValidateSenderHandler func(from string) bool
|
||||||
|
// ValidateRecipientHandler should return true if the recipient is valid,
|
||||||
|
// otherwise false. If nil, all recipients will be accepted.
|
||||||
|
ValidateRecipientHandler func(to string) bool
|
||||||
|
// ValidateAuthenticationhandler should return true if the authentication
|
||||||
|
// parameters are valid, otherwise false. If nil, all authentication
|
||||||
|
// attempts will be accepted.
|
||||||
ValidateAuthenticationHandler func(mechanism string, args ...string) bool
|
ValidateAuthenticationHandler func(mechanism string, args ...string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +62,8 @@ func (proto *Protocol) logf(message string, args ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start begins an SMTP conversation with a 220 reply
|
// Start begins an SMTP conversation with a 220 reply, placing the state
|
||||||
|
// machine in ESTABLISH state.
|
||||||
func (proto *Protocol) Start(hostname string) *Reply {
|
func (proto *Protocol) Start(hostname string) *Reply {
|
||||||
proto.state = ESTABLISH
|
proto.state = ESTABLISH
|
||||||
proto.hostname = hostname
|
proto.hostname = hostname
|
||||||
|
@ -60,7 +73,8 @@ func (proto *Protocol) Start(hostname string) *Reply {
|
||||||
// Parse parses a line string and returns any remaining line string
|
// Parse parses a line string and returns any remaining line string
|
||||||
// and a reply, if a command was found. Parse does nothing until a
|
// and a reply, if a command was found. Parse does nothing until a
|
||||||
// new line is found.
|
// new line is found.
|
||||||
// - TODO move this to a buffer inside proto?
|
// - TODO decide whether to move this to a buffer inside Protocol
|
||||||
|
// sort of like it this way, since it gives control back to the caller
|
||||||
func (proto *Protocol) Parse(line string) (string, *Reply) {
|
func (proto *Protocol) Parse(line string) (string, *Reply) {
|
||||||
var reply *Reply
|
var reply *Reply
|
||||||
|
|
||||||
|
@ -266,7 +280,7 @@ func ParseMAIL(mail string) (string, error) {
|
||||||
r := regexp.MustCompile("(?i:From):<([^>]+)>")
|
r := regexp.MustCompile("(?i:From):<([^>]+)>")
|
||||||
match := r.FindStringSubmatch(mail)
|
match := r.FindStringSubmatch(mail)
|
||||||
if len(match) != 2 {
|
if len(match) != 2 {
|
||||||
return "", errors.New("Invalid sender")
|
return "", errors.New("Invalid sender " + mail)
|
||||||
}
|
}
|
||||||
return match[1], nil
|
return match[1], nil
|
||||||
}
|
}
|
||||||
|
@ -276,7 +290,7 @@ func ParseRCPT(rcpt string) (string, error) {
|
||||||
r := regexp.MustCompile("(?i:To):<([^>]+)>")
|
r := regexp.MustCompile("(?i:To):<([^>]+)>")
|
||||||
match := r.FindStringSubmatch(rcpt)
|
match := r.FindStringSubmatch(rcpt)
|
||||||
if len(match) != 2 {
|
if len(match) != 2 {
|
||||||
return "", errors.New("Invalid recipient")
|
return "", errors.New("Invalid recipient " + rcpt)
|
||||||
}
|
}
|
||||||
return match[1], nil
|
return match[1], nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
// http://www.rfc-editor.org/rfc/rfc5321.txt
|
|
||||||
|
|
||||||
// State represents the state of an SMTP conversation
|
// State represents the state of an SMTP conversation
|
||||||
type State int
|
type State int
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package server
|
||||||
// http://www.rfc-editor.org/rfc/rfc5321.txt
|
// http://www.rfc-editor.org/rfc/rfc5321.txt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -11,7 +10,6 @@ import (
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/config"
|
"github.com/ian-kent/Go-MailHog/mailhog/config"
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/data"
|
"github.com/ian-kent/Go-MailHog/mailhog/data"
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/smtp/protocol"
|
"github.com/ian-kent/Go-MailHog/mailhog/smtp/protocol"
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Session represents a SMTP session using net.TCPConn
|
// Session represents a SMTP session using net.TCPConn
|
||||||
|
@ -43,6 +41,7 @@ func Accept(conn *net.TCPConn, conf *config.Config) {
|
||||||
func (c *Session) validateAuthentication(mechanism string, args ...string) bool {
|
func (c *Session) validateAuthentication(mechanism string, args ...string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Session) validateRecipient(to string) bool {
|
func (c *Session) validateRecipient(to string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -52,16 +51,8 @@ func (c *Session) validateSender(from string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Session) acceptMessage(msg *data.Message) (id string, err error) {
|
func (c *Session) acceptMessage(msg *data.Message) (id string, err error) {
|
||||||
switch c.conf.Storage.(type) {
|
c.logf("Storing message %s", msg.ID)
|
||||||
case *storage.MongoDB:
|
id, err = c.conf.Storage.Store(msg)
|
||||||
c.logf("Storing message using MongoDB")
|
|
||||||
id, err = c.conf.Storage.(*storage.MongoDB).Store(msg)
|
|
||||||
case *storage.InMemory:
|
|
||||||
c.logf("Storing message using Memory")
|
|
||||||
id, err = c.conf.Storage.(*storage.InMemory).Store(msg)
|
|
||||||
default:
|
|
||||||
err = errors.New("Unknown storage stype")
|
|
||||||
}
|
|
||||||
c.conf.MessageChan <- msg
|
c.conf.MessageChan <- msg
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import "github.com/ian-kent/Go-MailHog/mailhog/data"
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/config"
|
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InMemory is an in memory storage backend
|
// InMemory is an in memory storage backend
|
||||||
type InMemory struct {
|
type InMemory struct {
|
||||||
Config *config.Config
|
|
||||||
Messages map[string]*data.Message
|
Messages map[string]*data.Message
|
||||||
MessageIndex []string
|
MessageIndex []string
|
||||||
MessageRIndex map[string]int
|
MessageRIndex map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateInMemory creates a new in memory storage backend
|
// CreateInMemory creates a new in memory storage backend
|
||||||
func CreateInMemory(c *config.Config) *InMemory {
|
func CreateInMemory() *InMemory {
|
||||||
return &InMemory{
|
return &InMemory{
|
||||||
Config: c,
|
|
||||||
Messages: make(map[string]*data.Message, 0),
|
Messages: make(map[string]*data.Message, 0),
|
||||||
MessageIndex: make([]string, 0),
|
MessageIndex: make([]string, 0),
|
||||||
MessageRIndex: make(map[string]int, 0),
|
MessageRIndex: make(map[string]int, 0),
|
||||||
|
@ -32,15 +27,16 @@ func (memory *InMemory) Store(m *data.Message) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List lists stored messages by index
|
// List lists stored messages by index
|
||||||
func (memory *InMemory) List(start int, limit int) ([]*data.Message, error) {
|
func (memory *InMemory) List(start int, limit int) (*data.Messages, error) {
|
||||||
if limit > len(memory.MessageIndex) {
|
if limit > len(memory.MessageIndex) {
|
||||||
limit = len(memory.MessageIndex)
|
limit = len(memory.MessageIndex)
|
||||||
}
|
}
|
||||||
var messages []*data.Message
|
var messages []data.Message
|
||||||
for _, m := range memory.MessageIndex[start:limit] {
|
for _, m := range memory.MessageIndex[start:limit] {
|
||||||
messages = append(messages, memory.Messages[m])
|
messages = append(messages, *memory.Messages[m])
|
||||||
}
|
}
|
||||||
return messages, nil
|
msgs := data.Messages(messages)
|
||||||
|
return &msgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOne deletes an individual message by storage ID
|
// DeleteOne deletes an individual message by storage ID
|
||||||
|
|
|
@ -3,7 +3,6 @@ package storage
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/config"
|
|
||||||
"github.com/ian-kent/Go-MailHog/mailhog/data"
|
"github.com/ian-kent/Go-MailHog/mailhog/data"
|
||||||
"labix.org/v2/mgo"
|
"labix.org/v2/mgo"
|
||||||
"labix.org/v2/mgo/bson"
|
"labix.org/v2/mgo/bson"
|
||||||
|
@ -12,22 +11,20 @@ import (
|
||||||
// MongoDB represents MongoDB backed storage backend
|
// MongoDB represents MongoDB backed storage backend
|
||||||
type MongoDB struct {
|
type MongoDB struct {
|
||||||
Session *mgo.Session
|
Session *mgo.Session
|
||||||
Config *config.Config
|
|
||||||
Collection *mgo.Collection
|
Collection *mgo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateMongoDB creates a MongoDB backed storage backend
|
// CreateMongoDB creates a MongoDB backed storage backend
|
||||||
func CreateMongoDB(c *config.Config) *MongoDB {
|
func CreateMongoDB(uri, db, coll string) *MongoDB {
|
||||||
log.Printf("Connecting to MongoDB: %s\n", c.MongoUri)
|
log.Printf("Connecting to MongoDB: %s\n", uri)
|
||||||
session, err := mgo.Dial(c.MongoUri)
|
session, err := mgo.Dial(uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error connecting to MongoDB: %s", err)
|
log.Printf("Error connecting to MongoDB: %s", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &MongoDB{
|
return &MongoDB{
|
||||||
Session: session,
|
Session: session,
|
||||||
Config: c,
|
Collection: session.DB(db).C(coll),
|
||||||
Collection: session.DB(c.MongoDb).C(c.MongoColl),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
mailhog/storage/storage.go
Normal file
12
mailhog/storage/storage.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import "github.com/ian-kent/Go-MailHog/mailhog/data"
|
||||||
|
|
||||||
|
// Storage represents a storage backend
|
||||||
|
type Storage interface {
|
||||||
|
Store(m *data.Message) (string, error)
|
||||||
|
List(start int, limit int) (*data.Messages, error)
|
||||||
|
DeleteOne(id string) error
|
||||||
|
DeleteAll() error
|
||||||
|
Load(id string) (*data.Message, error)
|
||||||
|
}
|
6
main.go
6
main.go
|
@ -46,17 +46,17 @@ func configure() {
|
||||||
|
|
||||||
if storage_type == "mongodb" {
|
if storage_type == "mongodb" {
|
||||||
log.Println("Using MongoDB message storage")
|
log.Println("Using MongoDB message storage")
|
||||||
s := storage.CreateMongoDB(conf)
|
s := storage.CreateMongoDB(conf.MongoUri, conf.MongoDb, conf.MongoColl)
|
||||||
if s == nil {
|
if s == nil {
|
||||||
log.Println("MongoDB storage unavailable, reverting to in-memory storage")
|
log.Println("MongoDB storage unavailable, reverting to in-memory storage")
|
||||||
conf.Storage = storage.CreateInMemory(conf)
|
conf.Storage = storage.CreateInMemory()
|
||||||
} else {
|
} else {
|
||||||
log.Println("Connected to MongoDB")
|
log.Println("Connected to MongoDB")
|
||||||
conf.Storage = s
|
conf.Storage = s
|
||||||
}
|
}
|
||||||
} else if storage_type == "memory" {
|
} else if storage_type == "memory" {
|
||||||
log.Println("Using in-memory message storage")
|
log.Println("Using in-memory message storage")
|
||||||
conf.Storage = storage.CreateInMemory(conf)
|
conf.Storage = storage.CreateInMemory()
|
||||||
} else {
|
} else {
|
||||||
log.Fatalf("Invalid storage type %s", storage_type)
|
log.Fatalf("Invalid storage type %s", storage_type)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue