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

Server now writes raw smtp data to datastore. Does not yet create gob files.

This commit is contained in:
James Hillyerd
2012-10-07 22:05:07 -07:00
parent 9fa93acf0e
commit 6e8e0a300c
4 changed files with 145 additions and 4 deletions

113
app/inbucket/datastore.go Normal file
View File

@@ -0,0 +1,113 @@
package inbucket
import (
"bufio"
"fmt"
"github.com/robfig/revel"
"os"
"path/filepath"
"errors"
"time"
)
var ErrNotWritable = errors.New("MailObject not writable")
// Global because we only want one regardless of the number of DataStore objects
var countChannel = make(chan int, 10)
func init() {
// Start generator
go countGenerator(countChannel)
}
// Populates the channel with numbers
func countGenerator(c chan int) {
for i := 0; true; i = (i + 1) % 10000 {
c <- i
}
}
type DataStore struct {
path string
mailPath string
}
func NewDataStore() *DataStore {
path, found := rev.Config.String("datastore.path")
if found {
mailPath := filepath.Join(path, "mail")
return &DataStore{path: path, mailPath: mailPath}
}
rev.ERROR.Printf("No value configured for datastore.path")
return nil
}
type MailObject struct {
store *DataStore
mailbox string
rawPath string
gobPath string
writable bool
writerFile *os.File
writer *bufio.Writer
}
func (ds *DataStore) NewMailObject(emailAddress string) *MailObject {
mailbox := ParseMailboxName(emailAddress)
maildir := HashMailboxName(mailbox)
fileBase := time.Now().Format("20060102T150405") + "-" + fmt.Sprintf("%04d", <-countChannel)
boxPath := filepath.Join(ds.mailPath, maildir)
if err := os.MkdirAll(boxPath, 0770); err != nil {
rev.ERROR.Printf("Failed to create directory %v, %v", boxPath, err)
return nil
}
pathBase := filepath.Join(boxPath, fileBase)
return &MailObject{store: ds, mailbox: mailbox, rawPath: pathBase + ".raw",
gobPath: pathBase + ".gob", writable: true}
}
func (m *MailObject) Mailbox() string {
return m.mailbox
}
func (m *MailObject) Append(data []byte) error {
// Prevent Appending to a pre-existing MailObject
if !m.writable {
return ErrNotWritable
}
// Open file for writing if we haven't yet
if m.writer == nil {
file, err := os.Create(m.rawPath)
if err != nil {
// Set writable false just in case something calls me a million times
m.writable = false
return err
}
m.writerFile = file
m.writer = bufio.NewWriter(file)
}
_, err := m.writer.Write(data)
return err
}
func (m *MailObject) Close() error {
// nil out the fields so they can't be used
writer := m.writer
writerFile := m.writerFile
m.writer = nil
m.writerFile = nil
if (writer != nil) {
if err := writer.Flush(); err != nil {
return err
}
}
if (writerFile != nil) {
if err := writerFile.Close(); err != nil {
return err
}
}
return nil
}

View File

@@ -7,6 +7,7 @@ import (
"net"
"strings"
"time"
"github.com/jhillyerd/inbucket/app/inbucket"
)
type State int
@@ -235,7 +236,16 @@ func (ss *Session) mailHandler(cmd string, arg string) {
// DATA
func (ss *Session) dataHandler() {
var msgSize uint64 = 0
msgSize := uint64(0)
// Get a MailObject for each recipient
mailObjects := make([]*inbucket.MailObject, ss.recipients.Len())
i := 0
for e := ss.recipients.Front(); e != nil; e = e.Next() {
mailObjects[i] = ss.server.dataStore.NewMailObject(e.Value.(string))
i++
}
ss.send("354 Start mail input; end with <CRLF>.<CRLF>")
for {
line, err := ss.readLine()
@@ -251,6 +261,9 @@ func (ss *Session) dataHandler() {
}
if line == ".\r\n" || line == ".\n" {
// Mail data complete
for _, mo := range mailObjects {
mo.Close()
}
ss.send("250 Mail accepted for delivery")
ss.info("Message size %v bytes", msgSize)
ss.enterState(READY)
@@ -260,7 +273,16 @@ func (ss *Session) dataHandler() {
line = line[1:]
}
msgSize += uint64(len(line))
// TODO: Add variable line to something!
// Append to message objects
for _, mo := range mailObjects {
if err := mo.Append([]byte(line)); err != nil {
ss.error("Failed to append to mailbox %v: %v", mo.Mailbox(), err)
ss.send("554 Something went wrong")
ss.enterState(READY)
// TODO: Should really cleanup the crap on filesystem...
return
}
}
}
}

View File

@@ -2,8 +2,9 @@ package smtpd
import (
"fmt"
"net"
"github.com/jhillyerd/inbucket/app/inbucket"
"github.com/robfig/revel"
"net"
)
// Real server code starts here
@@ -12,11 +13,14 @@ type Server struct {
port int
maxRecips int
maxIdleSeconds int
dataStore *inbucket.DataStore
}
// Init a new Server object
func New(domain string, port int) *Server {
return &Server{domain: domain, port: port, maxRecips: 100, maxIdleSeconds: 60}
ds := inbucket.NewDataStore()
return &Server{domain: domain, port: port, maxRecips: 100, maxIdleSeconds: 60,
dataStore: ds}
}
// Loggers

View File

@@ -8,6 +8,7 @@ results.pretty=true
server.watcher=true
smtpd.domain=skynet
smtpd.port=2500
datastore.path=/tmp/inbucket
log.trace.output = off
log.info.output = stderr
@@ -19,6 +20,7 @@ results.pretty=false
server.watcher=false
smtpd.domain=skynet
smtpd.port=2500
datastore.path=/tmp/inbucket
log.trace.output = off
log.info.output = off