diff --git a/config/config.go b/config/config.go index 96a368a..82f1866 100644 --- a/config/config.go +++ b/config/config.go @@ -15,6 +15,7 @@ type SmtpConfig struct { Ip4address net.IP Ip4port int Domain string + DomainNoStore string MaxRecipients int MaxIdleSeconds int MaxMessageBytes int @@ -177,6 +178,15 @@ func parseSmtpConfig() error { } smtpConfig.Domain = str + option = "domain.nostore" + if Config.HasOption(section, option) { + str, err = Config.String(section, option) + if err != nil { + return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err) + } + smtpConfig.DomainNoStore = str + } + option = "max.recipients" smtpConfig.MaxRecipients, err = Config.Int(section, option) if err != nil { diff --git a/etc/devel.conf b/etc/devel.conf index ecb9b14..5d888b6 100644 --- a/etc/devel.conf +++ b/etc/devel.conf @@ -25,6 +25,10 @@ ip4.port=2500 # used in SMTP greeting domain=inbucket.local +# optional: mail sent to accounts at this domain will not be stored, +# for mixed use (content and load testing) +domain.nostore=bitbucket.local + # Maximum number of RCPT TO: addresses we allow from clients, the SMTP # RFC recommends this be at least 100. max.recipients=100 diff --git a/etc/inbucket.conf b/etc/inbucket.conf index ee6e57c..f8ca8b7 100644 --- a/etc/inbucket.conf +++ b/etc/inbucket.conf @@ -25,6 +25,10 @@ ip4.port=2500 # used in SMTP greeting domain=inbucket.local +# optional: mail sent to accounts at this domain will not be stored, +# for mixed use (content and load testing) +#domain.nostore=bitbucket.local + # Maximum number of RCPT TO: addresses we allow from clients, the SMTP # RFC recommends this be at least 100. max.recipients=100 diff --git a/smtpd/handler.go b/smtpd/handler.go index 4c1b991..e2a5768 100644 --- a/smtpd/handler.go +++ b/smtpd/handler.go @@ -300,15 +300,20 @@ func (ss *Session) dataHandler() { i := 0 for e := ss.recipients.Front(); e != nil; e = e.Next() { recip := e.Value.(string) - mb, err := ss.server.dataStore.MailboxFor(recip) - if err != nil { - ss.error("Failed to open mailbox for %v", recip) - ss.send(fmt.Sprintf("451 Failed to open mailbox for %v", recip)) - ss.reset() - return + if !strings.HasSuffix(strings.ToLower(recip), "@" + ss.server.domainNoStore) { + // Not our "no store" domain, so store the message + mb, err := ss.server.dataStore.MailboxFor(recip) + if err != nil { + ss.error("Failed to open mailbox for %v", recip) + ss.send(fmt.Sprintf("451 Failed to open mailbox for %v", recip)) + ss.reset() + return + } + mailboxes[i] = mb + messages[i] = mb.NewMessage() + } else { + log.Trace("Not storing message for '%v'", recip) } - mailboxes[i] = mb - messages[i] = mb.NewMessage() i++ } } @@ -333,8 +338,10 @@ func (ss *Session) dataHandler() { // Mail data complete if ss.server.storeMessages { for _, m := range messages { - m.Close() - expDeliveredTotal.Add(1) + if m != nil { + m.Close() + expDeliveredTotal.Add(1) + } } } else { expDeliveredTotal.Add(1) @@ -360,12 +367,14 @@ func (ss *Session) dataHandler() { // Append to message objects if ss.server.storeMessages { for i, m := range messages { - if err := m.Append(line); err != nil { - ss.error("Failed to append to mailbox %v: %v", mailboxes[i], err) - ss.send("554 Something went wrong") - ss.reset() - // TODO: Should really cleanup the crap on filesystem... - return + if m != nil { + if err := m.Append(line); err != nil { + ss.error("Failed to append to mailbox %v: %v", mailboxes[i], err) + ss.send("554 Something went wrong") + ss.reset() + // TODO: Should really cleanup the crap on filesystem... + return + } } } } diff --git a/smtpd/listener.go b/smtpd/listener.go index f6d8bc7..51c265f 100644 --- a/smtpd/listener.go +++ b/smtpd/listener.go @@ -7,12 +7,14 @@ import ( "github.com/jhillyerd/inbucket/config" "github.com/jhillyerd/inbucket/log" "net" + "strings" "time" ) // Real server code starts here type Server struct { domain string + domainNoStore string maxRecips int maxIdleSeconds int maxMessageBytes int @@ -45,7 +47,7 @@ func New() *Server { cfg := config.GetSmtpConfig() return &Server{dataStore: ds, domain: cfg.Domain, maxRecips: cfg.MaxRecipients, maxIdleSeconds: cfg.MaxIdleSeconds, maxMessageBytes: cfg.MaxMessageBytes, - storeMessages: cfg.StoreMessages} + storeMessages: cfg.StoreMessages, domainNoStore: strings.ToLower(cfg.DomainNoStore)} } // Main listener loop @@ -69,6 +71,8 @@ func (s *Server) Start() { if !s.storeMessages { log.Info("Load test mode active, messages will not be stored") + } else if s.domainNoStore != "" { + log.Info("Messages sent to domain '%v' will be discarded", s.domainNoStore) } // Start retention scanner