1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 09:37:02 +00:00

SMTP server is running with new config engine

Web still not working
This commit is contained in:
James Hillyerd
2012-10-20 21:36:57 -07:00
parent ce9289140a
commit 81fea97a90
6 changed files with 153 additions and 56 deletions

View File

@@ -4,11 +4,27 @@ import (
"container/list"
"fmt"
"github.com/robfig/goconfig/config"
"net"
"os"
)
// SmtpConfig houses the SMTP server configuration - not using pointers
// so that I can pass around copies of the object safely.
type SmtpConfig struct {
Ip4address net.IP
Ip4port int
Domain string
}
var smtpConfig *SmtpConfig
var Config *config.Config
// GetSmtpConfig returns a copy of the SmtpConfig
func GetSmtpConfig() SmtpConfig {
return *smtpConfig
}
// LoadConfig loads the specified configuration file into inbucket.Config
// and performs validations on it.
func LoadConfig(filename string) error {
@@ -49,6 +65,44 @@ func LoadConfig(filename string) error {
return fmt.Errorf("Failed to validate configuration")
}
err = parseSmtpConfig()
return err
}
// parseSmtpConfig trying to catch config errors early
func parseSmtpConfig() error {
smtpConfig = new(SmtpConfig)
// Parse IP4 address only, error on IP6.
option := "[smtp]ip4.address"
str, err := Config.String("smtp", "ip4.address")
if err != nil {
return fmt.Errorf("Failed to parse %v: %v", option, err)
}
addr := net.ParseIP(str)
if addr == nil {
return fmt.Errorf("Failed to parse %v '%v'", option, str)
}
addr = addr.To4()
if addr == nil {
return fmt.Errorf("Failed to parse %v '%v' not IPv4!", option, str)
}
smtpConfig.Ip4address = addr
option = "[smtp]ip4.port"
smtpConfig.Ip4port, err = Config.Int("smtp", "ip4.port")
if err != nil {
return fmt.Errorf("Failed to parse %v: %v", option, err)
}
option = "[smtp]domain"
str, err = Config.String("smtp", "domain")
if err != nil {
return fmt.Errorf("Failed to parse %v: %v", option, err)
}
smtpConfig.Domain = str
return nil
}

View File

@@ -5,7 +5,6 @@ import (
"encoding/gob"
"errors"
"fmt"
"github.com/robfig/revel"
"io/ioutil"
"net/mail"
"os"
@@ -38,16 +37,20 @@ type DataStore struct {
mailPath string
}
// NewDataStore creates a new DataStore object. It uses the Revel Config object to
// NewDataStore creates a new DataStore object. It uses the inbucket.Config object to
// construct it's path.
func NewDataStore() *DataStore {
path, found := rev.Config.String("datastore.path")
if found {
mailPath := filepath.Join(path, "mail")
return &DataStore{path: path, mailPath: mailPath}
path, err := Config.String("datastore", "path")
if err != nil {
Error("Error getting datastore path: %v", err)
return nil
}
rev.ERROR.Printf("No value configured for datastore.path")
return nil
if path == "" {
Error("No value configured for datastore path")
return nil
}
mailPath := filepath.Join(path, "mail")
return &DataStore{path: path, mailPath: mailPath}
}
// Retrieves the Mailbox object for a specified email address, if the mailbox
@@ -57,7 +60,7 @@ func (ds *DataStore) MailboxFor(emailAddress string) (*Mailbox, error) {
dir := HashMailboxName(name)
path := filepath.Join(ds.mailPath, dir)
if err := os.MkdirAll(path, 0770); err != nil {
rev.ERROR.Printf("Failed to create directory %v, %v", path, err)
Error("Failed to create directory %v, %v", path, err)
return nil, err
}
return &Mailbox{store: ds, name: name, dirName: dir, path: path}, nil
@@ -83,7 +86,7 @@ func (mb *Mailbox) GetMessages() ([]*Message, error) {
if err != nil {
return nil, err
}
rev.TRACE.Printf("Scanning %v files for %v", len(files), mb)
Trace("Scanning %v files for %v", len(files), mb)
messages := make([]*Message, 0, len(files))
for _, f := range files {
@@ -100,7 +103,7 @@ func (mb *Mailbox) GetMessages() ([]*Message, error) {
}
file.Close()
msg.mailbox = mb
rev.TRACE.Printf("Found: %v", msg)
Trace("Found: %v", msg)
messages = append(messages, msg)
}
}
@@ -121,7 +124,7 @@ func (mb *Mailbox) GetMessage(id string) (*Message, error) {
}
file.Close()
msg.mailbox = mb
rev.TRACE.Printf("Found: %v", msg)
Trace("Found: %v", msg)
return msg, nil
}
@@ -157,8 +160,8 @@ func (m *Message) gobPath() string {
}
func (m *Message) rawPath() string {
rev.TRACE.Println(m.mailbox.path)
rev.TRACE.Println(m.Id)
Trace(m.mailbox.path)
Trace(m.Id)
return filepath.Join(m.mailbox.path, m.Id+".raw")
}

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"github.com/jhillyerd/inbucket"
"github.com/jhillyerd/inbucket/smtpd"
"log"
"os"
)
@@ -26,15 +27,20 @@ func main() {
os.Exit(1)
}
err := inbucket.LoadConfig(flag.Arg(0))
configError(err)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse config: %v\n", err)
os.Exit(1)
}
log.Println("Logger test")
inbucket.Trace("trace test")
inbucket.Info("info test")
inbucket.Warn("warn test")
inbucket.Error("error test")
// Startup SMTP server
domain, err := inbucket.Config.String("smtp", "domain")
configError(err)
port, err := inbucket.Config.Int("smtp", "ip4.port")
configError(err)
server := smtpd.New(domain, port)
go server.Start()
server := smtpd.New()
server.Start()
}
func init() {
@@ -43,10 +49,3 @@ func init() {
flag.PrintDefaults()
}
}
func configError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", err)
os.Exit(1)
}
}

46
logging.go Normal file
View File

@@ -0,0 +1,46 @@
package inbucket
import (
"log"
)
type LogLevel int
const (
ERROR LogLevel = iota
WARN
INFO
TRACE
)
var MaxLogLevel LogLevel = TRACE
// Error logs a message to the 'standard' Logger (always)
func Error(msg string, args ...interface{}) {
msg = "[ERROR] " + msg
log.Printf(msg, args...)
}
// Warn logs a message to the 'standard' Logger if MaxLogLevel is >= WARN
func Warn(msg string, args ...interface{}) {
if MaxLogLevel >= WARN {
msg = "[WARN ] " + msg
log.Printf(msg, args...)
}
}
// Info logs a message to the 'standard' Logger if MaxLogLevel is >= INFO
func Info(msg string, args ...interface{}) {
if MaxLogLevel >= INFO {
msg = "[INFO ] " + msg
log.Printf(msg, args...)
}
}
// Trace logs a message to the 'standard' Logger if MaxLogLevel is >= TRACE
func Trace(msg string, args ...interface{}) {
if MaxLogLevel >= TRACE {
msg = "[TRACE] " + msg
log.Printf(msg, args...)
}
}

View File

@@ -87,7 +87,7 @@ func (ss *Session) String() string {
* 5. Goto 2
*/
func (s *Server) startSession(id int, conn net.Conn) {
s.info("Connection from %v, starting session <%v>", conn.RemoteAddr(), id)
inbucket.Info("Connection from %v, starting session <%v>", conn.RemoteAddr(), id)
defer conn.Close()
ss := NewSession(s, id, conn)
@@ -476,17 +476,17 @@ func (ss *Session) ooSeq(cmd string) {
// Session specific logging methods
func (ss *Session) trace(msg string, args ...interface{}) {
ss.server.trace("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
inbucket.Trace("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
}
func (ss *Session) info(msg string, args ...interface{}) {
ss.server.info("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
inbucket.Info("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
}
func (ss *Session) warn(msg string, args ...interface{}) {
ss.server.warn("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
inbucket.Warn("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
}
func (ss *Session) error(msg string, args ...interface{}) {
ss.server.error("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
inbucket.Error("%v<%v> %v", ss.remoteHost, ss.id, fmt.Sprintf(msg, args...))
}

View File

@@ -3,14 +3,12 @@ package smtpd
import (
"fmt"
"github.com/jhillyerd/inbucket"
"github.com/robfig/revel"
"net"
)
// Real server code starts here
type Server struct {
domain string
port int
maxRecips int
maxIdleSeconds int
maxMessageBytes int
@@ -18,39 +16,36 @@ type Server struct {
}
// Init a new Server object
func New(domain string, port int) *Server {
func New() *Server {
ds := inbucket.NewDataStore()
return &Server{domain: domain, port: port, maxRecips: 100, maxIdleSeconds: 300,
// TODO Make more of these configurable
return &Server{domain: inbucket.GetSmtpConfig().Domain, maxRecips: 100, maxIdleSeconds: 300,
dataStore: ds, maxMessageBytes: 2048000}
}
// Loggers
func (s *Server) trace(msg string, args ...interface{}) {
rev.TRACE.Printf(msg, args...)
}
func (s *Server) info(msg string, args ...interface{}) {
rev.INFO.Printf(msg, args...)
}
func (s *Server) warn(msg string, args ...interface{}) {
rev.WARN.Printf(msg, args...)
}
func (s *Server) error(msg string, args ...interface{}) {
rev.ERROR.Printf(msg, args...)
}
// Main listener loop
func (s *Server) Start() {
s.trace("Server Start() called")
ln, err := net.Listen("tcp", fmt.Sprintf(":%v", s.port))
cfg := inbucket.GetSmtpConfig()
addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%v:%v",
cfg.Ip4address, cfg.Ip4port))
if err != nil {
inbucket.Error("Failed to build tcp4 address: %v", err)
// TODO More graceful early-shutdown procedure
panic(err)
}
inbucket.Info("Listening on TCP4 %v", addr)
ln, err := net.ListenTCP("tcp4", addr)
if err != nil {
inbucket.Error("Failed to start tcp4 listener: %v", err)
// TODO More graceful early-shutdown procedure
panic(err)
}
for sid := 1; ; sid++ {
if conn, err := ln.Accept(); err != nil {
// TODO Implement a max error counter before shutdown?
// or maybe attempt to restart smtpd
panic(err)
} else {
go s.startSession(sid, conn)