Fix storage interface and fix dependency mess

This commit is contained in:
Ian Kent 2014-11-23 00:23:39 +00:00
parent 2b58b571cb
commit fc25fce4d2
8 changed files with 56 additions and 44 deletions

View file

@ -1,5 +1,9 @@
package config
import (
"github.com/ian-kent/Go-MailHog/mailhog/storage"
)
func DefaultConfig() *Config {
return &Config{
SMTPBindAddr: "0.0.0.0:1025",
@ -19,6 +23,6 @@ type Config struct {
MongoDb string
MongoColl string
MessageChan chan interface{}
Storage interface{}
Storage storage.Storage
Assets func(asset string) ([]byte, error)
}

View file

@ -23,10 +23,22 @@ type Protocol struct {
message *data.SMTPMessage
hostname string
LogHandler func(message string, args ...interface{})
MessageReceivedHandler func(*data.Message) (string, error)
ValidateSenderHandler func(from string) bool
ValidateRecipientHandler func(to string) bool
// LogHandler is called for each log message. If nil, log messages will
// be output using fmt.Printf instead.
LogHandler func(message string, args ...interface{})
// 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
}
@ -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 {
proto.state = ESTABLISH
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
// and a reply, if a command was found. Parse does nothing until a
// 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) {
var reply *Reply
@ -266,7 +280,7 @@ func ParseMAIL(mail string) (string, error) {
r := regexp.MustCompile("(?i:From):<([^>]+)>")
match := r.FindStringSubmatch(mail)
if len(match) != 2 {
return "", errors.New("Invalid sender")
return "", errors.New("Invalid sender " + mail)
}
return match[1], nil
}
@ -276,7 +290,7 @@ func ParseRCPT(rcpt string) (string, error) {
r := regexp.MustCompile("(?i:To):<([^>]+)>")
match := r.FindStringSubmatch(rcpt)
if len(match) != 2 {
return "", errors.New("Invalid recipient")
return "", errors.New("Invalid recipient " + rcpt)
}
return match[1], nil
}

View file

@ -1,7 +1,5 @@
package protocol
// http://www.rfc-editor.org/rfc/rfc5321.txt
// State represents the state of an SMTP conversation
type State int

View file

@ -3,7 +3,6 @@ package server
// http://www.rfc-editor.org/rfc/rfc5321.txt
import (
"errors"
"log"
"net"
"strings"
@ -11,7 +10,6 @@ import (
"github.com/ian-kent/Go-MailHog/mailhog/config"
"github.com/ian-kent/Go-MailHog/mailhog/data"
"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
@ -43,6 +41,7 @@ func Accept(conn *net.TCPConn, conf *config.Config) {
func (c *Session) validateAuthentication(mechanism string, args ...string) bool {
return true
}
func (c *Session) validateRecipient(to string) bool {
return true
}
@ -52,16 +51,8 @@ func (c *Session) validateSender(from string) bool {
}
func (c *Session) acceptMessage(msg *data.Message) (id string, err error) {
switch c.conf.Storage.(type) {
case *storage.MongoDB:
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.logf("Storing message %s", msg.ID)
id, err = c.conf.Storage.Store(msg)
c.conf.MessageChan <- msg
return
}

View file

@ -1,22 +1,17 @@
package storage
import (
"github.com/ian-kent/Go-MailHog/mailhog/config"
"github.com/ian-kent/Go-MailHog/mailhog/data"
)
import "github.com/ian-kent/Go-MailHog/mailhog/data"
// InMemory is an in memory storage backend
type InMemory struct {
Config *config.Config
Messages map[string]*data.Message
MessageIndex []string
MessageRIndex map[string]int
}
// CreateInMemory creates a new in memory storage backend
func CreateInMemory(c *config.Config) *InMemory {
func CreateInMemory() *InMemory {
return &InMemory{
Config: c,
Messages: make(map[string]*data.Message, 0),
MessageIndex: make([]string, 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
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) {
limit = len(memory.MessageIndex)
}
var messages []*data.Message
var messages []data.Message
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

View file

@ -3,7 +3,6 @@ package storage
import (
"log"
"github.com/ian-kent/Go-MailHog/mailhog/config"
"github.com/ian-kent/Go-MailHog/mailhog/data"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
@ -12,22 +11,20 @@ import (
// MongoDB represents MongoDB backed storage backend
type MongoDB struct {
Session *mgo.Session
Config *config.Config
Collection *mgo.Collection
}
// CreateMongoDB creates a MongoDB backed storage backend
func CreateMongoDB(c *config.Config) *MongoDB {
log.Printf("Connecting to MongoDB: %s\n", c.MongoUri)
session, err := mgo.Dial(c.MongoUri)
func CreateMongoDB(uri, db, coll string) *MongoDB {
log.Printf("Connecting to MongoDB: %s\n", uri)
session, err := mgo.Dial(uri)
if err != nil {
log.Printf("Error connecting to MongoDB: %s", err)
return nil
}
return &MongoDB{
Session: session,
Config: c,
Collection: session.DB(c.MongoDb).C(c.MongoColl),
Collection: session.DB(db).C(coll),
}
}

View 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)
}

View file

@ -46,17 +46,17 @@ func configure() {
if storage_type == "mongodb" {
log.Println("Using MongoDB message storage")
s := storage.CreateMongoDB(conf)
s := storage.CreateMongoDB(conf.MongoUri, conf.MongoDb, conf.MongoColl)
if s == nil {
log.Println("MongoDB storage unavailable, reverting to in-memory storage")
conf.Storage = storage.CreateInMemory(conf)
conf.Storage = storage.CreateInMemory()
} else {
log.Println("Connected to MongoDB")
conf.Storage = s
}
} else if storage_type == "memory" {
log.Println("Using in-memory message storage")
conf.Storage = storage.CreateInMemory(conf)
conf.Storage = storage.CreateInMemory()
} else {
log.Fatalf("Invalid storage type %s", storage_type)
}